summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig4
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/acpi/ec.c10
-rw-r--r--drivers/acpi/processor_driver.c1
-rw-r--r--drivers/acpi/video_detect.c2
-rw-r--r--drivers/amba/bus.c105
-rw-r--r--drivers/ata/ahci.c10
-rw-r--r--drivers/ata/ahci.h6
-rw-r--r--drivers/ata/ahci_platform.c11
-rw-r--r--drivers/ata/ata_piix.c8
-rw-r--r--drivers/ata/libahci.c5
-rw-r--r--drivers/ata/libata-core.c34
-rw-r--r--drivers/ata/libata-eh.c1
-rw-r--r--drivers/ata/libata-scsi.c13
-rw-r--r--drivers/ata/libata.h2
-rw-r--r--drivers/ata/pata_arasan_cf.c12
-rw-r--r--drivers/ata/pata_cmd64x.c147
-rw-r--r--drivers/ata/pata_legacy.c3
-rw-r--r--drivers/ata/pata_mpc52xx.c44
-rw-r--r--drivers/ata/sata_fsl.c111
-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/base/power/clock_ops.c1
-rw-r--r--drivers/base/power/common.c1
-rw-r--r--drivers/base/power/opp.c1
-rw-r--r--drivers/base/regmap/internal.h14
-rw-r--r--drivers/base/regmap/regcache-lzo.c17
-rw-r--r--drivers/base/regmap/regcache-rbtree.c28
-rw-r--r--drivers/base/regmap/regcache.c103
-rw-r--r--drivers/base/regmap/regmap-debugfs.c85
-rw-r--r--drivers/base/regmap/regmap-i2c.c17
-rw-r--r--drivers/base/regmap/regmap-irq.c1
-rw-r--r--drivers/base/regmap/regmap-spi.c17
-rw-r--r--drivers/base/regmap/regmap.c307
-rw-r--r--drivers/block/drbd/drbd_nl.c2
-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/block/xen-blkfront.c3
-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/agp/intel-agp.c1
-rw-r--r--drivers/char/agp/intel-gtt.c10
-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/hw_random/nomadik-rng.c13
-rw-r--r--drivers/char/hw_random/omap-rng.c2
-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.c1
-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/tcb_clksrc.c90
-rw-r--r--drivers/cpufreq/Kconfig.arm42
-rw-r--r--drivers/cpufreq/Makefile5
-rw-r--r--drivers/cpufreq/cpufreq.c24
-rw-r--r--drivers/cpufreq/cpufreq_ondemand.c58
-rw-r--r--drivers/cpufreq/db8500-cpufreq.c13
-rw-r--r--drivers/cpufreq/exynos-cpufreq.c6
-rw-r--r--drivers/cpufreq/exynos4210-cpufreq.c70
-rw-r--r--drivers/cpufreq/exynos4x12-cpufreq.c536
-rw-r--r--drivers/cpufreq/exynos5250-cpufreq.c347
-rw-r--r--drivers/cpufreq/omap-cpufreq.c73
-rw-r--r--drivers/cpufreq/powernow-k7.c1
-rw-r--r--drivers/cpufreq/s3c2416-cpufreq.c542
-rw-r--r--drivers/cpufreq/s3c64xx-cpufreq.c7
-rw-r--r--drivers/crypto/Kconfig9
-rw-r--r--drivers/devfreq/exynos4_bus.c230
-rw-r--r--drivers/dma/Kconfig9
-rw-r--r--drivers/dma/Makefile1
-rw-r--r--drivers/dma/imx-dma.c1
-rw-r--r--drivers/dma/imx-sdma.c1
-rw-r--r--drivers/dma/pl330.c13
-rw-r--r--drivers/dma/sa11x0-dma.c1109
-rw-r--r--drivers/edac/Kconfig2
-rw-r--r--drivers/edac/amd64_edac.c50
-rw-r--r--drivers/edac/amd76x_edac.c2
-rw-r--r--drivers/edac/e752x_edac.c2
-rw-r--r--drivers/edac/e7xxx_edac.c2
-rw-r--r--drivers/edac/edac_mc.c6
-rw-r--r--drivers/edac/edac_mc_sysfs.c4
-rw-r--r--drivers/edac/edac_stub.c1
-rw-r--r--drivers/edac/i3000_edac.c2
-rw-r--r--drivers/edac/i3200_edac.c2
-rw-r--r--drivers/edac/i5000_edac.c2
-rw-r--r--drivers/edac/i5100_edac.c15
-rw-r--r--drivers/edac/i5400_edac.c56
-rw-r--r--drivers/edac/i7300_edac.c2
-rw-r--r--drivers/edac/i7core_edac.c2
-rw-r--r--drivers/edac/i82443bxgx_edac.c2
-rw-r--r--drivers/edac/i82860_edac.c2
-rw-r--r--drivers/edac/i82875p_edac.c2
-rw-r--r--drivers/edac/i82975x_edac.c2
-rw-r--r--drivers/edac/mce_amd.c208
-rw-r--r--drivers/edac/mce_amd.h13
-rw-r--r--drivers/edac/mce_amd_inj.c1
-rw-r--r--drivers/edac/ppc4xx_edac.c4
-rw-r--r--drivers/edac/r82600_edac.c2
-rw-r--r--drivers/edac/sb_edac.c54
-rw-r--r--drivers/edac/x38_edac.c2
-rw-r--r--drivers/firewire/Kconfig5
-rw-r--r--drivers/firewire/core-card.c34
-rw-r--r--drivers/firewire/core-cdev.c25
-rw-r--r--drivers/firewire/core-device.c63
-rw-r--r--drivers/firewire/core-iso.c6
-rw-r--r--drivers/firewire/core-topology.c18
-rw-r--r--drivers/firewire/core-transaction.c44
-rw-r--r--drivers/firewire/core.h21
-rw-r--r--drivers/firewire/net.c43
-rw-r--r--drivers/firewire/nosy.c4
-rw-r--r--drivers/firewire/ohci.c351
-rw-r--r--drivers/firewire/sbp2.c131
-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.c15
-rw-r--r--drivers/gpio/gpio-lpc32xx.c19
-rw-r--r--drivers/gpio/gpio-mc9s08dz60.c161
-rw-r--r--drivers/gpio/gpio-omap.c1313
-rw-r--r--drivers/gpio/gpio-pl061.c7
-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.c63
-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/Kconfig20
-rw-r--r--drivers/gpu/drm/Makefile7
-rw-r--r--drivers/gpu/drm/drm_crtc.c448
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c22
-rw-r--r--drivers/gpu/drm/drm_drv.c16
-rw-r--r--drivers/gpu/drm/drm_edid.c12
-rw-r--r--drivers/gpu/drm/drm_edid_load.c250
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c88
-rw-r--r--drivers/gpu/drm/drm_fops.c8
-rw-r--r--drivers/gpu/drm/drm_gem.c4
-rw-r--r--drivers/gpu/drm/drm_ioctl.c8
-rw-r--r--drivers/gpu/drm/drm_irq.c4
-rw-r--r--drivers/gpu/drm/drm_memory.c19
-rw-r--r--drivers/gpu/drm/drm_modes.c30
-rw-r--r--drivers/gpu/drm/drm_pci.c2
-rw-r--r--drivers/gpu/drm/drm_platform.c12
-rw-r--r--drivers/gpu/drm/drm_stub.c26
-rw-r--r--drivers/gpu/drm/drm_sysfs.c7
-rw-r--r--drivers/gpu/drm/drm_usb.c2
-rw-r--r--drivers/gpu/drm/drm_vm.c5
-rw-r--r--drivers/gpu/drm/exynos/Kconfig14
-rw-r--r--drivers/gpu/drm/exynos/Makefile11
-rw-r--r--drivers/gpu/drm/exynos/exynos_ddc.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_buf.c191
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_buf.h22
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c35
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c140
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c12
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c94
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h38
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c24
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c6
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c90
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c20
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c364
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h29
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.c115
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.h5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c8
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c676
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.h36
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c1437
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.h50
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c57
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.h92
-rw-r--r--drivers/gpu/drm/exynos/regs-hdmi.h488
-rw-r--r--drivers/gpu/drm/gma500/Kconfig10
-rw-r--r--drivers/gpu/drm/gma500/Makefile10
-rw-r--r--drivers/gpu/drm/gma500/cdv_device.c169
-rw-r--r--drivers/gpu/drm/gma500/cdv_device.h2
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_crt.c1
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_display.c91
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_hdmi.c1
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_lvds.c16
-rw-r--r--drivers/gpu/drm/gma500/framebuffer.c64
-rw-r--r--drivers/gpu/drm/gma500/gem_glue.c1
-rw-r--r--drivers/gpu/drm/gma500/gtt.c4
-rw-r--r--drivers/gpu/drm/gma500/intel_gmbus.c2
-rw-r--r--drivers/gpu/drm/gma500/mdfld_device.c691
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_dpi.c1017
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_dpi.h79
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_output.c621
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_output.h378
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c694
-rw-r--r--drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h92
-rw-r--r--drivers/gpu/drm/gma500/mdfld_intel_display.c1180
-rw-r--r--drivers/gpu/drm/gma500/mdfld_output.c74
-rw-r--r--drivers/gpu/drm/gma500/mdfld_output.h77
-rw-r--r--drivers/gpu/drm/gma500/mdfld_tmd_vid.c201
-rw-r--r--drivers/gpu/drm/gma500/mdfld_tpo_vid.c124
-rw-r--r--drivers/gpu/drm/gma500/mmu.c13
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_crtc.c18
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_device.c211
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi.c401
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c6
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_lvds.c5
-rw-r--r--drivers/gpu/drm/gma500/power.c17
-rw-r--r--drivers/gpu/drm/gma500/psb_device.c34
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.c65
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.h435
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_display.c50
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_lvds.c12
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_reg.h9
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_sdvo.c30
-rw-r--r--drivers/gpu/drm/gma500/psb_irq.c62
-rw-r--r--drivers/gpu/drm/gma500/psb_irq.h2
-rw-r--r--drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c829
-rw-r--r--drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h38
-rw-r--r--drivers/gpu/drm/i2c/ch7006_drv.c5
-rw-r--r--drivers/gpu/drm/i810/i810_dma.c3
-rw-r--r--drivers/gpu/drm/i915/Makefile2
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c324
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c66
-rw-r--r--drivers/gpu/drm/i915/i915_drv.c56
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h164
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c528
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c21
-rw-r--r--drivers/gpu/drm/i915/i915_gem_execbuffer.c205
-rw-r--r--drivers/gpu/drm/i915/i915_gem_gtt.c275
-rw-r--r--drivers/gpu/drm/i915/i915_gem_tiling.c23
-rw-r--r--drivers/gpu/drm/i915/i915_irq.c197
-rw-r--r--drivers/gpu/drm/i915/i915_mem.c387
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h132
-rw-r--r--drivers/gpu/drm/i915/intel_acpi.c2
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c4
-rw-r--r--drivers/gpu/drm/i915/intel_crt.c5
-rw-r--r--drivers/gpu/drm/i915/intel_display.c388
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c23
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h3
-rw-r--r--drivers/gpu/drm/i915/intel_dvo.c1
-rw-r--r--drivers/gpu/drm/i915/intel_fb.c16
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c23
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c210
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c31
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c34
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c18
-rw-r--r--drivers/gpu/drm/i915/intel_panel.c6
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c210
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.h35
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c43
-rw-r--r--drivers/gpu/drm/i915/intel_sprite.c4
-rw-r--r--drivers/gpu/drm/i915/intel_tv.c2
-rw-r--r--drivers/gpu/drm/mga/mga_dma.c2
-rw-r--r--drivers/gpu/drm/nouveau/Makefile3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.c278
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bios.h8
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_bo.c11
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_channel.c34
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c45
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_crtc.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c51
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c61
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.h4
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dp.c325
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.c18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h190
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_encoder.h18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fbcon.c6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_fence.c35
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_gem.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c14
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c809
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mxm.c39
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_perf.c409
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.c235
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_pm.h22
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c378
-rw-r--r--drivers/gpu/drm/nouveau/nv04_fb.c34
-rw-r--r--drivers/gpu/drm/nouveau/nv10_fb.c126
-rw-r--r--drivers/gpu/drm/nouveau/nv20_fb.c148
-rw-r--r--drivers/gpu/drm/nouveau/nv40_fb.c45
-rw-r--r--drivers/gpu/drm/nouveau/nv50_crtc.c42
-rw-r--r--drivers/gpu/drm/nouveau/nv50_dac.c7
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.c39
-rw-r--r--drivers/gpu/drm/nouveau/nv50_display.h2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_evo.h3
-rw-r--r--drivers/gpu/drm/nouveau/nv50_pm.c397
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c213
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vm.c29
-rw-r--r--drivers/gpu/drm/nouveau/nv50_vram.c17
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c36
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_graph.c9
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_pm.c2
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_vm.c4
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_vram.c33
-rw-r--r--drivers/gpu/drm/nouveau/nvd0_display.c359
-rw-r--r--drivers/gpu/drm/r128/r128_drv.c1
-rw-r--r--drivers/gpu/drm/radeon/Makefile2
-rw-r--r--drivers/gpu/drm/radeon/ObjectID.h5
-rw-r--r--drivers/gpu/drm/radeon/atombios.h1109
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c128
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c12
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c75
-rw-r--r--drivers/gpu/drm/radeon/atombios_i2c.c139
-rw-r--r--drivers/gpu/drm/radeon/cayman_blit_shaders.c1
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c81
-rw-r--r--drivers/gpu/drm/radeon/evergreen_blit_kms.c14
-rw-r--r--drivers/gpu/drm/radeon/evergreen_blit_shaders.c1
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c1274
-rw-r--r--drivers/gpu/drm/radeon/evergreen_reg.h1
-rw-r--r--drivers/gpu/drm/radeon/evergreend.h385
-rw-r--r--drivers/gpu/drm/radeon/ni.c147
-rw-r--r--drivers/gpu/drm/radeon/nid.h1
-rw-r--r--drivers/gpu/drm/radeon/r100.c109
-rw-r--r--drivers/gpu/drm/radeon/r200.c29
-rw-r--r--drivers/gpu/drm/radeon/r300.c9
-rw-r--r--drivers/gpu/drm/radeon/r420.c2
-rw-r--r--drivers/gpu/drm/radeon/r500_reg.h2
-rw-r--r--drivers/gpu/drm/radeon/r520.c4
-rw-r--r--drivers/gpu/drm/radeon/r600.c51
-rw-r--r--drivers/gpu/drm/radeon/r600_blit_kms.c15
-rw-r--r--drivers/gpu/drm/radeon/r600_blit_shaders.c1
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c788
-rw-r--r--drivers/gpu/drm/radeon/r600d.h32
-rw-r--r--drivers/gpu/drm/radeon/radeon.h296
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.c1783
-rw-r--r--drivers/gpu/drm/radeon/radeon_asic.h49
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c92
-rw-r--r--drivers/gpu/drm/radeon/radeon_benchmark.c24
-rw-r--r--drivers/gpu/drm/radeon/radeon_blit_common.h44
-rw-r--r--drivers/gpu/drm/radeon/radeon_clocks.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c33
-rw-r--r--drivers/gpu/drm/radeon/radeon_cp.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c100
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c92
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_family.h4
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c11
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c26
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c32
-rw-r--r--drivers/gpu/drm/radeon/radeon_kms.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c74
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_pm.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_reg.h3
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c9
-rw-r--r--drivers/gpu/drm/radeon/radeon_ttm.c15
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/cayman25
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/evergreen25
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/r60021
-rw-r--r--drivers/gpu/drm/radeon/rs400.c2
-rw-r--r--drivers/gpu/drm/radeon/rs600.c23
-rw-r--r--drivers/gpu/drm/radeon/rs690.c4
-rw-r--r--drivers/gpu/drm/radeon/rv515.c5
-rw-r--r--drivers/gpu/drm/radeon/rv770.c4
-rw-r--r--drivers/gpu/drm/radeon/si.c4128
-rw-r--r--drivers/gpu/drm/radeon/si_blit_shaders.c253
-rw-r--r--drivers/gpu/drm/radeon/si_blit_shaders.h32
-rw-r--r--drivers/gpu/drm/radeon/si_reg.h33
-rw-r--r--drivers/gpu/drm/radeon/sid.h886
-rw-r--r--drivers/gpu/drm/savage/savage_state.c5
-rw-r--r--drivers/gpu/drm/sis/sis_drv.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_agp_backend.c4
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c72
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_memory.c12
-rw-r--r--drivers/gpu/drm/ttm/ttm_object.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc.c55
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc_dma.c60
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c8
-rw-r--r--drivers/gpu/drm/udl/Kconfig12
-rw-r--r--drivers/gpu/drm/udl/Makefile6
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c141
-rw-r--r--drivers/gpu/drm/udl/udl_drv.c99
-rw-r--r--drivers/gpu/drm/udl/udl_drv.h141
-rw-r--r--drivers/gpu/drm/udl/udl_encoder.c80
-rw-r--r--drivers/gpu/drm/udl/udl_fb.c611
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c227
-rw-r--r--drivers/gpu/drm/udl/udl_main.c338
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c414
-rw-r--r--drivers/gpu/drm/udl/udl_transfer.c253
-rw-r--r--drivers/gpu/drm/via/via_map.c2
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.c55
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_drv.h10
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c15
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fb.c22
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c252
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.h9
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.c91
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_kms.h8
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c4
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c38
-rw-r--r--drivers/hid/hid-core.c20
-rw-r--r--drivers/hid/hid-ids.h12
-rw-r--r--drivers/hwmon/Kconfig28
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/fam15h_power.c10
-rw-r--r--drivers/hwmon/fschmd.c4
-rw-r--r--drivers/hwmon/lm63.c586
-rw-r--r--drivers/hwmon/lm90.c98
-rw-r--r--drivers/hwmon/mc13783-adc.c105
-rw-r--r--drivers/hwmon/mcp3021.c171
-rw-r--r--drivers/hwmon/w83793.c4
-rw-r--r--drivers/hwmon/w83795.c132
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c8
-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/Kconfig45
-rw-r--r--drivers/i2c/busses/Makefile4
-rw-r--r--drivers/i2c/busses/i2c-acorn.c1
-rw-r--r--drivers/i2c/busses/i2c-designware-platdrv.c2
-rw-r--r--drivers/i2c/busses/i2c-eg20t.c44
-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-imx.c9
-rw-r--r--drivers/i2c/busses/i2c-isch.c10
-rw-r--r--drivers/i2c/busses/i2c-mpc.c63
-rw-r--r--drivers/i2c/busses/i2c-pxa.c95
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c14
-rw-r--r--drivers/i2c/busses/i2c-sirf.c459
-rw-r--r--drivers/i2c/busses/i2c-tegra.c1
-rw-r--r--drivers/i2c/busses/i2c-versatile.c10
-rw-r--r--drivers/i2c/busses/i2c-xlr.c278
-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/infiniband/ulp/srpt/ib_srpt.c64
-rw-r--r--drivers/input/Kconfig4
-rw-r--r--drivers/input/Makefile1
-rw-r--r--drivers/input/evdev.c52
-rw-r--r--drivers/input/input.c2
-rw-r--r--drivers/input/joydev.c1
-rw-r--r--drivers/input/joystick/amijoy.c1
-rw-r--r--drivers/input/joystick/as5011.c12
-rw-r--r--drivers/input/keyboard/Kconfig2
-rw-r--r--drivers/input/keyboard/adp5588-keys.c12
-rw-r--r--drivers/input/keyboard/adp5589-keys.c12
-rw-r--r--drivers/input/keyboard/jornada720_kbd.c1
-rw-r--r--drivers/input/keyboard/lm8323.c12
-rw-r--r--drivers/input/keyboard/max7359_keypad.c12
-rw-r--r--drivers/input/keyboard/mcs_touchkey.c13
-rw-r--r--drivers/input/keyboard/mpr121_touchkey.c12
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c17
-rw-r--r--drivers/input/keyboard/omap4-keypad.c2
-rw-r--r--drivers/input/keyboard/qt1070.c12
-rw-r--r--drivers/input/keyboard/qt2160.c12
-rw-r--r--drivers/input/keyboard/samsung-keypad.c6
-rw-r--r--drivers/input/keyboard/spear-keyboard.c16
-rw-r--r--drivers/input/keyboard/tegra-kbc.c72
-rw-r--r--drivers/input/misc/88pm860x_onkey.c26
-rw-r--r--drivers/input/misc/Kconfig26
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/ad714x-i2c.c12
-rw-r--r--drivers/input/misc/ad714x-spi.c12
-rw-r--r--drivers/input/misc/adxl34x-i2c.c12
-rw-r--r--drivers/input/misc/adxl34x-spi.c12
-rw-r--r--drivers/input/misc/bma150.c13
-rw-r--r--drivers/input/misc/cma3000_d0x_i2c.c13
-rw-r--r--drivers/input/misc/da9052_onkey.c169
-rw-r--r--drivers/input/misc/gp2ap002a00f.c13
-rw-r--r--drivers/input/misc/kxtj9.c34
-rw-r--r--drivers/input/misc/max8925_onkey.c115
-rw-r--r--drivers/input/misc/max8997_haptic.c407
-rw-r--r--drivers/input/misc/mma8450.c12
-rw-r--r--drivers/input/misc/mpu3050.c12
-rw-r--r--drivers/input/misc/pcf8574_keypad.c12
-rw-r--r--drivers/input/misc/twl4030-vibra.c2
-rw-r--r--drivers/input/mouse/Kconfig17
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/amimouse.c1
-rw-r--r--drivers/input/mouse/atarimouse.c1
-rw-r--r--drivers/input/mouse/bcm5974.c1
-rw-r--r--drivers/input/mouse/hgpk.c9
-rw-r--r--drivers/input/mouse/psmouse-base.c15
-rw-r--r--drivers/input/mouse/psmouse.h2
-rw-r--r--drivers/input/mouse/sentelic.c12
-rw-r--r--drivers/input/mouse/synaptics_i2c.c13
-rw-r--r--drivers/input/mouse/synaptics_usb.c557
-rw-r--r--drivers/input/of_keymap.c87
-rw-r--r--drivers/input/serio/altera_ps2.c4
-rw-r--r--drivers/input/serio/ambakmi.c13
-rw-r--r--drivers/input/serio/ams_delta_serio.c54
-rw-r--r--drivers/input/serio/at32psif.c22
-rw-r--r--drivers/input/serio/hp_sdc.c1
-rw-r--r--drivers/input/serio/maceps2.c1
-rw-r--r--drivers/input/serio/q40kbd.c139
-rw-r--r--drivers/input/serio/rpckbd.c45
-rw-r--r--drivers/input/serio/sa1111ps2.c60
-rw-r--r--drivers/input/tablet/wacom_sys.c10
-rw-r--r--drivers/input/tablet/wacom_wac.c28
-rw-r--r--drivers/input/tablet/wacom_wac.h2
-rw-r--r--drivers/input/touchscreen/Kconfig70
-rw-r--r--drivers/input/touchscreen/Makefile7
-rw-r--r--drivers/input/touchscreen/ad7877.c12
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c12
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c12
-rw-r--r--drivers/input/touchscreen/ads7846.c12
-rw-r--r--drivers/input/touchscreen/atmel-wm97xx.c20
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c13
-rw-r--r--drivers/input/touchscreen/auo-pixcir-ts.c12
-rw-r--r--drivers/input/touchscreen/bu21013_ts.c25
-rw-r--r--drivers/input/touchscreen/cy8ctmg110_ts.c13
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c625
-rw-r--r--drivers/input/touchscreen/cyttsp_core.h149
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c.c136
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c200
-rw-r--r--drivers/input/touchscreen/eeti_ts.c14
-rw-r--r--drivers/input/touchscreen/egalax_ts.c13
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c2
-rw-r--r--drivers/input/touchscreen/ili210x.c360
-rw-r--r--drivers/input/touchscreen/jornada720_ts.c1
-rw-r--r--drivers/input/touchscreen/max11801_ts.c13
-rw-r--r--drivers/input/touchscreen/mc13783_ts.c11
-rw-r--r--drivers/input/touchscreen/mcs5000_ts.c13
-rw-r--r--drivers/input/touchscreen/migor_ts.c13
-rw-r--r--drivers/input/touchscreen/pixcir_i2c_ts.c12
-rw-r--r--drivers/input/touchscreen/st1232.c12
-rw-r--r--drivers/input/touchscreen/ti_tscadc.c486
-rw-r--r--drivers/input/touchscreen/tsc2005.c12
-rw-r--r--drivers/input/touchscreen/tsc2007.c13
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c63
-rw-r--r--drivers/iommu/Kconfig20
-rw-r--r--drivers/iommu/Makefile2
-rw-r--r--drivers/iommu/amd_iommu_init.c187
-rw-r--r--drivers/iommu/amd_iommu_v2.c14
-rw-r--r--drivers/iommu/tegra-gart.c451
-rw-r--r--drivers/iommu/tegra-smmu.c1034
-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/Kconfig19
-rw-r--r--drivers/leds/Makefile2
-rw-r--r--drivers/leds/led-class.c70
-rw-r--r--drivers/leds/led-core.c70
-rw-r--r--drivers/leds/leds-88pm860x.c23
-rw-r--r--drivers/leds/leds-ams-delta.c126
-rw-r--r--drivers/leds/leds-gpio.c3
-rw-r--r--drivers/leds/leds-lm3530.c135
-rw-r--r--drivers/leds/leds-lp5521.c147
-rw-r--r--drivers/leds/leds-lp5523.c6
-rw-r--r--drivers/leds/leds-pca9633.c193
-rw-r--r--drivers/leds/leds-tca6507.c9
-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/bitmap.c152
-rw-r--r--drivers/md/bitmap.h22
-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.c69
-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/faulty.c2
-rw-r--r--drivers/md/linear.c32
-rw-r--r--drivers/md/md.c140
-rw-r--r--drivers/md/md.h13
-rw-r--r--drivers/md/multipath.c2
-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/md/raid0.c164
-rw-r--r--drivers/md/raid0.h11
-rw-r--r--drivers/md/raid1.c98
-rw-r--r--drivers/md/raid10.c187
-rw-r--r--drivers/md/raid5.c25
-rw-r--r--drivers/media/common/tuners/Makefile4
-rw-r--r--drivers/media/common/tuners/max2165.c9
-rw-r--r--drivers/media/common/tuners/mt2063.c4
-rw-r--r--drivers/media/common/tuners/mt2063.h4
-rw-r--r--drivers/media/common/tuners/tuner-types.c4
-rw-r--r--drivers/media/common/tuners/xc5000.c47
-rw-r--r--drivers/media/common/tuners/xc5000.h5
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge-core.c1
-rw-r--r--drivers/media/dvb/ddbridge/ddbridge.h2
-rw-r--r--drivers/media/dvb/dvb-core/dmxdev.c1
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c2
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig19
-rw-r--r--drivers/media/dvb/dvb-usb/Makefile14
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.c49
-rw-r--r--drivers/media/dvb/dvb-usb/af9015.h2
-rw-r--r--drivers/media/dvb/dvb-usb/anysee.c38
-rw-r--r--drivers/media/dvb/dvb-usb/az6007.c957
-rw-r--r--drivers/media/dvb/dvb-usb/dib0700_core.c10
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h8
-rw-r--r--drivers/media/dvb/dvb-usb/it913x.c170
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.c285
-rw-r--r--drivers/media/dvb/dvb-usb/lmedm04.h1
-rw-r--r--drivers/media/dvb/dvb-usb/mxl111sf.c6
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.c982
-rw-r--r--drivers/media/dvb/dvb-usb/rtl28xxu.h264
-rw-r--r--drivers/media/dvb/firewire/firedtv-fw.c1
-rw-r--r--drivers/media/dvb/frontends/Kconfig15
-rw-r--r--drivers/media/dvb/frontends/Makefile6
-rw-r--r--drivers/media/dvb/frontends/au8522_decoder.c13
-rw-r--r--drivers/media/dvb/frontends/au8522_dig.c10
-rw-r--r--drivers/media/dvb/frontends/cx22702.c22
-rw-r--r--drivers/media/dvb/frontends/dib0090.c2
-rw-r--r--drivers/media/dvb/frontends/dib9000.c121
-rw-r--r--drivers/media/dvb/frontends/drxd_hard.c6
-rw-r--r--drivers/media/dvb/frontends/drxk.h23
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.c46
-rw-r--r--drivers/media/dvb/frontends/drxk_hard.h1
-rw-r--r--drivers/media/dvb/frontends/it913x-fe-priv.h5
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.c91
-rw-r--r--drivers/media/dvb/frontends/it913x-fe.h4
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.c6
-rw-r--r--drivers/media/dvb/frontends/m88rs2000.c904
-rw-r--r--drivers/media/dvb/frontends/m88rs2000.h66
-rw-r--r--drivers/media/dvb/frontends/rtl2830.c562
-rw-r--r--drivers/media/dvb/frontends/rtl2830.h97
-rw-r--r--drivers/media/dvb/frontends/rtl2830_priv.h57
-rw-r--r--drivers/media/dvb/frontends/stb0899_drv.c12
-rw-r--r--drivers/media/dvb/frontends/stv0288.c2
-rw-r--r--drivers/media/dvb/frontends/tda10071.c2
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c1
-rw-r--r--drivers/media/dvb/pt1/pt1.c93
-rw-r--r--drivers/media/dvb/ttpci/av7110.c1
-rw-r--r--drivers/media/media-devnode.c3
-rw-r--r--drivers/media/radio/Kconfig125
-rw-r--r--drivers/media/radio/Makefile2
-rw-r--r--drivers/media/radio/radio-aimslab.c440
-rw-r--r--drivers/media/radio/radio-aztech.c372
-rw-r--r--drivers/media/radio/radio-gemtek.c494
-rw-r--r--drivers/media/radio/radio-isa.c340
-rw-r--r--drivers/media/radio/radio-isa.h105
-rw-r--r--drivers/media/radio/radio-keene.c427
-rw-r--r--drivers/media/radio/radio-maxiradio.c379
-rw-r--r--drivers/media/radio/radio-rtrack2.c332
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c61
-rw-r--r--drivers/media/radio/radio-tea5764.c19
-rw-r--r--drivers/media/radio/radio-terratec.c365
-rw-r--r--drivers/media/radio/radio-trust.c388
-rw-r--r--drivers/media/radio/radio-typhoon.c366
-rw-r--r--drivers/media/radio/radio-zoltrix.c442
-rw-r--r--drivers/media/radio/saa7706h.c13
-rw-r--r--drivers/media/radio/si470x/radio-si470x-i2c.c28
-rw-r--r--drivers/media/radio/si4713-i2c.c15
-rw-r--r--drivers/media/radio/tef6862.c14
-rw-r--r--drivers/media/rc/Kconfig9
-rw-r--r--drivers/media/rc/Makefile1
-rw-r--r--drivers/media/rc/fintek-cir.c26
-rw-r--r--drivers/media/rc/fintek-cir.h4
-rw-r--r--drivers/media/rc/gpio-ir-recv.c205
-rw-r--r--drivers/media/rc/ir-sony-decoder.c2
-rw-r--r--drivers/media/rc/keymaps/Makefile3
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v1.c95
-rw-r--r--drivers/media/rc/keymaps/rc-it913x-v2.c94
-rw-r--r--drivers/media/rc/keymaps/rc-kworld-pc150u.c102
-rw-r--r--drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c52
-rw-r--r--drivers/media/rc/mceusb.c2
-rw-r--r--drivers/media/rc/rc-core-priv.h2
-rw-r--r--drivers/media/rc/rc-main.c9
-rw-r--r--drivers/media/video/Kconfig49
-rw-r--r--drivers/media/video/Makefile24
-rw-r--r--drivers/media/video/adp1653.c20
-rw-r--r--drivers/media/video/adv7170.c13
-rw-r--r--drivers/media/video/adv7175.c13
-rw-r--r--drivers/media/video/adv7180.c14
-rw-r--r--drivers/media/video/adv7183.c699
-rw-r--r--drivers/media/video/adv7183_regs.h107
-rw-r--r--drivers/media/video/adv7343.c13
-rw-r--r--drivers/media/video/ak881x.c13
-rw-r--r--drivers/media/video/aptina-pll.c174
-rw-r--r--drivers/media/video/aptina-pll.h56
-rw-r--r--drivers/media/video/as3645a.c19
-rw-r--r--drivers/media/video/blackfin/Kconfig10
-rw-r--r--drivers/media/video/blackfin/Makefile2
-rw-r--r--drivers/media/video/blackfin/bfin_capture.c1059
-rw-r--r--drivers/media/video/blackfin/ppi.c271
-rw-r--r--drivers/media/video/bt819.c13
-rw-r--r--drivers/media/video/bt856.c13
-rw-r--r--drivers/media/video/bt866.c13
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c4
-rw-r--r--drivers/media/video/cs5345.c13
-rw-r--r--drivers/media/video/cs53l32a.c13
-rw-r--r--drivers/media/video/cx18/cx18-driver.c8
-rw-r--r--drivers/media/video/cx18/cx18-driver.h2
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c4
-rw-r--r--drivers/media/video/cx231xx/cx231xx-417.c1
-rw-r--r--drivers/media/video/cx231xx/cx231xx-cards.c1
-rw-r--r--drivers/media/video/cx231xx/cx231xx-video.c6
-rw-r--r--drivers/media/video/cx25821/cx25821-core.c9
-rw-r--r--drivers/media/video/cx25840/cx25840-core.c13
-rw-r--r--drivers/media/video/davinci/vpif.h2
-rw-r--r--drivers/media/video/davinci/vpif_display.c2
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c114
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c145
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c96
-rw-r--r--drivers/media/video/em28xx/em28xx-i2c.c8
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c10
-rw-r--r--drivers/media/video/em28xx/em28xx.h29
-rw-r--r--drivers/media/video/gspca/gl860/Makefile2
-rw-r--r--drivers/media/video/gspca/m5602/Makefile2
-rw-r--r--drivers/media/video/gspca/ov534_9.c49
-rw-r--r--drivers/media/video/gspca/pac7302.c583
-rw-r--r--drivers/media/video/gspca/sn9c20x.c1138
-rw-r--r--drivers/media/video/gspca/sonixj.c185
-rw-r--r--drivers/media/video/gspca/stv06xx/Makefile2
-rw-r--r--drivers/media/video/gspca/zc3xx.c328
-rw-r--r--drivers/media/video/imx074.c13
-rw-r--r--drivers/media/video/indycam.c13
-rw-r--r--drivers/media/video/ir-kbd-i2c.c17
-rw-r--r--drivers/media/video/ivtv/Makefile8
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.c62
-rw-r--r--drivers/media/video/ivtv/ivtv-controls.h2
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.c41
-rw-r--r--drivers/media/video/ivtv/ivtv-driver.h13
-rw-r--r--drivers/media/video/ivtv/ivtv-fileops.c2
-rw-r--r--drivers/media/video/ivtv/ivtv-ioctl.c188
-rw-r--r--drivers/media/video/ivtv/ivtv-streams.c20
-rw-r--r--drivers/media/video/ks0127.c13
-rw-r--r--drivers/media/video/m52790.c13
-rw-r--r--drivers/media/video/m5mols/m5mols_core.c15
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.c35
-rw-r--r--drivers/media/video/marvell-ccic/mcam-core.h1
-rw-r--r--drivers/media/video/marvell-ccic/mmp-driver.c13
-rw-r--r--drivers/media/video/msp3400-driver.c13
-rw-r--r--drivers/media/video/mt9m001.c13
-rw-r--r--drivers/media/video/mt9m032.c868
-rw-r--r--drivers/media/video/mt9m111.c13
-rw-r--r--drivers/media/video/mt9p031.c80
-rw-r--r--drivers/media/video/mt9t001.c13
-rw-r--r--drivers/media/video/mt9t031.c13
-rw-r--r--drivers/media/video/mt9t112.c16
-rw-r--r--drivers/media/video/mt9v011.c13
-rw-r--r--drivers/media/video/mt9v022.c13
-rw-r--r--drivers/media/video/mt9v032.c13
-rw-r--r--drivers/media/video/mx2_camera.c1214
-rw-r--r--drivers/media/video/mx2_emmaprp.c1008
-rw-r--r--drivers/media/video/noon010pc30.c15
-rw-r--r--drivers/media/video/omap/omap_vout.c3
-rw-r--r--drivers/media/video/ov2640.c16
-rw-r--r--drivers/media/video/ov5642.c13
-rw-r--r--drivers/media/video/ov6650.c13
-rw-r--r--drivers/media/video/ov7670.c13
-rw-r--r--drivers/media/video/ov772x.c17
-rw-r--r--drivers/media/video/ov9640.c13
-rw-r--r--drivers/media/video/ov9740.c13
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c10
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1
-rw-r--r--drivers/media/video/pwc/pwc-v4l.c10
-rw-r--r--drivers/media/video/pxa_camera.c4
-rw-r--r--drivers/media/video/rj54n1cb0c.c13
-rw-r--r--drivers/media/video/s2255drv.c33
-rw-r--r--drivers/media/video/s5k6aa.c15
-rw-r--r--drivers/media/video/s5p-fimc/fimc-capture.c121
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.c85
-rw-r--r--drivers/media/video/s5p-fimc/fimc-core.h2
-rw-r--r--drivers/media/video/s5p-fimc/fimc-mdevice.c7
-rw-r--r--drivers/media/video/s5p-fimc/mipi-csis.c111
-rw-r--r--drivers/media/video/s5p-g2d/g2d-hw.c5
-rw-r--r--drivers/media/video/s5p-g2d/g2d.c63
-rw-r--r--drivers/media/video/s5p-g2d/g2d.h6
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.c199
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-core.h11
-rw-r--r--drivers/media/video/s5p-jpeg/jpeg-hw.h18
-rw-r--r--drivers/media/video/s5p-mfc/s5p_mfc_pm.c24
-rw-r--r--drivers/media/video/s5p-tv/Kconfig10
-rw-r--r--drivers/media/video/s5p-tv/Makefile2
-rw-r--r--drivers/media/video/s5p-tv/hdmi_drv.c120
-rw-r--r--drivers/media/video/s5p-tv/hdmiphy_drv.c12
-rw-r--r--drivers/media/video/s5p-tv/mixer_drv.c2
-rw-r--r--drivers/media/video/s5p-tv/sdo_drv.c26
-rw-r--r--drivers/media/video/s5p-tv/sii9234_drv.c432
-rw-r--r--drivers/media/video/saa6588.c13
-rw-r--r--drivers/media/video/saa7110.c13
-rw-r--r--drivers/media/video/saa7115.c13
-rw-r--r--drivers/media/video/saa7127.c13
-rw-r--r--drivers/media/video/saa7134/Makefile8
-rw-r--r--drivers/media/video/saa7134/saa6752hs.c13
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c59
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c44
-rw-r--r--drivers/media/video/saa7134/saa7134-i2c.c14
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c63
-rw-r--r--drivers/media/video/saa7134/saa7134.h5
-rw-r--r--drivers/media/video/saa7164/Makefile8
-rw-r--r--drivers/media/video/saa7164/saa7164-encoder.c6
-rw-r--r--drivers/media/video/saa7164/saa7164-vbi.c6
-rw-r--r--drivers/media/video/saa717x.c13
-rw-r--r--drivers/media/video/saa7185.c13
-rw-r--r--drivers/media/video/saa7191.c13
-rw-r--r--drivers/media/video/sh_mobile_ceu_camera.c35
-rw-r--r--drivers/media/video/soc_camera.c32
-rw-r--r--drivers/media/video/sr030pc30.c13
-rw-r--r--drivers/media/video/tda7432.c13
-rw-r--r--drivers/media/video/tda9840.c13
-rw-r--r--drivers/media/video/tea6415c.c13
-rw-r--r--drivers/media/video/tea6420.c13
-rw-r--r--drivers/media/video/ths7303.c14
-rw-r--r--drivers/media/video/tlv320aic23b.c13
-rw-r--r--drivers/media/video/tm6000/tm6000-input.c3
-rw-r--r--drivers/media/video/tuner-core.c28
-rw-r--r--drivers/media/video/tvaudio.c13
-rw-r--r--drivers/media/video/tveeprom.c10
-rw-r--r--drivers/media/video/tvp514x.c13
-rw-r--r--drivers/media/video/tvp5150.c141
-rw-r--r--drivers/media/video/tvp7002.c25
-rw-r--r--drivers/media/video/tw9910.c16
-rw-r--r--drivers/media/video/upd64031a.c13
-rw-r--r--drivers/media/video/upd64083.c13
-rw-r--r--drivers/media/video/uvc/uvc_driver.c11
-rw-r--r--drivers/media/video/uvc/uvc_queue.c2
-rw-r--r--drivers/media/video/uvc/uvc_v4l2.c207
-rw-r--r--drivers/media/video/v4l2-common.c1
-rw-r--r--drivers/media/video/v4l2-compat-ioctl32.c22
-rw-r--r--drivers/media/video/v4l2-ctrls.c91
-rw-r--r--drivers/media/video/v4l2-dev.c3
-rw-r--r--drivers/media/video/v4l2-ioctl.c42
-rw-r--r--drivers/media/video/v4l2-subdev.c12
-rw-r--r--drivers/media/video/videobuf2-vmalloc.c70
-rw-r--r--drivers/media/video/vivi.c26
-rw-r--r--drivers/media/video/vp27smpx.c13
-rw-r--r--drivers/media/video/vpx3220.c13
-rw-r--r--drivers/media/video/vs6624.c928
-rw-r--r--drivers/media/video/vs6624_regs.h337
-rw-r--r--drivers/media/video/w9966.c4
-rw-r--r--drivers/media/video/wm8739.c13
-rw-r--r--drivers/media/video/wm8775.c13
-rw-r--r--drivers/message/fusion/mptbase.c2
-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-core.c20
-rw-r--r--drivers/mfd/wm831x-i2c.c2
-rw-r--r--drivers/mfd/wm831x-spi.c4
-rw-r--r--drivers/mfd/wm8400-core.c10
-rw-r--r--drivers/mfd/wm8994-core.c94
-rw-r--r--drivers/mfd/wm8994-regmap.c21
-rw-r--r--drivers/misc/atmel_tclib.c64
-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/Kconfig27
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/at91_mci.c1
-rw-r--r--drivers/mmc/host/atmel-mci.c2
-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.c180
-rw-r--r--drivers/mmc/host/mmci.h15
-rw-r--r--drivers/mmc/host/omap_hsmmc.c293
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c6
-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-s3c.c6
-rw-r--r--drivers/mmc/host/sdhci-spear.c9
-rw-r--r--drivers/mmc/host/sdhci-tegra.c101
-rw-r--r--drivers/mmc/host/sdhci.c38
-rw-r--r--drivers/mmc/host/sdhci.h2
-rw-r--r--drivers/mmc/host/sh_mmcif.c2
-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_pio.c108
-rw-r--r--drivers/mtd/Kconfig2
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c6
-rw-r--r--drivers/mtd/devices/Kconfig1
-rw-r--r--drivers/mtd/devices/pmc551.c1
-rw-r--r--drivers/mtd/devices/slram.c1
-rw-r--r--drivers/mtd/maps/Kconfig1
-rw-r--r--drivers/mtd/maps/pcmciamtd.c1
-rw-r--r--drivers/mtd/maps/sa1100-flash.c112
-rw-r--r--drivers/mtd/mtdchar.c2
-rw-r--r--drivers/mtd/nand/Kconfig4
-rw-r--r--drivers/mtd/nand/ams-delta.c74
-rw-r--r--drivers/mtd/nand/atmel_nand.c136
-rw-r--r--drivers/mtd/nand/bcm_umi_nand.c1
-rw-r--r--drivers/mtd/onenand/Kconfig1
-rw-r--r--drivers/mtd/ubi/build.c14
-rw-r--r--drivers/mtd/ubi/eba.c30
-rw-r--r--drivers/mtd/ubi/io.c14
-rw-r--r--drivers/mtd/ubi/scan.c16
-rw-r--r--drivers/mtd/ubi/ubi.h12
-rw-r--r--drivers/mtd/ubi/wl.c21
-rw-r--r--drivers/net/Space.c2
-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.c83
-rw-r--r--drivers/net/bonding/bonding.h18
-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/cnic.c12
-rw-r--r--drivers/net/ethernet/broadcom/cnic_defs.h28
-rw-r--r--drivers/net/ethernet/broadcom/cnic_if.h4
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c26
-rw-r--r--drivers/net/ethernet/broadcom/tg3.h1
-rw-r--r--drivers/net/ethernet/cirrus/Kconfig19
-rw-r--r--drivers/net/ethernet/cirrus/cs89x0.c149
-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/gianfar.c39
-rw-r--r--drivers/net/ethernet/freescale/gianfar.h2
-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/marvell/sky2.c11
-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/qlogic/qlcnic/qlcnic.h4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c4
-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/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/usb/cdc-phonet.c6
-rw-r--r--drivers/net/usb/qmi_wwan.c36
-rw-r--r--drivers/net/usb/usbnet.c11
-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/iwlegacy/3945.c3
-rw-r--r--drivers/net/wireless/iwlegacy/4965-mac.c3
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rx.c2
-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/net/xen-netfront.c4
-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/of/platform.c6
-rw-r--r--drivers/parisc/dino.c28
-rw-r--r--drivers/parisc/iosapic.c1
-rw-r--r--drivers/parisc/lba_pci.c32
-rw-r--r--drivers/pci/Kconfig13
-rw-r--r--drivers/pci/bus.c30
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c33
-rw-r--r--drivers/pci/hotplug/cpci_hotplug_pci.c2
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c2
-rw-r--r--drivers/pci/hotplug/cpqphp_pci.c2
-rw-r--r--drivers/pci/hotplug/fakephp.c2
-rw-r--r--drivers/pci/hotplug/ibmphp_core.c2
-rw-r--r--drivers/pci/hotplug/ibmphp_ebda.c6
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c133
-rw-r--r--drivers/pci/hotplug/pciehp_pci.c2
-rw-r--r--drivers/pci/hotplug/rpadlpar_core.c2
-rw-r--r--drivers/pci/hotplug/sgi_hotplug.c2
-rw-r--r--drivers/pci/hotplug/shpchp_pci.c2
-rw-r--r--drivers/pci/iov.c12
-rw-r--r--drivers/pci/pci-driver.c10
-rw-r--r--drivers/pci/pci-sysfs.c7
-rw-r--r--drivers/pci/pci.c133
-rw-r--r--drivers/pci/pci.h10
-rw-r--r--drivers/pci/pcie/Kconfig25
-rw-r--r--drivers/pci/pcie/aspm.c8
-rw-r--r--drivers/pci/pcie/portdrv.h12
-rw-r--r--drivers/pci/pcie/portdrv_core.c16
-rw-r--r--drivers/pci/probe.c298
-rw-r--r--drivers/pci/quirks.c182
-rw-r--r--drivers/pci/remove.c27
-rw-r--r--drivers/pci/setup-bus.c660
-rw-r--r--drivers/pci/setup-res.c94
-rw-r--r--drivers/pci/xen-pcifront.c4
-rw-r--r--drivers/pcmcia/Kconfig8
-rw-r--r--drivers/pcmcia/Makefile14
-rw-r--r--drivers/pcmcia/at91_cf.c5
-rw-r--r--drivers/pcmcia/cardbus.c2
-rw-r--r--drivers/pcmcia/cs.c1
-rw-r--r--drivers/pcmcia/i82092.c1
-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.c1
-rw-r--r--drivers/pcmcia/pd6729.c1
-rw-r--r--drivers/pcmcia/pxa2xx_balloon3.c22
-rw-r--r--drivers/pcmcia/pxa2xx_base.c6
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x255.c39
-rw-r--r--drivers/pcmcia/pxa2xx_cm_x270.c23
-rw-r--r--drivers/pcmcia/pxa2xx_colibri.c21
-rw-r--r--drivers/pcmcia/pxa2xx_e740.c71
-rw-r--r--drivers/pcmcia/pxa2xx_mainstone.c31
-rw-r--r--drivers/pcmcia/pxa2xx_palmld.c8
-rw-r--r--drivers/pcmcia/pxa2xx_palmtc.c8
-rw-r--r--drivers/pcmcia/pxa2xx_palmtx.c8
-rw-r--r--drivers/pcmcia/pxa2xx_sharpsl.c30
-rw-r--r--drivers/pcmcia/pxa2xx_stargate2.c34
-rw-r--r--drivers/pcmcia/pxa2xx_trizeps4.c62
-rw-r--r--drivers/pcmcia/pxa2xx_viper.c39
-rw-r--r--drivers/pcmcia/pxa2xx_vpac270.c54
-rw-r--r--drivers/pcmcia/sa1100_assabet.c65
-rw-r--r--drivers/pcmcia/sa1100_cerf.c52
-rw-r--r--drivers/pcmcia/sa1100_h3600.c94
-rw-r--r--drivers/pcmcia/sa1100_nanoengine.c132
-rw-r--r--drivers/pcmcia/sa1100_shannon.c56
-rw-r--r--drivers/pcmcia/sa1100_simpad.c27
-rw-r--r--drivers/pcmcia/sa1111_badge4.c (renamed from drivers/pcmcia/sa1100_badge4.c)3
-rw-r--r--drivers/pcmcia/sa1111_generic.c111
-rw-r--r--drivers/pcmcia/sa1111_generic.h1
-rw-r--r--drivers/pcmcia/sa1111_jornada720.c (renamed from drivers/pcmcia/sa1100_jornada720.c)8
-rw-r--r--drivers/pcmcia/sa1111_lubbock.c (renamed from drivers/pcmcia/pxa2xx_lubbock.c)1
-rw-r--r--drivers/pcmcia/sa1111_neponset.c (renamed from drivers/pcmcia/sa1100_neponset.c)18
-rw-r--r--drivers/pcmcia/sa11xx_base.c6
-rw-r--r--drivers/pcmcia/soc_common.c194
-rw-r--r--drivers/pcmcia/soc_common.h23
-rw-r--r--drivers/pcmcia/socket_sysfs.c1
-rw-r--r--drivers/pcmcia/tcic.c1
-rw-r--r--drivers/pcmcia/xxs1500_ss.c1
-rw-r--r--drivers/pinctrl/Kconfig42
-rw-r--r--drivers/pinctrl/Makefile8
-rw-r--r--drivers/pinctrl/core.c826
-rw-r--r--drivers/pinctrl/core.h117
-rw-r--r--drivers/pinctrl/pinconf-generic.c120
-rw-r--r--drivers/pinctrl/pinconf.c306
-rw-r--r--drivers/pinctrl/pinconf.h78
-rw-r--r--drivers/pinctrl/pinctrl-coh901.c139
-rw-r--r--drivers/pinctrl/pinctrl-coh901.h5
-rw-r--r--drivers/pinctrl/pinctrl-mmp2.c722
-rw-r--r--drivers/pinctrl/pinctrl-pxa168.c651
-rw-r--r--drivers/pinctrl/pinctrl-pxa3xx.c244
-rw-r--r--drivers/pinctrl/pinctrl-pxa3xx.h264
-rw-r--r--drivers/pinctrl/pinctrl-pxa910.c1007
-rw-r--r--drivers/pinctrl/pinctrl-tegra.c559
-rw-r--r--drivers/pinctrl/pinctrl-tegra.h163
-rw-r--r--drivers/pinctrl/pinctrl-tegra20.c2860
-rw-r--r--drivers/pinctrl/pinctrl-tegra30.c3726
-rw-r--r--drivers/pinctrl/pinctrl-u300.c80
-rw-r--r--drivers/pinctrl/pinmux.c1205
-rw-r--r--drivers/pinctrl/pinmux.h76
-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.c70
-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.c15
-rw-r--r--drivers/platform/x86/eeepc-wmi.c108
-rw-r--r--drivers/platform/x86/hdaps.c4
-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/pnpbios/bioscalls.c1
-rw-r--r--drivers/pnp/pnpbios/core.c1
-rw-r--r--drivers/power/apm_power.c1
-rw-r--r--drivers/power/max8998_charger.c1
-rw-r--r--drivers/power/power_supply.h4
-rw-r--r--drivers/power/power_supply_leds.c1
-rw-r--r--drivers/power/power_supply_sysfs.c1
-rw-r--r--drivers/regulator/Kconfig8
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/bq24022.c162
-rw-r--r--drivers/remoteproc/Kconfig28
-rw-r--r--drivers/remoteproc/Makefile9
-rw-r--r--drivers/remoteproc/omap_remoteproc.c229
-rw-r--r--drivers/remoteproc/omap_remoteproc.h69
-rw-r--r--drivers/remoteproc/remoteproc_core.c1586
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c179
-rw-r--r--drivers/remoteproc/remoteproc_internal.h44
-rw-r--r--drivers/remoteproc/remoteproc_virtio.c289
-rw-r--r--drivers/rpmsg/Kconfig10
-rw-r--r--drivers/rpmsg/Makefile1
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c1054
-rw-r--r--drivers/rtc/Kconfig23
-rw-r--r--drivers/rtc/Makefile2
-rw-r--r--drivers/rtc/interface.c5
-rw-r--r--drivers/rtc/rtc-88pm860x.c27
-rw-r--r--drivers/rtc/rtc-at91sam9.c87
-rw-r--r--drivers/rtc/rtc-bq32k.c12
-rw-r--r--drivers/rtc/rtc-cmos.c2
-rw-r--r--drivers/rtc/rtc-coh901331.c2
-rw-r--r--drivers/rtc/rtc-da9052.c293
-rw-r--r--drivers/rtc/rtc-davinci.c2
-rw-r--r--drivers/rtc/rtc-ds1305.c12
-rw-r--r--drivers/rtc/rtc-ds1307.c182
-rw-r--r--drivers/rtc/rtc-ds1374.c13
-rw-r--r--drivers/rtc/rtc-ds1390.c12
-rw-r--r--drivers/rtc/rtc-ds1511.c2
-rw-r--r--drivers/rtc/rtc-ds1553.c2
-rw-r--r--drivers/rtc/rtc-ds1672.c13
-rw-r--r--drivers/rtc/rtc-ds3232.c13
-rw-r--r--drivers/rtc/rtc-ds3234.c12
-rw-r--r--drivers/rtc/rtc-em3027.c13
-rw-r--r--drivers/rtc/rtc-fm3130.c12
-rw-r--r--drivers/rtc/rtc-isl12022.c13
-rw-r--r--drivers/rtc/rtc-isl1208.c15
-rw-r--r--drivers/rtc/rtc-lpc32xx.c2
-rw-r--r--drivers/rtc/rtc-ls1x.c210
-rw-r--r--drivers/rtc/rtc-m41t80.c13
-rw-r--r--drivers/rtc/rtc-m41t93.c12
-rw-r--r--drivers/rtc/rtc-m41t94.c14
-rw-r--r--drivers/rtc/rtc-max6900.c13
-rw-r--r--drivers/rtc/rtc-max6902.c12
-rw-r--r--drivers/rtc/rtc-max8925.c21
-rw-r--r--drivers/rtc/rtc-mpc5121.c6
-rw-r--r--drivers/rtc/rtc-mrst.c2
-rw-r--r--drivers/rtc/rtc-mv.c11
-rw-r--r--drivers/rtc/rtc-nuc900.c2
-rw-r--r--drivers/rtc/rtc-omap.c4
-rw-r--r--drivers/rtc/rtc-pcf2123.c13
-rw-r--r--drivers/rtc/rtc-pcf8563.c13
-rw-r--r--drivers/rtc/rtc-pcf8583.c13
-rw-r--r--drivers/rtc/rtc-pl030.c15
-rw-r--r--drivers/rtc/rtc-pl031.c15
-rw-r--r--drivers/rtc/rtc-pm8xxx.c2
-rw-r--r--drivers/rtc/rtc-pxa.c4
-rw-r--r--drivers/rtc/rtc-r9701.c12
-rw-r--r--drivers/rtc/rtc-rs5c348.c13
-rw-r--r--drivers/rtc/rtc-rs5c372.c13
-rw-r--r--drivers/rtc/rtc-rv3029c2.c13
-rw-r--r--drivers/rtc/rtc-rx8025.c13
-rw-r--r--drivers/rtc/rtc-rx8581.c13
-rw-r--r--drivers/rtc/rtc-s35390a.c13
-rw-r--r--drivers/rtc/rtc-s3c.c75
-rw-r--r--drivers/rtc/rtc-sa1100.c211
-rw-r--r--drivers/rtc/rtc-sh.c8
-rw-r--r--drivers/rtc/rtc-spear.c100
-rw-r--r--drivers/rtc/rtc-stk17ta8.c2
-rw-r--r--drivers/rtc/rtc-twl.c28
-rw-r--r--drivers/rtc/rtc-tx4939.c2
-rw-r--r--drivers/rtc/rtc-vr41xx.c4
-rw-r--r--drivers/rtc/rtc-x1205.c13
-rw-r--r--drivers/s390/block/dasd.c4
-rw-r--r--drivers/s390/block/dasd_diag.c8
-rw-r--r--drivers/s390/block/dasd_eckd.c8
-rw-r--r--drivers/s390/char/sclp.c4
-rw-r--r--drivers/s390/char/sclp_cmd.c6
-rw-r--r--drivers/s390/char/sclp_quiesce.c1
-rw-r--r--drivers/s390/char/sclp_sdias.c101
-rw-r--r--drivers/s390/char/zcore.c1
-rw-r--r--drivers/s390/cio/cio.c2
-rw-r--r--drivers/s390/cio/crw.c1
-rw-r--r--drivers/s390/cio/qdio_main.c7
-rw-r--r--drivers/s390/cio/qdio_setup.c3
-rw-r--r--drivers/s390/crypto/Makefile10
-rw-r--r--drivers/s390/crypto/ap_bus.c4
-rw-r--r--drivers/s390/crypto/zcrypt_api.c2
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c4
-rw-r--r--drivers/s390/crypto/zcrypt_mono.c100
-rw-r--r--drivers/s390/crypto/zcrypt_pcica.c4
-rw-r--r--drivers/s390/crypto/zcrypt_pcicc.c4
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c4
-rw-r--r--drivers/s390/kvm/kvm_virtio.c6
-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/Kconfig8
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/aacraid/aachba.c4
-rw-r--r--drivers/scsi/aacraid/aacraid.h27
-rw-r--r--drivers/scsi/aacraid/comminit.c21
-rw-r--r--drivers/scsi/aacraid/commsup.c26
-rw-r--r--drivers/scsi/aacraid/linit.c28
-rw-r--r--drivers/scsi/aacraid/rx.c1
-rw-r--r--drivers/scsi/aacraid/sa.c1
-rw-r--r--drivers/scsi/aacraid/src.c293
-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/aic94xx/aic94xx.h2
-rw-r--r--drivers/scsi/aic94xx/aic94xx_dev.c38
-rw-r--r--drivers/scsi/aic94xx/aic94xx_init.c6
-rw-r--r--drivers/scsi/aic94xx/aic94xx_tmf.c11
-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.c1
-rw-r--r--drivers/scsi/bfa/bfad_bsg.c4
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc.h8
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_constants.h1
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c20
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_io.c4
-rw-r--r--drivers/scsi/bnx2i/57xx_iscsi_constants.h1
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c12
-rw-r--r--drivers/scsi/bnx2i/bnx2i_init.c9
-rw-r--r--drivers/scsi/cxgbi/libcxgbi.c13
-rw-r--r--drivers/scsi/dtc.c1
-rw-r--r--drivers/scsi/fcoe/fcoe.c167
-rw-r--r--drivers/scsi/fcoe/fcoe.h3
-rw-r--r--drivers/scsi/fcoe/fcoe_transport.c4
-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/hpsa.c344
-rw-r--r--drivers/scsi/hpsa.h3
-rw-r--r--drivers/scsi/hpsa_cmd.h9
-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.c14
-rw-r--r--drivers/scsi/ipr.h4
-rw-r--r--drivers/scsi/isci/host.c17
-rw-r--r--drivers/scsi/isci/host.h19
-rw-r--r--drivers/scsi/isci/init.c24
-rw-r--r--drivers/scsi/isci/phy.c171
-rw-r--r--drivers/scsi/isci/phy.h155
-rw-r--r--drivers/scsi/isci/port.c263
-rw-r--r--drivers/scsi/isci/port.h114
-rw-r--r--drivers/scsi/isci/registers.h27
-rw-r--r--drivers/scsi/isci/remote_device.c82
-rw-r--r--drivers/scsi/isci/remote_device.h212
-rw-r--r--drivers/scsi/isci/remote_node_context.c19
-rw-r--r--drivers/scsi/isci/remote_node_context.h97
-rw-r--r--drivers/scsi/isci/request.c370
-rw-r--r--drivers/scsi/isci/request.h228
-rw-r--r--drivers/scsi/isci/scu_task_context.h55
-rw-r--r--drivers/scsi/isci/task.c158
-rw-r--r--drivers/scsi/isci/task.h40
-rw-r--r--drivers/scsi/iscsi_tcp.c13
-rw-r--r--drivers/scsi/libfc/fc_disc.c7
-rw-r--r--drivers/scsi/libfc/fc_elsct.c3
-rw-r--r--drivers/scsi/libfc/fc_exch.c7
-rw-r--r--drivers/scsi/libfc/fc_fcp.c5
-rw-r--r--drivers/scsi/libfc/fc_lport.c227
-rw-r--r--drivers/scsi/libiscsi.c28
-rw-r--r--drivers/scsi/libiscsi_tcp.c18
-rw-r--r--drivers/scsi/libsas/sas_ata.c828
-rw-r--r--drivers/scsi/libsas/sas_discover.c246
-rw-r--r--drivers/scsi/libsas/sas_event.c96
-rw-r--r--drivers/scsi/libsas/sas_expander.c342
-rw-r--r--drivers/scsi/libsas/sas_host_smp.c11
-rw-r--r--drivers/scsi/libsas/sas_init.c214
-rw-r--r--drivers/scsi/libsas/sas_internal.h97
-rw-r--r--drivers/scsi/libsas/sas_phy.c12
-rw-r--r--drivers/scsi/libsas/sas_port.c32
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c364
-rw-r--r--drivers/scsi/lpfc/lpfc.h13
-rw-r--r--drivers/scsi/lpfc/lpfc_attr.c12
-rw-r--r--drivers/scsi/lpfc/lpfc_bsg.c18
-rw-r--r--drivers/scsi/lpfc/lpfc_crtn.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_debugfs.c19
-rw-r--r--drivers/scsi/lpfc/lpfc_els.c5
-rw-r--r--drivers/scsi/lpfc/lpfc_hbadisc.c12
-rw-r--r--drivers/scsi/lpfc/lpfc_hw.h5
-rw-r--r--drivers/scsi/lpfc/lpfc_hw4.h49
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c137
-rw-r--r--drivers/scsi/lpfc/lpfc_nportdisc.c77
-rw-r--r--drivers/scsi/lpfc/lpfc_scsi.c1054
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c240
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.h1
-rw-r--r--drivers/scsi/lpfc/lpfc_sli4.h3
-rw-r--r--drivers/scsi/lpfc/lpfc_version.h2
-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.c7
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c6
-rw-r--r--drivers/scsi/mvsas/mv_init.c2
-rw-r--r--drivers/scsi/mvsas/mv_sas.c11
-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_chips.h4
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.c434
-rw-r--r--drivers/scsi/pm8001/pm8001_hwi.h2
-rw-r--r--drivers/scsi/pm8001/pm8001_init.c2
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.c127
-rw-r--r--drivers/scsi/pm8001/pm8001_sas.h6
-rw-r--r--drivers/scsi/qla1280.c1
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c177
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c120
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c630
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h63
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h117
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h13
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h22
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c86
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c540
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h51
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c167
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c445
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c410
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c2
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c90
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.h3
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c435
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c148
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h45
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h24
-rw-r--r--drivers/scsi/qla4xxx/ql4_glbl.h9
-rw-r--r--drivers/scsi/qla4xxx/ql4_init.c5
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c92
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c78
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c23
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c17
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.h1
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c563
-rw-r--r--drivers/scsi/qla4xxx/ql4_version.h2
-rw-r--r--drivers/scsi/qlogicpti.c1
-rw-r--r--drivers/scsi/scsi.c6
-rw-r--r--drivers/scsi/scsi_debug.c6
-rw-r--r--drivers/scsi/scsi_error.c24
-rw-r--r--drivers/scsi/scsi_lib.c5
-rw-r--r--drivers/scsi/scsi_transport_fc.c30
-rw-r--r--drivers/scsi/scsi_transport_iscsi.c268
-rw-r--r--drivers/scsi/scsi_transport_sas.c60
-rw-r--r--drivers/scsi/sd.c86
-rw-r--r--drivers/scsi/sd.h35
-rw-r--r--drivers/scsi/st.c12
-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/ultrastor.c1
-rw-r--r--drivers/scsi/virtio_scsi.c594
-rw-r--r--drivers/scsi/wd7000.c1
-rw-r--r--drivers/sh/clk/cpg.c16
-rw-r--r--drivers/spi/Kconfig2
-rw-r--r--drivers/spi/spi-omap-uwire.c1
-rw-r--r--drivers/spi/spi-orion.c5
-rw-r--r--drivers/spi/spi-s3c24xx.c2
-rw-r--r--drivers/staging/Kconfig2
-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/iio/gyro/adis16060_core.c1
-rw-r--r--drivers/staging/media/Kconfig2
-rw-r--r--drivers/staging/media/as102/as102_drv.c2
-rw-r--r--drivers/staging/media/as102/as102_drv.h2
-rw-r--r--drivers/staging/media/as102/as102_fe.c6
-rw-r--r--drivers/staging/media/as102/as102_fw.h2
-rw-r--r--drivers/staging/media/as102/as102_usb_drv.c17
-rw-r--r--drivers/staging/media/as102/as10x_cmd.h80
-rw-r--r--drivers/staging/media/as102/as10x_types.h2
-rw-r--r--drivers/staging/media/easycap/easycap_main.c242
-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.c9
-rw-r--r--drivers/staging/media/go7007/s2250-board.c16
-rw-r--r--drivers/staging/media/go7007/snd-go7007.c1
-rw-r--r--drivers/staging/media/lirc/lirc_serial.c3
-rw-r--r--drivers/staging/media/lirc/lirc_sir.c1
-rw-r--r--drivers/staging/media/solo6x10/Kconfig2
-rw-r--r--drivers/staging/media/solo6x10/core.c32
-rw-r--r--drivers/staging/mei/wd.c1
-rw-r--r--drivers/staging/panel/panel.c1
-rw-r--r--drivers/staging/rtl8187se/r8180_core.c2
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_dm.c4
-rw-r--r--drivers/staging/sbe-2t3e3/io.c1
-rw-r--r--drivers/staging/ste_rmi4/Makefile2
-rw-r--r--drivers/staging/telephony/ixj.c57
-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/hcf.c1
-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/staging/zcache/zcache-main.c10
-rw-r--r--drivers/target/iscsi/iscsi_target.c41
-rw-r--r--drivers/target/iscsi/iscsi_target_configfs.c53
-rw-r--r--drivers/target/iscsi/iscsi_target_core.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_device.c19
-rw-r--r--drivers/target/iscsi/iscsi_target_device.h2
-rw-r--r--drivers/target/iscsi/iscsi_target_erl0.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_erl1.c2
-rw-r--r--drivers/target/iscsi/iscsi_target_login.c10
-rw-r--r--drivers/target/iscsi/iscsi_target_nego.c10
-rw-r--r--drivers/target/iscsi/iscsi_target_nodeattrib.c16
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c19
-rw-r--r--drivers/target/iscsi/iscsi_target_tmr.c6
-rw-r--r--drivers/target/iscsi/iscsi_target_tq.c6
-rw-r--r--drivers/target/iscsi/iscsi_target_util.c7
-rw-r--r--drivers/target/loopback/tcm_loop.c363
-rw-r--r--drivers/target/loopback/tcm_loop.h4
-rw-r--r--drivers/target/target_core_alua.c7
-rw-r--r--drivers/target/target_core_cdb.c177
-rw-r--r--drivers/target/target_core_configfs.c30
-rw-r--r--drivers/target/target_core_device.c107
-rw-r--r--drivers/target/target_core_fabric_configfs.c6
-rw-r--r--drivers/target/target_core_iblock.c78
-rw-r--r--drivers/target/target_core_iblock.h7
-rw-r--r--drivers/target/target_core_internal.h3
-rw-r--r--drivers/target/target_core_pr.c23
-rw-r--r--drivers/target/target_core_pscsi.c18
-rw-r--r--drivers/target/target_core_pscsi.h2
-rw-r--r--drivers/target/target_core_stat.c48
-rw-r--r--drivers/target/target_core_tmr.c103
-rw-r--r--drivers/target/target_core_tpg.c115
-rw-r--r--drivers/target/target_core_transport.c367
-rw-r--r--drivers/target/target_core_ua.c8
-rw-r--r--drivers/target/tcm_fc/tcm_fc.h8
-rw-r--r--drivers/target/tcm_fc/tfc_cmd.c147
-rw-r--r--drivers/target/tcm_fc/tfc_conf.c4
-rw-r--r--drivers/target/tcm_fc/tfc_sess.c21
-rw-r--r--drivers/tty/amiserial.c1
-rw-r--r--drivers/tty/hvc/Kconfig8
-rw-r--r--drivers/tty/hvc/hvc_vio.c7
-rw-r--r--drivers/tty/hvc/hvc_xen.c465
-rw-r--r--drivers/tty/hvc/hvcs.c5
-rw-r--r--drivers/tty/isicom.c1
-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/atmel_serial.c2
-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/imx.c7
-rw-r--r--drivers/tty/serial/msm_serial_hs.c1
-rw-r--r--drivers/tty/serial/pxa.c49
-rw-r--r--drivers/tty/serial/sa1100.c1
-rw-r--r--drivers/tty/serial/sirfsoc_uart.c24
-rw-r--r--drivers/tty/serial/sirfsoc_uart.h2
-rw-r--r--drivers/tty/serial/sn_console.c1
-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/Kconfig4
-rw-r--r--drivers/usb/gadget/Kconfig8
-rw-r--r--drivers/usb/gadget/amd5536udc.c1
-rw-r--r--drivers/usb/gadget/at91_udc.c50
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c6
-rw-r--r--drivers/usb/gadget/dummy_hcd.c1
-rw-r--r--drivers/usb/gadget/f_phonet.c2
-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.c106
-rw-r--r--drivers/usb/host/ohci-hcd.c3
-rw-r--r--drivers/usb/host/ohci-sa1111.c297
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c1
-rw-r--r--drivers/usb/host/pci-quirks.c3
-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/serial/option.c6
-rw-r--r--drivers/uwb/allocator.c6
-rw-r--r--drivers/vhost/net.c2
-rw-r--r--drivers/vhost/vhost.c11
-rw-r--r--drivers/vhost/vhost.h2
-rw-r--r--drivers/video/Kconfig42
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/amifb.c1
-rw-r--r--drivers/video/atmel_lcdfb.c12
-rw-r--r--drivers/video/au1100fb.c32
-rw-r--r--drivers/video/au1200fb.c9
-rw-r--r--drivers/video/backlight/88pm860x_bl.c57
-rw-r--r--drivers/video/backlight/Kconfig21
-rw-r--r--drivers/video/backlight/Makefile4
-rw-r--r--drivers/video/backlight/aat2870_bl.c9
-rw-r--r--drivers/video/backlight/adp5520_bl.c6
-rw-r--r--drivers/video/backlight/adp8860_bl.c12
-rw-r--r--drivers/video/backlight/adp8870_bl.c12
-rw-r--r--drivers/video/backlight/ams369fg06.c13
-rw-r--r--drivers/video/backlight/apple_bl.c23
-rw-r--r--drivers/video/backlight/corgi_lcd.c12
-rw-r--r--drivers/video/backlight/cr_bllcd.c3
-rw-r--r--drivers/video/backlight/da903x_bl.c6
-rw-r--r--drivers/video/backlight/ep93xx_bl.c25
-rw-r--r--drivers/video/backlight/l4f00242t03.c13
-rw-r--r--drivers/video/backlight/ld9040.c13
-rw-r--r--drivers/video/backlight/lms283gf05.c13
-rw-r--r--drivers/video/backlight/lp855x_bl.c331
-rw-r--r--drivers/video/backlight/ltv350qv.c12
-rw-r--r--drivers/video/backlight/max8925_bl.c7
-rw-r--r--drivers/video/backlight/omap1_bl.c9
-rw-r--r--drivers/video/backlight/ot200_bl.c175
-rw-r--r--drivers/video/backlight/pandora_bl.c171
-rw-r--r--drivers/video/backlight/pcf50633-backlight.c16
-rw-r--r--drivers/video/backlight/platform_lcd.c19
-rw-r--r--drivers/video/backlight/pwm_bl.c7
-rw-r--r--drivers/video/backlight/s6e63m0.c13
-rw-r--r--drivers/video/backlight/tdo24m.c12
-rw-r--r--drivers/video/backlight/tosa_bl.c13
-rw-r--r--drivers/video/backlight/tosa_lcd.c15
-rw-r--r--drivers/video/backlight/vgg2432a4.c15
-rw-r--r--drivers/video/backlight/wm831x_bl.c6
-rw-r--r--drivers/video/bf537-lq035.c12
-rw-r--r--drivers/video/bf54x-lq043fb.c4
-rw-r--r--drivers/video/bfin-lq035q1-fb.c8
-rw-r--r--drivers/video/bfin_adv7393fb.c7
-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/da8xx-fb.c79
-rw-r--r--drivers/video/dnfb.c1
-rw-r--r--drivers/video/ep93xx-fb.c18
-rw-r--r--drivers/video/exynos/Kconfig37
-rw-r--r--drivers/video/exynos/Makefile8
-rw-r--r--drivers/video/exynos/exynos_dp_core.c1058
-rw-r--r--drivers/video/exynos/exynos_dp_core.h206
-rw-r--r--drivers/video/exynos/exynos_dp_reg.c1173
-rw-r--r--drivers/video/exynos/exynos_dp_reg.h335
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi.c600
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi_common.c896
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi_common.h46
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi_lowlevel.c618
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi_lowlevel.h112
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi_regs.h149
-rw-r--r--drivers/video/exynos/s6e8ax0.c898
-rw-r--r--drivers/video/exynos/s6e8ax0.h21
-rw-r--r--drivers/video/fbmem.c18
-rw-r--r--drivers/video/i740_reg.h309
-rw-r--r--drivers/video/i740fb.c1337
-rw-r--r--drivers/video/msm/mddi_client_nt35399.c7
-rw-r--r--drivers/video/msm/mddi_client_toshiba.c7
-rw-r--r--drivers/video/neofb.c1
-rw-r--r--drivers/video/omap/Kconfig16
-rw-r--r--drivers/video/omap/Makefile12
-rw-r--r--drivers/video/omap/blizzard.c1648
-rw-r--r--drivers/video/omap/dispc.c1547
-rw-r--r--drivers/video/omap/dispc.h46
-rw-r--r--drivers/video/omap/hwa742.c21
-rw-r--r--drivers/video/omap/lcd_ams_delta.c27
-rw-r--r--drivers/video/omap/lcd_inn1610.c10
-rw-r--r--drivers/video/omap/lcd_mipid.c14
-rw-r--r--drivers/video/omap/omapfb.h25
-rw-r--r--drivers/video/omap/omapfb_main.c30
-rw-r--r--drivers/video/omap/rfbi.c598
-rw-r--r--drivers/video/omap2/displays/panel-acx565akm.c13
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c23
-rw-r--r--drivers/video/omap2/displays/panel-lgphilips-lb035q02.c12
-rw-r--r--drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c12
-rw-r--r--drivers/video/omap2/displays/panel-taal.c4
-rw-r--r--drivers/video/omap2/displays/panel-tpo-td043mtea1.c177
-rw-r--r--drivers/video/omap2/dss/apply.c275
-rw-r--r--drivers/video/omap2/dss/core.c135
-rw-r--r--drivers/video/omap2/dss/dispc.c126
-rw-r--r--drivers/video/omap2/dss/dispc_coefs.c9
-rw-r--r--drivers/video/omap2/dss/display.c10
-rw-r--r--drivers/video/omap2/dss/dsi.c65
-rw-r--r--drivers/video/omap2/dss/dss.c20
-rw-r--r--drivers/video/omap2/dss/dss.h10
-rw-r--r--drivers/video/omap2/dss/dss_features.c181
-rw-r--r--drivers/video/omap2/dss/dss_features.h54
-rw-r--r--drivers/video/omap2/dss/hdmi.c278
-rw-r--r--drivers/video/omap2/dss/manager.c12
-rw-r--r--drivers/video/omap2/dss/rfbi.c36
-rw-r--r--drivers/video/omap2/dss/ti_hdmi.h56
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c94
-rw-r--r--drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h47
-rw-r--r--drivers/video/omap2/dss/venc.c32
-rw-r--r--drivers/video/omap2/omapfb/omapfb-ioctl.c2
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c101
-rw-r--r--drivers/video/omap2/vram.c99
-rw-r--r--drivers/video/pmag-ba-fb.c1
-rw-r--r--drivers/video/pmagb-b-fb.c1
-rw-r--r--drivers/video/pvr2fb.c4
-rw-r--r--drivers/video/pxa168fb.c15
-rw-r--r--drivers/video/pxafb.c10
-rw-r--r--drivers/video/q40fb.c1
-rw-r--r--drivers/video/riva/fbdev.c5
-rw-r--r--drivers/video/s3c-fb.c133
-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/video/sh_mipi_dsi.c99
-rw-r--r--drivers/video/sh_mobile_hdmi.c297
-rw-r--r--drivers/video/sh_mobile_lcdcfb.c1280
-rw-r--r--drivers/video/sh_mobile_lcdcfb.h84
-rw-r--r--drivers/video/sh_mobile_meram.c690
-rw-r--r--drivers/video/udlfb.c176
-rw-r--r--drivers/video/uvesafb.c16
-rw-r--r--drivers/video/via/Makefile5
-rw-r--r--drivers/video/via/chip.h3
-rw-r--r--drivers/video/via/dvi.c7
-rw-r--r--drivers/video/via/dvi.h3
-rw-r--r--drivers/video/via/hw.c130
-rw-r--r--drivers/video/via/hw.h9
-rw-r--r--drivers/video/via/lcd.c82
-rw-r--r--drivers/video/via/lcd.h3
-rw-r--r--drivers/video/via/share.h331
-rw-r--r--drivers/video/via/via_aux.c88
-rw-r--r--drivers/video/via/via_aux.h93
-rw-r--r--drivers/video/via/via_aux_ch7301.c50
-rw-r--r--drivers/video/via/via_aux_edid.c100
-rw-r--r--drivers/video/via/via_aux_sii164.c54
-rw-r--r--drivers/video/via/via_aux_vt1621.c44
-rw-r--r--drivers/video/via/via_aux_vt1622.c50
-rw-r--r--drivers/video/via/via_aux_vt1625.c50
-rw-r--r--drivers/video/via/via_aux_vt1631.c46
-rw-r--r--drivers/video/via/via_aux_vt1632.c54
-rw-r--r--drivers/video/via/via_aux_vt1636.c46
-rw-r--r--drivers/video/via/via_i2c.c10
-rw-r--r--drivers/video/via/viafbdev.c87
-rw-r--r--drivers/video/via/viafbdev.h6
-rw-r--r--drivers/video/via/viamode.c713
-rw-r--r--drivers/video/via/viamode.h11
-rw-r--r--drivers/virtio/config.c1
-rw-r--r--drivers/watchdog/Kconfig10
-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.c22
-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.c251
-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.c10
-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.c123
-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
-rw-r--r--drivers/xen/Kconfig16
-rw-r--r--drivers/xen/Makefile2
-rw-r--r--drivers/xen/events.c26
-rw-r--r--drivers/xen/platform-pci.c5
-rw-r--r--drivers/xen/sys-hypervisor.c6
-rw-r--r--drivers/xen/tmem.c31
-rw-r--r--drivers/xen/xen-acpi-processor.c562
-rw-r--r--drivers/xen/xen-balloon.c2
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c41
-rw-r--r--drivers/xen/xen-pciback/pciback.h1
-rw-r--r--drivers/xen/xen-selfballoon.c2
-rw-r--r--drivers/xen/xenbus/xenbus_client.c6
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c3
-rw-r--r--drivers/xen/xenbus/xenbus_probe_frontend.c6
1961 files changed, 119342 insertions, 44072 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index decf8e420856..6f0459cb745b 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -130,6 +130,10 @@ source "drivers/clocksource/Kconfig"
source "drivers/iommu/Kconfig"
+source "drivers/remoteproc/Kconfig"
+
+source "drivers/rpmsg/Kconfig"
+
source "drivers/virt/Kconfig"
source "drivers/devfreq/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 932e8bf20356..262b19d6b627 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -125,6 +125,8 @@ obj-y += clk/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
obj-$(CONFIG_NFC) += nfc/
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
+obj-$(CONFIG_REMOTEPROC) += remoteproc/
+obj-$(CONFIG_RPMSG) += rpmsg/
# Virtualization drivers
obj-$(CONFIG_VIRT_DRIVERS) += virt/
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index b19a18dd994f..e37615f310d7 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;
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 2801b418d7bb..d4d9cb7e016a 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>
diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c
index f3f0fe7e255a..45d8097ef4cf 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/amba/bus.c b/drivers/amba/bus.c
index 54eaf96ab217..01c2cf4efcdd 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -497,37 +497,22 @@ static void amba_device_release(struct device *dev)
}
/**
- * amba_device_register - register an AMBA device
- * @dev: AMBA device to register
- * @parent: parent memory resource
+ * amba_device_add - add a previously allocated AMBA device structure
+ * @dev: AMBA device allocated by amba_device_alloc
+ * @parent: resource parent for this devices resources
*
- * Setup the AMBA device, reading the cell ID if present.
- * Claim the resource, and register the AMBA device with
- * the Linux device manager.
+ * Claim the resource, and read the device cell ID if not already
+ * initialized. Register the AMBA device with the Linux device
+ * manager.
*/
-int amba_device_register(struct amba_device *dev, struct resource *parent)
+int amba_device_add(struct amba_device *dev, struct resource *parent)
{
u32 size;
void __iomem *tmp;
int i, ret;
- device_initialize(&dev->dev);
-
- /*
- * Copy from device_add
- */
- if (dev->dev.init_name) {
- dev_set_name(&dev->dev, "%s", dev->dev.init_name);
- dev->dev.init_name = NULL;
- }
-
- dev->dev.release = amba_device_release;
- dev->dev.bus = &amba_bustype;
- dev->dev.dma_mask = &dev->dma_mask;
- dev->res.name = dev_name(&dev->dev);
-
- if (!dev->dev.coherent_dma_mask && dev->dma_mask)
- dev_warn(&dev->dev, "coherent dma mask is unset\n");
+ WARN_ON(dev->irq[0] == (unsigned int)-1);
+ WARN_ON(dev->irq[1] == (unsigned int)-1);
ret = request_resource(parent, &dev->res);
if (ret)
@@ -582,9 +567,9 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
if (ret)
goto err_release;
- if (dev->irq[0] != NO_IRQ)
+ if (dev->irq[0] && dev->irq[0] != NO_IRQ)
ret = device_create_file(&dev->dev, &dev_attr_irq0);
- if (ret == 0 && dev->irq[1] != NO_IRQ)
+ if (ret == 0 && dev->irq[1] && dev->irq[1] != NO_IRQ)
ret = device_create_file(&dev->dev, &dev_attr_irq1);
if (ret == 0)
return ret;
@@ -596,6 +581,74 @@ int amba_device_register(struct amba_device *dev, struct resource *parent)
err_out:
return ret;
}
+EXPORT_SYMBOL_GPL(amba_device_add);
+
+static void amba_device_initialize(struct amba_device *dev, const char *name)
+{
+ device_initialize(&dev->dev);
+ if (name)
+ dev_set_name(&dev->dev, "%s", name);
+ dev->dev.release = amba_device_release;
+ dev->dev.bus = &amba_bustype;
+ dev->dev.dma_mask = &dev->dma_mask;
+ dev->res.name = dev_name(&dev->dev);
+}
+
+/**
+ * amba_device_alloc - allocate an AMBA device
+ * @name: sysfs name of the AMBA device
+ * @base: base of AMBA device
+ * @size: size of AMBA device
+ *
+ * Allocate and initialize an AMBA device structure. Returns %NULL
+ * on failure.
+ */
+struct amba_device *amba_device_alloc(const char *name, resource_size_t base,
+ size_t size)
+{
+ struct amba_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev) {
+ amba_device_initialize(dev, name);
+ dev->res.start = base;
+ dev->res.end = base + size - 1;
+ dev->res.flags = IORESOURCE_MEM;
+ }
+
+ return dev;
+}
+EXPORT_SYMBOL_GPL(amba_device_alloc);
+
+/**
+ * amba_device_register - register an AMBA device
+ * @dev: AMBA device to register
+ * @parent: parent memory resource
+ *
+ * Setup the AMBA device, reading the cell ID if present.
+ * Claim the resource, and register the AMBA device with
+ * the Linux device manager.
+ */
+int amba_device_register(struct amba_device *dev, struct resource *parent)
+{
+ amba_device_initialize(dev, dev->dev.init_name);
+ dev->dev.init_name = NULL;
+
+ if (!dev->dev.coherent_dma_mask && dev->dma_mask)
+ dev_warn(&dev->dev, "coherent dma mask is unset\n");
+
+ return amba_device_add(dev, parent);
+}
+
+/**
+ * amba_device_put - put an AMBA device
+ * @dev: AMBA device to put
+ */
+void amba_device_put(struct amba_device *dev)
+{
+ put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(amba_device_put);
/**
* amba_device_unregister - unregister an AMBA device
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index d07bf0366d99..79a1e9dd56d9 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -103,8 +103,6 @@ static struct ata_port_operations ahci_p5wdh_ops = {
.hardreset = ahci_p5wdh_hardreset,
};
-#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
-
static const struct ata_port_info ahci_port_info[] = {
/* by features */
[board_ahci] =
@@ -261,6 +259,14 @@ static const struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VDEVICE(INTEL, 0x1e06), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e07), board_ahci }, /* Panther Point RAID */
{ PCI_VDEVICE(INTEL, 0x1e0e), board_ahci }, /* Panther Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c02), board_ahci }, /* Lynx Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x8c03), board_ahci }, /* Lynx Point AHCI */
+ { PCI_VDEVICE(INTEL, 0x8c04), board_ahci }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c05), board_ahci }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c06), board_ahci }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c07), board_ahci }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c0e), board_ahci }, /* Lynx Point RAID */
+ { PCI_VDEVICE(INTEL, 0x8c0f), board_ahci }, /* Lynx Point RAID */
/* JMicron 360/1/3/5/6, match class to avoid IDE function */
{ PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index b1750007c8dc..c2594ddf25b0 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -195,6 +195,9 @@ enum {
PORT_FBS_EN = (1 << 0), /* Enable FBS */
/* hpriv->flags bits */
+
+#define AHCI_HFLAGS(flags) .private_data = (void *)(flags)
+
AHCI_HFLAG_NO_NCQ = (1 << 0),
AHCI_HFLAG_IGN_IRQ_IF_ERR = (1 << 1), /* ignore IRQ_IF_ERR */
AHCI_HFLAG_IGN_SERR_INTERNAL = (1 << 2), /* ignore SERR_INTERNAL */
@@ -210,6 +213,9 @@ enum {
AHCI_HFLAG_NO_SNTF = (1 << 12), /* no sntf */
AHCI_HFLAG_NO_FPDMA_AA = (1 << 13), /* no FPDMA AA */
AHCI_HFLAG_YES_FBS = (1 << 14), /* force FBS cap on */
+ AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
+ port start (wait until
+ error-handling stage) */
/* ap->flags bits */
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 48be4e189163..0c86c77764bc 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -26,6 +26,7 @@
enum ahci_type {
AHCI, /* standard platform ahci */
IMX53_AHCI, /* ahci on i.mx53 */
+ STRICT_AHCI, /* delayed DMA engine start */
};
static struct platform_device_id ahci_devtype[] = {
@@ -36,6 +37,9 @@ static struct platform_device_id ahci_devtype[] = {
.name = "imx53-ahci",
.driver_data = IMX53_AHCI,
}, {
+ .name = "strict-ahci",
+ .driver_data = STRICT_AHCI,
+ }, {
/* sentinel */
}
};
@@ -56,6 +60,13 @@ static const struct ata_port_info ahci_port_info[] = {
.udma_mask = ATA_UDMA6,
.port_ops = &ahci_pmp_retry_srst_ops,
},
+ [STRICT_AHCI] = {
+ AHCI_HFLAGS (AHCI_HFLAG_DELAY_ENGINE),
+ .flags = AHCI_FLAG_COMMON,
+ .pio_mask = ATA_PIO4,
+ .udma_mask = ATA_UDMA6,
+ .port_ops = &ahci_ops,
+ },
};
static struct scsi_host_template ahci_platform_sht = {
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index fdf27b9fce43..68013f96729f 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -321,6 +321,14 @@ static const struct pci_device_id piix_pci_tbl[] = {
{ 0x8086, 0x1e08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
/* SATA Controller IDE (Panther Point) */
{ 0x8086, 0x1e09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Lynx Point) */
+ { 0x8086, 0x8c00, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
+ /* SATA Controller IDE (Lynx Point) */
+ { 0x8086, 0x8c01, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_sata_snb },
+ /* SATA Controller IDE (Lynx Point) */
+ { 0x8086, 0x8c08, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
+ /* SATA Controller IDE (Lynx Point) */
+ { 0x8086, 0x8c09, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich8_2port_sata },
{ } /* terminate list */
};
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index a72bfd0ecfee..f9eaa82311a9 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -737,6 +737,7 @@ static void ahci_power_down(struct ata_port *ap)
static void ahci_start_port(struct ata_port *ap)
{
+ struct ahci_host_priv *hpriv = ap->host->private_data;
struct ahci_port_priv *pp = ap->private_data;
struct ata_link *link;
struct ahci_em_priv *emp;
@@ -746,6 +747,10 @@ static void ahci_start_port(struct ata_port *ap)
/* enable FIS reception */
ahci_start_fis_rx(ap);
+ /* enable DMA */
+ if (!(hpriv->flags & AHCI_HFLAG_DELAY_ENGINE))
+ ahci_start_engine(ap);
+
/* turn on LEDs */
if (ap->flags & ATA_FLAG_EM) {
ata_for_each_link(link, ap, EDGE) {
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index c06e0ec11556..e0bda9ff89cd 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5936,29 +5936,31 @@ void ata_host_init(struct ata_host *host, struct device *dev,
host->ops = ops;
}
-int ata_port_probe(struct ata_port *ap)
+void __ata_port_probe(struct ata_port *ap)
{
- int rc = 0;
+ struct ata_eh_info *ehi = &ap->link.eh_info;
+ unsigned long flags;
- /* probe */
- if (ap->ops->error_handler) {
- struct ata_eh_info *ehi = &ap->link.eh_info;
- unsigned long flags;
+ /* kick EH for boot probing */
+ spin_lock_irqsave(ap->lock, flags);
- /* kick EH for boot probing */
- spin_lock_irqsave(ap->lock, flags);
+ ehi->probe_mask |= ATA_ALL_DEVICES;
+ ehi->action |= ATA_EH_RESET;
+ ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
- ehi->probe_mask |= ATA_ALL_DEVICES;
- ehi->action |= ATA_EH_RESET;
- ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
+ ap->pflags &= ~ATA_PFLAG_INITIALIZING;
+ ap->pflags |= ATA_PFLAG_LOADING;
+ ata_port_schedule_eh(ap);
- ap->pflags &= ~ATA_PFLAG_INITIALIZING;
- ap->pflags |= ATA_PFLAG_LOADING;
- ata_port_schedule_eh(ap);
+ spin_unlock_irqrestore(ap->lock, flags);
+}
- spin_unlock_irqrestore(ap->lock, flags);
+int ata_port_probe(struct ata_port *ap)
+{
+ int rc = 0;
- /* wait for EH to finish */
+ if (ap->ops->error_handler) {
+ __ata_port_probe(ap);
ata_port_wait_eh(ap);
} else {
DPRINTK("ata%u: bus probe begin\n", ap->print_id);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index a9b282038000..c61316e9d2f7 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -863,6 +863,7 @@ void ata_port_wait_eh(struct ata_port *ap)
goto retry;
}
}
+EXPORT_SYMBOL_GPL(ata_port_wait_eh);
static int ata_eh_nr_in_flight(struct ata_port *ap)
{
diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
index 508a60bfe5c1..1ee00c8b5b04 100644
--- a/drivers/ata/libata-scsi.c
+++ b/drivers/ata/libata-scsi.c
@@ -3838,6 +3838,19 @@ void ata_sas_port_stop(struct ata_port *ap)
}
EXPORT_SYMBOL_GPL(ata_sas_port_stop);
+int ata_sas_async_port_init(struct ata_port *ap)
+{
+ int rc = ap->ops->port_start(ap);
+
+ if (!rc) {
+ ap->print_id = ata_print_id++;
+ __ata_port_probe(ap);
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL_GPL(ata_sas_async_port_init);
+
/**
* ata_sas_port_init - Initialize a SATA device
* @ap: SATA port to initialize
diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h
index 814486d35c44..2e26fcaf635b 100644
--- a/drivers/ata/libata.h
+++ b/drivers/ata/libata.h
@@ -105,6 +105,7 @@ extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
extern struct ata_port *ata_port_alloc(struct ata_host *host);
extern const char *sata_spd_string(unsigned int spd);
extern int ata_port_probe(struct ata_port *ap);
+extern void __ata_port_probe(struct ata_port *ap);
/* libata-acpi.c */
#ifdef CONFIG_ATA_ACPI
@@ -151,7 +152,6 @@ extern void ata_eh_acquire(struct ata_port *ap);
extern void ata_eh_release(struct ata_port *ap);
extern enum blk_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
-extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_eh_fastdrain_timerfn(unsigned long arg);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
extern void ata_dev_disable(struct ata_device *dev);
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index 048589fad2ca..fc2db2a89a6b 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -925,11 +925,10 @@ static int arasan_cf_suspend(struct device *dev)
struct ata_host *host = dev_get_drvdata(dev);
struct arasan_cf_dev *acdev = host->ports[0]->private_data;
- if (acdev->dma_chan) {
+ if (acdev->dma_chan)
acdev->dma_chan->device->device_control(acdev->dma_chan,
DMA_TERMINATE_ALL, 0);
- dma_release_channel(acdev->dma_chan);
- }
+
cf_exit(acdev);
return ata_host_suspend(host, PMSG_SUSPEND);
}
@@ -945,10 +944,7 @@ static int arasan_cf_resume(struct device *dev)
return 0;
}
-static const struct dev_pm_ops arasan_cf_pm_ops = {
- .suspend = arasan_cf_suspend,
- .resume = arasan_cf_resume,
-};
+static SIMPLE_DEV_PM_OPS(arasan_cf_pm_ops, arasan_cf_suspend, arasan_cf_resume);
#endif
static struct platform_driver arasan_cf_driver = {
@@ -958,7 +954,7 @@ static struct platform_driver arasan_cf_driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_PM
- .pm = &arasan_cf_pm_ops,
+ .pm = &arasan_cf_pm_ops,
#endif
},
};
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index e1fb39a74ce1..1c17cd1e8b2d 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -3,6 +3,7 @@
* (C) 2005 Red Hat Inc
* Alan Cox <alan@lxorguk.ukuu.org.uk>
* (C) 2009-2010 Bartlomiej Zolnierkiewicz
+ * (C) 2012 MontaVista Software, LLC <source@mvista.com>
*
* Based upon
* linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002
@@ -32,7 +33,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_cmd64x"
-#define DRV_VERSION "0.2.5"
+#define DRV_VERSION "0.2.18"
/*
* CMD64x specific registers definition.
@@ -229,28 +230,85 @@ static void cmd64x_set_dmamode(struct ata_port *ap, struct ata_device *adev)
}
/**
- * cmd648_dma_stop - DMA stop callback
- * @qc: Command in progress
+ * cmd64x_sff_irq_check - check IDE interrupt
+ * @ap: ATA interface
*
- * DMA has completed.
+ * Check IDE interrupt in CFR/ARTTIM23 registers.
*/
-static void cmd648_bmdma_stop(struct ata_queued_cmd *qc)
+static bool cmd64x_sff_irq_check(struct ata_port *ap)
{
- struct ata_port *ap = qc->ap;
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- u8 dma_intr;
- int dma_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0;
- int dma_reg = ap->port_no ? ARTTIM23 : CFR;
+ int irq_mask = ap->port_no ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0;
+ int irq_reg = ap->port_no ? ARTTIM23 : CFR;
+ u8 irq_stat;
- ata_bmdma_stop(qc);
+ /* NOTE: reading the register should clear the interrupt */
+ pci_read_config_byte(pdev, irq_reg, &irq_stat);
+
+ return irq_stat & irq_mask;
+}
+
+/**
+ * cmd64x_sff_irq_clear - clear IDE interrupt
+ * @ap: ATA interface
+ *
+ * Clear IDE interrupt in CFR/ARTTIM23 and DMA status registers.
+ */
+
+static void cmd64x_sff_irq_clear(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ int irq_reg = ap->port_no ? ARTTIM23 : CFR;
+ u8 irq_stat;
+
+ ata_bmdma_irq_clear(ap);
- pci_read_config_byte(pdev, dma_reg, &dma_intr);
- pci_write_config_byte(pdev, dma_reg, dma_intr | dma_mask);
+ /* Reading the register should be enough to clear the interrupt */
+ pci_read_config_byte(pdev, irq_reg, &irq_stat);
}
/**
- * cmd646r1_dma_stop - DMA stop callback
+ * cmd648_sff_irq_check - check IDE interrupt
+ * @ap: ATA interface
+ *
+ * Check IDE interrupt in MRDMODE register.
+ */
+
+static bool cmd648_sff_irq_check(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ unsigned long base = pci_resource_start(pdev, 4);
+ int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0;
+ u8 mrdmode = inb(base + 1);
+
+ return mrdmode & irq_mask;
+}
+
+/**
+ * cmd648_sff_irq_clear - clear IDE interrupt
+ * @ap: ATA interface
+ *
+ * Clear IDE interrupt in MRDMODE and DMA status registers.
+ */
+
+static void cmd648_sff_irq_clear(struct ata_port *ap)
+{
+ struct pci_dev *pdev = to_pci_dev(ap->host->dev);
+ unsigned long base = pci_resource_start(pdev, 4);
+ int irq_mask = ap->port_no ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0;
+ u8 mrdmode;
+
+ ata_bmdma_irq_clear(ap);
+
+ /* Clear this port's interrupt bit (leaving the other port alone) */
+ mrdmode = inb(base + 1);
+ mrdmode &= ~(MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1);
+ outb(mrdmode | irq_mask, base + 1);
+}
+
+/**
+ * cmd646r1_bmdma_stop - DMA stop callback
* @qc: Command in progress
*
* Stub for now while investigating the r1 quirk in the old driver.
@@ -273,18 +331,30 @@ static const struct ata_port_operations cmd64x_base_ops = {
static struct ata_port_operations cmd64x_port_ops = {
.inherits = &cmd64x_base_ops,
+ .sff_irq_check = cmd64x_sff_irq_check,
+ .sff_irq_clear = cmd64x_sff_irq_clear,
.cable_detect = ata_cable_40wire,
};
static struct ata_port_operations cmd646r1_port_ops = {
.inherits = &cmd64x_base_ops,
+ .sff_irq_check = cmd64x_sff_irq_check,
+ .sff_irq_clear = cmd64x_sff_irq_clear,
.bmdma_stop = cmd646r1_bmdma_stop,
.cable_detect = ata_cable_40wire,
};
+static struct ata_port_operations cmd646r3_port_ops = {
+ .inherits = &cmd64x_base_ops,
+ .sff_irq_check = cmd648_sff_irq_check,
+ .sff_irq_clear = cmd648_sff_irq_clear,
+ .cable_detect = ata_cable_40wire,
+};
+
static struct ata_port_operations cmd648_port_ops = {
.inherits = &cmd64x_base_ops,
- .bmdma_stop = cmd648_bmdma_stop,
+ .sff_irq_check = cmd648_sff_irq_check,
+ .sff_irq_clear = cmd648_sff_irq_clear,
.cable_detect = cmd648_cable_detect,
};
@@ -306,7 +376,7 @@ static void cmd64x_fixup(struct pci_dev *pdev)
static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
- static const struct ata_port_info cmd_info[6] = {
+ static const struct ata_port_info cmd_info[7] = {
{ /* CMD 643 - no UDMA */
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
@@ -319,12 +389,18 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
.mwdma_mask = ATA_MWDMA2,
.port_ops = &cmd64x_port_ops
},
- { /* CMD 646 with working UDMA */
+ { /* CMD 646U with broken UDMA */
+ .flags = ATA_FLAG_SLAVE_POSS,
+ .pio_mask = ATA_PIO4,
+ .mwdma_mask = ATA_MWDMA2,
+ .port_ops = &cmd646r3_port_ops
+ },
+ { /* CMD 646U2 with working UDMA */
.flags = ATA_FLAG_SLAVE_POSS,
.pio_mask = ATA_PIO4,
.mwdma_mask = ATA_MWDMA2,
.udma_mask = ATA_UDMA2,
- .port_ops = &cmd64x_port_ops
+ .port_ops = &cmd646r3_port_ops
},
{ /* CMD 646 rev 1 */
.flags = ATA_FLAG_SLAVE_POSS,
@@ -368,21 +444,30 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
if (id->driver_data == 0) /* 643 */
ata_pci_bmdma_clear_simplex(pdev);
- if (pdev->device == PCI_DEVICE_ID_CMD_646) {
- /* Does UDMA work ? */
- if (pdev->revision > 4) {
- ppi[0] = &cmd_info[2];
- ppi[1] = &cmd_info[2];
- }
- /* Early rev with other problems ? */
- else if (pdev->revision == 1) {
+ if (pdev->device == PCI_DEVICE_ID_CMD_646)
+ switch (pdev->revision) {
+ /* UDMA works since rev 5 */
+ default:
ppi[0] = &cmd_info[3];
ppi[1] = &cmd_info[3];
- }
- /* revs 1,2 have no CNTRL_CH0 */
- if (pdev->revision < 3)
+ break;
+ /* Interrupts in MRDMODE since rev 3 */
+ case 3:
+ case 4:
+ ppi[0] = &cmd_info[2];
+ ppi[1] = &cmd_info[2];
+ break;
+ /* Rev 1 with other problems? */
+ case 1:
+ ppi[0] = &cmd_info[4];
+ ppi[1] = &cmd_info[4];
+ /* FALL THRU */
+ /* Early revs have no CNTRL_CH0 */
+ case 2:
+ case 0:
cntrl_ch0_ok = 0;
- }
+ break;
+ }
cmd64x_fixup(pdev);
@@ -423,8 +508,8 @@ static int cmd64x_reinit_one(struct pci_dev *pdev)
static const struct pci_device_id cmd64x[] = {
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 },
{ PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_646), 1 },
- { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 4 },
- { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 5 },
+ { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_648), 5 },
+ { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_649), 6 },
{ },
};
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index 35aca7d1a3eb..4fe9d2138d48 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -401,8 +401,7 @@ static void ht6560b_set_piomode(struct ata_port *ap, struct ata_device *adev)
ata_timing_compute(adev, adev->pio_mode, &t, 20000, 1000);
active = clamp_val(t.active, 2, 15);
- recover = clamp_val(t.recover, 2, 16);
- recover &= 0x15;
+ recover = clamp_val(t.recover, 2, 16) & 0x0F;
inb(0x3E6);
inb(0x3E6);
diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c
index 00748ae1a016..d2c102fd4330 100644
--- a/drivers/ata/pata_mpc52xx.c
+++ b/drivers/ata/pata_mpc52xx.c
@@ -687,11 +687,11 @@ mpc52xx_ata_probe(struct platform_device *op)
int ata_irq = 0;
struct mpc52xx_ata __iomem *ata_regs;
struct mpc52xx_ata_priv *priv = NULL;
- int rv, ret, task_irq = 0;
+ int rv, task_irq;
int mwdma_mask = 0, udma_mask = 0;
const __be32 *prop;
int proplen;
- struct bcom_task *dmatsk = NULL;
+ struct bcom_task *dmatsk;
/* Get ipb frequency */
ipb_freq = mpc5xxx_get_bus_frequency(op->dev.of_node);
@@ -717,8 +717,7 @@ mpc52xx_ata_probe(struct platform_device *op)
ata_regs = devm_ioremap(&op->dev, res_mem.start, sizeof(*ata_regs));
if (!ata_regs) {
dev_err(&op->dev, "error mapping device registers\n");
- rv = -ENOMEM;
- goto err;
+ return -ENOMEM;
}
/*
@@ -753,7 +752,7 @@ mpc52xx_ata_probe(struct platform_device *op)
if (!priv) {
dev_err(&op->dev, "error allocating private structure\n");
rv = -ENOMEM;
- goto err;
+ goto err1;
}
priv->ipb_period = 1000000000 / (ipb_freq / 1000);
@@ -776,15 +775,15 @@ mpc52xx_ata_probe(struct platform_device *op)
if (!dmatsk) {
dev_err(&op->dev, "bestcomm initialization failed\n");
rv = -ENOMEM;
- goto err;
+ goto err1;
}
task_irq = bcom_get_task_irq(dmatsk);
- ret = request_irq(task_irq, &mpc52xx_ata_task_irq, 0,
+ rv = devm_request_irq(&op->dev, task_irq, &mpc52xx_ata_task_irq, 0,
"ATA task", priv);
- if (ret) {
+ if (rv) {
dev_err(&op->dev, "error requesting DMA IRQ\n");
- goto err;
+ goto err2;
}
priv->dmatsk = dmatsk;
@@ -792,7 +791,7 @@ mpc52xx_ata_probe(struct platform_device *op)
rv = mpc52xx_ata_hw_init(priv);
if (rv) {
dev_err(&op->dev, "error initializing hardware\n");
- goto err;
+ goto err2;
}
/* Register ourselves to libata */
@@ -800,23 +799,16 @@ mpc52xx_ata_probe(struct platform_device *op)
mwdma_mask, udma_mask);
if (rv) {
dev_err(&op->dev, "error registering with ATA layer\n");
- goto err;
+ goto err2;
}
return 0;
- err:
- devm_release_mem_region(&op->dev, res_mem.start, sizeof(*ata_regs));
- if (ata_irq)
- irq_dispose_mapping(ata_irq);
- if (task_irq)
- irq_dispose_mapping(task_irq);
- if (dmatsk)
- bcom_ata_release(dmatsk);
- if (ata_regs)
- devm_iounmap(&op->dev, ata_regs);
- if (priv)
- devm_kfree(&op->dev, priv);
+ err2:
+ irq_dispose_mapping(task_irq);
+ bcom_ata_release(dmatsk);
+ err1:
+ irq_dispose_mapping(ata_irq);
return rv;
}
@@ -835,12 +827,6 @@ mpc52xx_ata_remove(struct platform_device *op)
bcom_ata_release(priv->dmatsk);
irq_dispose_mapping(priv->ata_irq);
- /* Clear up IO allocations */
- devm_iounmap(&op->dev, priv->ata_regs);
- devm_release_mem_region(&op->dev, priv->ata_regs_pa,
- sizeof(*priv->ata_regs));
- devm_kfree(&op->dev, priv);
-
return 0;
}
diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c
index 0120b0d1e9a5..d6577b93bee3 100644
--- a/drivers/ata/sata_fsl.c
+++ b/drivers/ata/sata_fsl.c
@@ -6,7 +6,7 @@
* Author: Ashish Kalra <ashish.kalra@freescale.com>
* Li Yang <leoli@freescale.com>
*
- * Copyright (c) 2006-2007, 2011 Freescale Semiconductor, Inc.
+ * Copyright (c) 2006-2007, 2011-2012 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -26,6 +26,15 @@
#include <asm/io.h>
#include <linux/of_platform.h>
+static unsigned int intr_coalescing_count;
+module_param(intr_coalescing_count, int, S_IRUGO);
+MODULE_PARM_DESC(intr_coalescing_count,
+ "INT coalescing count threshold (1..31)");
+
+static unsigned int intr_coalescing_ticks;
+module_param(intr_coalescing_ticks, int, S_IRUGO);
+MODULE_PARM_DESC(intr_coalescing_ticks,
+ "INT coalescing timer threshold in AHB ticks");
/* Controller information */
enum {
SATA_FSL_QUEUE_DEPTH = 16,
@@ -83,6 +92,16 @@ enum {
};
/*
+ * Interrupt Coalescing Control Register bitdefs */
+enum {
+ ICC_MIN_INT_COUNT_THRESHOLD = 1,
+ ICC_MAX_INT_COUNT_THRESHOLD = ((1 << 5) - 1),
+ ICC_MIN_INT_TICKS_THRESHOLD = 0,
+ ICC_MAX_INT_TICKS_THRESHOLD = ((1 << 19) - 1),
+ ICC_SAFE_INT_TICKS = 1,
+};
+
+/*
* Host Controller command register set - per port
*/
enum {
@@ -263,8 +282,65 @@ struct sata_fsl_host_priv {
void __iomem *csr_base;
int irq;
int data_snoop;
+ struct device_attribute intr_coalescing;
};
+static void fsl_sata_set_irq_coalescing(struct ata_host *host,
+ unsigned int count, unsigned int ticks)
+{
+ struct sata_fsl_host_priv *host_priv = host->private_data;
+ void __iomem *hcr_base = host_priv->hcr_base;
+
+ if (count > ICC_MAX_INT_COUNT_THRESHOLD)
+ count = ICC_MAX_INT_COUNT_THRESHOLD;
+ else if (count < ICC_MIN_INT_COUNT_THRESHOLD)
+ count = ICC_MIN_INT_COUNT_THRESHOLD;
+
+ if (ticks > ICC_MAX_INT_TICKS_THRESHOLD)
+ ticks = ICC_MAX_INT_TICKS_THRESHOLD;
+ else if ((ICC_MIN_INT_TICKS_THRESHOLD == ticks) &&
+ (count > ICC_MIN_INT_COUNT_THRESHOLD))
+ ticks = ICC_SAFE_INT_TICKS;
+
+ spin_lock(&host->lock);
+ iowrite32((count << 24 | ticks), hcr_base + ICC);
+
+ intr_coalescing_count = count;
+ intr_coalescing_ticks = ticks;
+ spin_unlock(&host->lock);
+
+ DPRINTK("intrrupt coalescing, count = 0x%x, ticks = %x\n",
+ intr_coalescing_count, intr_coalescing_ticks);
+ DPRINTK("ICC register status: (hcr base: 0x%x) = 0x%x\n",
+ hcr_base, ioread32(hcr_base + ICC));
+}
+
+static ssize_t fsl_sata_intr_coalescing_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d %d\n",
+ intr_coalescing_count, intr_coalescing_ticks);
+}
+
+static ssize_t fsl_sata_intr_coalescing_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned int coalescing_count, coalescing_ticks;
+
+ if (sscanf(buf, "%d%d",
+ &coalescing_count,
+ &coalescing_ticks) != 2) {
+ printk(KERN_ERR "fsl-sata: wrong parameter format.\n");
+ return -EINVAL;
+ }
+
+ fsl_sata_set_irq_coalescing(dev_get_drvdata(dev),
+ coalescing_count, coalescing_ticks);
+
+ return strlen(buf);
+}
+
static inline unsigned int sata_fsl_tag(unsigned int tag,
void __iomem *hcr_base)
{
@@ -346,10 +422,10 @@ static unsigned int sata_fsl_fill_sg(struct ata_queued_cmd *qc, void *cmd_desc,
(unsigned long long)sg_addr, sg_len);
/* warn if each s/g element is not dword aligned */
- if (sg_addr & 0x03)
+ if (unlikely(sg_addr & 0x03))
ata_port_err(qc->ap, "s/g addr unaligned : 0x%llx\n",
(unsigned long long)sg_addr);
- if (sg_len & 0x03)
+ if (unlikely(sg_len & 0x03))
ata_port_err(qc->ap, "s/g len unaligned : 0x%x\n",
sg_len);
@@ -1245,6 +1321,13 @@ static int sata_fsl_init_controller(struct ata_host *host)
iowrite32(0x00000FFFF, hcr_base + CE);
iowrite32(0x00000FFFF, hcr_base + DE);
+ /*
+ * reset the number of command complete bits which will cause the
+ * interrupt to be signaled
+ */
+ fsl_sata_set_irq_coalescing(host, intr_coalescing_count,
+ intr_coalescing_ticks);
+
/*
* host controller will be brought on-line, during xx_port_start()
* callback, that should also initiate the OOB, COMINIT sequence
@@ -1309,7 +1392,7 @@ static int sata_fsl_probe(struct platform_device *ofdev)
void __iomem *csr_base = NULL;
struct sata_fsl_host_priv *host_priv = NULL;
int irq;
- struct ata_host *host;
+ struct ata_host *host = NULL;
u32 temp;
struct ata_port_info pi = sata_fsl_port_info[0];
@@ -1356,6 +1439,10 @@ static int sata_fsl_probe(struct platform_device *ofdev)
/* allocate host structure */
host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS);
+ if (!host) {
+ retval = -ENOMEM;
+ goto error_exit_with_cleanup;
+ }
/* host->iomap is not used currently */
host->private_data = host_priv;
@@ -1373,10 +1460,24 @@ static int sata_fsl_probe(struct platform_device *ofdev)
dev_set_drvdata(&ofdev->dev, host);
+ host_priv->intr_coalescing.show = fsl_sata_intr_coalescing_show;
+ host_priv->intr_coalescing.store = fsl_sata_intr_coalescing_store;
+ sysfs_attr_init(&host_priv->intr_coalescing.attr);
+ host_priv->intr_coalescing.attr.name = "intr_coalescing";
+ host_priv->intr_coalescing.attr.mode = S_IRUGO | S_IWUSR;
+ retval = device_create_file(host->dev, &host_priv->intr_coalescing);
+ if (retval)
+ goto error_exit_with_cleanup;
+
return 0;
error_exit_with_cleanup:
+ if (host) {
+ dev_set_drvdata(&ofdev->dev, NULL);
+ ata_host_detach(host);
+ }
+
if (hcr_base)
iounmap(hcr_base);
if (host_priv)
@@ -1390,6 +1491,8 @@ static int sata_fsl_remove(struct platform_device *ofdev)
struct ata_host *host = dev_get_drvdata(&ofdev->dev);
struct sata_fsl_host_priv *host_priv = host->private_data;
+ device_remove_file(&ofdev->dev, &host_priv->intr_coalescing);
+
ata_host_detach(host);
dev_set_drvdata(&ofdev->dev, NULL);
diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c
index 6ff612d099c3..2059ee460b0c 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 5072f8ac16fd..86fed1b91695 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 b81210330aca..75fd691cd43e 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 487a54739854..45d506363aba 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 9e373ba20308..d4386019af5d 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 90f1ccca9e52..02159345566c 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 d889f56e8d8c..abe4e20b0766 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 e38ad243b4bb..07cbbc6fddb4 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/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 428e55e012dc..869d7ff2227f 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -8,6 +8,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/io.h>
#include <linux/pm.h>
#include <linux/pm_clock.h>
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 4af7c1cbf909..a14085cc613f 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -8,6 +8,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/pm_clock.h>
diff --git a/drivers/base/power/opp.c b/drivers/base/power/opp.c
index 95706fa24c73..ac993eafec82 100644
--- a/drivers/base/power/opp.c
+++ b/drivers/base/power/opp.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/cpufreq.h>
+#include <linux/device.h>
#include <linux/list.h>
#include <linux/rculist.h>
#include <linux/rcupdate.h>
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 1a02b7537c8b..fcafc5b2e651 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -22,6 +22,7 @@ struct regcache_ops;
struct regmap_format {
size_t buf_size;
size_t reg_bytes;
+ size_t pad_bytes;
size_t val_bytes;
void (*format_write)(struct regmap *map,
unsigned int reg, unsigned int val);
@@ -65,16 +66,19 @@ struct regmap {
unsigned int num_reg_defaults_raw;
/* if set, only the cache is modified not the HW */
- unsigned int cache_only:1;
+ u32 cache_only;
/* if set, only the HW is modified not the cache */
- unsigned int cache_bypass:1;
+ u32 cache_bypass;
/* if set, remember to free reg_defaults_raw */
- unsigned int cache_free:1;
+ bool cache_free;
struct reg_default *reg_defaults;
const void *reg_defaults_raw;
void *cache;
- bool cache_dirty;
+ u32 cache_dirty;
+
+ struct reg_default *patch;
+ int patch_regs;
};
struct regcache_ops {
@@ -84,7 +88,7 @@ struct regcache_ops {
int (*exit)(struct regmap *map);
int (*read)(struct regmap *map, unsigned int reg, unsigned int *value);
int (*write)(struct regmap *map, unsigned int reg, unsigned int value);
- int (*sync)(struct regmap *map);
+ int (*sync)(struct regmap *map, unsigned int min, unsigned int max);
};
bool regmap_writeable(struct regmap *map, unsigned int reg);
diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c
index b7d16143edeb..483b06d4a380 100644
--- a/drivers/base/regmap/regcache-lzo.c
+++ b/drivers/base/regmap/regcache-lzo.c
@@ -11,6 +11,7 @@
*/
#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/lzo.h>
#include "internal.h"
@@ -331,7 +332,8 @@ out:
return ret;
}
-static int regcache_lzo_sync(struct regmap *map)
+static int regcache_lzo_sync(struct regmap *map, unsigned int min,
+ unsigned int max)
{
struct regcache_lzo_ctx **lzo_blocks;
unsigned int val;
@@ -339,10 +341,21 @@ static int regcache_lzo_sync(struct regmap *map)
int ret;
lzo_blocks = map->cache;
- for_each_set_bit(i, lzo_blocks[0]->sync_bmp, lzo_blocks[0]->sync_bmp_nbits) {
+ i = min;
+ for_each_set_bit_from(i, lzo_blocks[0]->sync_bmp,
+ lzo_blocks[0]->sync_bmp_nbits) {
+ if (i > max)
+ continue;
+
ret = regcache_read(map, i, &val);
if (ret)
return ret;
+
+ /* Is this the hardware default? If so skip. */
+ ret = regcache_lookup_reg(map, i);
+ if (ret > 0 && val == map->reg_defaults[ret].def)
+ continue;
+
map->cache_bypass = 1;
ret = _regmap_write(map, i, val);
map->cache_bypass = 0;
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c
index 32620c4f1683..5157fa04c2f0 100644
--- a/drivers/base/regmap/regcache-rbtree.c
+++ b/drivers/base/regmap/regcache-rbtree.c
@@ -11,6 +11,7 @@
*/
#include <linux/slab.h>
+#include <linux/device.h>
#include <linux/debugfs.h>
#include <linux/rbtree.h>
#include <linux/seq_file.h>
@@ -357,7 +358,8 @@ static int regcache_rbtree_write(struct regmap *map, unsigned int reg,
return 0;
}
-static int regcache_rbtree_sync(struct regmap *map)
+static int regcache_rbtree_sync(struct regmap *map, unsigned int min,
+ unsigned int max)
{
struct regcache_rbtree_ctx *rbtree_ctx;
struct rb_node *node;
@@ -365,19 +367,37 @@ static int regcache_rbtree_sync(struct regmap *map)
unsigned int regtmp;
unsigned int val;
int ret;
- int i;
+ int i, base, end;
rbtree_ctx = map->cache;
for (node = rb_first(&rbtree_ctx->root); node; node = rb_next(node)) {
rbnode = rb_entry(node, struct regcache_rbtree_node, node);
- for (i = 0; i < rbnode->blklen; i++) {
+
+ if (rbnode->base_reg < min)
+ continue;
+ if (rbnode->base_reg > max)
+ break;
+ if (rbnode->base_reg + rbnode->blklen < min)
+ continue;
+
+ if (min > rbnode->base_reg)
+ base = min - rbnode->base_reg;
+ else
+ base = 0;
+
+ if (max < rbnode->base_reg + rbnode->blklen)
+ end = rbnode->base_reg + rbnode->blklen - max;
+ else
+ end = rbnode->blklen;
+
+ for (i = base; i < end; i++) {
regtmp = rbnode->base_reg + i;
val = regcache_rbtree_get_register(rbnode, i,
map->cache_word_size);
/* Is this the hardware default? If so skip. */
ret = regcache_lookup_reg(map, i);
- if (ret > 0 && val == map->reg_defaults[ret].def)
+ if (ret >= 0 && val == map->reg_defaults[ret].def)
continue;
map->cache_bypass = 1;
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index d1daa5e9fadf..87f54dbf601b 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -12,6 +12,7 @@
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/device.h>
#include <trace/events/regmap.h>
#include <linux/bsearch.h>
#include <linux/sort.h>
@@ -35,12 +36,17 @@ static int regcache_hw_init(struct regmap *map)
return -EINVAL;
if (!map->reg_defaults_raw) {
+ u32 cache_bypass = map->cache_bypass;
dev_warn(map->dev, "No cache defaults, reading back from HW\n");
+
+ /* Bypass the cache access till data read from HW*/
+ map->cache_bypass = 1;
tmp_buf = kmalloc(map->cache_size_raw, GFP_KERNEL);
if (!tmp_buf)
return -EINVAL;
ret = regmap_bulk_read(map, 0, tmp_buf,
map->num_reg_defaults_raw);
+ map->cache_bypass = cache_bypass;
if (ret < 0) {
kfree(tmp_buf);
return ret;
@@ -211,7 +217,6 @@ int regcache_read(struct regmap *map,
return -EINVAL;
}
-EXPORT_SYMBOL_GPL(regcache_read);
/**
* regcache_write: Set the value of a given register in the cache.
@@ -238,7 +243,6 @@ int regcache_write(struct regmap *map,
return 0;
}
-EXPORT_SYMBOL_GPL(regcache_write);
/**
* regcache_sync: Sync the register cache with the hardware.
@@ -254,12 +258,11 @@ EXPORT_SYMBOL_GPL(regcache_write);
int regcache_sync(struct regmap *map)
{
int ret = 0;
- unsigned int val;
unsigned int i;
const char *name;
unsigned int bypass;
- BUG_ON(!map->cache_ops);
+ BUG_ON(!map->cache_ops || !map->cache_ops->sync);
mutex_lock(&map->lock);
/* Remember the initial bypass state */
@@ -268,26 +271,27 @@ int regcache_sync(struct regmap *map)
map->cache_ops->name);
name = map->cache_ops->name;
trace_regcache_sync(map->dev, name, "start");
+
if (!map->cache_dirty)
goto out;
- if (map->cache_ops->sync) {
- ret = map->cache_ops->sync(map);
- } else {
- for (i = 0; i < map->num_reg_defaults; i++) {
- ret = regcache_read(map, i, &val);
- if (ret < 0)
- goto out;
- map->cache_bypass = 1;
- ret = _regmap_write(map, i, val);
- map->cache_bypass = 0;
- if (ret < 0)
- goto out;
- dev_dbg(map->dev, "Synced register %#x, value %#x\n",
- map->reg_defaults[i].reg,
- map->reg_defaults[i].def);
- }
+ /* Apply any patch first */
+ map->cache_bypass = 1;
+ for (i = 0; i < map->patch_regs; i++) {
+ ret = _regmap_write(map, map->patch[i].reg, map->patch[i].def);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to write %x = %x: %d\n",
+ map->patch[i].reg, map->patch[i].def, ret);
+ goto out;
+ }
}
+ map->cache_bypass = 0;
+
+ ret = map->cache_ops->sync(map, 0, map->max_register);
+
+ if (ret == 0)
+ map->cache_dirty = false;
+
out:
trace_regcache_sync(map->dev, name, "stop");
/* Restore the bypass state */
@@ -299,6 +303,51 @@ out:
EXPORT_SYMBOL_GPL(regcache_sync);
/**
+ * regcache_sync_region: Sync part of the register cache with the hardware.
+ *
+ * @map: map to sync.
+ * @min: first register to sync
+ * @max: last register to sync
+ *
+ * Write all non-default register values in the specified region to
+ * the hardware.
+ *
+ * Return a negative value on failure, 0 on success.
+ */
+int regcache_sync_region(struct regmap *map, unsigned int min,
+ unsigned int max)
+{
+ int ret = 0;
+ const char *name;
+ unsigned int bypass;
+
+ BUG_ON(!map->cache_ops || !map->cache_ops->sync);
+
+ mutex_lock(&map->lock);
+
+ /* Remember the initial bypass state */
+ bypass = map->cache_bypass;
+
+ name = map->cache_ops->name;
+ dev_dbg(map->dev, "Syncing %s cache from %d-%d\n", name, min, max);
+
+ trace_regcache_sync(map->dev, name, "start region");
+
+ if (!map->cache_dirty)
+ goto out;
+
+ ret = map->cache_ops->sync(map, min, max);
+
+out:
+ trace_regcache_sync(map->dev, name, "stop region");
+ /* Restore the bypass state */
+ map->cache_bypass = bypass;
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+
+/**
* regcache_cache_only: Put a register map into cache only mode
*
* @map: map to configure
@@ -315,6 +364,7 @@ void regcache_cache_only(struct regmap *map, bool enable)
mutex_lock(&map->lock);
WARN_ON(map->cache_bypass && enable);
map->cache_only = enable;
+ trace_regmap_cache_only(map->dev, enable);
mutex_unlock(&map->lock);
}
EXPORT_SYMBOL_GPL(regcache_cache_only);
@@ -352,6 +402,7 @@ void regcache_cache_bypass(struct regmap *map, bool enable)
mutex_lock(&map->lock);
WARN_ON(map->cache_only && enable);
map->cache_bypass = enable;
+ trace_regmap_cache_bypass(map->dev, enable);
mutex_unlock(&map->lock);
}
EXPORT_SYMBOL_GPL(regcache_cache_bypass);
@@ -374,10 +425,16 @@ bool regcache_set_val(void *base, unsigned int idx,
cache[idx] = val;
break;
}
+ case 4: {
+ u32 *cache = base;
+ if (cache[idx] == val)
+ return true;
+ cache[idx] = val;
+ break;
+ }
default:
BUG();
}
- /* unreachable */
return false;
}
@@ -396,6 +453,10 @@ unsigned int regcache_get_val(const void *base, unsigned int idx,
const u16 *cache = base;
return cache[idx];
}
+ case 4: {
+ const u32 *cache = base;
+ return cache[idx];
+ }
default:
BUG();
}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 6f397476e27c..58517a5dac13 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -11,10 +11,10 @@
*/
#include <linux/slab.h>
-#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/debugfs.h>
#include <linux/uaccess.h>
+#include <linux/device.h>
#include "internal.h"
@@ -33,6 +33,35 @@ static int regmap_open_file(struct inode *inode, struct file *file)
return 0;
}
+static ssize_t regmap_name_read_file(struct file *file,
+ char __user *user_buf, size_t count,
+ loff_t *ppos)
+{
+ struct regmap *map = file->private_data;
+ int ret;
+ char *buf;
+
+ buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name);
+ if (ret < 0) {
+ kfree(buf);
+ return ret;
+ }
+
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations regmap_name_fops = {
+ .open = regmap_open_file,
+ .read = regmap_name_read_file,
+ .llseek = default_llseek,
+};
+
static ssize_t regmap_map_read_file(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
@@ -103,9 +132,51 @@ out:
return ret;
}
+#undef REGMAP_ALLOW_WRITE_DEBUGFS
+#ifdef REGMAP_ALLOW_WRITE_DEBUGFS
+/*
+ * This can be dangerous especially when we have clients such as
+ * PMICs, therefore don't provide any real compile time configuration option
+ * for this feature, people who want to use this will need to modify
+ * the source code directly.
+ */
+static ssize_t regmap_map_write_file(struct file *file,
+ const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ char buf[32];
+ size_t buf_size;
+ char *start = buf;
+ unsigned long reg, value;
+ struct regmap *map = file->private_data;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+ buf[buf_size] = 0;
+
+ while (*start == ' ')
+ start++;
+ reg = simple_strtoul(start, &start, 16);
+ while (*start == ' ')
+ start++;
+ if (strict_strtoul(start, 16, &value))
+ return -EINVAL;
+
+ /* Userspace has been fiddling around behind the kernel's back */
+ add_taint(TAINT_USER);
+
+ regmap_write(map, reg, value);
+ return buf_size;
+}
+#else
+#define regmap_map_write_file NULL
+#endif
+
static const struct file_operations regmap_map_fops = {
.open = regmap_open_file,
.read = regmap_map_read_file,
+ .write = regmap_map_write_file,
.llseek = default_llseek,
};
@@ -186,12 +257,24 @@ void regmap_debugfs_init(struct regmap *map)
return;
}
+ debugfs_create_file("name", 0400, map->debugfs,
+ map, &regmap_name_fops);
+
if (map->max_register) {
debugfs_create_file("registers", 0400, map->debugfs,
map, &regmap_map_fops);
debugfs_create_file("access", 0400, map->debugfs,
map, &regmap_access_fops);
}
+
+ if (map->cache_type) {
+ debugfs_create_bool("cache_only", 0400, map->debugfs,
+ &map->cache_only);
+ debugfs_create_bool("cache_dirty", 0400, map->debugfs,
+ &map->cache_dirty);
+ debugfs_create_bool("cache_bypass", 0400, map->debugfs,
+ &map->cache_bypass);
+ }
}
void regmap_debugfs_exit(struct regmap *map)
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index 38621ec87c05..9a3a8c564389 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -111,4 +111,21 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c,
}
EXPORT_SYMBOL_GPL(regmap_init_i2c);
+/**
+ * devm_regmap_init_i2c(): Initialise managed register map
+ *
+ * @i2c: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(&i2c->dev, &regmap_i2c, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index 428836fc5835..1befaa7a31cb 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -11,6 +11,7 @@
*/
#include <linux/export.h>
+#include <linux/device.h>
#include <linux/regmap.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
index 2560658de344..7c0c35a39c33 100644
--- a/drivers/base/regmap/regmap-spi.c
+++ b/drivers/base/regmap/regmap-spi.c
@@ -70,4 +70,21 @@ struct regmap *regmap_init_spi(struct spi_device *spi,
}
EXPORT_SYMBOL_GPL(regmap_init_spi);
+/**
+ * devm_regmap_init_spi(): Initialise register map
+ *
+ * @spi: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The map will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_spi(struct spi_device *spi,
+ const struct regmap_config *config)
+{
+ return devm_regmap_init(&spi->dev, &regmap_spi, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_spi);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 65558034318f..7a3f535e481c 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -10,8 +10,9 @@
* published by the Free Software Foundation.
*/
+#include <linux/device.h>
#include <linux/slab.h>
-#include <linux/module.h>
+#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/err.h>
@@ -36,6 +37,9 @@ bool regmap_readable(struct regmap *map, unsigned int reg)
if (map->max_register && reg > map->max_register)
return false;
+ if (map->format.format_write)
+ return false;
+
if (map->readable_reg)
return map->readable_reg(map->dev, reg);
@@ -44,7 +48,7 @@ bool regmap_readable(struct regmap *map, unsigned int reg)
bool regmap_volatile(struct regmap *map, unsigned int reg)
{
- if (map->max_register && reg > map->max_register)
+ if (!regmap_readable(map, reg))
return false;
if (map->volatile_reg)
@@ -55,7 +59,7 @@ bool regmap_volatile(struct regmap *map, unsigned int reg)
bool regmap_precious(struct regmap *map, unsigned int reg)
{
- if (map->max_register && reg > map->max_register)
+ if (!regmap_readable(map, reg))
return false;
if (map->precious_reg)
@@ -76,6 +80,14 @@ static bool regmap_volatile_range(struct regmap *map, unsigned int reg,
return true;
}
+static void regmap_format_2_6_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ *out = (reg << 6) | val;
+}
+
static void regmap_format_4_12_write(struct regmap *map,
unsigned int reg, unsigned int val)
{
@@ -114,6 +126,13 @@ static void regmap_format_16(void *buf, unsigned int val)
b[0] = cpu_to_be16(val);
}
+static void regmap_format_32(void *buf, unsigned int val)
+{
+ __be32 *b = buf;
+
+ b[0] = cpu_to_be32(val);
+}
+
static unsigned int regmap_parse_8(void *buf)
{
u8 *b = buf;
@@ -130,6 +149,15 @@ static unsigned int regmap_parse_16(void *buf)
return b[0];
}
+static unsigned int regmap_parse_32(void *buf)
+{
+ __be32 *b = buf;
+
+ b[0] = be32_to_cpu(b[0]);
+
+ return b[0];
+}
+
/**
* regmap_init(): Initialise register map
*
@@ -159,8 +187,10 @@ struct regmap *regmap_init(struct device *dev,
mutex_init(&map->lock);
map->format.buf_size = (config->reg_bits + config->val_bits) / 8;
- map->format.reg_bytes = config->reg_bits / 8;
- map->format.val_bytes = config->val_bits / 8;
+ map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
+ map->format.pad_bytes = config->pad_bits / 8;
+ map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
+ map->format.buf_size += map->format.pad_bytes;
map->dev = dev;
map->bus = bus;
map->max_register = config->max_register;
@@ -178,6 +208,16 @@ struct regmap *regmap_init(struct device *dev,
}
switch (config->reg_bits) {
+ case 2:
+ switch (config->val_bits) {
+ case 6:
+ map->format.format_write = regmap_format_2_6_write;
+ break;
+ default:
+ goto err_map;
+ }
+ break;
+
case 4:
switch (config->val_bits) {
case 12:
@@ -216,6 +256,10 @@ struct regmap *regmap_init(struct device *dev,
map->format.format_reg = regmap_format_16;
break;
+ case 32:
+ map->format.format_reg = regmap_format_32;
+ break;
+
default:
goto err_map;
}
@@ -229,13 +273,17 @@ struct regmap *regmap_init(struct device *dev,
map->format.format_val = regmap_format_16;
map->format.parse_val = regmap_parse_16;
break;
+ case 32:
+ map->format.format_val = regmap_format_32;
+ map->format.parse_val = regmap_parse_32;
+ break;
}
if (!map->format.format_write &&
!(map->format.format_reg && map->format.format_val))
goto err_map;
- map->work_buf = kmalloc(map->format.buf_size, GFP_KERNEL);
+ map->work_buf = kzalloc(map->format.buf_size, GFP_KERNEL);
if (map->work_buf == NULL) {
ret = -ENOMEM;
goto err_map;
@@ -258,6 +306,45 @@ err:
}
EXPORT_SYMBOL_GPL(regmap_init);
+static void devm_regmap_release(struct device *dev, void *res)
+{
+ regmap_exit(*(struct regmap **)res);
+}
+
+/**
+ * devm_regmap_init(): Initialise managed register map
+ *
+ * @dev: Device that will be interacted with
+ * @bus: Bus-specific callbacks to use with device
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. This function should generally not be called
+ * directly, it should be called by bus-specific init functions. The
+ * map will be automatically freed by the device management code.
+ */
+struct regmap *devm_regmap_init(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ struct regmap **ptr, *regmap;
+
+ ptr = devres_alloc(devm_regmap_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ regmap = regmap_init(dev, bus, config);
+ if (!IS_ERR(regmap)) {
+ *ptr = regmap;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return regmap;
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init);
+
/**
* regmap_reinit_cache(): Reinitialise the current register cache
*
@@ -276,6 +363,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
mutex_lock(&map->lock);
regcache_exit(map);
+ regmap_debugfs_exit(map);
map->max_register = config->max_register;
map->writeable_reg = config->writeable_reg;
@@ -284,6 +372,8 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
map->precious_reg = config->precious_reg;
map->cache_type = config->cache_type;
+ regmap_debugfs_init(map);
+
map->cache_bypass = false;
map->cache_only = false;
@@ -321,6 +411,26 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
if (!map->writeable_reg(map->dev, reg + i))
return -EINVAL;
+ if (!map->cache_bypass && map->format.parse_val) {
+ unsigned int ival;
+ int val_bytes = map->format.val_bytes;
+ for (i = 0; i < val_len / val_bytes; i++) {
+ memcpy(map->work_buf, val + (i * val_bytes), val_bytes);
+ ival = map->format.parse_val(map->work_buf);
+ ret = regcache_write(map, reg + i, ival);
+ if (ret) {
+ dev_err(map->dev,
+ "Error in caching of register: %u ret: %d\n",
+ reg + i, ret);
+ return ret;
+ }
+ }
+ if (map->cache_only) {
+ map->cache_dirty = true;
+ return 0;
+ }
+ }
+
map->format.format_reg(map->work_buf, reg);
u8[0] |= map->write_flag_mask;
@@ -332,23 +442,28 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg,
* send the work_buf directly, otherwise try to do a gather
* write.
*/
- if (val == map->work_buf + map->format.reg_bytes)
+ if (val == (map->work_buf + map->format.pad_bytes +
+ map->format.reg_bytes))
ret = map->bus->write(map->dev, map->work_buf,
- map->format.reg_bytes + val_len);
+ map->format.reg_bytes +
+ map->format.pad_bytes +
+ val_len);
else if (map->bus->gather_write)
ret = map->bus->gather_write(map->dev, map->work_buf,
- map->format.reg_bytes,
+ map->format.reg_bytes +
+ map->format.pad_bytes,
val, val_len);
/* If that didn't work fall back on linearising by hand. */
if (ret == -ENOTSUPP) {
- len = map->format.reg_bytes + val_len;
- buf = kmalloc(len, GFP_KERNEL);
+ len = map->format.reg_bytes + map->format.pad_bytes + val_len;
+ buf = kzalloc(len, GFP_KERNEL);
if (!buf)
return -ENOMEM;
memcpy(buf, map->work_buf, map->format.reg_bytes);
- memcpy(buf + map->format.reg_bytes, val, val_len);
+ memcpy(buf + map->format.reg_bytes + map->format.pad_bytes,
+ val, val_len);
ret = map->bus->write(map->dev, buf, len);
kfree(buf);
@@ -366,7 +481,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
int ret;
BUG_ON(!map->format.format_write && !map->format.format_val);
- if (!map->cache_bypass) {
+ if (!map->cache_bypass && map->format.format_write) {
ret = regcache_write(map, reg, val);
if (ret != 0)
return ret;
@@ -390,10 +505,12 @@ int _regmap_write(struct regmap *map, unsigned int reg,
return ret;
} else {
- map->format.format_val(map->work_buf + map->format.reg_bytes,
- val);
+ map->format.format_val(map->work_buf + map->format.reg_bytes
+ + map->format.pad_bytes, val);
return _regmap_raw_write(map, reg,
- map->work_buf + map->format.reg_bytes,
+ map->work_buf +
+ map->format.reg_bytes +
+ map->format.pad_bytes,
map->format.val_bytes);
}
}
@@ -441,12 +558,8 @@ EXPORT_SYMBOL_GPL(regmap_write);
int regmap_raw_write(struct regmap *map, unsigned int reg,
const void *val, size_t val_len)
{
- size_t val_count = val_len / map->format.val_bytes;
int ret;
- WARN_ON(!regmap_volatile_range(map, reg, val_count) &&
- map->cache_type != REGCACHE_NONE);
-
mutex_lock(&map->lock);
ret = _regmap_raw_write(map, reg, val, val_len);
@@ -457,6 +570,56 @@ int regmap_raw_write(struct regmap *map, unsigned int reg,
}
EXPORT_SYMBOL_GPL(regmap_raw_write);
+/*
+ * regmap_bulk_write(): Write multiple registers to the device
+ *
+ * @map: Register map to write to
+ * @reg: First register to be write from
+ * @val: Block of data to be written, in native register size for device
+ * @val_count: Number of registers to write
+ *
+ * This function is intended to be used for writing a large block of
+ * data to be device either in single transfer or multiple transfer.
+ *
+ * A value of zero will be returned on success, a negative errno will
+ * be returned in error cases.
+ */
+int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
+ size_t val_count)
+{
+ int ret = 0, i;
+ size_t val_bytes = map->format.val_bytes;
+ void *wval;
+
+ if (!map->format.parse_val)
+ return -EINVAL;
+
+ mutex_lock(&map->lock);
+
+ /* No formatting is require if val_byte is 1 */
+ if (val_bytes == 1) {
+ wval = (void *)val;
+ } else {
+ wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
+ if (!wval) {
+ ret = -ENOMEM;
+ dev_err(map->dev, "Error in memory allocation\n");
+ goto out;
+ }
+ for (i = 0; i < val_count * val_bytes; i += val_bytes)
+ map->format.parse_val(wval + i);
+ }
+ ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count);
+
+ if (val_bytes != 1)
+ kfree(wval);
+
+out:
+ mutex_unlock(&map->lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_bulk_write);
+
static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
unsigned int val_len)
{
@@ -476,7 +639,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
trace_regmap_hw_read_start(map->dev, reg,
val_len / map->format.val_bytes);
- ret = map->bus->read(map->dev, map->work_buf, map->format.reg_bytes,
+ ret = map->bus->read(map->dev, map->work_buf,
+ map->format.reg_bytes + map->format.pad_bytes,
val, val_len);
trace_regmap_hw_read_done(map->dev, reg,
@@ -549,16 +713,32 @@ EXPORT_SYMBOL_GPL(regmap_read);
int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
size_t val_len)
{
- size_t val_count = val_len / map->format.val_bytes;
- int ret;
-
- WARN_ON(!regmap_volatile_range(map, reg, val_count) &&
- map->cache_type != REGCACHE_NONE);
+ size_t val_bytes = map->format.val_bytes;
+ size_t val_count = val_len / val_bytes;
+ unsigned int v;
+ int ret, i;
mutex_lock(&map->lock);
- ret = _regmap_raw_read(map, reg, val, val_len);
+ if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass ||
+ map->cache_type == REGCACHE_NONE) {
+ /* Physical block read if there's no cache involved */
+ ret = _regmap_raw_read(map, reg, val, val_len);
+ } else {
+ /* Otherwise go word by word for the cache; should be low
+ * cost as we expect to hit the cache.
+ */
+ for (i = 0; i < val_count; i++) {
+ ret = _regmap_read(map, reg + i, &v);
+ if (ret != 0)
+ goto out;
+
+ map->format.format_val(val + (i * val_bytes), v);
+ }
+ }
+
+ out:
mutex_unlock(&map->lock);
return ret;
@@ -672,6 +852,79 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg,
}
EXPORT_SYMBOL_GPL(regmap_update_bits_check);
+/**
+ * regmap_register_patch: Register and apply register updates to be applied
+ * on device initialistion
+ *
+ * @map: Register map to apply updates to.
+ * @regs: Values to update.
+ * @num_regs: Number of entries in regs.
+ *
+ * Register a set of register updates to be applied to the device
+ * whenever the device registers are synchronised with the cache and
+ * apply them immediately. Typically this is used to apply
+ * corrections to be applied to the device defaults on startup, such
+ * as the updates some vendors provide to undocumented registers.
+ */
+int regmap_register_patch(struct regmap *map, const struct reg_default *regs,
+ int num_regs)
+{
+ int i, ret;
+ bool bypass;
+
+ /* If needed the implementation can be extended to support this */
+ if (map->patch)
+ return -EBUSY;
+
+ mutex_lock(&map->lock);
+
+ bypass = map->cache_bypass;
+
+ map->cache_bypass = true;
+
+ /* Write out first; it's useful to apply even if we fail later. */
+ for (i = 0; i < num_regs; i++) {
+ ret = _regmap_write(map, regs[i].reg, regs[i].def);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to write %x = %x: %d\n",
+ regs[i].reg, regs[i].def, ret);
+ goto out;
+ }
+ }
+
+ map->patch = kcalloc(num_regs, sizeof(struct reg_default), GFP_KERNEL);
+ if (map->patch != NULL) {
+ memcpy(map->patch, regs,
+ num_regs * sizeof(struct reg_default));
+ map->patch_regs = num_regs;
+ } else {
+ ret = -ENOMEM;
+ }
+
+out:
+ map->cache_bypass = bypass;
+
+ mutex_unlock(&map->lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regmap_register_patch);
+
+/*
+ * regmap_get_val_bytes(): Report the size of a register value
+ *
+ * Report the size of a register value, mainly intended to for use by
+ * generic infrastructure built on top of regmap.
+ */
+int regmap_get_val_bytes(struct regmap *map)
+{
+ if (map->format.format_write)
+ return -EINVAL;
+
+ return map->format.val_bytes;
+}
+EXPORT_SYMBOL_GPL(regmap_get_val_bytes);
+
static int __init regmap_initcall(void)
{
regmap_debugfs_initcall();
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index e09f9cebbb20..abfaacaaf346 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -179,7 +179,7 @@ int drbd_khelper(struct drbd_conf *mdev, char *cmd)
dev_info(DEV, "helper command: %s %s %s\n", usermode_helper, cmd, mb);
drbd_bcast_ev_helper(mdev, cmd);
- ret = call_usermodehelper(usermode_helper, argv, envp, 1);
+ ret = call_usermodehelper(usermode_helper, argv, envp, UMH_WAIT_PROC);
if (ret)
dev_warn(DEV, "helper command: %s %s %s exit code %u (0x%x)\n",
usermode_helper, cmd, mb,
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index 744f078f4dd8..76a08236430a 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 b52c9ca146fc..bf397bf108b7 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 c3f0ee16594d..061427a75d37 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 a6278e7e61a0..013c7a549fb6 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 fc6c678aa2cb..950708688f17 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 48e8fee9f2d4..9dcf76a10bb6 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 51a972704db5..ff540520bada 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/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 2f22874c0a37..d5e1ab956740 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -1475,6 +1475,9 @@ static int __init xlblk_init(void)
if (!xen_domain())
return -ENODEV;
+ if (!xen_platform_pci_unplug)
+ return -ENODEV;
+
if (register_blkdev(XENVBD_MAJOR, DEV_NAME)) {
printk(KERN_WARNING "xen_blk: can't get major %d with name %s\n",
XENVBD_MAJOR, DEV_NAME);
diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
index 9c09d6f05dc9..308c8599ab55 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 194224d07f7c..c4fc2f3fc32c 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 049c0594a76b..6e8d96189684 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/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index b427711be4be..962e75dc4781 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -850,6 +850,7 @@ static struct pci_device_id agp_intel_pci_table[] = {
.subvendor = PCI_ANY_ID, \
.subdevice = PCI_ANY_ID, \
}
+ ID(PCI_DEVICE_ID_INTEL_82441), /* for HAS2 support */
ID(PCI_DEVICE_ID_INTEL_82443LX_0),
ID(PCI_DEVICE_ID_INTEL_82443BX_0),
ID(PCI_DEVICE_ID_INTEL_82443GX_0),
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index c92424ca1a55..5cf47ac2d401 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -76,7 +76,6 @@ static struct _intel_private {
struct resource ifp_resource;
int resource_valid;
struct page *scratch_page;
- dma_addr_t scratch_page_dma;
} intel_private;
#define INTEL_GTT_GEN intel_private.driver->gen
@@ -306,9 +305,9 @@ static int intel_gtt_setup_scratch_page(void)
if (pci_dma_mapping_error(intel_private.pcidev, dma_addr))
return -EINVAL;
- intel_private.scratch_page_dma = dma_addr;
+ intel_private.base.scratch_page_dma = dma_addr;
} else
- intel_private.scratch_page_dma = page_to_phys(page);
+ intel_private.base.scratch_page_dma = page_to_phys(page);
intel_private.scratch_page = page;
@@ -631,7 +630,7 @@ static unsigned int intel_gtt_mappable_entries(void)
static void intel_gtt_teardown_scratch_page(void)
{
set_pages_wb(intel_private.scratch_page, 1);
- pci_unmap_page(intel_private.pcidev, intel_private.scratch_page_dma,
+ pci_unmap_page(intel_private.pcidev, intel_private.base.scratch_page_dma,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
put_page(intel_private.scratch_page);
__free_page(intel_private.scratch_page);
@@ -681,6 +680,7 @@ static int intel_gtt_init(void)
iounmap(intel_private.registers);
return -ENOMEM;
}
+ intel_private.base.gtt = intel_private.gtt;
global_cache_flush(); /* FIXME: ? */
@@ -975,7 +975,7 @@ void intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries)
unsigned int i;
for (i = first_entry; i < (first_entry + num_entries); i++) {
- intel_private.driver->write_entry(intel_private.scratch_page_dma,
+ intel_private.driver->write_entry(intel_private.base.scratch_page_dma,
i, 0);
}
readl(intel_private.gtt+i-1);
diff --git a/drivers/char/apm-emulation.c b/drivers/char/apm-emulation.c
index f4837a893dfa..57501ca9204b 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 ed8303f9890c..7d34b203718a 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 53c524e7b829..a082d00b0f11 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 f773a9dd14f3..21cb980f1157 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 0833896cf6f2..3845ab44c330 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/hw_random/nomadik-rng.c b/drivers/char/hw_random/nomadik-rng.c
index 3d3c1e6703b4..96de0249e595 100644
--- a/drivers/char/hw_random/nomadik-rng.c
+++ b/drivers/char/hw_random/nomadik-rng.c
@@ -107,17 +107,6 @@ static struct amba_driver nmk_rng_driver = {
.id_table = nmk_rng_ids,
};
-static int __init nmk_rng_init(void)
-{
- return amba_driver_register(&nmk_rng_driver);
-}
-
-static void __devexit nmk_rng_exit(void)
-{
- amba_driver_unregister(&nmk_rng_driver);
-}
-
-module_init(nmk_rng_init);
-module_exit(nmk_rng_exit);
+module_amba_driver(nmk_rng_driver);
MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index b757fac3cd1f..a07a5caa599c 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -26,6 +26,8 @@
#include <asm/io.h>
+#include <plat/cpu.h>
+
#define RNG_OUT_REG 0x00 /* Output register */
#define RNG_STAT_REG 0x04 /* Status register
[0] = STAT_BUSY */
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 2aa3977aae5e..9eb360ff8cab 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 cf82fedae099..e53fc24c6af3 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 58c0e6387cf7..2c29942b1326 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 50fcf9c04569..1e638fff40ea 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 34767a6d7f42..7ed356e52035 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 f43485607063..0fbf1a776b52 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
diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c
index 1aeaaba680d2..47ff7e470d87 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 5c0d96a820fa..8b78750f1efe 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 492dbfb2efd6..881c9e595939 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 eaade8a1ecd7..9df78e2cc45d 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 bf586ae1ee83..d45c3345b4af 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 f6453df4921c..0a484b4a1b02 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 872e09a02d23..af9437488b6c 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 1ee8ce7d2762..45713f0e7d61 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 e90e1c74fd4c..31ba11ca75e1 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 9b3cd08cd0ed..165e1febae53 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 07613fa172c9..1f736bc11c4b 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 000000000000..d5ac6a75ea57
--- /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 000000000000..90c79fb5d1bd
--- /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 000000000000..b5902e2ef2fd
--- /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 000000000000..c71ad1f41a97
--- /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 000000000000..9cf6f59e3e19
--- /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/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c
index 55d0f95f82f9..32cb929b8eb6 100644
--- a/drivers/clocksource/tcb_clksrc.c
+++ b/drivers/clocksource/tcb_clksrc.c
@@ -19,6 +19,8 @@
* - Two channels combine to create a free-running 32 bit counter
* with a base rate of 5+ MHz, packaged as a clocksource (with
* resolution better than 200 nsec).
+ * - Some chips support 32 bit counter. A single channel is used for
+ * this 32 bit free-running counter. the second channel is not used.
*
* - The third channel may be used to provide a 16-bit clockevent
* source, used in either periodic or oneshot mode. This runs
@@ -54,6 +56,11 @@ static cycle_t tc_get_cycles(struct clocksource *cs)
return (upper << 16) | lower;
}
+static cycle_t tc_get_cycles32(struct clocksource *cs)
+{
+ return __raw_readl(tcaddr + ATMEL_TC_REG(0, CV));
+}
+
static struct clocksource clksrc = {
.name = "tcb_clksrc",
.rating = 200,
@@ -209,6 +216,48 @@ static void __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx)
#endif
+static void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx)
+{
+ /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
+ __raw_writel(mck_divisor_idx /* likely divide-by-8 */
+ | ATMEL_TC_WAVE
+ | ATMEL_TC_WAVESEL_UP /* free-run */
+ | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
+ | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
+ tcaddr + ATMEL_TC_REG(0, CMR));
+ __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
+ __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
+ __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
+ __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
+
+ /* channel 1: waveform mode, input TIOA0 */
+ __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */
+ | ATMEL_TC_WAVE
+ | ATMEL_TC_WAVESEL_UP, /* free-run */
+ tcaddr + ATMEL_TC_REG(1, CMR));
+ __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
+ __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
+
+ /* chain channel 0 to channel 1*/
+ __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
+ /* then reset all the timers */
+ __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
+}
+
+static void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx)
+{
+ /* channel 0: waveform mode, input mclk/8 */
+ __raw_writel(mck_divisor_idx /* likely divide-by-8 */
+ | ATMEL_TC_WAVE
+ | ATMEL_TC_WAVESEL_UP, /* free-run */
+ tcaddr + ATMEL_TC_REG(0, CMR));
+ __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
+ __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
+
+ /* then reset all the timers */
+ __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
+}
+
static int __init tcb_clksrc_init(void)
{
static char bootinfo[] __initdata
@@ -260,34 +309,19 @@ static int __init tcb_clksrc_init(void)
divided_rate / 1000000,
((divided_rate + 500000) % 1000000) / 1000);
- /* tclib will give us three clocks no matter what the
- * underlying platform supports.
- */
- clk_enable(tc->clk[1]);
-
- /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */
- __raw_writel(best_divisor_idx /* likely divide-by-8 */
- | ATMEL_TC_WAVE
- | ATMEL_TC_WAVESEL_UP /* free-run */
- | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */
- | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */
- tcaddr + ATMEL_TC_REG(0, CMR));
- __raw_writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA));
- __raw_writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC));
- __raw_writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */
- __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR));
-
- /* channel 1: waveform mode, input TIOA0 */
- __raw_writel(ATMEL_TC_XC1 /* input: TIOA0 */
- | ATMEL_TC_WAVE
- | ATMEL_TC_WAVESEL_UP, /* free-run */
- tcaddr + ATMEL_TC_REG(1, CMR));
- __raw_writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */
- __raw_writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR));
-
- /* chain channel 0 to channel 1, then reset all the timers */
- __raw_writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR);
- __raw_writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR);
+ if (tc->tcb_config && tc->tcb_config->counter_width == 32) {
+ /* use apropriate function to read 32 bit counter */
+ clksrc.read = tc_get_cycles32;
+ /* setup ony channel 0 */
+ tcb_setup_single_chan(tc, best_divisor_idx);
+ } else {
+ /* tclib will give us three clocks no matter what the
+ * underlying platform supports.
+ */
+ clk_enable(tc->clk[1]);
+ /* setup both channel 0 & 1 */
+ tcb_setup_dual_chan(tc, best_divisor_idx);
+ }
/* and away we go! */
clocksource_register_hz(&clksrc, divided_rate);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index e0664fed018a..32d790dd8180 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -2,6 +2,33 @@
# ARM CPU Frequency scaling drivers
#
+config ARM_OMAP2PLUS_CPUFREQ
+ bool "TI OMAP2+"
+ default ARCH_OMAP2PLUS
+ select CPU_FREQ_TABLE
+
+config ARM_S3C2416_CPUFREQ
+ bool "S3C2416 CPU Frequency scaling support"
+ depends on CPU_S3C2416
+ help
+ This adds the CPUFreq driver for the Samsung S3C2416 and
+ S3C2450 SoC. The S3C2416 supports changing the rate of the
+ armdiv clock source and also entering a so called dynamic
+ voltage scaling mode in which it is possible to reduce the
+ core voltage of the cpu.
+
+ If in doubt, say N.
+
+config ARM_S3C2416_CPUFREQ_VCORESCALE
+ bool "Allow voltage scaling for S3C2416 arm core (EXPERIMENTAL)"
+ depends on ARM_S3C2416_CPUFREQ && REGULATOR && EXPERIMENTAL
+ help
+ Enable CPU voltage scaling when entering the dvs mode.
+ It uses information gathered through existing hardware and
+ tests but not documented in any datasheet.
+
+ If in doubt, say N.
+
config ARM_S3C64XX_CPUFREQ
bool "Samsung S3C64XX"
depends on CPU_S3C6410
@@ -25,6 +52,8 @@ config ARM_EXYNOS_CPUFREQ
bool "SAMSUNG EXYNOS SoCs"
depends on ARCH_EXYNOS
select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210
+ select ARM_EXYNOS4X12_CPUFREQ if (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+ select ARM_EXYNOS5250_CPUFREQ if SOC_EXYNOS5250
default y
help
This adds the CPUFreq driver common part for Samsung
@@ -34,6 +63,19 @@ config ARM_EXYNOS_CPUFREQ
config ARM_EXYNOS4210_CPUFREQ
bool "Samsung EXYNOS4210"
+ depends on ARCH_EXYNOS
help
This adds the CPUFreq driver for Samsung EXYNOS4210
SoC (S5PV310 or S5PC210).
+
+config ARM_EXYNOS4X12_CPUFREQ
+ bool "Samsung EXYNOS4X12"
+ help
+ This adds the CPUFreq driver for Samsung EXYNOS4X12
+ SoC (EXYNOS4212 or EXYNOS4412).
+
+config ARM_EXYNOS5250_CPUFREQ
+ bool "Samsung EXYNOS5250"
+ help
+ This adds the CPUFreq driver for Samsung EXYNOS5250
+ SoC.
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index ac000fa76bbb..9531fc2eda22 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -40,11 +40,14 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o
##################################################################################
# ARM SoC drivers
obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o
+obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o
-obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o
+obj-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
+obj-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
+obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
##################################################################################
# PowerPC platform drivers
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 622013fb7890..7f2f149ae40f 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -126,6 +126,15 @@ static int __init init_cpufreq_transition_notifier_list(void)
}
pure_initcall(init_cpufreq_transition_notifier_list);
+static int off __read_mostly;
+int cpufreq_disabled(void)
+{
+ return off;
+}
+void disable_cpufreq(void)
+{
+ off = 1;
+}
static LIST_HEAD(cpufreq_governor_list);
static DEFINE_MUTEX(cpufreq_governor_mutex);
@@ -1441,6 +1450,9 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
{
int retval = -EINVAL;
+ if (cpufreq_disabled())
+ return -ENODEV;
+
pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu,
target_freq, relation);
if (cpu_online(policy->cpu) && cpufreq_driver->target)
@@ -1549,6 +1561,9 @@ int cpufreq_register_governor(struct cpufreq_governor *governor)
if (!governor)
return -EINVAL;
+ if (cpufreq_disabled())
+ return -ENODEV;
+
mutex_lock(&cpufreq_governor_mutex);
err = -EBUSY;
@@ -1572,6 +1587,9 @@ void cpufreq_unregister_governor(struct cpufreq_governor *governor)
if (!governor)
return;
+ if (cpufreq_disabled())
+ return;
+
#ifdef CONFIG_HOTPLUG_CPU
for_each_present_cpu(cpu) {
if (cpu_online(cpu))
@@ -1814,6 +1832,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
unsigned long flags;
int ret;
+ if (cpufreq_disabled())
+ return -ENODEV;
+
if (!driver_data || !driver_data->verify || !driver_data->init ||
((!driver_data->setpolicy) && (!driver_data->target)))
return -EINVAL;
@@ -1901,6 +1922,9 @@ static int __init cpufreq_core_init(void)
{
int cpu;
+ if (cpufreq_disabled())
+ return -ENODEV;
+
for_each_possible_cpu(cpu) {
per_cpu(cpufreq_policy_cpu, cpu) = -1;
init_rwsem(&per_cpu(cpu_policy_rwsem, cpu));
diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c
index c3e0652520a1..836e9b062e5e 100644
--- a/drivers/cpufreq/cpufreq_ondemand.c
+++ b/drivers/cpufreq/cpufreq_ondemand.c
@@ -257,6 +257,62 @@ show_one(sampling_down_factor, sampling_down_factor);
show_one(ignore_nice_load, ignore_nice);
show_one(powersave_bias, powersave_bias);
+/**
+ * update_sampling_rate - update sampling rate effective immediately if needed.
+ * @new_rate: new sampling rate
+ *
+ * If new rate is smaller than the old, simply updaing
+ * dbs_tuners_int.sampling_rate might not be appropriate. For example,
+ * if the original sampling_rate was 1 second and the requested new sampling
+ * rate is 10 ms because the user needs immediate reaction from ondemand
+ * governor, but not sure if higher frequency will be required or not,
+ * then, the governor may change the sampling rate too late; up to 1 second
+ * later. Thus, if we are reducing the sampling rate, we need to make the
+ * new value effective immediately.
+ */
+static void update_sampling_rate(unsigned int new_rate)
+{
+ int cpu;
+
+ dbs_tuners_ins.sampling_rate = new_rate
+ = max(new_rate, min_sampling_rate);
+
+ for_each_online_cpu(cpu) {
+ struct cpufreq_policy *policy;
+ struct cpu_dbs_info_s *dbs_info;
+ unsigned long next_sampling, appointed_at;
+
+ policy = cpufreq_cpu_get(cpu);
+ if (!policy)
+ continue;
+ dbs_info = &per_cpu(od_cpu_dbs_info, policy->cpu);
+ cpufreq_cpu_put(policy);
+
+ mutex_lock(&dbs_info->timer_mutex);
+
+ if (!delayed_work_pending(&dbs_info->work)) {
+ mutex_unlock(&dbs_info->timer_mutex);
+ continue;
+ }
+
+ next_sampling = jiffies + usecs_to_jiffies(new_rate);
+ appointed_at = dbs_info->work.timer.expires;
+
+
+ if (time_before(next_sampling, appointed_at)) {
+
+ mutex_unlock(&dbs_info->timer_mutex);
+ cancel_delayed_work_sync(&dbs_info->work);
+ mutex_lock(&dbs_info->timer_mutex);
+
+ schedule_delayed_work_on(dbs_info->cpu, &dbs_info->work,
+ usecs_to_jiffies(new_rate));
+
+ }
+ mutex_unlock(&dbs_info->timer_mutex);
+ }
+}
+
static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
const char *buf, size_t count)
{
@@ -265,7 +321,7 @@ static ssize_t store_sampling_rate(struct kobject *a, struct attribute *b,
ret = sscanf(buf, "%u", &input);
if (ret != 1)
return -EINVAL;
- dbs_tuners_ins.sampling_rate = max(input, min_sampling_rate);
+ update_sampling_rate(input);
return count;
}
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c
index f5002015d82e..a22ffa5bff9f 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);
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
index 5467879ea07d..b243a7ee01f6 100644
--- a/drivers/cpufreq/exynos-cpufreq.c
+++ b/drivers/cpufreq/exynos-cpufreq.c
@@ -210,6 +210,8 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu);
+ locking_frequency = exynos_getspeed(0);
+
/* set the transition latency value */
policy->cpuinfo.transition_latency = 100000;
@@ -252,6 +254,10 @@ static int __init exynos_cpufreq_init(void)
if (soc_is_exynos4210())
ret = exynos4210_cpufreq_init(exynos_info);
+ else if (soc_is_exynos4212() || soc_is_exynos4412())
+ ret = exynos4x12_cpufreq_init(exynos_info);
+ else if (soc_is_exynos5250())
+ ret = exynos5250_cpufreq_init(exynos_info);
else
pr_err("%s: CPU type not found\n", __func__);
diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c
index 065da5b702f1..fb148fa27678 100644
--- a/drivers/cpufreq/exynos4210-cpufreq.c
+++ b/drivers/cpufreq/exynos4210-cpufreq.c
@@ -121,25 +121,25 @@ static void exynos4210_set_clkdiv(unsigned int div_index)
tmp = exynos4210_clkdiv_table[div_index].clkdiv;
- __raw_writel(tmp, S5P_CLKDIV_CPU);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
do {
- tmp = __raw_readl(S5P_CLKDIV_STATCPU);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU);
} while (tmp & 0x1111111);
/* Change Divider - CPU1 */
- tmp = __raw_readl(S5P_CLKDIV_CPU1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1);
tmp &= ~((0x7 << 4) | 0x7);
tmp |= ((clkdiv_cpu1[div_index][0] << 4) |
(clkdiv_cpu1[div_index][1] << 0));
- __raw_writel(tmp, S5P_CLKDIV_CPU1);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
do {
- tmp = __raw_readl(S5P_CLKDIV_STATCPU1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1);
} while (tmp & 0x11);
}
@@ -151,32 +151,32 @@ static void exynos4210_set_apll(unsigned int index)
clk_set_parent(moutcore, mout_mpll);
do {
- tmp = (__raw_readl(S5P_CLKMUX_STATCPU)
- >> S5P_CLKSRC_CPU_MUXCORE_SHIFT);
+ tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
+ >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
tmp &= 0x7;
} while (tmp != 0x2);
/* 2. Set APLL Lock time */
- __raw_writel(S5P_APLL_LOCKTIME, S5P_APLL_LOCK);
+ __raw_writel(EXYNOS4_APLL_LOCKTIME, EXYNOS4_APLL_LOCK);
/* 3. Change PLL PMS values */
- tmp = __raw_readl(S5P_APLL_CON0);
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
tmp |= exynos4210_apll_pms_table[index];
- __raw_writel(tmp, S5P_APLL_CON0);
+ __raw_writel(tmp, EXYNOS4_APLL_CON0);
/* 4. wait_lock_time */
do {
- tmp = __raw_readl(S5P_APLL_CON0);
- } while (!(tmp & (0x1 << S5P_APLLCON0_LOCKED_SHIFT)));
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
+ } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT)));
/* 5. MUX_CORE_SEL = APLL */
clk_set_parent(moutcore, mout_apll);
do {
- tmp = __raw_readl(S5P_CLKMUX_STATCPU);
- tmp &= S5P_CLKMUX_STATCPU_MUXCORE_MASK;
- } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT));
+ tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
+ tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
+ } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
}
bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index)
@@ -198,10 +198,10 @@ static void exynos4210_set_frequency(unsigned int old_index,
exynos4210_set_clkdiv(new_index);
/* 2. Change just s value in apll m,p,s value */
- tmp = __raw_readl(S5P_APLL_CON0);
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
tmp &= ~(0x7 << 0);
tmp |= (exynos4210_apll_pms_table[new_index] & 0x7);
- __raw_writel(tmp, S5P_APLL_CON0);
+ __raw_writel(tmp, EXYNOS4_APLL_CON0);
} else {
/* Clock Configuration Procedure */
/* 1. Change the system clock divider values */
@@ -212,10 +212,10 @@ static void exynos4210_set_frequency(unsigned int old_index,
} else if (old_index < new_index) {
if (!exynos4210_pms_change(old_index, new_index)) {
/* 1. Change just s value in apll m,p,s value */
- tmp = __raw_readl(S5P_APLL_CON0);
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
tmp &= ~(0x7 << 0);
tmp |= (exynos4210_apll_pms_table[new_index] & 0x7);
- __raw_writel(tmp, S5P_APLL_CON0);
+ __raw_writel(tmp, EXYNOS4_APLL_CON0);
/* 2. Change the system clock divider values */
exynos4210_set_clkdiv(new_index);
@@ -253,24 +253,24 @@ int exynos4210_cpufreq_init(struct exynos_dvfs_info *info)
if (IS_ERR(mout_apll))
goto err_mout_apll;
- tmp = __raw_readl(S5P_CLKDIV_CPU);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_CPU);
for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
- tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK |
- S5P_CLKDIV_CPU0_COREM0_MASK |
- S5P_CLKDIV_CPU0_COREM1_MASK |
- S5P_CLKDIV_CPU0_PERIPH_MASK |
- S5P_CLKDIV_CPU0_ATB_MASK |
- S5P_CLKDIV_CPU0_PCLKDBG_MASK |
- S5P_CLKDIV_CPU0_APLL_MASK);
-
- tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) |
- (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) |
- (clkdiv_cpu0[i][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) |
- (clkdiv_cpu0[i][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) |
- (clkdiv_cpu0[i][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) |
- (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) |
- (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT));
+ tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK |
+ EXYNOS4_CLKDIV_CPU0_COREM0_MASK |
+ EXYNOS4_CLKDIV_CPU0_COREM1_MASK |
+ EXYNOS4_CLKDIV_CPU0_PERIPH_MASK |
+ EXYNOS4_CLKDIV_CPU0_ATB_MASK |
+ EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK |
+ EXYNOS4_CLKDIV_CPU0_APLL_MASK);
+
+ tmp |= ((clkdiv_cpu0[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
+ (clkdiv_cpu0[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
+ (clkdiv_cpu0[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
+ (clkdiv_cpu0[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
+ (clkdiv_cpu0[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
+ (clkdiv_cpu0[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
+ (clkdiv_cpu0[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT));
exynos4210_clkdiv_table[i].clkdiv = tmp;
}
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c
new file mode 100644
index 000000000000..8c5a7afa5b0b
--- /dev/null
+++ b/drivers/cpufreq/exynos4x12-cpufreq.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * EXYNOS4X12 - CPU frequency scaling support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/cpufreq.h>
+
+#include <mach/regs-clock.h>
+#include <mach/cpufreq.h>
+
+#define CPUFREQ_LEVEL_END (L13 + 1)
+
+static int max_support_idx;
+static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
+
+static struct clk *cpu_clk;
+static struct clk *moutcore;
+static struct clk *mout_mpll;
+static struct clk *mout_apll;
+
+struct cpufreq_clkdiv {
+ unsigned int index;
+ unsigned int clkdiv;
+ unsigned int clkdiv1;
+};
+
+static unsigned int exynos4x12_volt_table[CPUFREQ_LEVEL_END];
+
+static struct cpufreq_frequency_table exynos4x12_freq_table[] = {
+ {L0, 1500 * 1000},
+ {L1, 1400 * 1000},
+ {L2, 1300 * 1000},
+ {L3, 1200 * 1000},
+ {L4, 1100 * 1000},
+ {L5, 1000 * 1000},
+ {L6, 900 * 1000},
+ {L7, 800 * 1000},
+ {L8, 700 * 1000},
+ {L9, 600 * 1000},
+ {L10, 500 * 1000},
+ {L11, 400 * 1000},
+ {L12, 300 * 1000},
+ {L13, 200 * 1000},
+ {0, CPUFREQ_TABLE_END},
+};
+
+static struct cpufreq_clkdiv exynos4x12_clkdiv_table[CPUFREQ_LEVEL_END];
+
+static unsigned int clkdiv_cpu0_4212[CPUFREQ_LEVEL_END][8] = {
+ /*
+ * Clock divider value for following
+ * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
+ * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 }
+ */
+ /* ARM L0: 1500 MHz */
+ { 0, 3, 7, 0, 6, 1, 2, 0 },
+
+ /* ARM L1: 1400 MHz */
+ { 0, 3, 7, 0, 6, 1, 2, 0 },
+
+ /* ARM L2: 1300 MHz */
+ { 0, 3, 7, 0, 5, 1, 2, 0 },
+
+ /* ARM L3: 1200 MHz */
+ { 0, 3, 7, 0, 5, 1, 2, 0 },
+
+ /* ARM L4: 1100 MHz */
+ { 0, 3, 6, 0, 4, 1, 2, 0 },
+
+ /* ARM L5: 1000 MHz */
+ { 0, 2, 5, 0, 4, 1, 1, 0 },
+
+ /* ARM L6: 900 MHz */
+ { 0, 2, 5, 0, 3, 1, 1, 0 },
+
+ /* ARM L7: 800 MHz */
+ { 0, 2, 5, 0, 3, 1, 1, 0 },
+
+ /* ARM L8: 700 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L9: 600 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L10: 500 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L11: 400 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L12: 300 MHz */
+ { 0, 2, 4, 0, 2, 1, 1, 0 },
+
+ /* ARM L13: 200 MHz */
+ { 0, 1, 3, 0, 1, 1, 1, 0 },
+};
+
+static unsigned int clkdiv_cpu0_4412[CPUFREQ_LEVEL_END][8] = {
+ /*
+ * Clock divider value for following
+ * { DIVCORE, DIVCOREM0, DIVCOREM1, DIVPERIPH,
+ * DIVATB, DIVPCLK_DBG, DIVAPLL, DIVCORE2 }
+ */
+ /* ARM L0: 1500 MHz */
+ { 0, 3, 7, 0, 6, 1, 2, 0 },
+
+ /* ARM L1: 1400 MHz */
+ { 0, 3, 7, 0, 6, 1, 2, 0 },
+
+ /* ARM L2: 1300 MHz */
+ { 0, 3, 7, 0, 5, 1, 2, 0 },
+
+ /* ARM L3: 1200 MHz */
+ { 0, 3, 7, 0, 5, 1, 2, 0 },
+
+ /* ARM L4: 1100 MHz */
+ { 0, 3, 6, 0, 4, 1, 2, 0 },
+
+ /* ARM L5: 1000 MHz */
+ { 0, 2, 5, 0, 4, 1, 1, 0 },
+
+ /* ARM L6: 900 MHz */
+ { 0, 2, 5, 0, 3, 1, 1, 0 },
+
+ /* ARM L7: 800 MHz */
+ { 0, 2, 5, 0, 3, 1, 1, 0 },
+
+ /* ARM L8: 700 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L9: 600 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L10: 500 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L11: 400 MHz */
+ { 0, 2, 4, 0, 3, 1, 1, 0 },
+
+ /* ARM L12: 300 MHz */
+ { 0, 2, 4, 0, 2, 1, 1, 0 },
+
+ /* ARM L13: 200 MHz */
+ { 0, 1, 3, 0, 1, 1, 1, 0 },
+};
+
+static unsigned int clkdiv_cpu1_4212[CPUFREQ_LEVEL_END][2] = {
+ /* Clock divider value for following
+ * { DIVCOPY, DIVHPM }
+ */
+ /* ARM L0: 1500 MHz */
+ { 6, 0 },
+
+ /* ARM L1: 1400 MHz */
+ { 6, 0 },
+
+ /* ARM L2: 1300 MHz */
+ { 5, 0 },
+
+ /* ARM L3: 1200 MHz */
+ { 5, 0 },
+
+ /* ARM L4: 1100 MHz */
+ { 4, 0 },
+
+ /* ARM L5: 1000 MHz */
+ { 4, 0 },
+
+ /* ARM L6: 900 MHz */
+ { 3, 0 },
+
+ /* ARM L7: 800 MHz */
+ { 3, 0 },
+
+ /* ARM L8: 700 MHz */
+ { 3, 0 },
+
+ /* ARM L9: 600 MHz */
+ { 3, 0 },
+
+ /* ARM L10: 500 MHz */
+ { 3, 0 },
+
+ /* ARM L11: 400 MHz */
+ { 3, 0 },
+
+ /* ARM L12: 300 MHz */
+ { 3, 0 },
+
+ /* ARM L13: 200 MHz */
+ { 3, 0 },
+};
+
+static unsigned int clkdiv_cpu1_4412[CPUFREQ_LEVEL_END][3] = {
+ /* Clock divider value for following
+ * { DIVCOPY, DIVHPM, DIVCORES }
+ */
+ /* ARM L0: 1500 MHz */
+ { 6, 0, 7 },
+
+ /* ARM L1: 1400 MHz */
+ { 6, 0, 6 },
+
+ /* ARM L2: 1300 MHz */
+ { 5, 0, 6 },
+
+ /* ARM L3: 1200 MHz */
+ { 5, 0, 5 },
+
+ /* ARM L4: 1100 MHz */
+ { 4, 0, 5 },
+
+ /* ARM L5: 1000 MHz */
+ { 4, 0, 4 },
+
+ /* ARM L6: 900 MHz */
+ { 3, 0, 4 },
+
+ /* ARM L7: 800 MHz */
+ { 3, 0, 3 },
+
+ /* ARM L8: 700 MHz */
+ { 3, 0, 3 },
+
+ /* ARM L9: 600 MHz */
+ { 3, 0, 2 },
+
+ /* ARM L10: 500 MHz */
+ { 3, 0, 2 },
+
+ /* ARM L11: 400 MHz */
+ { 3, 0, 1 },
+
+ /* ARM L12: 300 MHz */
+ { 3, 0, 1 },
+
+ /* ARM L13: 200 MHz */
+ { 3, 0, 0 },
+};
+
+static unsigned int exynos4x12_apll_pms_table[CPUFREQ_LEVEL_END] = {
+ /* APLL FOUT L0: 1500 MHz */
+ ((250 << 16) | (4 << 8) | (0x0)),
+
+ /* APLL FOUT L1: 1400 MHz */
+ ((175 << 16) | (3 << 8) | (0x0)),
+
+ /* APLL FOUT L2: 1300 MHz */
+ ((325 << 16) | (6 << 8) | (0x0)),
+
+ /* APLL FOUT L3: 1200 MHz */
+ ((200 << 16) | (4 << 8) | (0x0)),
+
+ /* APLL FOUT L4: 1100 MHz */
+ ((275 << 16) | (6 << 8) | (0x0)),
+
+ /* APLL FOUT L5: 1000 MHz */
+ ((125 << 16) | (3 << 8) | (0x0)),
+
+ /* APLL FOUT L6: 900 MHz */
+ ((150 << 16) | (4 << 8) | (0x0)),
+
+ /* APLL FOUT L7: 800 MHz */
+ ((100 << 16) | (3 << 8) | (0x0)),
+
+ /* APLL FOUT L8: 700 MHz */
+ ((175 << 16) | (3 << 8) | (0x1)),
+
+ /* APLL FOUT L9: 600 MHz */
+ ((200 << 16) | (4 << 8) | (0x1)),
+
+ /* APLL FOUT L10: 500 MHz */
+ ((125 << 16) | (3 << 8) | (0x1)),
+
+ /* APLL FOUT L11 400 MHz */
+ ((100 << 16) | (3 << 8) | (0x1)),
+
+ /* APLL FOUT L12: 300 MHz */
+ ((200 << 16) | (4 << 8) | (0x2)),
+
+ /* APLL FOUT L13: 200 MHz */
+ ((100 << 16) | (3 << 8) | (0x2)),
+};
+
+static const unsigned int asv_voltage_4x12[CPUFREQ_LEVEL_END] = {
+ 1350000, 1287500, 1250000, 1187500, 1137500, 1087500, 1037500,
+ 1000000, 987500, 975000, 950000, 925000, 900000, 900000
+};
+
+static void exynos4x12_set_clkdiv(unsigned int div_index)
+{
+ unsigned int tmp;
+ unsigned int stat_cpu1;
+
+ /* Change Divider - CPU0 */
+
+ tmp = exynos4x12_clkdiv_table[div_index].clkdiv;
+
+ __raw_writel(tmp, EXYNOS4_CLKDIV_CPU);
+
+ while (__raw_readl(EXYNOS4_CLKDIV_STATCPU) & 0x11111111)
+ cpu_relax();
+
+ /* Change Divider - CPU1 */
+ tmp = exynos4x12_clkdiv_table[div_index].clkdiv1;
+
+ __raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
+ if (soc_is_exynos4212())
+ stat_cpu1 = 0x11;
+ else
+ stat_cpu1 = 0x111;
+
+ while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1)
+ cpu_relax();
+}
+
+static void exynos4x12_set_apll(unsigned int index)
+{
+ unsigned int tmp, pdiv;
+
+ /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
+ clk_set_parent(moutcore, mout_mpll);
+
+ do {
+ cpu_relax();
+ tmp = (__raw_readl(EXYNOS4_CLKMUX_STATCPU)
+ >> EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT);
+ tmp &= 0x7;
+ } while (tmp != 0x2);
+
+ /* 2. Set APLL Lock time */
+ pdiv = ((exynos4x12_apll_pms_table[index] >> 8) & 0x3f);
+
+ __raw_writel((pdiv * 250), EXYNOS4_APLL_LOCK);
+
+ /* 3. Change PLL PMS values */
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
+ tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
+ tmp |= exynos4x12_apll_pms_table[index];
+ __raw_writel(tmp, EXYNOS4_APLL_CON0);
+
+ /* 4. wait_lock_time */
+ do {
+ cpu_relax();
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
+ } while (!(tmp & (0x1 << EXYNOS4_APLLCON0_LOCKED_SHIFT)));
+
+ /* 5. MUX_CORE_SEL = APLL */
+ clk_set_parent(moutcore, mout_apll);
+
+ do {
+ cpu_relax();
+ tmp = __raw_readl(EXYNOS4_CLKMUX_STATCPU);
+ tmp &= EXYNOS4_CLKMUX_STATCPU_MUXCORE_MASK;
+ } while (tmp != (0x1 << EXYNOS4_CLKSRC_CPU_MUXCORE_SHIFT));
+}
+
+bool exynos4x12_pms_change(unsigned int old_index, unsigned int new_index)
+{
+ unsigned int old_pm = exynos4x12_apll_pms_table[old_index] >> 8;
+ unsigned int new_pm = exynos4x12_apll_pms_table[new_index] >> 8;
+
+ return (old_pm == new_pm) ? 0 : 1;
+}
+
+static void exynos4x12_set_frequency(unsigned int old_index,
+ unsigned int new_index)
+{
+ unsigned int tmp;
+
+ if (old_index > new_index) {
+ if (!exynos4x12_pms_change(old_index, new_index)) {
+ /* 1. Change the system clock divider values */
+ exynos4x12_set_clkdiv(new_index);
+ /* 2. Change just s value in apll m,p,s value */
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
+ tmp &= ~(0x7 << 0);
+ tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7);
+ __raw_writel(tmp, EXYNOS4_APLL_CON0);
+
+ } else {
+ /* Clock Configuration Procedure */
+ /* 1. Change the system clock divider values */
+ exynos4x12_set_clkdiv(new_index);
+ /* 2. Change the apll m,p,s value */
+ exynos4x12_set_apll(new_index);
+ }
+ } else if (old_index < new_index) {
+ if (!exynos4x12_pms_change(old_index, new_index)) {
+ /* 1. Change just s value in apll m,p,s value */
+ tmp = __raw_readl(EXYNOS4_APLL_CON0);
+ tmp &= ~(0x7 << 0);
+ tmp |= (exynos4x12_apll_pms_table[new_index] & 0x7);
+ __raw_writel(tmp, EXYNOS4_APLL_CON0);
+ /* 2. Change the system clock divider values */
+ exynos4x12_set_clkdiv(new_index);
+ } else {
+ /* Clock Configuration Procedure */
+ /* 1. Change the apll m,p,s value */
+ exynos4x12_set_apll(new_index);
+ /* 2. Change the system clock divider values */
+ exynos4x12_set_clkdiv(new_index);
+ }
+ }
+}
+
+static void __init set_volt_table(void)
+{
+ unsigned int i;
+
+ max_support_idx = L1;
+
+ /* Not supported */
+ exynos4x12_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
+
+ for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
+ exynos4x12_volt_table[i] = asv_voltage_4x12[i];
+}
+
+int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
+{
+ int i;
+ unsigned int tmp;
+ unsigned long rate;
+
+ set_volt_table();
+
+ cpu_clk = clk_get(NULL, "armclk");
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ moutcore = clk_get(NULL, "moutcore");
+ if (IS_ERR(moutcore))
+ goto err_moutcore;
+
+ mout_mpll = clk_get(NULL, "mout_mpll");
+ if (IS_ERR(mout_mpll))
+ goto err_mout_mpll;
+
+ rate = clk_get_rate(mout_mpll) / 1000;
+
+ mout_apll = clk_get(NULL, "mout_apll");
+ if (IS_ERR(mout_apll))
+ goto err_mout_apll;
+
+ for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
+
+ exynos4x12_clkdiv_table[i].index = i;
+
+ tmp = __raw_readl(EXYNOS4_CLKDIV_CPU);
+
+ tmp &= ~(EXYNOS4_CLKDIV_CPU0_CORE_MASK |
+ EXYNOS4_CLKDIV_CPU0_COREM0_MASK |
+ EXYNOS4_CLKDIV_CPU0_COREM1_MASK |
+ EXYNOS4_CLKDIV_CPU0_PERIPH_MASK |
+ EXYNOS4_CLKDIV_CPU0_ATB_MASK |
+ EXYNOS4_CLKDIV_CPU0_PCLKDBG_MASK |
+ EXYNOS4_CLKDIV_CPU0_APLL_MASK);
+
+ if (soc_is_exynos4212()) {
+ tmp |= ((clkdiv_cpu0_4212[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
+ (clkdiv_cpu0_4212[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
+ (clkdiv_cpu0_4212[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
+ (clkdiv_cpu0_4212[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
+ (clkdiv_cpu0_4212[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
+ (clkdiv_cpu0_4212[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
+ (clkdiv_cpu0_4212[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT));
+ } else {
+ tmp &= ~EXYNOS4_CLKDIV_CPU0_CORE2_MASK;
+
+ tmp |= ((clkdiv_cpu0_4412[i][0] << EXYNOS4_CLKDIV_CPU0_CORE_SHIFT) |
+ (clkdiv_cpu0_4412[i][1] << EXYNOS4_CLKDIV_CPU0_COREM0_SHIFT) |
+ (clkdiv_cpu0_4412[i][2] << EXYNOS4_CLKDIV_CPU0_COREM1_SHIFT) |
+ (clkdiv_cpu0_4412[i][3] << EXYNOS4_CLKDIV_CPU0_PERIPH_SHIFT) |
+ (clkdiv_cpu0_4412[i][4] << EXYNOS4_CLKDIV_CPU0_ATB_SHIFT) |
+ (clkdiv_cpu0_4412[i][5] << EXYNOS4_CLKDIV_CPU0_PCLKDBG_SHIFT) |
+ (clkdiv_cpu0_4412[i][6] << EXYNOS4_CLKDIV_CPU0_APLL_SHIFT) |
+ (clkdiv_cpu0_4412[i][7] << EXYNOS4_CLKDIV_CPU0_CORE2_SHIFT));
+ }
+
+ exynos4x12_clkdiv_table[i].clkdiv = tmp;
+
+ tmp = __raw_readl(EXYNOS4_CLKDIV_CPU1);
+
+ if (soc_is_exynos4212()) {
+ tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK |
+ EXYNOS4_CLKDIV_CPU1_HPM_MASK);
+ tmp |= ((clkdiv_cpu1_4212[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) |
+ (clkdiv_cpu1_4212[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT));
+ } else {
+ tmp &= ~(EXYNOS4_CLKDIV_CPU1_COPY_MASK |
+ EXYNOS4_CLKDIV_CPU1_HPM_MASK |
+ EXYNOS4_CLKDIV_CPU1_CORES_MASK);
+ tmp |= ((clkdiv_cpu1_4412[i][0] << EXYNOS4_CLKDIV_CPU1_COPY_SHIFT) |
+ (clkdiv_cpu1_4412[i][1] << EXYNOS4_CLKDIV_CPU1_HPM_SHIFT) |
+ (clkdiv_cpu1_4412[i][2] << EXYNOS4_CLKDIV_CPU1_CORES_SHIFT));
+ }
+ exynos4x12_clkdiv_table[i].clkdiv1 = tmp;
+ }
+
+ info->mpll_freq_khz = rate;
+ info->pm_lock_idx = L5;
+ info->pll_safe_idx = L7;
+ info->max_support_idx = max_support_idx;
+ info->min_support_idx = min_support_idx;
+ info->cpu_clk = cpu_clk;
+ info->volt_table = exynos4x12_volt_table;
+ info->freq_table = exynos4x12_freq_table;
+ info->set_freq = exynos4x12_set_frequency;
+ info->need_apll_change = exynos4x12_pms_change;
+
+ return 0;
+
+err_mout_apll:
+ clk_put(mout_mpll);
+err_mout_mpll:
+ clk_put(moutcore);
+err_moutcore:
+ clk_put(cpu_clk);
+
+ pr_debug("%s: failed initialization\n", __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(exynos4x12_cpufreq_init);
diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c
new file mode 100644
index 000000000000..a88331644ebf
--- /dev/null
+++ b/drivers/cpufreq/exynos5250-cpufreq.c
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2010-20122Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * EXYNOS5250 - CPU frequency scaling support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/cpufreq.h>
+
+#include <mach/map.h>
+#include <mach/regs-clock.h>
+#include <mach/cpufreq.h>
+
+#define CPUFREQ_LEVEL_END (L15 + 1)
+
+static int max_support_idx;
+static int min_support_idx = (CPUFREQ_LEVEL_END - 1);
+static struct clk *cpu_clk;
+static struct clk *moutcore;
+static struct clk *mout_mpll;
+static struct clk *mout_apll;
+
+struct cpufreq_clkdiv {
+ unsigned int index;
+ unsigned int clkdiv;
+ unsigned int clkdiv1;
+};
+
+static unsigned int exynos5250_volt_table[CPUFREQ_LEVEL_END];
+
+static struct cpufreq_frequency_table exynos5250_freq_table[] = {
+ {L0, 1700 * 1000},
+ {L1, 1600 * 1000},
+ {L2, 1500 * 1000},
+ {L3, 1400 * 1000},
+ {L4, 1300 * 1000},
+ {L5, 1200 * 1000},
+ {L6, 1100 * 1000},
+ {L7, 1000 * 1000},
+ {L8, 900 * 1000},
+ {L9, 800 * 1000},
+ {L10, 700 * 1000},
+ {L11, 600 * 1000},
+ {L12, 500 * 1000},
+ {L13, 400 * 1000},
+ {L14, 300 * 1000},
+ {L15, 200 * 1000},
+ {0, CPUFREQ_TABLE_END},
+};
+
+static struct cpufreq_clkdiv exynos5250_clkdiv_table[CPUFREQ_LEVEL_END];
+
+static unsigned int clkdiv_cpu0_5250[CPUFREQ_LEVEL_END][8] = {
+ /*
+ * Clock divider value for following
+ * { ARM, CPUD, ACP, PERIPH, ATB, PCLK_DBG, APLL, ARM2 }
+ */
+ { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1700 MHz - N/A */
+ { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1600 MHz - N/A */
+ { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1500 MHz - N/A */
+ { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1400 MHz */
+ { 0, 3, 7, 7, 6, 1, 3, 0 }, /* 1300 MHz */
+ { 0, 3, 7, 7, 5, 1, 3, 0 }, /* 1200 MHz */
+ { 0, 2, 7, 7, 5, 1, 2, 0 }, /* 1100 MHz */
+ { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 1000 MHz */
+ { 0, 2, 7, 7, 4, 1, 2, 0 }, /* 900 MHz */
+ { 0, 2, 7, 7, 3, 1, 1, 0 }, /* 800 MHz */
+ { 0, 1, 7, 7, 3, 1, 1, 0 }, /* 700 MHz */
+ { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 600 MHz */
+ { 0, 1, 7, 7, 2, 1, 1, 0 }, /* 500 MHz */
+ { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 400 MHz */
+ { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 300 MHz */
+ { 0, 1, 7, 7, 1, 1, 1, 0 }, /* 200 MHz */
+};
+
+static unsigned int clkdiv_cpu1_5250[CPUFREQ_LEVEL_END][2] = {
+ /* Clock divider value for following
+ * { COPY, HPM }
+ */
+ { 0, 2 }, /* 1700 MHz - N/A */
+ { 0, 2 }, /* 1600 MHz - N/A */
+ { 0, 2 }, /* 1500 MHz - N/A */
+ { 0, 2 }, /* 1400 MHz */
+ { 0, 2 }, /* 1300 MHz */
+ { 0, 2 }, /* 1200 MHz */
+ { 0, 2 }, /* 1100 MHz */
+ { 0, 2 }, /* 1000 MHz */
+ { 0, 2 }, /* 900 MHz */
+ { 0, 2 }, /* 800 MHz */
+ { 0, 2 }, /* 700 MHz */
+ { 0, 2 }, /* 600 MHz */
+ { 0, 2 }, /* 500 MHz */
+ { 0, 2 }, /* 400 MHz */
+ { 0, 2 }, /* 300 MHz */
+ { 0, 2 }, /* 200 MHz */
+};
+
+static unsigned int exynos5_apll_pms_table[CPUFREQ_LEVEL_END] = {
+ (0), /* 1700 MHz - N/A */
+ (0), /* 1600 MHz - N/A */
+ (0), /* 1500 MHz - N/A */
+ (0), /* 1400 MHz */
+ ((325 << 16) | (6 << 8) | 0), /* 1300 MHz */
+ ((200 << 16) | (4 << 8) | 0), /* 1200 MHz */
+ ((275 << 16) | (6 << 8) | 0), /* 1100 MHz */
+ ((125 << 16) | (3 << 8) | 0), /* 1000 MHz */
+ ((150 << 16) | (4 << 8) | 0), /* 900 MHz */
+ ((100 << 16) | (3 << 8) | 0), /* 800 MHz */
+ ((175 << 16) | (3 << 8) | 1), /* 700 MHz */
+ ((200 << 16) | (4 << 8) | 1), /* 600 MHz */
+ ((125 << 16) | (3 << 8) | 1), /* 500 MHz */
+ ((100 << 16) | (3 << 8) | 1), /* 400 MHz */
+ ((200 << 16) | (4 << 8) | 2), /* 300 MHz */
+ ((100 << 16) | (3 << 8) | 2), /* 200 MHz */
+};
+
+/* ASV group voltage table */
+static const unsigned int asv_voltage_5250[CPUFREQ_LEVEL_END] = {
+ 0, 0, 0, 0, 0, 0, 0, /* 1700 MHz ~ 1100 MHz Not supported */
+ 1175000, 1125000, 1075000, 1050000, 1000000,
+ 950000, 925000, 925000, 900000
+};
+
+static void set_clkdiv(unsigned int div_index)
+{
+ unsigned int tmp;
+
+ /* Change Divider - CPU0 */
+
+ tmp = exynos5250_clkdiv_table[div_index].clkdiv;
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_CPU0);
+
+ while (__raw_readl(EXYNOS5_CLKDIV_STATCPU0) & 0x11111111)
+ cpu_relax();
+
+ /* Change Divider - CPU1 */
+ tmp = exynos5250_clkdiv_table[div_index].clkdiv1;
+
+ __raw_writel(tmp, EXYNOS5_CLKDIV_CPU1);
+
+ while (__raw_readl(EXYNOS5_CLKDIV_STATCPU1) & 0x11)
+ cpu_relax();
+}
+
+static void set_apll(unsigned int new_index,
+ unsigned int old_index)
+{
+ unsigned int tmp, pdiv;
+
+ /* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
+ clk_set_parent(moutcore, mout_mpll);
+
+ do {
+ cpu_relax();
+ tmp = (__raw_readl(EXYNOS5_CLKMUX_STATCPU) >> 16);
+ tmp &= 0x7;
+ } while (tmp != 0x2);
+
+ /* 2. Set APLL Lock time */
+ pdiv = ((exynos5_apll_pms_table[new_index] >> 8) & 0x3f);
+
+ __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK);
+
+ /* 3. Change PLL PMS values */
+ tmp = __raw_readl(EXYNOS5_APLL_CON0);
+ tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
+ tmp |= exynos5_apll_pms_table[new_index];
+ __raw_writel(tmp, EXYNOS5_APLL_CON0);
+
+ /* 4. wait_lock_time */
+ do {
+ cpu_relax();
+ tmp = __raw_readl(EXYNOS5_APLL_CON0);
+ } while (!(tmp & (0x1 << 29)));
+
+ /* 5. MUX_CORE_SEL = APLL */
+ clk_set_parent(moutcore, mout_apll);
+
+ do {
+ cpu_relax();
+ tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
+ tmp &= (0x7 << 16);
+ } while (tmp != (0x1 << 16));
+
+}
+
+bool exynos5250_pms_change(unsigned int old_index, unsigned int new_index)
+{
+ unsigned int old_pm = (exynos5_apll_pms_table[old_index] >> 8);
+ unsigned int new_pm = (exynos5_apll_pms_table[new_index] >> 8);
+
+ return (old_pm == new_pm) ? 0 : 1;
+}
+
+static void exynos5250_set_frequency(unsigned int old_index,
+ unsigned int new_index)
+{
+ unsigned int tmp;
+
+ if (old_index > new_index) {
+ if (!exynos5250_pms_change(old_index, new_index)) {
+ /* 1. Change the system clock divider values */
+ set_clkdiv(new_index);
+ /* 2. Change just s value in apll m,p,s value */
+ tmp = __raw_readl(EXYNOS5_APLL_CON0);
+ tmp &= ~(0x7 << 0);
+ tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
+ __raw_writel(tmp, EXYNOS5_APLL_CON0);
+
+ } else {
+ /* Clock Configuration Procedure */
+ /* 1. Change the system clock divider values */
+ set_clkdiv(new_index);
+ /* 2. Change the apll m,p,s value */
+ set_apll(new_index, old_index);
+ }
+ } else if (old_index < new_index) {
+ if (!exynos5250_pms_change(old_index, new_index)) {
+ /* 1. Change just s value in apll m,p,s value */
+ tmp = __raw_readl(EXYNOS5_APLL_CON0);
+ tmp &= ~(0x7 << 0);
+ tmp |= (exynos5_apll_pms_table[new_index] & 0x7);
+ __raw_writel(tmp, EXYNOS5_APLL_CON0);
+ /* 2. Change the system clock divider values */
+ set_clkdiv(new_index);
+ } else {
+ /* Clock Configuration Procedure */
+ /* 1. Change the apll m,p,s value */
+ set_apll(new_index, old_index);
+ /* 2. Change the system clock divider values */
+ set_clkdiv(new_index);
+ }
+ }
+}
+
+static void __init set_volt_table(void)
+{
+ unsigned int i;
+
+ exynos5250_freq_table[L0].frequency = CPUFREQ_ENTRY_INVALID;
+ exynos5250_freq_table[L1].frequency = CPUFREQ_ENTRY_INVALID;
+ exynos5250_freq_table[L2].frequency = CPUFREQ_ENTRY_INVALID;
+ exynos5250_freq_table[L3].frequency = CPUFREQ_ENTRY_INVALID;
+ exynos5250_freq_table[L4].frequency = CPUFREQ_ENTRY_INVALID;
+ exynos5250_freq_table[L5].frequency = CPUFREQ_ENTRY_INVALID;
+ exynos5250_freq_table[L6].frequency = CPUFREQ_ENTRY_INVALID;
+
+ max_support_idx = L7;
+
+ for (i = 0 ; i < CPUFREQ_LEVEL_END ; i++)
+ exynos5250_volt_table[i] = asv_voltage_5250[i];
+}
+
+int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
+{
+ int i;
+ unsigned int tmp;
+ unsigned long rate;
+
+ set_volt_table();
+
+ cpu_clk = clk_get(NULL, "armclk");
+ if (IS_ERR(cpu_clk))
+ return PTR_ERR(cpu_clk);
+
+ moutcore = clk_get(NULL, "mout_cpu");
+ if (IS_ERR(moutcore))
+ goto err_moutcore;
+
+ mout_mpll = clk_get(NULL, "mout_mpll");
+ if (IS_ERR(mout_mpll))
+ goto err_mout_mpll;
+
+ rate = clk_get_rate(mout_mpll) / 1000;
+
+ mout_apll = clk_get(NULL, "mout_apll");
+ if (IS_ERR(mout_apll))
+ goto err_mout_apll;
+
+ for (i = L0; i < CPUFREQ_LEVEL_END; i++) {
+
+ exynos5250_clkdiv_table[i].index = i;
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_CPU0);
+
+ tmp &= ~((0x7 << 0) | (0x7 << 4) | (0x7 << 8) |
+ (0x7 << 12) | (0x7 << 16) | (0x7 << 20) |
+ (0x7 << 24) | (0x7 << 28));
+
+ tmp |= ((clkdiv_cpu0_5250[i][0] << 0) |
+ (clkdiv_cpu0_5250[i][1] << 4) |
+ (clkdiv_cpu0_5250[i][2] << 8) |
+ (clkdiv_cpu0_5250[i][3] << 12) |
+ (clkdiv_cpu0_5250[i][4] << 16) |
+ (clkdiv_cpu0_5250[i][5] << 20) |
+ (clkdiv_cpu0_5250[i][6] << 24) |
+ (clkdiv_cpu0_5250[i][7] << 28));
+
+ exynos5250_clkdiv_table[i].clkdiv = tmp;
+
+ tmp = __raw_readl(EXYNOS5_CLKDIV_CPU1);
+
+ tmp &= ~((0x7 << 0) | (0x7 << 4));
+
+ tmp |= ((clkdiv_cpu1_5250[i][0] << 0) |
+ (clkdiv_cpu1_5250[i][1] << 4));
+
+ exynos5250_clkdiv_table[i].clkdiv1 = tmp;
+ }
+
+ info->mpll_freq_khz = rate;
+ /* 1000Mhz */
+ info->pm_lock_idx = L7;
+ /* 800Mhz */
+ info->pll_safe_idx = L9;
+ info->max_support_idx = max_support_idx;
+ info->min_support_idx = min_support_idx;
+ info->cpu_clk = cpu_clk;
+ info->volt_table = exynos5250_volt_table;
+ info->freq_table = exynos5250_freq_table;
+ info->set_freq = exynos5250_set_frequency;
+ info->need_apll_change = exynos5250_pms_change;
+
+ return 0;
+
+err_mout_apll:
+ clk_put(mout_mpll);
+err_mout_mpll:
+ clk_put(moutcore);
+err_moutcore:
+ clk_put(cpu_clk);
+
+ pr_err("%s: failed initialization\n", __func__);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(exynos5250_cpufreq_init);
diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c
index 5d04c57aae30..17fa04d08be9 100644
--- a/drivers/cpufreq/omap-cpufreq.c
+++ b/drivers/cpufreq/omap-cpufreq.c
@@ -25,8 +25,8 @@
#include <linux/opp.h>
#include <linux/cpu.h>
#include <linux/module.h>
+#include <linux/regulator/consumer.h>
-#include <asm/system.h>
#include <asm/smp_plat.h>
#include <asm/cpu.h>
@@ -37,6 +37,9 @@
#include <mach/hardware.h>
+/* OPP tolerance in percentage */
+#define OPP_TOLERANCE 4
+
#ifdef CONFIG_SMP
struct lpj_info {
unsigned long ref;
@@ -52,6 +55,7 @@ static atomic_t freq_table_users = ATOMIC_INIT(0);
static struct clk *mpu_clk;
static char *mpu_clk_name;
static struct device *mpu_dev;
+static struct regulator *mpu_reg;
static int omap_verify_speed(struct cpufreq_policy *policy)
{
@@ -76,8 +80,10 @@ static int omap_target(struct cpufreq_policy *policy,
unsigned int relation)
{
unsigned int i;
- int ret = 0;
+ int r, ret = 0;
struct cpufreq_freqs freqs;
+ struct opp *opp;
+ unsigned long freq, volt = 0, volt_old = 0, tol = 0;
if (!freq_table) {
dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__,
@@ -111,13 +117,50 @@ static int omap_target(struct cpufreq_policy *policy,
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
}
-#ifdef CONFIG_CPU_FREQ_DEBUG
- pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new);
-#endif
+ freq = freqs.new * 1000;
+
+ if (mpu_reg) {
+ opp = opp_find_freq_ceil(mpu_dev, &freq);
+ if (IS_ERR(opp)) {
+ dev_err(mpu_dev, "%s: unable to find MPU OPP for %d\n",
+ __func__, freqs.new);
+ return -EINVAL;
+ }
+ volt = opp_get_voltage(opp);
+ tol = volt * OPP_TOLERANCE / 100;
+ volt_old = regulator_get_voltage(mpu_reg);
+ }
+
+ dev_dbg(mpu_dev, "cpufreq-omap: %u MHz, %ld mV --> %u MHz, %ld mV\n",
+ freqs.old / 1000, volt_old ? volt_old / 1000 : -1,
+ freqs.new / 1000, volt ? volt / 1000 : -1);
+
+ /* scaling up? scale voltage before frequency */
+ if (mpu_reg && (freqs.new > freqs.old)) {
+ r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
+ if (r < 0) {
+ dev_warn(mpu_dev, "%s: unable to scale voltage up.\n",
+ __func__);
+ freqs.new = freqs.old;
+ goto done;
+ }
+ }
ret = clk_set_rate(mpu_clk, freqs.new * 1000);
- freqs.new = omap_getspeed(policy->cpu);
+ /* scaling down? scale voltage after frequency */
+ if (mpu_reg && (freqs.new < freqs.old)) {
+ r = regulator_set_voltage(mpu_reg, volt - tol, volt + tol);
+ if (r < 0) {
+ dev_warn(mpu_dev, "%s: unable to scale voltage down.\n",
+ __func__);
+ ret = clk_set_rate(mpu_clk, freqs.old * 1000);
+ freqs.new = freqs.old;
+ goto done;
+ }
+ }
+
+ freqs.new = omap_getspeed(policy->cpu);
#ifdef CONFIG_SMP
/*
* Note that loops_per_jiffy is not updated on SMP systems in
@@ -144,6 +187,7 @@ static int omap_target(struct cpufreq_policy *policy,
freqs.new);
#endif
+done:
/* notifiers */
for_each_cpu(i, policy->cpus) {
freqs.cpu = i;
@@ -260,6 +304,23 @@ static int __init omap_cpufreq_init(void)
return -EINVAL;
}
+ mpu_reg = regulator_get(mpu_dev, "vcc");
+ if (IS_ERR(mpu_reg)) {
+ pr_warning("%s: unable to get MPU regulator\n", __func__);
+ mpu_reg = NULL;
+ } else {
+ /*
+ * Ensure physical regulator is present.
+ * (e.g. could be dummy regulator.)
+ */
+ if (regulator_get_voltage(mpu_reg) < 0) {
+ pr_warn("%s: physical regulator not present for MPU\n",
+ __func__);
+ regulator_put(mpu_reg);
+ mpu_reg = NULL;
+ }
+ }
+
return cpufreq_register_driver(&omap_driver);
}
diff --git a/drivers/cpufreq/powernow-k7.c b/drivers/cpufreq/powernow-k7.c
index cf7e1ee005a2..334cc2f1e9f1 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/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c
new file mode 100644
index 000000000000..50d2f15a3c8a
--- /dev/null
+++ b/drivers/cpufreq/s3c2416-cpufreq.c
@@ -0,0 +1,542 @@
+/*
+ * S3C2416/2450 CPUfreq Support
+ *
+ * Copyright 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on s3c64xx_cpufreq.c
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/cpufreq.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reboot.h>
+#include <linux/module.h>
+
+static DEFINE_MUTEX(cpufreq_lock);
+
+struct s3c2416_data {
+ struct clk *armdiv;
+ struct clk *armclk;
+ struct clk *hclk;
+
+ unsigned long regulator_latency;
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ struct regulator *vddarm;
+#endif
+
+ struct cpufreq_frequency_table *freq_table;
+
+ bool is_dvs;
+ bool disable_dvs;
+};
+
+static struct s3c2416_data s3c2416_cpufreq;
+
+struct s3c2416_dvfs {
+ unsigned int vddarm_min;
+ unsigned int vddarm_max;
+};
+
+/* pseudo-frequency for dvs mode */
+#define FREQ_DVS 132333
+
+/* frequency to sleep and reboot in
+ * it's essential to leave dvs, as some boards do not reconfigure the
+ * regulator on reboot
+ */
+#define FREQ_SLEEP 133333
+
+/* Sources for the ARMCLK */
+#define SOURCE_HCLK 0
+#define SOURCE_ARMDIV 1
+
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+/* S3C2416 only supports changing the voltage in the dvs-mode.
+ * Voltages down to 1.0V seem to work, so we take what the regulator
+ * can get us.
+ */
+static struct s3c2416_dvfs s3c2416_dvfs_table[] = {
+ [SOURCE_HCLK] = { 950000, 1250000 },
+ [SOURCE_ARMDIV] = { 1250000, 1350000 },
+};
+#endif
+
+static struct cpufreq_frequency_table s3c2416_freq_table[] = {
+ { SOURCE_HCLK, FREQ_DVS },
+ { SOURCE_ARMDIV, 133333 },
+ { SOURCE_ARMDIV, 266666 },
+ { SOURCE_ARMDIV, 400000 },
+ { 0, CPUFREQ_TABLE_END },
+};
+
+static struct cpufreq_frequency_table s3c2450_freq_table[] = {
+ { SOURCE_HCLK, FREQ_DVS },
+ { SOURCE_ARMDIV, 133500 },
+ { SOURCE_ARMDIV, 267000 },
+ { SOURCE_ARMDIV, 534000 },
+ { 0, CPUFREQ_TABLE_END },
+};
+
+static int s3c2416_cpufreq_verify_speed(struct cpufreq_policy *policy)
+{
+ struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ return cpufreq_frequency_table_verify(policy, s3c_freq->freq_table);
+}
+
+static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu)
+{
+ struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
+
+ if (cpu != 0)
+ return 0;
+
+ /* return our pseudo-frequency when in dvs mode */
+ if (s3c_freq->is_dvs)
+ return FREQ_DVS;
+
+ return clk_get_rate(s3c_freq->armclk) / 1000;
+}
+
+static int s3c2416_cpufreq_set_armdiv(struct s3c2416_data *s3c_freq,
+ unsigned int freq)
+{
+ int ret;
+
+ if (clk_get_rate(s3c_freq->armdiv) / 1000 != freq) {
+ ret = clk_set_rate(s3c_freq->armdiv, freq * 1000);
+ if (ret < 0) {
+ pr_err("cpufreq: Failed to set armdiv rate %dkHz: %d\n",
+ freq, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int s3c2416_cpufreq_enter_dvs(struct s3c2416_data *s3c_freq, int idx)
+{
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ struct s3c2416_dvfs *dvfs;
+#endif
+ int ret;
+
+ if (s3c_freq->is_dvs) {
+ pr_debug("cpufreq: already in dvs mode, nothing to do\n");
+ return 0;
+ }
+
+ pr_debug("cpufreq: switching armclk to hclk (%lukHz)\n",
+ clk_get_rate(s3c_freq->hclk) / 1000);
+ ret = clk_set_parent(s3c_freq->armclk, s3c_freq->hclk);
+ if (ret < 0) {
+ pr_err("cpufreq: Failed to switch armclk to hclk: %d\n", ret);
+ return ret;
+ }
+
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ /* changing the core voltage is only allowed when in dvs mode */
+ if (s3c_freq->vddarm) {
+ dvfs = &s3c2416_dvfs_table[idx];
+
+ pr_debug("cpufreq: setting regultor to %d-%d\n",
+ dvfs->vddarm_min, dvfs->vddarm_max);
+ ret = regulator_set_voltage(s3c_freq->vddarm,
+ dvfs->vddarm_min,
+ dvfs->vddarm_max);
+
+ /* when lowering the voltage failed, there is nothing to do */
+ if (ret != 0)
+ pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
+ }
+#endif
+
+ s3c_freq->is_dvs = 1;
+
+ return 0;
+}
+
+static int s3c2416_cpufreq_leave_dvs(struct s3c2416_data *s3c_freq, int idx)
+{
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ struct s3c2416_dvfs *dvfs;
+#endif
+ int ret;
+
+ if (!s3c_freq->is_dvs) {
+ pr_debug("cpufreq: not in dvs mode, so can't leave\n");
+ return 0;
+ }
+
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ if (s3c_freq->vddarm) {
+ dvfs = &s3c2416_dvfs_table[idx];
+
+ pr_debug("cpufreq: setting regultor to %d-%d\n",
+ dvfs->vddarm_min, dvfs->vddarm_max);
+ ret = regulator_set_voltage(s3c_freq->vddarm,
+ dvfs->vddarm_min,
+ dvfs->vddarm_max);
+ if (ret != 0) {
+ pr_err("cpufreq: Failed to set VDDARM: %d\n", ret);
+ return ret;
+ }
+ }
+#endif
+
+ /* force armdiv to hclk frequency for transition from dvs*/
+ if (clk_get_rate(s3c_freq->armdiv) > clk_get_rate(s3c_freq->hclk)) {
+ pr_debug("cpufreq: force armdiv to hclk frequency (%lukHz)\n",
+ clk_get_rate(s3c_freq->hclk) / 1000);
+ ret = s3c2416_cpufreq_set_armdiv(s3c_freq,
+ clk_get_rate(s3c_freq->hclk) / 1000);
+ if (ret < 0) {
+ pr_err("cpufreq: Failed to to set the armdiv to %lukHz: %d\n",
+ clk_get_rate(s3c_freq->hclk) / 1000, ret);
+ return ret;
+ }
+ }
+
+ pr_debug("cpufreq: switching armclk parent to armdiv (%lukHz)\n",
+ clk_get_rate(s3c_freq->armdiv) / 1000);
+
+ ret = clk_set_parent(s3c_freq->armclk, s3c_freq->armdiv);
+ if (ret < 0) {
+ pr_err("cpufreq: Failed to switch armclk clock parent to armdiv: %d\n",
+ ret);
+ return ret;
+ }
+
+ s3c_freq->is_dvs = 0;
+
+ return 0;
+}
+
+static int s3c2416_cpufreq_set_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
+ struct cpufreq_freqs freqs;
+ int idx, ret, to_dvs = 0;
+ unsigned int i;
+
+ mutex_lock(&cpufreq_lock);
+
+ pr_debug("cpufreq: to %dKHz, relation %d\n", target_freq, relation);
+
+ ret = cpufreq_frequency_table_target(policy, s3c_freq->freq_table,
+ target_freq, relation, &i);
+ if (ret != 0)
+ goto out;
+
+ idx = s3c_freq->freq_table[i].index;
+
+ if (idx == SOURCE_HCLK)
+ to_dvs = 1;
+
+ /* switching to dvs when it's not allowed */
+ if (to_dvs && s3c_freq->disable_dvs) {
+ pr_debug("cpufreq: entering dvs mode not allowed\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ freqs.cpu = 0;
+ freqs.flags = 0;
+ freqs.old = s3c_freq->is_dvs ? FREQ_DVS
+ : clk_get_rate(s3c_freq->armclk) / 1000;
+
+ /* When leavin dvs mode, always switch the armdiv to the hclk rate
+ * The S3C2416 has stability issues when switching directly to
+ * higher frequencies.
+ */
+ freqs.new = (s3c_freq->is_dvs && !to_dvs)
+ ? clk_get_rate(s3c_freq->hclk) / 1000
+ : s3c_freq->freq_table[i].frequency;
+
+ pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new);
+
+ if (!to_dvs && freqs.old == freqs.new)
+ goto out;
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+ if (to_dvs) {
+ pr_debug("cpufreq: enter dvs\n");
+ ret = s3c2416_cpufreq_enter_dvs(s3c_freq, idx);
+ } else if (s3c_freq->is_dvs) {
+ pr_debug("cpufreq: leave dvs\n");
+ ret = s3c2416_cpufreq_leave_dvs(s3c_freq, idx);
+ } else {
+ pr_debug("cpufreq: change armdiv to %dkHz\n", freqs.new);
+ ret = s3c2416_cpufreq_set_armdiv(s3c_freq, freqs.new);
+ }
+
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+out:
+ mutex_unlock(&cpufreq_lock);
+
+ return ret;
+}
+
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+static void __init s3c2416_cpufreq_cfg_regulator(struct s3c2416_data *s3c_freq)
+{
+ int count, v, i, found;
+ struct cpufreq_frequency_table *freq;
+ struct s3c2416_dvfs *dvfs;
+
+ count = regulator_count_voltages(s3c_freq->vddarm);
+ if (count < 0) {
+ pr_err("cpufreq: Unable to check supported voltages\n");
+ return;
+ }
+
+ freq = s3c_freq->freq_table;
+ while (count > 0 && freq->frequency != CPUFREQ_TABLE_END) {
+ if (freq->frequency == CPUFREQ_ENTRY_INVALID)
+ continue;
+
+ dvfs = &s3c2416_dvfs_table[freq->index];
+ found = 0;
+
+ /* Check only the min-voltage, more is always ok on S3C2416 */
+ for (i = 0; i < count; i++) {
+ v = regulator_list_voltage(s3c_freq->vddarm, i);
+ if (v >= dvfs->vddarm_min)
+ found = 1;
+ }
+
+ if (!found) {
+ pr_debug("cpufreq: %dkHz unsupported by regulator\n",
+ freq->frequency);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ freq++;
+ }
+
+ /* Guessed */
+ s3c_freq->regulator_latency = 1 * 1000 * 1000;
+}
+#endif
+
+static int s3c2416_cpufreq_reboot_notifier_evt(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
+ int ret;
+
+ mutex_lock(&cpufreq_lock);
+
+ /* disable further changes */
+ s3c_freq->disable_dvs = 1;
+
+ mutex_unlock(&cpufreq_lock);
+
+ /* some boards don't reconfigure the regulator on reboot, which
+ * could lead to undervolting the cpu when the clock is reset.
+ * Therefore we always leave the DVS mode on reboot.
+ */
+ if (s3c_freq->is_dvs) {
+ pr_debug("cpufreq: leave dvs on reboot\n");
+ ret = cpufreq_driver_target(cpufreq_cpu_get(0), FREQ_SLEEP, 0);
+ if (ret < 0)
+ return NOTIFY_BAD;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block s3c2416_cpufreq_reboot_notifier = {
+ .notifier_call = s3c2416_cpufreq_reboot_notifier_evt,
+};
+
+static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
+{
+ struct s3c2416_data *s3c_freq = &s3c2416_cpufreq;
+ struct cpufreq_frequency_table *freq;
+ struct clk *msysclk;
+ unsigned long rate;
+ int ret;
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ msysclk = clk_get(NULL, "msysclk");
+ if (IS_ERR(msysclk)) {
+ ret = PTR_ERR(msysclk);
+ pr_err("cpufreq: Unable to obtain msysclk: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * S3C2416 and S3C2450 share the same processor-ID and also provide no
+ * other means to distinguish them other than through the rate of
+ * msysclk. On S3C2416 msysclk runs at 800MHz and on S3C2450 at 533MHz.
+ */
+ rate = clk_get_rate(msysclk);
+ if (rate == 800 * 1000 * 1000) {
+ pr_info("cpufreq: msysclk running at %lukHz, using S3C2416 frequency table\n",
+ rate / 1000);
+ s3c_freq->freq_table = s3c2416_freq_table;
+ policy->cpuinfo.max_freq = 400000;
+ } else if (rate / 1000 == 534000) {
+ pr_info("cpufreq: msysclk running at %lukHz, using S3C2450 frequency table\n",
+ rate / 1000);
+ s3c_freq->freq_table = s3c2450_freq_table;
+ policy->cpuinfo.max_freq = 534000;
+ }
+
+ /* not needed anymore */
+ clk_put(msysclk);
+
+ if (s3c_freq->freq_table == NULL) {
+ pr_err("cpufreq: No frequency information for this CPU, msysclk at %lukHz\n",
+ rate / 1000);
+ return -ENODEV;
+ }
+
+ s3c_freq->is_dvs = 0;
+
+ s3c_freq->armdiv = clk_get(NULL, "armdiv");
+ if (IS_ERR(s3c_freq->armdiv)) {
+ ret = PTR_ERR(s3c_freq->armdiv);
+ pr_err("cpufreq: Unable to obtain ARMDIV: %d\n", ret);
+ return ret;
+ }
+
+ s3c_freq->hclk = clk_get(NULL, "hclk");
+ if (IS_ERR(s3c_freq->hclk)) {
+ ret = PTR_ERR(s3c_freq->hclk);
+ pr_err("cpufreq: Unable to obtain HCLK: %d\n", ret);
+ goto err_hclk;
+ }
+
+ /* chech hclk rate, we only support the common 133MHz for now
+ * hclk could also run at 66MHz, but this not often used
+ */
+ rate = clk_get_rate(s3c_freq->hclk);
+ if (rate < 133 * 1000 * 1000) {
+ pr_err("cpufreq: HCLK not at 133MHz\n");
+ clk_put(s3c_freq->hclk);
+ ret = -EINVAL;
+ goto err_armclk;
+ }
+
+ s3c_freq->armclk = clk_get(NULL, "armclk");
+ if (IS_ERR(s3c_freq->armclk)) {
+ ret = PTR_ERR(s3c_freq->armclk);
+ pr_err("cpufreq: Unable to obtain ARMCLK: %d\n", ret);
+ goto err_armclk;
+ }
+
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ s3c_freq->vddarm = regulator_get(NULL, "vddarm");
+ if (IS_ERR(s3c_freq->vddarm)) {
+ ret = PTR_ERR(s3c_freq->vddarm);
+ pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret);
+ goto err_vddarm;
+ }
+
+ s3c2416_cpufreq_cfg_regulator(s3c_freq);
+#else
+ s3c_freq->regulator_latency = 0;
+#endif
+
+ freq = s3c_freq->freq_table;
+ while (freq->frequency != CPUFREQ_TABLE_END) {
+ /* special handling for dvs mode */
+ if (freq->index == 0) {
+ if (!s3c_freq->hclk) {
+ pr_debug("cpufreq: %dkHz unsupported as it would need unavailable dvs mode\n",
+ freq->frequency);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ } else {
+ freq++;
+ continue;
+ }
+ }
+
+ /* Check for frequencies we can generate */
+ rate = clk_round_rate(s3c_freq->armdiv,
+ freq->frequency * 1000);
+ rate /= 1000;
+ if (rate != freq->frequency) {
+ pr_debug("cpufreq: %dkHz unsupported by clock (clk_round_rate return %lu)\n",
+ freq->frequency, rate);
+ freq->frequency = CPUFREQ_ENTRY_INVALID;
+ }
+
+ freq++;
+ }
+
+ policy->cur = clk_get_rate(s3c_freq->armclk) / 1000;
+
+ /* Datasheet says PLL stabalisation time must be at least 300us,
+ * so but add some fudge. (reference in LOCKCON0 register description)
+ */
+ policy->cpuinfo.transition_latency = (500 * 1000) +
+ s3c_freq->regulator_latency;
+
+ ret = cpufreq_frequency_table_cpuinfo(policy, s3c_freq->freq_table);
+ if (ret)
+ goto err_freq_table;
+
+ cpufreq_frequency_table_get_attr(s3c_freq->freq_table, 0);
+
+ register_reboot_notifier(&s3c2416_cpufreq_reboot_notifier);
+
+ return 0;
+
+err_freq_table:
+#ifdef CONFIG_ARM_S3C2416_CPUFREQ_VCORESCALE
+ regulator_put(s3c_freq->vddarm);
+err_vddarm:
+#endif
+ clk_put(s3c_freq->armclk);
+err_armclk:
+ clk_put(s3c_freq->hclk);
+err_hclk:
+ clk_put(s3c_freq->armdiv);
+
+ return ret;
+}
+
+static struct freq_attr *s3c2416_cpufreq_attr[] = {
+ &cpufreq_freq_attr_scaling_available_freqs,
+ NULL,
+};
+
+static struct cpufreq_driver s3c2416_cpufreq_driver = {
+ .owner = THIS_MODULE,
+ .flags = 0,
+ .verify = s3c2416_cpufreq_verify_speed,
+ .target = s3c2416_cpufreq_set_target,
+ .get = s3c2416_cpufreq_get_speed,
+ .init = s3c2416_cpufreq_driver_init,
+ .name = "s3c2416",
+ .attr = s3c2416_cpufreq_attr,
+};
+
+static int __init s3c2416_cpufreq_init(void)
+{
+ return cpufreq_register_driver(&s3c2416_cpufreq_driver);
+}
+module_init(s3c2416_cpufreq_init);
diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c
index a5e72cb5f53c..6f9490b3c356 100644
--- a/drivers/cpufreq/s3c64xx-cpufreq.c
+++ b/drivers/cpufreq/s3c64xx-cpufreq.c
@@ -217,13 +217,6 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
} else {
s3c64xx_cpufreq_config_regulator();
}
-
- vddint = regulator_get(NULL, "vddint");
- if (IS_ERR(vddint)) {
- ret = PTR_ERR(vddint);
- pr_err("Failed to obtain VDDINT: %d\n", ret);
- vddint = NULL;
- }
#endif
freq = s3c64xx_freq_table;
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index e707979767fb..ab9abb46d01a 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -64,7 +64,6 @@ config CRYPTO_DEV_GEODE
config ZCRYPT
tristate "Support for PCI-attached cryptographic adapters"
depends on S390
- select ZCRYPT_MONOLITHIC if ZCRYPT="y"
select HW_RANDOM
help
Select this option if you want to use a PCI-attached cryptographic
@@ -77,14 +76,6 @@ config ZCRYPT
+ Crypto Express3 Coprocessor (CEX3C)
+ Crypto Express3 Accelerator (CEX3A)
-config ZCRYPT_MONOLITHIC
- bool "Monolithic zcrypt module"
- depends on ZCRYPT
- help
- Select this option if you want to have a single module z90crypt,
- that contains all parts of the crypto device driver (ap bus,
- request router and all the card drivers).
-
config CRYPTO_SHA1_S390
tristate "SHA1 digest algorithm"
depends on S390
diff --git a/drivers/devfreq/exynos4_bus.c b/drivers/devfreq/exynos4_bus.c
index 1a361e99965a..88ddc77a9bb1 100644
--- a/drivers/devfreq/exynos4_bus.c
+++ b/drivers/devfreq/exynos4_bus.c
@@ -311,51 +311,51 @@ static int exynos4210_set_busclk(struct busfreq_data *data, struct opp *opp)
/* Change Divider - DMC0 */
tmp = data->dmc_divtable[index];
- __raw_writel(tmp, S5P_CLKDIV_DMC0);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0);
} while (tmp & 0x11111111);
/* Change Divider - TOP */
tmp = data->top_divtable[index];
- __raw_writel(tmp, S5P_CLKDIV_TOP);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_TOP);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_TOP);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP);
} while (tmp & 0x11111);
/* Change Divider - LEFTBUS */
- tmp = __raw_readl(S5P_CLKDIV_LEFTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS);
- tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK);
tmp |= ((exynos4210_clkdiv_lr_bus[index][0] <<
- S5P_CLKDIV_BUS_GDLR_SHIFT) |
+ EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) |
(exynos4210_clkdiv_lr_bus[index][1] <<
- S5P_CLKDIV_BUS_GPLR_SHIFT));
+ EXYNOS4_CLKDIV_BUS_GPLR_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_LEFTBUS);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS);
} while (tmp & 0x11);
/* Change Divider - RIGHTBUS */
- tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS);
- tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK);
tmp |= ((exynos4210_clkdiv_lr_bus[index][0] <<
- S5P_CLKDIV_BUS_GDLR_SHIFT) |
+ EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) |
(exynos4210_clkdiv_lr_bus[index][1] <<
- S5P_CLKDIV_BUS_GPLR_SHIFT));
+ EXYNOS4_CLKDIV_BUS_GPLR_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS);
} while (tmp & 0x11);
return 0;
@@ -376,137 +376,137 @@ static int exynos4x12_set_busclk(struct busfreq_data *data, struct opp *opp)
/* Change Divider - DMC0 */
tmp = data->dmc_divtable[index];
- __raw_writel(tmp, S5P_CLKDIV_DMC0);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_DMC0);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC0);
} while (tmp & 0x11111111);
/* Change Divider - DMC1 */
- tmp = __raw_readl(S5P_CLKDIV_DMC1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_DMC1);
- tmp &= ~(S5P_CLKDIV_DMC1_G2D_ACP_MASK |
- S5P_CLKDIV_DMC1_C2C_MASK |
- S5P_CLKDIV_DMC1_C2CACLK_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_DMC1_G2D_ACP_MASK |
+ EXYNOS4_CLKDIV_DMC1_C2C_MASK |
+ EXYNOS4_CLKDIV_DMC1_C2CACLK_MASK);
tmp |= ((exynos4x12_clkdiv_dmc1[index][0] <<
- S5P_CLKDIV_DMC1_G2D_ACP_SHIFT) |
+ EXYNOS4_CLKDIV_DMC1_G2D_ACP_SHIFT) |
(exynos4x12_clkdiv_dmc1[index][1] <<
- S5P_CLKDIV_DMC1_C2C_SHIFT) |
+ EXYNOS4_CLKDIV_DMC1_C2C_SHIFT) |
(exynos4x12_clkdiv_dmc1[index][2] <<
- S5P_CLKDIV_DMC1_C2CACLK_SHIFT));
+ EXYNOS4_CLKDIV_DMC1_C2CACLK_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_DMC1);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_DMC1);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_DMC1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_DMC1);
} while (tmp & 0x111111);
/* Change Divider - TOP */
- tmp = __raw_readl(S5P_CLKDIV_TOP);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_TOP);
- tmp &= ~(S5P_CLKDIV_TOP_ACLK266_GPS_MASK |
- S5P_CLKDIV_TOP_ACLK100_MASK |
- S5P_CLKDIV_TOP_ACLK160_MASK |
- S5P_CLKDIV_TOP_ACLK133_MASK |
- S5P_CLKDIV_TOP_ONENAND_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK266_GPS_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK100_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK160_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK133_MASK |
+ EXYNOS4_CLKDIV_TOP_ONENAND_MASK);
tmp |= ((exynos4x12_clkdiv_top[index][0] <<
- S5P_CLKDIV_TOP_ACLK266_GPS_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK266_GPS_SHIFT) |
(exynos4x12_clkdiv_top[index][1] <<
- S5P_CLKDIV_TOP_ACLK100_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) |
(exynos4x12_clkdiv_top[index][2] <<
- S5P_CLKDIV_TOP_ACLK160_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) |
(exynos4x12_clkdiv_top[index][3] <<
- S5P_CLKDIV_TOP_ACLK133_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) |
(exynos4x12_clkdiv_top[index][4] <<
- S5P_CLKDIV_TOP_ONENAND_SHIFT));
+ EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_TOP);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_TOP);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_TOP);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_TOP);
} while (tmp & 0x11111);
/* Change Divider - LEFTBUS */
- tmp = __raw_readl(S5P_CLKDIV_LEFTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_LEFTBUS);
- tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK);
tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] <<
- S5P_CLKDIV_BUS_GDLR_SHIFT) |
+ EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) |
(exynos4x12_clkdiv_lr_bus[index][1] <<
- S5P_CLKDIV_BUS_GPLR_SHIFT));
+ EXYNOS4_CLKDIV_BUS_GPLR_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_LEFTBUS);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_LEFTBUS);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_LEFTBUS);
} while (tmp & 0x11);
/* Change Divider - RIGHTBUS */
- tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_RIGHTBUS);
- tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_BUS_GDLR_MASK | EXYNOS4_CLKDIV_BUS_GPLR_MASK);
tmp |= ((exynos4x12_clkdiv_lr_bus[index][0] <<
- S5P_CLKDIV_BUS_GDLR_SHIFT) |
+ EXYNOS4_CLKDIV_BUS_GDLR_SHIFT) |
(exynos4x12_clkdiv_lr_bus[index][1] <<
- S5P_CLKDIV_BUS_GPLR_SHIFT));
+ EXYNOS4_CLKDIV_BUS_GPLR_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_RIGHTBUS);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_RIGHTBUS);
} while (tmp & 0x11);
/* Change Divider - MFC */
- tmp = __raw_readl(S5P_CLKDIV_MFC);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_MFC);
- tmp &= ~(S5P_CLKDIV_MFC_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_MFC_MASK);
tmp |= ((exynos4x12_clkdiv_sclkip[index][0] <<
- S5P_CLKDIV_MFC_SHIFT));
+ EXYNOS4_CLKDIV_MFC_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_MFC);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_MFC);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_MFC);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_MFC);
} while (tmp & 0x1);
/* Change Divider - JPEG */
- tmp = __raw_readl(S5P_CLKDIV_CAM1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_CAM1);
- tmp &= ~(S5P_CLKDIV_CAM1_JPEG_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_CAM1_JPEG_MASK);
tmp |= ((exynos4x12_clkdiv_sclkip[index][1] <<
- S5P_CLKDIV_CAM1_JPEG_SHIFT));
+ EXYNOS4_CLKDIV_CAM1_JPEG_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_CAM1);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_CAM1);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_CAM1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1);
} while (tmp & 0x1);
/* Change Divider - FIMC0~3 */
- tmp = __raw_readl(S5P_CLKDIV_CAM);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_CAM);
- tmp &= ~(S5P_CLKDIV_CAM_FIMC0_MASK | S5P_CLKDIV_CAM_FIMC1_MASK |
- S5P_CLKDIV_CAM_FIMC2_MASK | S5P_CLKDIV_CAM_FIMC3_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_CAM_FIMC0_MASK | EXYNOS4_CLKDIV_CAM_FIMC1_MASK |
+ EXYNOS4_CLKDIV_CAM_FIMC2_MASK | EXYNOS4_CLKDIV_CAM_FIMC3_MASK);
tmp |= ((exynos4x12_clkdiv_sclkip[index][2] <<
- S5P_CLKDIV_CAM_FIMC0_SHIFT) |
+ EXYNOS4_CLKDIV_CAM_FIMC0_SHIFT) |
(exynos4x12_clkdiv_sclkip[index][2] <<
- S5P_CLKDIV_CAM_FIMC1_SHIFT) |
+ EXYNOS4_CLKDIV_CAM_FIMC1_SHIFT) |
(exynos4x12_clkdiv_sclkip[index][2] <<
- S5P_CLKDIV_CAM_FIMC2_SHIFT) |
+ EXYNOS4_CLKDIV_CAM_FIMC2_SHIFT) |
(exynos4x12_clkdiv_sclkip[index][2] <<
- S5P_CLKDIV_CAM_FIMC3_SHIFT));
+ EXYNOS4_CLKDIV_CAM_FIMC3_SHIFT));
- __raw_writel(tmp, S5P_CLKDIV_CAM);
+ __raw_writel(tmp, EXYNOS4_CLKDIV_CAM);
do {
- tmp = __raw_readl(S5P_CLKDIV_STAT_CAM1);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STAT_CAM1);
} while (tmp & 0x1111);
return 0;
@@ -760,55 +760,55 @@ static int exynos4210_init_tables(struct busfreq_data *data)
int mgrp;
int i, err = 0;
- tmp = __raw_readl(S5P_CLKDIV_DMC0);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0);
for (i = LV_0; i < EX4210_LV_NUM; i++) {
- tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK |
- S5P_CLKDIV_DMC0_ACPPCLK_MASK |
- S5P_CLKDIV_DMC0_DPHY_MASK |
- S5P_CLKDIV_DMC0_DMC_MASK |
- S5P_CLKDIV_DMC0_DMCD_MASK |
- S5P_CLKDIV_DMC0_DMCP_MASK |
- S5P_CLKDIV_DMC0_COPY2_MASK |
- S5P_CLKDIV_DMC0_CORETI_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK |
+ EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK |
+ EXYNOS4_CLKDIV_DMC0_DPHY_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMC_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMCD_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMCP_MASK |
+ EXYNOS4_CLKDIV_DMC0_COPY2_MASK |
+ EXYNOS4_CLKDIV_DMC0_CORETI_MASK);
tmp |= ((exynos4210_clkdiv_dmc0[i][0] <<
- S5P_CLKDIV_DMC0_ACP_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) |
(exynos4210_clkdiv_dmc0[i][1] <<
- S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) |
(exynos4210_clkdiv_dmc0[i][2] <<
- S5P_CLKDIV_DMC0_DPHY_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) |
(exynos4210_clkdiv_dmc0[i][3] <<
- S5P_CLKDIV_DMC0_DMC_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) |
(exynos4210_clkdiv_dmc0[i][4] <<
- S5P_CLKDIV_DMC0_DMCD_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) |
(exynos4210_clkdiv_dmc0[i][5] <<
- S5P_CLKDIV_DMC0_DMCP_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT) |
(exynos4210_clkdiv_dmc0[i][6] <<
- S5P_CLKDIV_DMC0_COPY2_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_COPY2_SHIFT) |
(exynos4210_clkdiv_dmc0[i][7] <<
- S5P_CLKDIV_DMC0_CORETI_SHIFT));
+ EXYNOS4_CLKDIV_DMC0_CORETI_SHIFT));
data->dmc_divtable[i] = tmp;
}
- tmp = __raw_readl(S5P_CLKDIV_TOP);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_TOP);
for (i = LV_0; i < EX4210_LV_NUM; i++) {
- tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK |
- S5P_CLKDIV_TOP_ACLK100_MASK |
- S5P_CLKDIV_TOP_ACLK160_MASK |
- S5P_CLKDIV_TOP_ACLK133_MASK |
- S5P_CLKDIV_TOP_ONENAND_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_TOP_ACLK200_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK100_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK160_MASK |
+ EXYNOS4_CLKDIV_TOP_ACLK133_MASK |
+ EXYNOS4_CLKDIV_TOP_ONENAND_MASK);
tmp |= ((exynos4210_clkdiv_top[i][0] <<
- S5P_CLKDIV_TOP_ACLK200_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK200_SHIFT) |
(exynos4210_clkdiv_top[i][1] <<
- S5P_CLKDIV_TOP_ACLK100_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK100_SHIFT) |
(exynos4210_clkdiv_top[i][2] <<
- S5P_CLKDIV_TOP_ACLK160_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK160_SHIFT) |
(exynos4210_clkdiv_top[i][3] <<
- S5P_CLKDIV_TOP_ACLK133_SHIFT) |
+ EXYNOS4_CLKDIV_TOP_ACLK133_SHIFT) |
(exynos4210_clkdiv_top[i][4] <<
- S5P_CLKDIV_TOP_ONENAND_SHIFT));
+ EXYNOS4_CLKDIV_TOP_ONENAND_SHIFT));
data->top_divtable[i] = tmp;
}
@@ -868,32 +868,32 @@ static int exynos4x12_init_tables(struct busfreq_data *data)
int ret;
/* Enable pause function for DREX2 DVFS */
- tmp = __raw_readl(S5P_DMC_PAUSE_CTRL);
- tmp |= DMC_PAUSE_ENABLE;
- __raw_writel(tmp, S5P_DMC_PAUSE_CTRL);
+ tmp = __raw_readl(EXYNOS4_DMC_PAUSE_CTRL);
+ tmp |= EXYNOS4_DMC_PAUSE_ENABLE;
+ __raw_writel(tmp, EXYNOS4_DMC_PAUSE_CTRL);
- tmp = __raw_readl(S5P_CLKDIV_DMC0);
+ tmp = __raw_readl(EXYNOS4_CLKDIV_DMC0);
for (i = 0; i < EX4x12_LV_NUM; i++) {
- tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK |
- S5P_CLKDIV_DMC0_ACPPCLK_MASK |
- S5P_CLKDIV_DMC0_DPHY_MASK |
- S5P_CLKDIV_DMC0_DMC_MASK |
- S5P_CLKDIV_DMC0_DMCD_MASK |
- S5P_CLKDIV_DMC0_DMCP_MASK);
+ tmp &= ~(EXYNOS4_CLKDIV_DMC0_ACP_MASK |
+ EXYNOS4_CLKDIV_DMC0_ACPPCLK_MASK |
+ EXYNOS4_CLKDIV_DMC0_DPHY_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMC_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMCD_MASK |
+ EXYNOS4_CLKDIV_DMC0_DMCP_MASK);
tmp |= ((exynos4x12_clkdiv_dmc0[i][0] <<
- S5P_CLKDIV_DMC0_ACP_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_ACP_SHIFT) |
(exynos4x12_clkdiv_dmc0[i][1] <<
- S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_ACPPCLK_SHIFT) |
(exynos4x12_clkdiv_dmc0[i][2] <<
- S5P_CLKDIV_DMC0_DPHY_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DPHY_SHIFT) |
(exynos4x12_clkdiv_dmc0[i][3] <<
- S5P_CLKDIV_DMC0_DMC_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DMC_SHIFT) |
(exynos4x12_clkdiv_dmc0[i][4] <<
- S5P_CLKDIV_DMC0_DMCD_SHIFT) |
+ EXYNOS4_CLKDIV_DMC0_DMCD_SHIFT) |
(exynos4x12_clkdiv_dmc0[i][5] <<
- S5P_CLKDIV_DMC0_DMCP_SHIFT));
+ EXYNOS4_CLKDIV_DMC0_DMCP_SHIFT));
data->dmc_divtable[i] = tmp;
}
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f1a274994bb1..4a6c46dea8a0 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -252,6 +252,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 009a222e8283..86b795baba98 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/imx-dma.c b/drivers/dma/imx-dma.c
index e4383ee2c9ac..38586ba8da91 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -14,7 +14,6 @@
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/init.h>
-#include <linux/module.h>
#include <linux/types.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 8bc5acf36ee5..63540d3e2153 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -35,7 +35,6 @@
#include <linux/dmaengine.h>
#include <linux/of.h>
#include <linux/of_device.h>
-#include <linux/module.h>
#include <asm/irq.h>
#include <mach/sdma.h>
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index b8ec03ee8e22..16b66c827f19 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -1035,18 +1035,7 @@ static struct amba_driver pl330_driver = {
.remove = pl330_remove,
};
-static int __init pl330_init(void)
-{
- return amba_driver_register(&pl330_driver);
-}
-module_init(pl330_init);
-
-static void __exit pl330_exit(void)
-{
- amba_driver_unregister(&pl330_driver);
- return;
-}
-module_exit(pl330_exit);
+module_amba_driver(pl330_driver);
MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>");
MODULE_DESCRIPTION("API Driver for PL330 DMAC");
diff --git a/drivers/dma/sa11x0-dma.c b/drivers/dma/sa11x0-dma.c
new file mode 100644
index 000000000000..16a6b48883cf
--- /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/edac/Kconfig b/drivers/edac/Kconfig
index 5948a2194f50..fdffa1beca17 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/amd64_edac.c b/drivers/edac/amd64_edac.c
index c9eee6d33e9a..7ef73c919c5d 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -1132,12 +1132,36 @@ static int k8_dbam_to_chip_select(struct amd64_pvt *pvt, u8 dct,
return ddr2_cs_size(cs_mode, dclr & WIDTH_128);
}
else if (pvt->ext_model >= K8_REV_D) {
+ unsigned diff;
WARN_ON(cs_mode > 10);
- if (cs_mode == 3 || cs_mode == 8)
- return 32 << (cs_mode - 1);
- else
- return 32 << cs_mode;
+ /*
+ * the below calculation, besides trying to win an obfuscated C
+ * contest, maps cs_mode values to DIMM chip select sizes. The
+ * mappings are:
+ *
+ * cs_mode CS size (mb)
+ * ======= ============
+ * 0 32
+ * 1 64
+ * 2 128
+ * 3 128
+ * 4 256
+ * 5 512
+ * 6 256
+ * 7 512
+ * 8 1024
+ * 9 1024
+ * 10 2048
+ *
+ * Basically, it calculates a value with which to shift the
+ * smallest CS size of 32MB.
+ *
+ * ddr[23]_cs_size have a similar purpose.
+ */
+ diff = cs_mode/3 + (unsigned)(cs_mode > 5);
+
+ return 32 << (cs_mode - diff);
}
else {
WARN_ON(cs_mode > 6);
@@ -2133,6 +2157,7 @@ static void read_mc_regs(struct amd64_pvt *pvt)
static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
{
u32 cs_mode, nr_pages;
+ u32 dbam = dct ? pvt->dbam1 : pvt->dbam0;
/*
* The math on this doesn't look right on the surface because x/2*4 can
@@ -2141,16 +2166,10 @@ static u32 amd64_csrow_nr_pages(struct amd64_pvt *pvt, u8 dct, int csrow_nr)
* number of bits to shift the DBAM register to extract the proper CSROW
* field.
*/
- cs_mode = (pvt->dbam0 >> ((csrow_nr / 2) * 4)) & 0xF;
+ cs_mode = (dbam >> ((csrow_nr / 2) * 4)) & 0xF;
nr_pages = pvt->ops->dbam_to_cs(pvt, dct, cs_mode) << (20 - PAGE_SHIFT);
- /*
- * If dual channel then double the memory size of single channel.
- * Channel count is 1 or 2
- */
- nr_pages <<= (pvt->channel_count - 1);
-
debugf0(" (csrow=%d) DBAM map index= %d\n", csrow_nr, cs_mode);
debugf0(" nr_pages= %u channel-count = %d\n",
nr_pages, pvt->channel_count);
@@ -2181,7 +2200,7 @@ static int init_csrows(struct mem_ctl_info *mci)
for_each_chip_select(i, 0, pvt) {
csrow = &mci->csrows[i];
- if (!csrow_enabled(i, 0, pvt)) {
+ if (!csrow_enabled(i, 0, pvt) && !csrow_enabled(i, 1, pvt)) {
debugf1("----CSROW %d EMPTY for node %d\n", i,
pvt->mc_node_id);
continue;
@@ -2191,7 +2210,10 @@ static int init_csrows(struct mem_ctl_info *mci)
i, pvt->mc_node_id);
empty = 0;
- csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
+ if (csrow_enabled(i, 0, pvt))
+ csrow->nr_pages = amd64_csrow_nr_pages(pvt, 0, i);
+ if (csrow_enabled(i, 1, pvt))
+ csrow->nr_pages += amd64_csrow_nr_pages(pvt, 1, i);
find_csrow_limits(mci, i, &input_addr_min, &input_addr_max);
sys_addr = input_addr_to_sys_addr(mci, input_addr_min);
csrow->first_page = (u32) (sys_addr >> PAGE_SHIFT);
@@ -2685,7 +2707,7 @@ static void __devexit amd64_remove_one_instance(struct pci_dev *pdev)
* PCI core identifies what devices are on a system during boot, and then
* inquiry this table to see if this driver is for a given device found.
*/
-static const struct pci_device_id amd64_pci_table[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(amd64_pci_table) = {
{
.vendor = PCI_VENDOR_ID_AMD,
.device = PCI_DEVICE_ID_AMD_K8_NB_MEMCTL,
diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c
index e47e73bbbcc5..f8fd3c807bde 100644
--- a/drivers/edac/amd76x_edac.c
+++ b/drivers/edac/amd76x_edac.c
@@ -321,7 +321,7 @@ static void __devexit amd76x_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id amd76x_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(amd76x_pci_tbl) = {
{
PCI_VEND_DEV(AMD, FE_GATE_700C), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
AMD762},
diff --git a/drivers/edac/e752x_edac.c b/drivers/edac/e752x_edac.c
index 1af531a11d21..41223261ede9 100644
--- a/drivers/edac/e752x_edac.c
+++ b/drivers/edac/e752x_edac.c
@@ -1380,7 +1380,7 @@ static void __devexit e752x_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id e752x_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(e752x_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 7520_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
E7520},
diff --git a/drivers/edac/e7xxx_edac.c b/drivers/edac/e7xxx_edac.c
index 6ffb6d23281f..68dea87b72e6 100644
--- a/drivers/edac/e7xxx_edac.c
+++ b/drivers/edac/e7xxx_edac.c
@@ -525,7 +525,7 @@ static void __devexit e7xxx_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id e7xxx_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(e7xxx_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 7205_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
E7205},
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index da09cd74bc5b..feef7733fae7 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/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c
index d56e63477d5c..e9a28f576d14 100644
--- a/drivers/edac/edac_mc_sysfs.c
+++ b/drivers/edac/edac_mc_sysfs.c
@@ -452,7 +452,7 @@ static ssize_t mci_sdram_scrub_rate_store(struct mem_ctl_info *mci,
int new_bw = 0;
if (!mci->set_sdram_scrub_rate)
- return -EINVAL;
+ return -ENODEV;
if (strict_strtoul(data, 10, &bandwidth) < 0)
return -EINVAL;
@@ -475,7 +475,7 @@ static ssize_t mci_sdram_scrub_rate_show(struct mem_ctl_info *mci, char *data)
int bandwidth = 0;
if (!mci->get_sdram_scrub_rate)
- return -EINVAL;
+ return -ENODEV;
bandwidth = mci->get_sdram_scrub_rate(mci);
if (bandwidth < 0) {
diff --git a/drivers/edac/edac_stub.c b/drivers/edac/edac_stub.c
index 670c4481453b..6c86f6e54558 100644
--- a/drivers/edac/edac_stub.c
+++ b/drivers/edac/edac_stub.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/edac.h>
#include <linux/atomic.h>
+#include <linux/device.h>
#include <asm/edac.h>
int edac_op_state = EDAC_OPSTATE_INVAL;
diff --git a/drivers/edac/i3000_edac.c b/drivers/edac/i3000_edac.c
index c0510b3d7035..277689a68841 100644
--- a/drivers/edac/i3000_edac.c
+++ b/drivers/edac/i3000_edac.c
@@ -470,7 +470,7 @@ static void __devexit i3000_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id i3000_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i3000_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 3000_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I3000},
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 73f55e2008c2..046808c6357d 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -445,7 +445,7 @@ static void __devexit i3200_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id i3200_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i3200_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 3200_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I3200},
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 4dc3ac25a422..a2680d8e744b 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1516,7 +1516,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev)
*
* The "E500P" device is the first device supported.
*/
-static const struct pci_device_id i5000_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i5000_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I5000_DEV16),
.driver_data = I5000P},
diff --git a/drivers/edac/i5100_edac.c b/drivers/edac/i5100_edac.c
index bcbdeeca48b8..d500749464ea 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
@@ -1051,7 +1048,7 @@ static void __devexit i5100_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id i5100_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i5100_pci_tbl) = {
/* Device 16, Function 0, Channel 0 Memory Map, Error Flag/Mask, ... */
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5100_16) },
{ 0, }
diff --git a/drivers/edac/i5400_edac.c b/drivers/edac/i5400_edac.c
index 74d6ec342afb..1869a1018fb5 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;
}
/*
@@ -1383,7 +1405,7 @@ static void __devexit i5400_remove_one(struct pci_dev *pdev)
*
* The "E500P" device is the first device supported.
*/
-static const struct pci_device_id i5400_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i5400_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR)},
{0,} /* 0 terminated list. */
};
diff --git a/drivers/edac/i7300_edac.c b/drivers/edac/i7300_edac.c
index 6104dba380b6..3bafa3bca148 100644
--- a/drivers/edac/i7300_edac.c
+++ b/drivers/edac/i7300_edac.c
@@ -1192,7 +1192,7 @@ static void __devexit i7300_remove_one(struct pci_dev *pdev)
*
* Has only 8086:360c PCI ID
*/
-static const struct pci_device_id i7300_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i7300_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_I7300_MCH_ERR)},
{0,} /* 0 terminated list. */
};
diff --git a/drivers/edac/i7core_edac.c b/drivers/edac/i7core_edac.c
index 8568d9b61875..85226ccf5290 100644
--- a/drivers/edac/i7core_edac.c
+++ b/drivers/edac/i7core_edac.c
@@ -391,7 +391,7 @@ static const struct pci_id_table pci_dev_table[] = {
/*
* pci_device_id table for which devices we are looking for
*/
-static const struct pci_device_id i7core_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i7core_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_X58_HUB_MGMT)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_LYNNFIELD_QPI_LINK0)},
{0,} /* 0 terminated list. */
diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c
index 4329d39f902c..3bf2b2f490e7 100644
--- a/drivers/edac/i82443bxgx_edac.c
+++ b/drivers/edac/i82443bxgx_edac.c
@@ -380,7 +380,7 @@ static void __devexit i82443bxgx_edacmc_remove_one(struct pci_dev *pdev)
EXPORT_SYMBOL_GPL(i82443bxgx_edacmc_remove_one);
-static const struct pci_device_id i82443bxgx_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i82443bxgx_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443BX_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443GX_0)},
diff --git a/drivers/edac/i82860_edac.c b/drivers/edac/i82860_edac.c
index 931a05775049..c779092d18d1 100644
--- a/drivers/edac/i82860_edac.c
+++ b/drivers/edac/i82860_edac.c
@@ -270,7 +270,7 @@ static void __devexit i82860_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id i82860_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i82860_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 82860_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I82860},
diff --git a/drivers/edac/i82875p_edac.c b/drivers/edac/i82875p_edac.c
index 33864c63c684..10f15d85fb5e 100644
--- a/drivers/edac/i82875p_edac.c
+++ b/drivers/edac/i82875p_edac.c
@@ -511,7 +511,7 @@ static void __devexit i82875p_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id i82875p_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i82875p_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 82875_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I82875P},
diff --git a/drivers/edac/i82975x_edac.c b/drivers/edac/i82975x_edac.c
index 4184e0171f00..0cd8368f88f8 100644
--- a/drivers/edac/i82975x_edac.c
+++ b/drivers/edac/i82975x_edac.c
@@ -612,7 +612,7 @@ static void __devexit i82975x_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id i82975x_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(i82975x_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, 82975_0), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
I82975X
diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c
index bd926ea2e00c..36e1486eb9aa 100644
--- a/drivers/edac/mce_amd.c
+++ b/drivers/edac/mce_amd.c
@@ -39,42 +39,31 @@ EXPORT_SYMBOL_GPL(amd_unregister_ecc_decoder);
*/
/* transaction type */
-const char *tt_msgs[] = { "INSN", "DATA", "GEN", "RESV" };
+const char * const tt_msgs[] = { "INSN", "DATA", "GEN", "RESV" };
EXPORT_SYMBOL_GPL(tt_msgs);
/* cache level */
-const char *ll_msgs[] = { "RESV", "L1", "L2", "L3/GEN" };
+const char * const ll_msgs[] = { "RESV", "L1", "L2", "L3/GEN" };
EXPORT_SYMBOL_GPL(ll_msgs);
/* memory transaction type */
-const char *rrrr_msgs[] = {
+const char * const rrrr_msgs[] = {
"GEN", "RD", "WR", "DRD", "DWR", "IRD", "PRF", "EV", "SNP"
};
EXPORT_SYMBOL_GPL(rrrr_msgs);
/* participating processor */
-const char *pp_msgs[] = { "SRC", "RES", "OBS", "GEN" };
+const char * const pp_msgs[] = { "SRC", "RES", "OBS", "GEN" };
EXPORT_SYMBOL_GPL(pp_msgs);
/* request timeout */
-const char *to_msgs[] = { "no timeout", "timed out" };
+const char * const to_msgs[] = { "no timeout", "timed out" };
EXPORT_SYMBOL_GPL(to_msgs);
/* memory or i/o */
-const char *ii_msgs[] = { "MEM", "RESV", "IO", "GEN" };
+const char * const ii_msgs[] = { "MEM", "RESV", "IO", "GEN" };
EXPORT_SYMBOL_GPL(ii_msgs);
-static const char *f10h_nb_mce_desc[] = {
- "HT link data error",
- "Protocol error (link, L3, probe filter, etc.)",
- "Parity error in NB-internal arrays",
- "Link Retry due to IO link transmission error",
- "L3 ECC data cache error",
- "ECC error in L3 cache tag",
- "L3 LRU parity bits error",
- "ECC Error in the Probe Filter directory"
-};
-
static const char * const f15h_ic_mce_desc[] = {
"UC during a demand linefill from L2",
"Parity error during data load from IC",
@@ -88,7 +77,7 @@ static const char * const f15h_ic_mce_desc[] = {
"Parity error for IC probe tag valid bit",
"PFB non-cacheable bit parity error",
"PFB valid bit parity error", /* xec = 0xd */
- "patch RAM", /* xec = 010 */
+ "Microcode Patch Buffer", /* xec = 010 */
"uop queue",
"insn buffer",
"predecode buffer",
@@ -104,7 +93,7 @@ static const char * const f15h_cu_mce_desc[] = {
"WCC Tag ECC error",
"WCC Data ECC error",
"WCB Data parity error",
- "VB Data/ECC error",
+ "VB Data ECC or parity error",
"L2 Tag ECC error", /* xec = 0x10 */
"Hard L2 Tag ECC error",
"Multiple hits on L2 tag",
@@ -112,6 +101,28 @@ static const char * const f15h_cu_mce_desc[] = {
"PRB address parity error"
};
+static const char * const nb_mce_desc[] = {
+ "DRAM ECC error detected on the NB",
+ "CRC error detected on HT link",
+ "Link-defined sync error packets detected on HT link",
+ "HT Master abort",
+ "HT Target abort",
+ "Invalid GART PTE entry during GART table walk",
+ "Unsupported atomic RMW received from an IO link",
+ "Watchdog timeout due to lack of progress",
+ "DRAM ECC error detected on the NB",
+ "SVM DMA Exclusion Vector error",
+ "HT data error detected on link",
+ "Protocol error (link, L3, probe filter)",
+ "NB internal arrays parity error",
+ "DRAM addr/ctl signals parity error",
+ "IO link transmission error",
+ "L3 data cache ECC error", /* xec = 0x1c */
+ "L3 cache tag error",
+ "L3 LRU parity bits error",
+ "ECC Error in the Probe Filter directory"
+};
+
static const char * const fr_ex_mce_desc[] = {
"CPU Watchdog timer expire",
"Wakeup array dest tag",
@@ -125,7 +136,7 @@ static const char * const fr_ex_mce_desc[] = {
"Physical register file AG0 port",
"Physical register file AG1 port",
"Flag register file",
- "DE correctable error could not be corrected"
+ "DE error occurred"
};
static bool f12h_dc_mce(u16 ec, u8 xec)
@@ -255,10 +266,9 @@ static bool f15h_dc_mce(u16 ec, u8 xec)
} else if (BUS_ERROR(ec)) {
if (!xec)
- pr_cont("during system linefill.\n");
+ pr_cont("System Read Data Error.\n");
else
- pr_cont(" Internal %s condition.\n",
- ((xec == 1) ? "livelock" : "deadlock"));
+ pr_cont(" Internal error condition type %d.\n", xec);
} else
ret = false;
@@ -355,7 +365,11 @@ static bool f15h_ic_mce(u16 ec, u8 xec)
pr_cont("%s.\n", f15h_ic_mce_desc[xec-2]);
break;
- case 0x10 ... 0x14:
+ case 0x10:
+ pr_cont("%s.\n", f15h_ic_mce_desc[xec-4]);
+ break;
+
+ case 0x11 ... 0x14:
pr_cont("Decoder %s parity error.\n", f15h_ic_mce_desc[xec-4]);
break;
@@ -496,58 +510,31 @@ wrong_ls_mce:
pr_emerg(HW_ERR "Corrupted LS MCE info?\n");
}
-static bool k8_nb_mce(u16 ec, u8 xec)
+void amd_decode_nb_mce(struct mce *m)
{
- bool ret = true;
-
- switch (xec) {
- case 0x1:
- pr_cont("CRC error detected on HT link.\n");
- break;
-
- case 0x5:
- pr_cont("Invalid GART PTE entry during GART table walk.\n");
- break;
-
- case 0x6:
- pr_cont("Unsupported atomic RMW received from an IO link.\n");
- break;
-
- case 0x0:
- case 0x8:
- if (boot_cpu_data.x86 == 0x11)
- return false;
-
- pr_cont("DRAM ECC error detected on the NB.\n");
- break;
-
- case 0xd:
- pr_cont("Parity error on the DRAM addr/ctl signals.\n");
- break;
-
- default:
- ret = false;
- break;
- }
+ struct cpuinfo_x86 *c = &boot_cpu_data;
+ int node_id = amd_get_nb_id(m->extcpu);
+ u16 ec = EC(m->status);
+ u8 xec = XEC(m->status, 0x1f);
+ u8 offset = 0;
- return ret;
-}
+ pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id);
-static bool f10h_nb_mce(u16 ec, u8 xec)
-{
- bool ret = true;
- u8 offset = 0;
+ switch (xec) {
+ case 0x0 ... 0xe:
- if (k8_nb_mce(ec, xec))
- return true;
+ /* special handling for DRAM ECCs */
+ if (xec == 0x0 || xec == 0x8) {
+ /* no ECCs on F11h */
+ if (c->x86 == 0x11)
+ goto wrong_nb_mce;
- switch(xec) {
- case 0xa ... 0xc:
- offset = 10;
- break;
+ pr_cont("%s.\n", nb_mce_desc[xec]);
- case 0xe:
- offset = 11;
+ if (nb_bus_decoder)
+ nb_bus_decoder(node_id, m);
+ return;
+ }
break;
case 0xf:
@@ -556,83 +543,25 @@ static bool f10h_nb_mce(u16 ec, u8 xec)
else if (BUS_ERROR(ec))
pr_cont("DMA Exclusion Vector Table Walk error.\n");
else
- ret = false;
-
- goto out;
- break;
+ goto wrong_nb_mce;
+ return;
case 0x19:
if (boot_cpu_data.x86 == 0x15)
pr_cont("Compute Unit Data Error.\n");
else
- ret = false;
-
- goto out;
- break;
+ goto wrong_nb_mce;
+ return;
case 0x1c ... 0x1f:
- offset = 24;
+ offset = 13;
break;
default:
- ret = false;
-
- goto out;
- break;
- }
-
- pr_cont("%s.\n", f10h_nb_mce_desc[xec - offset]);
-
-out:
- return ret;
-}
-
-static bool nb_noop_mce(u16 ec, u8 xec)
-{
- return false;
-}
-
-void amd_decode_nb_mce(struct mce *m)
-{
- struct cpuinfo_x86 *c = &boot_cpu_data;
- int node_id = amd_get_nb_id(m->extcpu);
- u16 ec = EC(m->status);
- u8 xec = XEC(m->status, 0x1f);
-
- pr_emerg(HW_ERR "Northbridge Error (node %d): ", node_id);
-
- switch (xec) {
- case 0x2:
- pr_cont("Sync error (sync packets on HT link detected).\n");
- return;
-
- case 0x3:
- pr_cont("HT Master abort.\n");
- return;
-
- case 0x4:
- pr_cont("HT Target abort.\n");
- return;
-
- case 0x7:
- pr_cont("NB Watchdog timeout.\n");
- return;
-
- case 0x9:
- pr_cont("SVM DMA Exclusion Vector error.\n");
- return;
-
- default:
- break;
- }
-
- if (!fam_ops->nb_mce(ec, xec))
goto wrong_nb_mce;
+ }
- if (c->x86 == 0xf || c->x86 == 0x10 || c->x86 == 0x15)
- if ((xec == 0x8 || xec == 0x0) && nb_bus_decoder)
- nb_bus_decoder(node_id, m);
-
+ pr_cont("%s.\n", nb_mce_desc[xec - offset]);
return;
wrong_nb_mce:
@@ -648,9 +577,6 @@ static void amd_decode_fr_mce(struct mce *m)
if (c->x86 == 0xf || c->x86 == 0x11)
goto wrong_fr_mce;
- if (c->x86 != 0x15 && xec != 0x0)
- goto wrong_fr_mce;
-
pr_emerg(HW_ERR "%s Error: ",
(c->x86 == 0x15 ? "Execution Unit" : "FIROB"));
@@ -841,39 +767,33 @@ static int __init mce_amd_init(void)
case 0xf:
fam_ops->dc_mce = k8_dc_mce;
fam_ops->ic_mce = k8_ic_mce;
- fam_ops->nb_mce = k8_nb_mce;
break;
case 0x10:
fam_ops->dc_mce = f10h_dc_mce;
fam_ops->ic_mce = k8_ic_mce;
- fam_ops->nb_mce = f10h_nb_mce;
break;
case 0x11:
fam_ops->dc_mce = k8_dc_mce;
fam_ops->ic_mce = k8_ic_mce;
- fam_ops->nb_mce = f10h_nb_mce;
break;
case 0x12:
fam_ops->dc_mce = f12h_dc_mce;
fam_ops->ic_mce = k8_ic_mce;
- fam_ops->nb_mce = nb_noop_mce;
break;
case 0x14:
nb_err_cpumask = 0x3;
fam_ops->dc_mce = f14h_dc_mce;
fam_ops->ic_mce = f14h_ic_mce;
- fam_ops->nb_mce = nb_noop_mce;
break;
case 0x15:
xec_mask = 0x1f;
fam_ops->dc_mce = f15h_dc_mce;
fam_ops->ic_mce = f15h_ic_mce;
- fam_ops->nb_mce = f10h_nb_mce;
break;
default:
diff --git a/drivers/edac/mce_amd.h b/drivers/edac/mce_amd.h
index 0106747e240c..c6074c5cd1ef 100644
--- a/drivers/edac/mce_amd.h
+++ b/drivers/edac/mce_amd.h
@@ -69,12 +69,12 @@ enum rrrr_ids {
R4_SNOOP,
};
-extern const char *tt_msgs[];
-extern const char *ll_msgs[];
-extern const char *rrrr_msgs[];
-extern const char *pp_msgs[];
-extern const char *to_msgs[];
-extern const char *ii_msgs[];
+extern const char * const tt_msgs[];
+extern const char * const ll_msgs[];
+extern const char * const rrrr_msgs[];
+extern const char * const pp_msgs[];
+extern const char * const to_msgs[];
+extern const char * const ii_msgs[];
/*
* per-family decoder ops
@@ -82,7 +82,6 @@ extern const char *ii_msgs[];
struct amd_decoder_ops {
bool (*dc_mce)(u16, u8);
bool (*ic_mce)(u16, u8);
- bool (*nb_mce)(u16, u8);
};
void amd_report_gart_errors(bool);
diff --git a/drivers/edac/mce_amd_inj.c b/drivers/edac/mce_amd_inj.c
index 885e8ad8fdcf..66b5151c1080 100644
--- a/drivers/edac/mce_amd_inj.c
+++ b/drivers/edac/mce_amd_inj.c
@@ -11,6 +11,7 @@
*/
#include <linux/kobject.h>
+#include <linux/device.h>
#include <linux/edac.h>
#include <linux/module.h>
#include <asm/mce.h>
diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c
index fc757069c6af..d427c69bb8b1 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/r82600_edac.c b/drivers/edac/r82600_edac.c
index e294e1b3616c..6d908ad72d64 100644
--- a/drivers/edac/r82600_edac.c
+++ b/drivers/edac/r82600_edac.c
@@ -373,7 +373,7 @@ static void __devexit r82600_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id r82600_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(r82600_pci_tbl) = {
{
PCI_DEVICE(PCI_VENDOR_ID_RADISYS, R82600_BRIDGE_ID)
},
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index 1dc118d83cc6..a203536d90dd 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>
@@ -367,7 +368,7 @@ static const struct pci_id_table pci_dev_descr_sbridge_table[] = {
/*
* pci_device_id table for which devices we are looking for
*/
-static const struct pci_device_id sbridge_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(sbridge_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_TA)},
{0,} /* 0 terminated list. */
};
@@ -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/edac/x38_edac.c b/drivers/edac/x38_edac.c
index b6f47de152f3..a438297389e5 100644
--- a/drivers/edac/x38_edac.c
+++ b/drivers/edac/x38_edac.c
@@ -440,7 +440,7 @@ static void __devexit x38_remove_one(struct pci_dev *pdev)
edac_mc_free(mci);
}
-static const struct pci_device_id x38_pci_tbl[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(x38_pci_tbl) = {
{
PCI_VEND_DEV(INTEL, X38_HB), PCI_ANY_ID, PCI_ANY_ID, 0, 0,
X38},
diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig
index 2be6f4520772..7224533e8ca6 100644
--- a/drivers/firewire/Kconfig
+++ b/drivers/firewire/Kconfig
@@ -28,11 +28,6 @@ config FIREWIRE_OHCI
To compile this driver as a module, say M here: The module will be
called firewire-ohci.
-config FIREWIRE_OHCI_DEBUG
- bool
- depends on FIREWIRE_OHCI
- default y
-
config FIREWIRE_SBP2
tristate "Storage devices (SBP-2 protocol)"
depends on FIREWIRE && SCSI
diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c
index 85661b060ed7..cc595eba7ba9 100644
--- a/drivers/firewire/core-card.c
+++ b/drivers/firewire/core-card.c
@@ -37,6 +37,22 @@
#include "core.h"
+#define define_fw_printk_level(func, kern_level) \
+void func(const struct fw_card *card, const char *fmt, ...) \
+{ \
+ struct va_format vaf; \
+ va_list args; \
+ \
+ va_start(args, fmt); \
+ vaf.fmt = fmt; \
+ vaf.va = &args; \
+ printk(kern_level KBUILD_MODNAME " %s: %pV", \
+ dev_name(card->device), &vaf); \
+ va_end(args); \
+}
+define_fw_printk_level(fw_err, KERN_ERR);
+define_fw_printk_level(fw_notice, KERN_NOTICE);
+
int fw_compute_block_crc(__be32 *block)
{
int length;
@@ -260,7 +276,7 @@ static void allocate_broadcast_channel(struct fw_card *card, int generation)
fw_iso_resource_manage(card, generation, 1ULL << 31,
&channel, &bandwidth, true);
if (channel != 31) {
- fw_notify("failed to allocate broadcast channel\n");
+ fw_notice(card, "failed to allocate broadcast channel\n");
return;
}
card->broadcast_channel_allocated = true;
@@ -343,14 +359,14 @@ static void bm_work(struct work_struct *work)
if (!card->irm_node->link_on) {
new_root_id = local_id;
- fw_notify("%s, making local node (%02x) root.\n",
+ fw_notice(card, "%s, making local node (%02x) root\n",
"IRM has link off", new_root_id);
goto pick_me;
}
if (irm_is_1394_1995_only && !keep_this_irm) {
new_root_id = local_id;
- fw_notify("%s, making local node (%02x) root.\n",
+ fw_notice(card, "%s, making local node (%02x) root\n",
"IRM is not 1394a compliant", new_root_id);
goto pick_me;
}
@@ -405,7 +421,7 @@ static void bm_work(struct work_struct *work)
* root, and thus, IRM.
*/
new_root_id = local_id;
- fw_notify("%s, making local node (%02x) root.\n",
+ fw_notice(card, "%s, making local node (%02x) root\n",
"BM lock failed", new_root_id);
goto pick_me;
}
@@ -478,8 +494,8 @@ static void bm_work(struct work_struct *work)
spin_unlock_irq(&card->lock);
if (do_reset) {
- fw_notify("phy config: card %d, new root=%x, gap_count=%d\n",
- card->index, new_root_id, gap_count);
+ fw_notice(card, "phy config: new root=%x, gap_count=%d\n",
+ new_root_id, gap_count);
fw_send_phy_config(card, new_root_id, generation, gap_count);
reset_bus(card, true);
/* Will allocate broadcast channel after the reset. */
@@ -634,6 +650,11 @@ static void dummy_flush_queue_iso(struct fw_iso_context *ctx)
{
}
+static int dummy_flush_iso_completions(struct fw_iso_context *ctx)
+{
+ return -ENODEV;
+}
+
static const struct fw_card_driver dummy_driver_template = {
.read_phy_reg = dummy_read_phy_reg,
.update_phy_reg = dummy_update_phy_reg,
@@ -646,6 +667,7 @@ static const struct fw_card_driver dummy_driver_template = {
.set_iso_channels = dummy_set_iso_channels,
.queue_iso = dummy_queue_iso,
.flush_queue_iso = dummy_flush_queue_iso,
+ .flush_iso_completions = dummy_flush_iso_completions,
};
void fw_card_release(struct kref *kref)
diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
index 4799393247c8..2e6b24547e2a 100644
--- a/drivers/firewire/core-cdev.c
+++ b/drivers/firewire/core-cdev.c
@@ -44,14 +44,13 @@
#include <linux/wait.h>
#include <linux/workqueue.h>
-#include <asm/system.h>
#include "core.h"
/*
* ABI version history is documented in linux/firewire-cdev.h.
*/
-#define FW_CDEV_KERNEL_VERSION 4
+#define FW_CDEV_KERNEL_VERSION 5
#define FW_CDEV_VERSION_EVENT_REQUEST2 4
#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4
@@ -389,7 +388,7 @@ static void queue_bus_reset_event(struct client *client)
e = kzalloc(sizeof(*e), GFP_KERNEL);
if (e == NULL) {
- fw_notify("Out of memory when allocating event\n");
+ fw_notice(client->device->card, "out of memory when allocating event\n");
return;
}
@@ -438,6 +437,7 @@ union ioctl_arg {
struct fw_cdev_send_phy_packet send_phy_packet;
struct fw_cdev_receive_phy_packets receive_phy_packets;
struct fw_cdev_set_iso_channels set_iso_channels;
+ struct fw_cdev_flush_iso flush_iso;
};
static int ioctl_get_info(struct client *client, union ioctl_arg *arg)
@@ -691,7 +691,7 @@ static void handle_request(struct fw_card *card, struct fw_request *request,
r = kmalloc(sizeof(*r), GFP_ATOMIC);
e = kmalloc(sizeof(*e), GFP_ATOMIC);
if (r == NULL || e == NULL) {
- fw_notify("Out of memory when allocating event\n");
+ fw_notice(card, "out of memory when allocating event\n");
goto failed;
}
r->card = card;
@@ -928,7 +928,7 @@ static void iso_callback(struct fw_iso_context *context, u32 cycle,
e = kmalloc(sizeof(*e) + header_length, GFP_ATOMIC);
if (e == NULL) {
- fw_notify("Out of memory when allocating event\n");
+ fw_notice(context->card, "out of memory when allocating event\n");
return;
}
e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
@@ -948,7 +948,7 @@ static void iso_mc_callback(struct fw_iso_context *context,
e = kmalloc(sizeof(*e), GFP_ATOMIC);
if (e == NULL) {
- fw_notify("Out of memory when allocating event\n");
+ fw_notice(context->card, "out of memory when allocating event\n");
return;
}
e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL;
@@ -1168,6 +1168,16 @@ static int ioctl_stop_iso(struct client *client, union ioctl_arg *arg)
return fw_iso_context_stop(client->iso_context);
}
+static int ioctl_flush_iso(struct client *client, union ioctl_arg *arg)
+{
+ struct fw_cdev_flush_iso *a = &arg->flush_iso;
+
+ if (client->iso_context == NULL || a->handle != 0)
+ return -EINVAL;
+
+ return fw_iso_context_flush_completions(client->iso_context);
+}
+
static int ioctl_get_cycle_timer2(struct client *client, union ioctl_arg *arg)
{
struct fw_cdev_get_cycle_timer2 *a = &arg->get_cycle_timer2;
@@ -1548,7 +1558,7 @@ void fw_cdev_handle_phy_packet(struct fw_card *card, struct fw_packet *p)
list_for_each_entry(client, &card->phy_receiver_list, phy_receiver_link) {
e = kmalloc(sizeof(*e) + 8, GFP_ATOMIC);
if (e == NULL) {
- fw_notify("Out of memory when allocating event\n");
+ fw_notice(card, "out of memory when allocating event\n");
break;
}
e->phy_packet.closure = client->phy_receiver_closure;
@@ -1589,6 +1599,7 @@ static int (* const ioctl_handlers[])(struct client *, union ioctl_arg *) = {
[0x15] = ioctl_send_phy_packet,
[0x16] = ioctl_receive_phy_packets,
[0x17] = ioctl_set_iso_channels,
+ [0x18] = ioctl_flush_iso,
};
static int dispatch_ioctl(struct client *client,
diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c
index f3b890da1e87..68109e9bb04e 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"
@@ -485,6 +484,7 @@ static int read_rom(struct fw_device *device,
*/
static int read_config_rom(struct fw_device *device, int generation)
{
+ struct fw_card *card = device->card;
const u32 *old_rom, *new_rom;
u32 *rom, *stack;
u32 sp, key;
@@ -529,12 +529,12 @@ static int read_config_rom(struct fw_device *device, int generation)
*/
if ((rom[2] & 0x7) < device->max_speed ||
device->max_speed == SCODE_BETA ||
- device->card->beta_repeaters_present) {
+ card->beta_repeaters_present) {
u32 dummy;
/* for S1600 and S3200 */
if (device->max_speed == SCODE_BETA)
- device->max_speed = device->card->link_speed;
+ device->max_speed = card->link_speed;
while (device->max_speed > SCODE_100) {
if (read_rom(device, generation, 0, &dummy) ==
@@ -576,9 +576,9 @@ static int read_config_rom(struct fw_device *device, int generation)
* a firmware bug. Ignore this whole block, i.e.
* simply set a fake block length of 0.
*/
- fw_error("skipped invalid ROM block %x at %llx\n",
- rom[i],
- i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM);
+ fw_err(card, "skipped invalid ROM block %x at %llx\n",
+ rom[i],
+ i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM);
rom[i] = 0;
end = i;
}
@@ -604,9 +604,10 @@ static int read_config_rom(struct fw_device *device, int generation)
* the ROM don't have to check offsets all the time.
*/
if (i + (rom[i] & 0xffffff) >= MAX_CONFIG_ROM_SIZE) {
- fw_error("skipped unsupported ROM entry %x at %llx\n",
- rom[i],
- i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM);
+ fw_err(card,
+ "skipped unsupported ROM entry %x at %llx\n",
+ rom[i],
+ i * 4 | CSR_REGISTER_BASE | CSR_CONFIG_ROM);
rom[i] = 0;
continue;
}
@@ -641,6 +642,7 @@ static void fw_unit_release(struct device *dev)
{
struct fw_unit *unit = fw_unit(dev);
+ fw_device_put(fw_parent_device(unit));
kfree(unit);
}
@@ -672,7 +674,7 @@ static void create_units(struct fw_device *device)
*/
unit = kzalloc(sizeof(*unit), GFP_KERNEL);
if (unit == NULL) {
- fw_error("failed to allocate memory for unit\n");
+ fw_err(device->card, "out of memory for unit\n");
continue;
}
@@ -692,6 +694,7 @@ static void create_units(struct fw_device *device)
if (device_register(&unit->device) < 0)
goto skip_unit;
+ fw_device_get(device);
continue;
skip_unit:
@@ -873,7 +876,7 @@ static int lookup_existing_device(struct device *dev, void *data)
smp_wmb(); /* update node_id before generation */
old->generation = card->generation;
old->config_rom_retries = 0;
- fw_notify("rediscovered device %s\n", dev_name(dev));
+ fw_notice(card, "rediscovered device %s\n", dev_name(dev));
PREPARE_DELAYED_WORK(&old->work, fw_device_update);
fw_schedule_device_work(old, 0);
@@ -954,6 +957,7 @@ static void fw_device_init(struct work_struct *work)
{
struct fw_device *device =
container_of(work, struct fw_device, work.work);
+ struct fw_card *card = device->card;
struct device *revived_dev;
int minor, ret;
@@ -970,16 +974,16 @@ static void fw_device_init(struct work_struct *work)
fw_schedule_device_work(device, RETRY_DELAY);
} else {
if (device->node->link_on)
- fw_notify("giving up on config rom for node id %x\n",
+ fw_notice(card, "giving up on Config ROM for node id %x\n",
device->node_id);
- if (device->node == device->card->root_node)
- fw_schedule_bm_work(device->card, 0);
+ if (device->node == card->root_node)
+ fw_schedule_bm_work(card, 0);
fw_device_release(&device->device);
}
return;
}
- revived_dev = device_find_child(device->card->device,
+ revived_dev = device_find_child(card->device,
device, lookup_existing_device);
if (revived_dev) {
put_device(revived_dev);
@@ -1002,7 +1006,7 @@ static void fw_device_init(struct work_struct *work)
device->device.bus = &fw_bus_type;
device->device.type = &fw_device_type;
- device->device.parent = device->card->device;
+ device->device.parent = card->device;
device->device.devt = MKDEV(fw_cdev_major, minor);
dev_set_name(&device->device, "fw%d", minor);
@@ -1014,7 +1018,7 @@ static void fw_device_init(struct work_struct *work)
&device->attribute_group);
if (device_add(&device->device)) {
- fw_error("Failed to add device.\n");
+ fw_err(card, "failed to add device\n");
goto error_with_cdev;
}
@@ -1035,18 +1039,10 @@ static void fw_device_init(struct work_struct *work)
PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
fw_schedule_device_work(device, SHUTDOWN_DELAY);
} else {
- if (device->config_rom_retries)
- fw_notify("created device %s: GUID %08x%08x, S%d00, "
- "%d config ROM retries\n",
- dev_name(&device->device),
- device->config_rom[3], device->config_rom[4],
- 1 << device->max_speed,
- device->config_rom_retries);
- else
- fw_notify("created device %s: GUID %08x%08x, S%d00\n",
- dev_name(&device->device),
- device->config_rom[3], device->config_rom[4],
- 1 << device->max_speed);
+ fw_notice(card, "created device %s: GUID %08x%08x, S%d00\n",
+ dev_name(&device->device),
+ device->config_rom[3], device->config_rom[4],
+ 1 << device->max_speed);
device->config_rom_retries = 0;
set_broadcast_channel(device, device->generation);
@@ -1058,8 +1054,8 @@ static void fw_device_init(struct work_struct *work)
* just end up running the IRM work a couple of extra times -
* pretty harmless.
*/
- if (device->node == device->card->root_node)
- fw_schedule_bm_work(device->card, 0);
+ if (device->node == card->root_node)
+ fw_schedule_bm_work(card, 0);
return;
@@ -1163,12 +1159,13 @@ static void fw_device_refresh(struct work_struct *work)
FW_DEVICE_RUNNING) == FW_DEVICE_GONE)
goto gone;
- fw_notify("refreshed device %s\n", dev_name(&device->device));
+ fw_notice(card, "refreshed device %s\n", dev_name(&device->device));
device->config_rom_retries = 0;
goto out;
give_up:
- fw_notify("giving up on refresh of device %s\n", dev_name(&device->device));
+ fw_notice(card, "giving up on refresh of device %s\n",
+ dev_name(&device->device));
gone:
atomic_set(&device->state, FW_DEVICE_GONE);
PREPARE_DELAYED_WORK(&device->work, fw_device_shutdown);
diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c
index 0f90e0071875..d1565828ae2c 100644
--- a/drivers/firewire/core-iso.c
+++ b/drivers/firewire/core-iso.c
@@ -192,6 +192,12 @@ void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
}
EXPORT_SYMBOL(fw_iso_context_queue_flush);
+int fw_iso_context_flush_completions(struct fw_iso_context *ctx)
+{
+ return ctx->card->driver->flush_iso_completions(ctx);
+}
+EXPORT_SYMBOL(fw_iso_context_flush_completions);
+
int fw_iso_context_stop(struct fw_iso_context *ctx)
{
return ctx->card->driver->stop_iso(ctx);
diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c
index 94d3b494ddfb..0de83508f321 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"
@@ -205,19 +204,19 @@ static struct fw_node *build_tree(struct fw_card *card,
next_sid = count_ports(sid, &port_count, &child_port_count);
if (next_sid == NULL) {
- fw_error("Inconsistent extended self IDs.\n");
+ fw_err(card, "inconsistent extended self IDs\n");
return NULL;
}
q = *sid;
if (phy_id != SELF_ID_PHY_ID(q)) {
- fw_error("PHY ID mismatch in self ID: %d != %d.\n",
- phy_id, SELF_ID_PHY_ID(q));
+ fw_err(card, "PHY ID mismatch in self ID: %d != %d\n",
+ phy_id, SELF_ID_PHY_ID(q));
return NULL;
}
if (child_port_count > stack_depth) {
- fw_error("Topology stack underflow\n");
+ fw_err(card, "topology stack underflow\n");
return NULL;
}
@@ -235,7 +234,7 @@ static struct fw_node *build_tree(struct fw_card *card,
node = fw_node_create(q, port_count, card->color);
if (node == NULL) {
- fw_error("Out of memory while building topology.\n");
+ fw_err(card, "out of memory while building topology\n");
return NULL;
}
@@ -284,8 +283,8 @@ static struct fw_node *build_tree(struct fw_card *card,
*/
if ((next_sid == end && parent_count != 0) ||
(next_sid < end && parent_count != 1)) {
- fw_error("Parent port inconsistency for node %d: "
- "parent_count=%d\n", phy_id, parent_count);
+ fw_err(card, "parent port inconsistency for node %d: "
+ "parent_count=%d\n", phy_id, parent_count);
return NULL;
}
@@ -530,7 +529,6 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
*/
if (!is_next_generation(generation, card->generation) &&
card->local_node != NULL) {
- fw_notify("skipped bus generations, destroying all nodes\n");
fw_destroy_nodes(card);
card->bm_retries = 0;
}
@@ -557,7 +555,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
card->color++;
if (local_node == NULL) {
- fw_error("topology build failed\n");
+ fw_err(card, "topology build failed\n");
/* FIXME: We need to issue a bus reset in this case. */
} else if (card->local_node == NULL) {
card->local_node = local_node;
diff --git a/drivers/firewire/core-transaction.c b/drivers/firewire/core-transaction.c
index 855ab3f5936f..dea2dcc9310d 100644
--- a/drivers/firewire/core-transaction.c
+++ b/drivers/firewire/core-transaction.c
@@ -565,7 +565,6 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
const struct fw_address_region *region)
{
struct fw_address_handler *other;
- unsigned long flags;
int ret = -EBUSY;
if (region->start & 0xffff000000000003ULL ||
@@ -575,7 +574,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
handler->length == 0)
return -EINVAL;
- spin_lock_irqsave(&address_handler_lock, flags);
+ spin_lock_bh(&address_handler_lock);
handler->offset = region->start;
while (handler->offset + handler->length <= region->end) {
@@ -594,7 +593,7 @@ int fw_core_add_address_handler(struct fw_address_handler *handler,
}
}
- spin_unlock_irqrestore(&address_handler_lock, flags);
+ spin_unlock_bh(&address_handler_lock);
return ret;
}
@@ -602,14 +601,15 @@ EXPORT_SYMBOL(fw_core_add_address_handler);
/**
* fw_core_remove_address_handler() - unregister an address handler
+ *
+ * When fw_core_remove_address_handler() returns, @handler->callback() is
+ * guaranteed to not run on any CPU anymore.
*/
void fw_core_remove_address_handler(struct fw_address_handler *handler)
{
- unsigned long flags;
-
- spin_lock_irqsave(&address_handler_lock, flags);
+ spin_lock_bh(&address_handler_lock);
list_del(&handler->link);
- spin_unlock_irqrestore(&address_handler_lock, flags);
+ spin_unlock_bh(&address_handler_lock);
}
EXPORT_SYMBOL(fw_core_remove_address_handler);
@@ -770,7 +770,7 @@ static struct fw_request *allocate_request(struct fw_card *card,
break;
default:
- fw_error("ERROR - corrupt request received - %08x %08x %08x\n",
+ fw_notice(card, "ERROR - corrupt request received - %08x %08x %08x\n",
p->header[0], p->header[1], p->header[2]);
return NULL;
}
@@ -826,7 +826,6 @@ static void handle_exclusive_region_request(struct fw_card *card,
unsigned long long offset)
{
struct fw_address_handler *handler;
- unsigned long flags;
int tcode, destination, source;
destination = HEADER_GET_DESTINATION(p->header[0]);
@@ -835,27 +834,19 @@ static void handle_exclusive_region_request(struct fw_card *card,
if (tcode == TCODE_LOCK_REQUEST)
tcode = 0x10 + HEADER_GET_EXTENDED_TCODE(p->header[3]);
- spin_lock_irqsave(&address_handler_lock, flags);
+ spin_lock_bh(&address_handler_lock);
handler = lookup_enclosing_address_handler(&address_handler_list,
offset, request->length);
- spin_unlock_irqrestore(&address_handler_lock, flags);
-
- /*
- * FIXME: lookup the fw_node corresponding to the sender of
- * this request and pass that to the address handler instead
- * of the node ID. We may also want to move the address
- * allocations to fw_node so we only do this callback if the
- * upper layers registered it for this node.
- */
-
- if (handler == NULL)
- fw_send_response(card, request, RCODE_ADDRESS_ERROR);
- else
+ if (handler)
handler->address_callback(card, request,
tcode, destination, source,
p->generation, offset,
request->data, request->length,
handler->callback_data);
+ spin_unlock_bh(&address_handler_lock);
+
+ if (!handler)
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
}
static void handle_fcp_region_request(struct fw_card *card,
@@ -864,7 +855,6 @@ static void handle_fcp_region_request(struct fw_card *card,
unsigned long long offset)
{
struct fw_address_handler *handler;
- unsigned long flags;
int tcode, destination, source;
if ((offset != (CSR_REGISTER_BASE | CSR_FCP_COMMAND) &&
@@ -886,7 +876,7 @@ static void handle_fcp_region_request(struct fw_card *card,
return;
}
- spin_lock_irqsave(&address_handler_lock, flags);
+ spin_lock_bh(&address_handler_lock);
list_for_each_entry(handler, &address_handler_list, link) {
if (is_enclosing_handler(handler, offset, request->length))
handler->address_callback(card, NULL, tcode,
@@ -896,7 +886,7 @@ static void handle_fcp_region_request(struct fw_card *card,
request->length,
handler->callback_data);
}
- spin_unlock_irqrestore(&address_handler_lock, flags);
+ spin_unlock_bh(&address_handler_lock);
fw_send_response(card, request, RCODE_COMPLETE);
}
@@ -960,7 +950,7 @@ void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
if (&t->link == &card->transaction_list) {
timed_out:
- fw_notify("Unsolicited response (source %x, tlabel %x)\n",
+ fw_notice(card, "unsolicited response (source %x, tlabel %x)\n",
source, tlabel);
return;
}
diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h
index b45be5767529..9047f5547d98 100644
--- a/drivers/firewire/core.h
+++ b/drivers/firewire/core.h
@@ -1,6 +1,8 @@
#ifndef _FIREWIRE_CORE_H
#define _FIREWIRE_CORE_H
+#include <linux/compiler.h>
+#include <linux/device.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/idr.h>
@@ -23,6 +25,11 @@ struct fw_packet;
/* -card */
+extern __printf(2, 3)
+void fw_err(const struct fw_card *card, const char *fmt, ...);
+extern __printf(2, 3)
+void fw_notice(const struct fw_card *card, const char *fmt, ...);
+
/* bitfields within the PHY registers */
#define PHY_LINK_ACTIVE 0x80
#define PHY_CONTENDER 0x40
@@ -99,6 +106,8 @@ struct fw_card_driver {
void (*flush_queue_iso)(struct fw_iso_context *ctx);
+ int (*flush_iso_completions)(struct fw_iso_context *ctx);
+
int (*stop_iso)(struct fw_iso_context *ctx);
};
@@ -141,6 +150,18 @@ extern struct rw_semaphore fw_device_rwsem;
extern struct idr fw_device_idr;
extern int fw_cdev_major;
+static inline struct fw_device *fw_device_get(struct fw_device *device)
+{
+ get_device(&device->device);
+
+ return device;
+}
+
+static inline void fw_device_put(struct fw_device *device)
+{
+ put_device(&device->device);
+}
+
struct fw_device *fw_device_get_by_devt(dev_t devt);
int fw_device_set_broadcast_channel(struct device *dev, void *gen);
void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c
index a20f45b1e7e5..08c674957af8 100644
--- a/drivers/firewire/net.c
+++ b/drivers/firewire/net.c
@@ -256,8 +256,8 @@ static int fwnet_header_rebuild(struct sk_buff *skb)
if (get_unaligned_be16(&h->h_proto) == ETH_P_IP)
return arp_find((unsigned char *)&h->h_dest, skb);
- fw_notify("%s: unable to resolve type %04x addresses\n",
- skb->dev->name, be16_to_cpu(h->h_proto));
+ dev_notice(&skb->dev->dev, "unable to resolve type %04x addresses\n",
+ be16_to_cpu(h->h_proto));
return 0;
}
@@ -369,7 +369,7 @@ static struct fwnet_fragment_info *fwnet_frag_new(
new = kmalloc(sizeof(*new), GFP_ATOMIC);
if (!new) {
- fw_error("out of memory\n");
+ dev_err(&pd->skb->dev->dev, "out of memory\n");
return NULL;
}
@@ -414,7 +414,7 @@ fail_w_fi:
fail_w_new:
kfree(new);
fail:
- fw_error("out of memory\n");
+ dev_err(&net->dev, "out of memory\n");
return NULL;
}
@@ -554,7 +554,7 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
sspd = arp1394->sspd;
/* Sanity check. OS X 10.3 PPC reportedly sends 131. */
if (sspd > SCODE_3200) {
- fw_notify("sspd %x out of range\n", sspd);
+ dev_notice(&net->dev, "sspd %x out of range\n", sspd);
sspd = SCODE_3200;
}
max_payload = fwnet_max_payload(arp1394->max_rec, sspd);
@@ -574,8 +574,9 @@ static int fwnet_finish_incoming_packet(struct net_device *net,
spin_unlock_irqrestore(&dev->lock, flags);
if (!peer) {
- fw_notify("No peer for ARP packet from %016llx\n",
- (unsigned long long)peer_guid);
+ dev_notice(&net->dev,
+ "no peer for ARP packet from %016llx\n",
+ (unsigned long long)peer_guid);
goto no_peer;
}
@@ -691,7 +692,7 @@ static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len,
skb = dev_alloc_skb(len + net->hard_header_len + 15);
if (unlikely(!skb)) {
- fw_error("out of memory\n");
+ dev_err(&net->dev, "out of memory\n");
net->stats.rx_dropped++;
return -ENOMEM;
@@ -814,7 +815,7 @@ static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r,
rcode = RCODE_TYPE_ERROR;
else if (fwnet_incoming_packet(dev, payload, length,
source, generation, false) != 0) {
- fw_error("Incoming packet failure\n");
+ dev_err(&dev->netdev->dev, "incoming packet failure\n");
rcode = RCODE_CONFLICT_ERROR;
} else
rcode = RCODE_COMPLETE;
@@ -881,7 +882,7 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
if (retval >= 0)
fw_iso_context_queue_flush(dev->broadcast_rcv_context);
else
- fw_error("requeue failed\n");
+ dev_err(&dev->netdev->dev, "requeue failed\n");
}
static struct kmem_cache *fwnet_packet_task_cache;
@@ -936,9 +937,10 @@ static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask)
case RFC2374_HDR_LASTFRAG:
case RFC2374_HDR_UNFRAG:
default:
- fw_error("Outstanding packet %x lf %x, header %x,%x\n",
- ptask->outstanding_pkts, lf, ptask->hdr.w0,
- ptask->hdr.w1);
+ dev_err(&dev->netdev->dev,
+ "outstanding packet %x lf %x, header %x,%x\n",
+ ptask->outstanding_pkts, lf, ptask->hdr.w0,
+ ptask->hdr.w1);
BUG();
case RFC2374_HDR_FIRSTFRAG:
@@ -1010,8 +1012,9 @@ static void fwnet_write_complete(struct fw_card *card, int rcode,
fwnet_transmit_packet_failed(ptask);
if (printk_timed_ratelimit(&j, 1000) || rcode != last_rcode) {
- fw_error("fwnet_write_complete: "
- "failed: %x (skipped %d)\n", rcode, errors_skipped);
+ dev_err(&ptask->dev->netdev->dev,
+ "fwnet_write_complete failed: %x (skipped %d)\n",
+ rcode, errors_skipped);
errors_skipped = 0;
last_rcode = rcode;
@@ -1539,14 +1542,12 @@ static int fwnet_probe(struct device *_dev)
put_unaligned_be64(card->guid, net->dev_addr);
put_unaligned_be64(~0ULL, net->broadcast);
ret = register_netdev(net);
- if (ret) {
- fw_error("Cannot register the driver\n");
+ if (ret)
goto out;
- }
list_add_tail(&dev->dev_link, &fwnet_device_list);
- fw_notify("%s: IPv4 over FireWire on device %016llx\n",
- net->name, (unsigned long long)card->guid);
+ dev_notice(&net->dev, "IPv4 over IEEE 1394 on card %s\n",
+ dev_name(card->device));
have_dev:
ret = fwnet_add_peer(dev, unit, device);
if (ret && allocated_netdev) {
@@ -1648,7 +1649,7 @@ static const struct ieee1394_device_id fwnet_id_table[] = {
static struct fw_driver fwnet_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = "net",
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
.probe = fwnet_probe,
.remove = fwnet_remove,
diff --git a/drivers/firewire/nosy.c b/drivers/firewire/nosy.c
index 763626b739d1..a7c4422a688e 100644
--- a/drivers/firewire/nosy.c
+++ b/drivers/firewire/nosy.c
@@ -36,7 +36,7 @@
#include <linux/timex.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
-
+#include <linux/dma-mapping.h>
#include <linux/atomic.h>
#include <asm/byteorder.h>
@@ -536,7 +536,7 @@ add_card(struct pci_dev *dev, const struct pci_device_id *unused)
u32 p, end;
int ret, i;
- if (pci_set_dma_mask(dev, 0xffffffff)) {
+ if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
dev_err(&dev->dev,
"DMA address limits not supported for PCILynx hardware\n");
return -ENXIO;
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 7f5f0da726da..2b5460075a9f 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>
@@ -170,10 +169,12 @@ struct context {
struct iso_context {
struct fw_iso_context base;
struct context context;
- int excess_bytes;
void *header;
size_t header_length;
-
+ unsigned long flushing_completions;
+ u32 mc_buffer_bus;
+ u16 mc_completed;
+ u16 last_timestamp;
u8 sync;
u8 tags;
};
@@ -338,8 +339,6 @@ MODULE_PARM_DESC(quirks, "Chip quirks (default = 0"
#define OHCI_PARAM_DEBUG_IRQS 4
#define OHCI_PARAM_DEBUG_BUSRESETS 8 /* only effective before chip init */
-#ifdef CONFIG_FIREWIRE_OHCI_DEBUG
-
static int param_debug;
module_param_named(debug, param_debug, int, 0644);
MODULE_PARM_DESC(debug, "Verbose logging (default = 0"
@@ -349,7 +348,7 @@ MODULE_PARM_DESC(debug, "Verbose logging (default = 0"
", busReset events = " __stringify(OHCI_PARAM_DEBUG_BUSRESETS)
", or a combination, or all = -1)");
-static void log_irqs(u32 evt)
+static void log_irqs(struct fw_ohci *ohci, u32 evt)
{
if (likely(!(param_debug &
(OHCI_PARAM_DEBUG_IRQS | OHCI_PARAM_DEBUG_BUSRESETS))))
@@ -359,7 +358,8 @@ static void log_irqs(u32 evt)
!(evt & OHCI1394_busReset))
return;
- fw_notify("IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt,
+ dev_notice(ohci->card.device,
+ "IRQ %08x%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n", evt,
evt & OHCI1394_selfIDComplete ? " selfID" : "",
evt & OHCI1394_RQPkt ? " AR_req" : "",
evt & OHCI1394_RSPkt ? " AR_resp" : "",
@@ -398,24 +398,29 @@ static char _p(u32 *s, int shift)
return port[*s >> shift & 3];
}
-static void log_selfids(int node_id, int generation, int self_id_count, u32 *s)
+static void log_selfids(struct fw_ohci *ohci, int generation, int self_id_count)
{
+ u32 *s;
+
if (likely(!(param_debug & OHCI_PARAM_DEBUG_SELFIDS)))
return;
- fw_notify("%d selfIDs, generation %d, local node ID %04x\n",
- self_id_count, generation, node_id);
+ dev_notice(ohci->card.device,
+ "%d selfIDs, generation %d, local node ID %04x\n",
+ self_id_count, generation, ohci->node_id);
- for (; self_id_count--; ++s)
+ for (s = ohci->self_id_buffer; self_id_count--; ++s)
if ((*s & 1 << 23) == 0)
- fw_notify("selfID 0: %08x, phy %d [%c%c%c] "
+ dev_notice(ohci->card.device,
+ "selfID 0: %08x, phy %d [%c%c%c] "
"%s gc=%d %s %s%s%s\n",
*s, *s >> 24 & 63, _p(s, 6), _p(s, 4), _p(s, 2),
speed[*s >> 14 & 3], *s >> 16 & 63,
power[*s >> 8 & 7], *s >> 22 & 1 ? "L" : "",
*s >> 11 & 1 ? "c" : "", *s & 2 ? "i" : "");
else
- fw_notify("selfID n: %08x, phy %d [%c%c%c%c%c%c%c%c]\n",
+ dev_notice(ohci->card.device,
+ "selfID n: %08x, phy %d [%c%c%c%c%c%c%c%c]\n",
*s, *s >> 24 & 63,
_p(s, 16), _p(s, 14), _p(s, 12), _p(s, 10),
_p(s, 8), _p(s, 6), _p(s, 4), _p(s, 2));
@@ -451,7 +456,8 @@ static const char *tcodes[] = {
[0xe] = "link internal", [0xf] = "-reserved-",
};
-static void log_ar_at_event(char dir, int speed, u32 *header, int evt)
+static void log_ar_at_event(struct fw_ohci *ohci,
+ char dir, int speed, u32 *header, int evt)
{
int tcode = header[0] >> 4 & 0xf;
char specific[12];
@@ -463,8 +469,9 @@ static void log_ar_at_event(char dir, int speed, u32 *header, int evt)
evt = 0x1f;
if (evt == OHCI1394_evt_bus_reset) {
- fw_notify("A%c evt_bus_reset, generation %d\n",
- dir, (header[2] >> 16) & 0xff);
+ dev_notice(ohci->card.device,
+ "A%c evt_bus_reset, generation %d\n",
+ dir, (header[2] >> 16) & 0xff);
return;
}
@@ -483,39 +490,35 @@ static void log_ar_at_event(char dir, int speed, u32 *header, int evt)
switch (tcode) {
case 0xa:
- fw_notify("A%c %s, %s\n", dir, evts[evt], tcodes[tcode]);
+ dev_notice(ohci->card.device,
+ "A%c %s, %s\n",
+ dir, evts[evt], tcodes[tcode]);
break;
case 0xe:
- fw_notify("A%c %s, PHY %08x %08x\n",
- dir, evts[evt], header[1], header[2]);
+ dev_notice(ohci->card.device,
+ "A%c %s, PHY %08x %08x\n",
+ dir, evts[evt], header[1], header[2]);
break;
case 0x0: case 0x1: case 0x4: case 0x5: case 0x9:
- fw_notify("A%c spd %x tl %02x, "
- "%04x -> %04x, %s, "
- "%s, %04x%08x%s\n",
- dir, speed, header[0] >> 10 & 0x3f,
- header[1] >> 16, header[0] >> 16, evts[evt],
- tcodes[tcode], header[1] & 0xffff, header[2], specific);
+ dev_notice(ohci->card.device,
+ "A%c spd %x tl %02x, "
+ "%04x -> %04x, %s, "
+ "%s, %04x%08x%s\n",
+ dir, speed, header[0] >> 10 & 0x3f,
+ header[1] >> 16, header[0] >> 16, evts[evt],
+ tcodes[tcode], header[1] & 0xffff, header[2], specific);
break;
default:
- fw_notify("A%c spd %x tl %02x, "
- "%04x -> %04x, %s, "
- "%s%s\n",
- dir, speed, header[0] >> 10 & 0x3f,
- header[1] >> 16, header[0] >> 16, evts[evt],
- tcodes[tcode], specific);
+ dev_notice(ohci->card.device,
+ "A%c spd %x tl %02x, "
+ "%04x -> %04x, %s, "
+ "%s%s\n",
+ dir, speed, header[0] >> 10 & 0x3f,
+ header[1] >> 16, header[0] >> 16, evts[evt],
+ tcodes[tcode], specific);
}
}
-#else
-
-#define param_debug 0
-static inline void log_irqs(u32 evt) {}
-static inline void log_selfids(int node_id, int generation, int self_id_count, u32 *s) {}
-static inline void log_ar_at_event(char dir, int speed, u32 *header, int evt) {}
-
-#endif /* CONFIG_FIREWIRE_OHCI_DEBUG */
-
static inline void reg_write(const struct fw_ohci *ohci, int offset, u32 data)
{
writel(data, ohci->registers + offset);
@@ -559,7 +562,7 @@ static int read_phy_reg(struct fw_ohci *ohci, int addr)
if (i >= 3)
msleep(1);
}
- fw_error("failed to read phy reg\n");
+ dev_err(ohci->card.device, "failed to read phy reg\n");
return -EBUSY;
}
@@ -581,7 +584,7 @@ static int write_phy_reg(const struct fw_ohci *ohci, int addr, u32 val)
if (i >= 3)
msleep(1);
}
- fw_error("failed to write phy reg\n");
+ dev_err(ohci->card.device, "failed to write phy reg\n");
return -EBUSY;
}
@@ -680,11 +683,14 @@ static void ar_context_release(struct ar_context *ctx)
static void ar_context_abort(struct ar_context *ctx, const char *error_msg)
{
- if (reg_read(ctx->ohci, CONTROL_CLEAR(ctx->regs)) & CONTEXT_RUN) {
- reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
- flush_writes(ctx->ohci);
+ struct fw_ohci *ohci = ctx->ohci;
+
+ if (reg_read(ohci, CONTROL_CLEAR(ctx->regs)) & CONTEXT_RUN) {
+ reg_write(ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
+ flush_writes(ohci);
- fw_error("AR error: %s; DMA stopped\n", error_msg);
+ dev_err(ohci->card.device, "AR error: %s; DMA stopped\n",
+ error_msg);
}
/* FIXME: restart? */
}
@@ -854,7 +860,7 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
p.timestamp = status & 0xffff;
p.generation = ohci->request_generation;
- log_ar_at_event('R', p.speed, p.header, evt);
+ log_ar_at_event(ohci, 'R', p.speed, p.header, evt);
/*
* Several controllers, notably from NEC and VIA, forget to
@@ -1226,21 +1232,22 @@ static void context_append(struct context *ctx,
static void context_stop(struct context *ctx)
{
+ struct fw_ohci *ohci = ctx->ohci;
u32 reg;
int i;
- reg_write(ctx->ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
+ reg_write(ohci, CONTROL_CLEAR(ctx->regs), CONTEXT_RUN);
ctx->running = false;
for (i = 0; i < 1000; i++) {
- reg = reg_read(ctx->ohci, CONTROL_SET(ctx->regs));
+ reg = reg_read(ohci, CONTROL_SET(ctx->regs));
if ((reg & CONTEXT_ACTIVE) == 0)
return;
if (i)
udelay(10);
}
- fw_error("Error: DMA context still active (0x%08x)\n", reg);
+ dev_err(ohci->card.device, "DMA context still active (0x%08x)\n", reg);
}
struct driver_data {
@@ -1420,7 +1427,7 @@ static int handle_at_packet(struct context *context,
evt = le16_to_cpu(last->transfer_status) & 0x1f;
packet->timestamp = le16_to_cpu(last->res_count);
- log_ar_at_event('T', packet->speed, packet->header, evt);
+ log_ar_at_event(ohci, 'T', packet->speed, packet->header, evt);
switch (evt) {
case OHCI1394_evt_timeout:
@@ -1549,7 +1556,7 @@ static void handle_local_lock(struct fw_ohci *ohci,
goto out;
}
- fw_error("swap not done (CSR lock timeout)\n");
+ dev_err(ohci->card.device, "swap not done (CSR lock timeout)\n");
fw_fill_response(&response, packet->header, RCODE_BUSY, NULL, 0);
out:
@@ -1623,15 +1630,10 @@ static void detect_dead_context(struct fw_ohci *ohci,
u32 ctl;
ctl = reg_read(ohci, CONTROL_SET(regs));
- if (ctl & CONTEXT_DEAD) {
-#ifdef CONFIG_FIREWIRE_OHCI_DEBUG
- fw_error("DMA context %s has stopped, error code: %s\n",
- name, evts[ctl & 0x1f]);
-#else
- fw_error("DMA context %s has stopped, error code: %#x\n",
- name, ctl & 0x1f);
-#endif
- }
+ if (ctl & CONTEXT_DEAD)
+ dev_err(ohci->card.device,
+ "DMA context %s has stopped, error code: %s\n",
+ name, evts[ctl & 0x1f]);
}
static void handle_dead_contexts(struct fw_ohci *ohci)
@@ -1781,7 +1783,8 @@ static int find_and_insert_self_id(struct fw_ohci *ohci, int self_id_count)
reg = reg_read(ohci, OHCI1394_NodeID);
if (!(reg & OHCI1394_NodeID_idValid)) {
- fw_notify("node ID not valid, new bus reset in progress\n");
+ dev_notice(ohci->card.device,
+ "node ID not valid, new bus reset in progress\n");
return -EBUSY;
}
self_id |= ((reg & 0x3f) << 24); /* phy ID */
@@ -1827,11 +1830,12 @@ static void bus_reset_work(struct work_struct *work)
reg = reg_read(ohci, OHCI1394_NodeID);
if (!(reg & OHCI1394_NodeID_idValid)) {
- fw_notify("node ID not valid, new bus reset in progress\n");
+ dev_notice(ohci->card.device,
+ "node ID not valid, new bus reset in progress\n");
return;
}
if ((reg & OHCI1394_NodeID_nodeNumber) == 63) {
- fw_notify("malconfigured bus\n");
+ dev_notice(ohci->card.device, "malconfigured bus\n");
return;
}
ohci->node_id = reg & (OHCI1394_NodeID_busNumber |
@@ -1845,7 +1849,7 @@ static void bus_reset_work(struct work_struct *work)
reg = reg_read(ohci, OHCI1394_SelfIDCount);
if (reg & OHCI1394_SelfIDCount_selfIDError) {
- fw_notify("inconsistent self IDs\n");
+ dev_notice(ohci->card.device, "inconsistent self IDs\n");
return;
}
/*
@@ -1857,7 +1861,7 @@ static void bus_reset_work(struct work_struct *work)
self_id_count = (reg >> 3) & 0xff;
if (self_id_count > 252) {
- fw_notify("inconsistent self IDs\n");
+ dev_notice(ohci->card.device, "inconsistent self IDs\n");
return;
}
@@ -1875,11 +1879,13 @@ static void bus_reset_work(struct work_struct *work)
*/
if (cond_le32_to_cpu(ohci->self_id_cpu[i])
== 0xffff008f) {
- fw_notify("ignoring spurious self IDs\n");
+ dev_notice(ohci->card.device,
+ "ignoring spurious self IDs\n");
self_id_count = j;
break;
} else {
- fw_notify("inconsistent self IDs\n");
+ dev_notice(ohci->card.device,
+ "inconsistent self IDs\n");
return;
}
}
@@ -1890,13 +1896,14 @@ static void bus_reset_work(struct work_struct *work)
if (ohci->quirks & QUIRK_TI_SLLZ059) {
self_id_count = find_and_insert_self_id(ohci, self_id_count);
if (self_id_count < 0) {
- fw_notify("could not construct local self ID\n");
+ dev_notice(ohci->card.device,
+ "could not construct local self ID\n");
return;
}
}
if (self_id_count == 0) {
- fw_notify("inconsistent self IDs\n");
+ dev_notice(ohci->card.device, "inconsistent self IDs\n");
return;
}
rmb();
@@ -1917,8 +1924,8 @@ static void bus_reset_work(struct work_struct *work)
new_generation = (reg_read(ohci, OHCI1394_SelfIDCount) >> 16) & 0xff;
if (new_generation != generation) {
- fw_notify("recursive bus reset detected, "
- "discarding self ids\n");
+ dev_notice(ohci->card.device,
+ "new bus reset, discarding self ids\n");
return;
}
@@ -1989,8 +1996,7 @@ static void bus_reset_work(struct work_struct *work)
dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
free_rom, free_rom_bus);
- log_selfids(ohci->node_id, generation,
- self_id_count, ohci->self_id_buffer);
+ log_selfids(ohci, generation, self_id_count);
fw_core_handle_bus_reset(&ohci->card, ohci->node_id, generation,
self_id_count, ohci->self_id_buffer,
@@ -2015,7 +2021,7 @@ static irqreturn_t irq_handler(int irq, void *data)
*/
reg_write(ohci, OHCI1394_IntEventClear,
event & ~(OHCI1394_busReset | OHCI1394_postedWriteErr));
- log_irqs(event);
+ log_irqs(ohci, event);
if (event & OHCI1394_selfIDComplete)
queue_work(fw_workqueue, &ohci->bus_reset_work);
@@ -2057,8 +2063,7 @@ static irqreturn_t irq_handler(int irq, void *data)
}
if (unlikely(event & OHCI1394_regAccessFail))
- fw_error("Register access failure - "
- "please notify linux1394-devel@lists.sf.net\n");
+ dev_err(ohci->card.device, "register access failure\n");
if (unlikely(event & OHCI1394_postedWriteErr)) {
reg_read(ohci, OHCI1394_PostedWriteAddressHi);
@@ -2066,12 +2071,13 @@ static irqreturn_t irq_handler(int irq, void *data)
reg_write(ohci, OHCI1394_IntEventClear,
OHCI1394_postedWriteErr);
if (printk_ratelimit())
- fw_error("PCI posted write error\n");
+ dev_err(ohci->card.device, "PCI posted write error\n");
}
if (unlikely(event & OHCI1394_cycleTooLong)) {
if (printk_ratelimit())
- fw_notify("isochronous cycle too long\n");
+ dev_notice(ohci->card.device,
+ "isochronous cycle too long\n");
reg_write(ohci, OHCI1394_LinkControlSet,
OHCI1394_LinkControl_cycleMaster);
}
@@ -2084,7 +2090,8 @@ static irqreturn_t irq_handler(int irq, void *data)
* them at least two cycles later. (FIXME?)
*/
if (printk_ratelimit())
- fw_notify("isochronous cycle inconsistent\n");
+ dev_notice(ohci->card.device,
+ "isochronous cycle inconsistent\n");
}
if (unlikely(event & OHCI1394_unrecoverableError))
@@ -2211,7 +2218,7 @@ static int ohci_enable(struct fw_card *card,
int i, ret;
if (software_reset(ohci)) {
- fw_error("Failed to reset ohci card.\n");
+ dev_err(card->device, "failed to reset ohci card\n");
return -EBUSY;
}
@@ -2235,7 +2242,7 @@ static int ohci_enable(struct fw_card *card,
}
if (!lps) {
- fw_error("Failed to set Link Power Status\n");
+ dev_err(card->device, "failed to set Link Power Status\n");
return -EIO;
}
@@ -2244,7 +2251,7 @@ static int ohci_enable(struct fw_card *card,
if (ret < 0)
return ret;
if (ret)
- fw_notify("local TSB41BA3D phy\n");
+ dev_notice(card->device, "local TSB41BA3D phy\n");
else
ohci->quirks &= ~QUIRK_TI_SLLZ059;
}
@@ -2344,7 +2351,8 @@ static int ohci_enable(struct fw_card *card,
if (request_irq(dev->irq, irq_handler,
pci_dev_msi_enabled(dev) ? 0 : IRQF_SHARED,
ohci_driver_name, ohci)) {
- fw_error("Failed to allocate interrupt %d.\n", dev->irq);
+ dev_err(card->device, "failed to allocate interrupt %d\n",
+ dev->irq);
pci_disable_msi(dev);
if (config_rom) {
@@ -2509,7 +2517,7 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
dma_unmap_single(ohci->card.device, packet->payload_bus,
packet->payload_length, DMA_TO_DEVICE);
- log_ar_at_event('T', packet->speed, packet->header, 0x20);
+ log_ar_at_event(ohci, 'T', packet->speed, packet->header, 0x20);
driver_data->packet = NULL;
packet->ack = RCODE_CANCELLED;
packet->callback(packet, &ohci->card, packet->ack);
@@ -2674,25 +2682,35 @@ static void ohci_write_csr(struct fw_card *card, int csr_offset, u32 value)
}
}
-static void copy_iso_headers(struct iso_context *ctx, void *p)
+static void flush_iso_completions(struct iso_context *ctx)
{
- int i = ctx->header_length;
+ ctx->base.callback.sc(&ctx->base, ctx->last_timestamp,
+ ctx->header_length, ctx->header,
+ ctx->base.callback_data);
+ ctx->header_length = 0;
+}
- if (i + ctx->base.header_size > PAGE_SIZE)
- return;
+static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr)
+{
+ u32 *ctx_hdr;
+
+ if (ctx->header_length + ctx->base.header_size > PAGE_SIZE)
+ flush_iso_completions(ctx);
+
+ ctx_hdr = ctx->header + ctx->header_length;
+ ctx->last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]);
/*
- * The iso header is byteswapped to little endian by
- * the controller, but the remaining header quadlets
- * are big endian. We want to present all the headers
- * as big endian, so we have to swap the first quadlet.
+ * The two iso header quadlets are byteswapped to little
+ * endian by the controller, but we want to present them
+ * as big endian for consistency with the bus endianness.
*/
if (ctx->base.header_size > 0)
- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+ ctx_hdr[0] = swab32(dma_hdr[1]); /* iso packet header */
if (ctx->base.header_size > 4)
- *(u32 *) (ctx->header + i + 4) = __swab32(*(u32 *) p);
+ ctx_hdr[1] = swab32(dma_hdr[0]); /* timestamp */
if (ctx->base.header_size > 8)
- memcpy(ctx->header + i + 8, p + 8, ctx->base.header_size - 8);
+ memcpy(&ctx_hdr[2], &dma_hdr[2], ctx->base.header_size - 8);
ctx->header_length += ctx->base.header_size;
}
@@ -2704,8 +2722,6 @@ static int handle_ir_packet_per_buffer(struct context *context,
container_of(context, struct iso_context, context);
struct descriptor *pd;
u32 buffer_dma;
- __le32 *ir_header;
- void *p;
for (pd = d; pd <= last; pd++)
if (pd->transfer_status)
@@ -2724,17 +2740,10 @@ static int handle_ir_packet_per_buffer(struct context *context,
DMA_FROM_DEVICE);
}
- p = last + 1;
- copy_iso_headers(ctx, p);
+ copy_iso_headers(ctx, (u32 *) (last + 1));
- if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) {
- ir_header = (__le32 *) p;
- ctx->base.callback.sc(&ctx->base,
- le32_to_cpu(ir_header[0]) & 0xffff,
- ctx->header_length, ctx->header,
- ctx->base.callback_data);
- ctx->header_length = 0;
- }
+ if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS))
+ flush_iso_completions(ctx);
return 1;
}
@@ -2746,29 +2755,51 @@ static int handle_ir_buffer_fill(struct context *context,
{
struct iso_context *ctx =
container_of(context, struct iso_context, context);
+ unsigned int req_count, res_count, completed;
u32 buffer_dma;
- if (!last->transfer_status)
+ req_count = le16_to_cpu(last->req_count);
+ res_count = le16_to_cpu(ACCESS_ONCE(last->res_count));
+ completed = req_count - res_count;
+ buffer_dma = le32_to_cpu(last->data_address);
+
+ if (completed > 0) {
+ ctx->mc_buffer_bus = buffer_dma;
+ ctx->mc_completed = completed;
+ }
+
+ if (res_count != 0)
/* Descriptor(s) not done yet, stop iteration */
return 0;
- buffer_dma = le32_to_cpu(last->data_address);
dma_sync_single_range_for_cpu(context->ohci->card.device,
buffer_dma & PAGE_MASK,
buffer_dma & ~PAGE_MASK,
- le16_to_cpu(last->req_count),
- DMA_FROM_DEVICE);
+ completed, DMA_FROM_DEVICE);
- if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS)
+ if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) {
ctx->base.callback.mc(&ctx->base,
- le32_to_cpu(last->data_address) +
- le16_to_cpu(last->req_count) -
- le16_to_cpu(last->res_count),
+ buffer_dma + completed,
ctx->base.callback_data);
+ ctx->mc_completed = 0;
+ }
return 1;
}
+static void flush_ir_buffer_fill(struct iso_context *ctx)
+{
+ dma_sync_single_range_for_cpu(ctx->context.ohci->card.device,
+ ctx->mc_buffer_bus & PAGE_MASK,
+ ctx->mc_buffer_bus & ~PAGE_MASK,
+ ctx->mc_completed, DMA_FROM_DEVICE);
+
+ ctx->base.callback.mc(&ctx->base,
+ ctx->mc_buffer_bus + ctx->mc_completed,
+ ctx->base.callback_data);
+ ctx->mc_completed = 0;
+}
+
static inline void sync_it_packet_for_cpu(struct context *context,
struct descriptor *pd)
{
@@ -2812,8 +2843,8 @@ static int handle_it_packet(struct context *context,
{
struct iso_context *ctx =
container_of(context, struct iso_context, context);
- int i;
struct descriptor *pd;
+ __be32 *ctx_hdr;
for (pd = d; pd <= last; pd++)
if (pd->transfer_status)
@@ -2824,20 +2855,19 @@ static int handle_it_packet(struct context *context,
sync_it_packet_for_cpu(context, d);
- i = ctx->header_length;
- if (i + 4 < PAGE_SIZE) {
- /* Present this value as big-endian to match the receive code */
- *(__be32 *)(ctx->header + i) = cpu_to_be32(
- ((u32)le16_to_cpu(pd->transfer_status) << 16) |
- le16_to_cpu(pd->res_count));
- ctx->header_length += 4;
- }
- if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) {
- ctx->base.callback.sc(&ctx->base, le16_to_cpu(last->res_count),
- ctx->header_length, ctx->header,
- ctx->base.callback_data);
- ctx->header_length = 0;
- }
+ if (ctx->header_length + 4 > PAGE_SIZE)
+ flush_iso_completions(ctx);
+
+ ctx_hdr = ctx->header + ctx->header_length;
+ ctx->last_timestamp = le16_to_cpu(last->res_count);
+ /* Present this value as big-endian to match the receive code */
+ *ctx_hdr = cpu_to_be32((le16_to_cpu(pd->transfer_status) << 16) |
+ le16_to_cpu(pd->res_count));
+ ctx->header_length += 4;
+
+ if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS))
+ flush_iso_completions(ctx);
+
return 1;
}
@@ -2924,8 +2954,10 @@ static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
if (ret < 0)
goto out_with_header;
- if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL)
+ if (type == FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL) {
set_multichannel_mask(ohci, 0);
+ ctx->mc_completed = 0;
+ }
return &ctx->base;
@@ -3387,6 +3419,39 @@ static void ohci_flush_queue_iso(struct fw_iso_context *base)
reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
}
+static int ohci_flush_iso_completions(struct fw_iso_context *base)
+{
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ int ret = 0;
+
+ tasklet_disable(&ctx->context.tasklet);
+
+ if (!test_and_set_bit_lock(0, &ctx->flushing_completions)) {
+ context_tasklet((unsigned long)&ctx->context);
+
+ switch (base->type) {
+ case FW_ISO_CONTEXT_TRANSMIT:
+ case FW_ISO_CONTEXT_RECEIVE:
+ if (ctx->header_length != 0)
+ flush_iso_completions(ctx);
+ break;
+ case FW_ISO_CONTEXT_RECEIVE_MULTICHANNEL:
+ if (ctx->mc_completed != 0)
+ flush_ir_buffer_fill(ctx);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ clear_bit_unlock(0, &ctx->flushing_completions);
+ smp_mb__after_clear_bit();
+ }
+
+ tasklet_enable(&ctx->context.tasklet);
+
+ return ret;
+}
+
static const struct fw_card_driver ohci_driver = {
.enable = ohci_enable,
.read_phy_reg = ohci_read_phy_reg,
@@ -3404,6 +3469,7 @@ static const struct fw_card_driver ohci_driver = {
.set_iso_channels = ohci_set_iso_channels,
.queue_iso = ohci_queue_iso,
.flush_queue_iso = ohci_flush_queue_iso,
+ .flush_iso_completions = ohci_flush_iso_completions,
.start_iso = ohci_start_iso,
.stop_iso = ohci_stop_iso,
};
@@ -3463,7 +3529,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
err = pci_enable_device(dev);
if (err) {
- fw_error("Failed to enable OHCI hardware\n");
+ dev_err(&dev->dev, "failed to enable OHCI hardware\n");
goto fail_free;
}
@@ -3478,13 +3544,13 @@ static int __devinit pci_probe(struct pci_dev *dev,
err = pci_request_region(dev, 0, ohci_driver_name);
if (err) {
- fw_error("MMIO resource unavailable\n");
+ dev_err(&dev->dev, "MMIO resource unavailable\n");
goto fail_disable;
}
ohci->registers = pci_iomap(dev, 0, OHCI1394_REGISTER_SIZE);
if (ohci->registers == NULL) {
- fw_error("Failed to remap registers\n");
+ dev_err(&dev->dev, "failed to remap registers\n");
err = -ENXIO;
goto fail_iomem;
}
@@ -3573,9 +3639,10 @@ static int __devinit pci_probe(struct pci_dev *dev,
goto fail_contexts;
version = reg_read(ohci, OHCI1394_Version) & 0x00ff00ff;
- fw_notify("Added fw-ohci device %s, OHCI v%x.%x, "
+ dev_notice(&dev->dev,
+ "added OHCI v%x.%x device as card %d, "
"%d IR + %d IT contexts, quirks 0x%x\n",
- dev_name(&dev->dev), version >> 16, version & 0xff,
+ version >> 16, version & 0xff, ohci->card.index,
ohci->n_ir, ohci->n_it, ohci->quirks);
return 0;
@@ -3604,7 +3671,7 @@ static int __devinit pci_probe(struct pci_dev *dev,
pmac_ohci_off(dev);
fail:
if (err == -ENOMEM)
- fw_error("Out of memory\n");
+ dev_err(&dev->dev, "out of memory\n");
return err;
}
@@ -3648,7 +3715,7 @@ static void pci_remove(struct pci_dev *dev)
kfree(ohci);
pmac_ohci_off(dev);
- fw_notify("Removed fw-ohci device.\n");
+ dev_notice(&dev->dev, "removed fw-ohci device\n");
}
#ifdef CONFIG_PM
@@ -3662,12 +3729,12 @@ static int pci_suspend(struct pci_dev *dev, pm_message_t state)
pci_disable_msi(dev);
err = pci_save_state(dev);
if (err) {
- fw_error("pci_save_state failed\n");
+ dev_err(&dev->dev, "pci_save_state failed\n");
return err;
}
err = pci_set_power_state(dev, pci_choose_state(dev, state));
if (err)
- fw_error("pci_set_power_state failed with %d\n", err);
+ dev_err(&dev->dev, "pci_set_power_state failed with %d\n", err);
pmac_ohci_off(dev);
return 0;
@@ -3683,7 +3750,7 @@ static int pci_resume(struct pci_dev *dev)
pci_restore_state(dev);
err = pci_enable_device(dev);
if (err) {
- fw_error("pci_enable_device failed\n");
+ dev_err(&dev->dev, "pci_enable_device failed\n");
return err;
}
diff --git a/drivers/firewire/sbp2.c b/drivers/firewire/sbp2.c
index 80e95aa3bf14..b7e65d7eab64 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>
@@ -125,8 +124,6 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
-static const char sbp2_driver_name[] = "sbp2";
-
/*
* We create one struct sbp2_logical_unit per SBP-2 Logical Unit Number Entry
* and one struct scsi_device per sbp2_logical_unit.
@@ -165,7 +162,6 @@ static void sbp2_queue_work(struct sbp2_logical_unit *lu, unsigned long delay)
*/
struct sbp2_target {
struct fw_unit *unit;
- const char *bus_id;
struct list_head lu_list;
u64 management_agent_address;
@@ -181,11 +177,21 @@ struct sbp2_target {
int blocked; /* ditto */
};
-static struct fw_device *target_device(struct sbp2_target *tgt)
+static struct fw_device *target_parent_device(struct sbp2_target *tgt)
{
return fw_parent_device(tgt->unit);
}
+static const struct device *tgt_dev(const struct sbp2_target *tgt)
+{
+ return &tgt->unit->device;
+}
+
+static const struct device *lu_dev(const struct sbp2_logical_unit *lu)
+{
+ return &lu->tgt->unit->device;
+}
+
/* Impossible login_id, to detect logout attempt before successful login */
#define INVALID_LOGIN_ID 0x10000
@@ -211,6 +217,7 @@ static struct fw_device *target_device(struct sbp2_target *tgt)
#define SBP2_CSR_UNIT_CHARACTERISTICS 0x3a
#define SBP2_CSR_FIRMWARE_REVISION 0x3c
#define SBP2_CSR_LOGICAL_UNIT_NUMBER 0x14
+#define SBP2_CSR_UNIT_UNIQUE_ID 0x8d
#define SBP2_CSR_LOGICAL_UNIT_DIRECTORY 0xd4
/* Management orb opcodes */
@@ -430,7 +437,8 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request,
memcpy(status.data, payload + 8, length - 8);
if (STATUS_GET_SOURCE(status) == 2 || STATUS_GET_SOURCE(status) == 3) {
- fw_notify("non-orb related status write, not handled\n");
+ dev_notice(lu_dev(lu),
+ "non-ORB related status write, not handled\n");
fw_send_response(card, request, RCODE_COMPLETE);
return;
}
@@ -451,7 +459,7 @@ static void sbp2_status_write(struct fw_card *card, struct fw_request *request,
orb->callback(orb, &status);
kref_put(&orb->kref, free_orb); /* orb callback reference */
} else {
- fw_error("status write for unknown orb\n");
+ dev_err(lu_dev(lu), "status write for unknown ORB\n");
}
fw_send_response(card, request, RCODE_COMPLETE);
@@ -492,7 +500,7 @@ static void complete_transaction(struct fw_card *card, int rcode,
static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
int node_id, int generation, u64 offset)
{
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
struct sbp2_pointer orb_pointer;
unsigned long flags;
@@ -513,7 +521,7 @@ static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
static int sbp2_cancel_orbs(struct sbp2_logical_unit *lu)
{
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
struct sbp2_orb *orb, *next;
struct list_head list;
unsigned long flags;
@@ -552,7 +560,7 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
int generation, int function,
int lun_or_login_id, void *response)
{
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
struct sbp2_management_orb *orb;
unsigned int timeout;
int retval = -ENOMEM;
@@ -560,7 +568,7 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
if (function == SBP2_LOGOUT_REQUEST && fw_device_is_shutdown(device))
return 0;
- orb = kzalloc(sizeof(*orb), GFP_ATOMIC);
+ orb = kzalloc(sizeof(*orb), GFP_NOIO);
if (orb == NULL)
return -ENOMEM;
@@ -612,20 +620,20 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
retval = -EIO;
if (sbp2_cancel_orbs(lu) == 0) {
- fw_error("%s: orb reply timed out, rcode=0x%02x\n",
- lu->tgt->bus_id, orb->base.rcode);
+ dev_err(lu_dev(lu), "ORB reply timed out, rcode 0x%02x\n",
+ orb->base.rcode);
goto out;
}
if (orb->base.rcode != RCODE_COMPLETE) {
- fw_error("%s: management write failed, rcode 0x%02x\n",
- lu->tgt->bus_id, orb->base.rcode);
+ dev_err(lu_dev(lu), "management write failed, rcode 0x%02x\n",
+ orb->base.rcode);
goto out;
}
if (STATUS_GET_RESPONSE(orb->status) != 0 ||
STATUS_GET_SBP_STATUS(orb->status) != 0) {
- fw_error("%s: error status: %d:%d\n", lu->tgt->bus_id,
+ dev_err(lu_dev(lu), "error status: %d:%d\n",
STATUS_GET_RESPONSE(orb->status),
STATUS_GET_SBP_STATUS(orb->status));
goto out;
@@ -648,7 +656,7 @@ static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
static void sbp2_agent_reset(struct sbp2_logical_unit *lu)
{
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
__be32 d = 0;
fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST,
@@ -665,7 +673,7 @@ static void complete_agent_reset_write_no_wait(struct fw_card *card,
static void sbp2_agent_reset_no_wait(struct sbp2_logical_unit *lu)
{
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
struct fw_transaction *t;
static __be32 d;
@@ -704,7 +712,7 @@ static inline void sbp2_allow_block(struct sbp2_logical_unit *lu)
static void sbp2_conditionally_block(struct sbp2_logical_unit *lu)
{
struct sbp2_target *tgt = lu->tgt;
- struct fw_card *card = target_device(tgt)->card;
+ struct fw_card *card = target_parent_device(tgt)->card;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
unsigned long flags;
@@ -728,7 +736,7 @@ static void sbp2_conditionally_block(struct sbp2_logical_unit *lu)
static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu)
{
struct sbp2_target *tgt = lu->tgt;
- struct fw_card *card = target_device(tgt)->card;
+ struct fw_card *card = target_parent_device(tgt)->card;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
unsigned long flags;
@@ -753,7 +761,7 @@ static void sbp2_conditionally_unblock(struct sbp2_logical_unit *lu)
*/
static void sbp2_unblock(struct sbp2_target *tgt)
{
- struct fw_card *card = target_device(tgt)->card;
+ struct fw_card *card = target_parent_device(tgt)->card;
struct Scsi_Host *shost =
container_of((void *)tgt, struct Scsi_Host, hostdata[0]);
unsigned long flags;
@@ -794,7 +802,7 @@ static int sbp2_lun2int(u16 lun)
*/
static void sbp2_set_busy_timeout(struct sbp2_logical_unit *lu)
{
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
__be32 d = cpu_to_be32(SBP2_CYCLE_LIMIT | SBP2_RETRY_LIMIT);
fw_run_transaction(device->card, TCODE_WRITE_QUADLET_REQUEST,
@@ -809,7 +817,7 @@ static void sbp2_login(struct work_struct *work)
struct sbp2_logical_unit *lu =
container_of(work, struct sbp2_logical_unit, work.work);
struct sbp2_target *tgt = lu->tgt;
- struct fw_device *device = target_device(tgt);
+ struct fw_device *device = target_parent_device(tgt);
struct Scsi_Host *shost;
struct scsi_device *sdev;
struct sbp2_login_response response;
@@ -833,8 +841,8 @@ static void sbp2_login(struct work_struct *work)
if (lu->retries++ < 5) {
sbp2_queue_work(lu, DIV_ROUND_UP(HZ, 5));
} else {
- fw_error("%s: failed to login to LUN %04x\n",
- tgt->bus_id, lu->lun);
+ dev_err(tgt_dev(tgt), "failed to login to LUN %04x\n",
+ lu->lun);
/* Let any waiting I/O fail from now on. */
sbp2_unblock(lu->tgt);
}
@@ -851,8 +859,8 @@ static void sbp2_login(struct work_struct *work)
<< 32) | be32_to_cpu(response.command_block_agent.low);
lu->login_id = be32_to_cpu(response.misc) & 0xffff;
- fw_notify("%s: logged in to LUN %04x (%d retries)\n",
- tgt->bus_id, lu->lun, lu->retries);
+ dev_notice(tgt_dev(tgt), "logged in to LUN %04x (%d retries)\n",
+ lu->lun, lu->retries);
/* set appropriate retry limit(s) in BUSY_TIMEOUT register */
sbp2_set_busy_timeout(lu);
@@ -919,7 +927,7 @@ static void sbp2_reconnect(struct work_struct *work)
struct sbp2_logical_unit *lu =
container_of(work, struct sbp2_logical_unit, work.work);
struct sbp2_target *tgt = lu->tgt;
- struct fw_device *device = target_device(tgt);
+ struct fw_device *device = target_parent_device(tgt);
int generation, node_id, local_node_id;
if (fw_device_is_shutdown(device))
@@ -943,7 +951,7 @@ static void sbp2_reconnect(struct work_struct *work)
smp_rmb(); /* get current card generation */
if (generation == device->card->generation ||
lu->retries++ >= 5) {
- fw_error("%s: failed to reconnect\n", tgt->bus_id);
+ dev_err(tgt_dev(tgt), "failed to reconnect\n");
lu->retries = 0;
PREPARE_DELAYED_WORK(&lu->work, sbp2_login);
}
@@ -957,8 +965,8 @@ static void sbp2_reconnect(struct work_struct *work)
smp_wmb(); /* node IDs must not be older than generation */
lu->generation = generation;
- fw_notify("%s: reconnected to LUN %04x (%d retries)\n",
- tgt->bus_id, lu->lun, lu->retries);
+ dev_notice(tgt_dev(tgt), "reconnected to LUN %04x (%d retries)\n",
+ lu->lun, lu->retries);
sbp2_agent_reset(lu);
sbp2_cancel_orbs(lu);
@@ -997,6 +1005,13 @@ static int sbp2_add_logical_unit(struct sbp2_target *tgt, int lun_entry)
return 0;
}
+static void sbp2_get_unit_unique_id(struct sbp2_target *tgt,
+ const u32 *leaf)
+{
+ if ((leaf[0] & 0xffff0000) == 0x00020000)
+ tgt->guid = (u64)leaf[1] << 32 | leaf[2];
+}
+
static int sbp2_scan_logical_unit_dir(struct sbp2_target *tgt,
const u32 *directory)
{
@@ -1048,6 +1063,10 @@ static int sbp2_scan_unit_dir(struct sbp2_target *tgt, const u32 *directory,
return -ENOMEM;
break;
+ case SBP2_CSR_UNIT_UNIQUE_ID:
+ sbp2_get_unit_unique_id(tgt, ci.p - 1 + value);
+ break;
+
case SBP2_CSR_LOGICAL_UNIT_DIRECTORY:
/* Adjust for the increment in the iterator */
if (sbp2_scan_logical_unit_dir(tgt, ci.p - 1 + value) < 0)
@@ -1068,8 +1087,8 @@ static void sbp2_clamp_management_orb_timeout(struct sbp2_target *tgt)
unsigned int timeout = tgt->mgt_orb_timeout;
if (timeout > 40000)
- fw_notify("%s: %ds mgt_ORB_timeout limited to 40s\n",
- tgt->bus_id, timeout / 1000);
+ dev_notice(tgt_dev(tgt), "%ds mgt_ORB_timeout limited to 40s\n",
+ timeout / 1000);
tgt->mgt_orb_timeout = clamp_val(timeout, 5000, 40000);
}
@@ -1081,9 +1100,9 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
unsigned int w = sbp2_param_workarounds;
if (w)
- fw_notify("Please notify linux1394-devel@lists.sourceforge.net "
- "if you need the workarounds parameter for %s\n",
- tgt->bus_id);
+ dev_notice(tgt_dev(tgt),
+ "Please notify linux1394-devel@lists.sf.net "
+ "if you need the workarounds parameter\n");
if (w & SBP2_WORKAROUND_OVERRIDE)
goto out;
@@ -1103,9 +1122,9 @@ static void sbp2_init_workarounds(struct sbp2_target *tgt, u32 model,
}
out:
if (w)
- fw_notify("Workarounds for %s: 0x%x "
- "(firmware_revision 0x%06x, model_id 0x%06x)\n",
- tgt->bus_id, w, firmware_revision, model);
+ dev_notice(tgt_dev(tgt), "workarounds 0x%x "
+ "(firmware_revision 0x%06x, model_id 0x%06x)\n",
+ w, firmware_revision, model);
tgt->workarounds = w;
}
@@ -1121,6 +1140,10 @@ static int sbp2_probe(struct device *dev)
struct Scsi_Host *shost;
u32 model, firmware_revision;
+ /* cannot (or should not) handle targets on the local node */
+ if (device->is_local)
+ return -ENODEV;
+
if (dma_get_max_seg_size(device->card->device) > SBP2_MAX_SEG_SIZE)
BUG_ON(dma_set_max_seg_size(device->card->device,
SBP2_MAX_SEG_SIZE));
@@ -1133,7 +1156,6 @@ static int sbp2_probe(struct device *dev)
dev_set_drvdata(&unit->device, tgt);
tgt->unit = unit;
INIT_LIST_HEAD(&tgt->lu_list);
- tgt->bus_id = dev_name(&unit->device);
tgt->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4];
if (fw_device_enable_phys_dma(device) < 0)
@@ -1239,7 +1261,7 @@ static int sbp2_remove(struct device *dev)
kfree(lu);
}
scsi_remove_host(shost);
- fw_notify("released %s, target %d:0:0\n", tgt->bus_id, shost->host_no);
+ dev_notice(dev, "released target %d:0:0\n", shost->host_no);
scsi_host_put(shost);
return 0;
@@ -1261,7 +1283,7 @@ static const struct ieee1394_device_id sbp2_id_table[] = {
static struct fw_driver sbp2_driver = {
.driver = {
.owner = THIS_MODULE,
- .name = sbp2_driver_name,
+ .name = KBUILD_MODNAME,
.bus = &fw_bus_type,
.probe = sbp2_probe,
.remove = sbp2_remove,
@@ -1286,10 +1308,19 @@ static void sbp2_unmap_scatterlist(struct device *card_device,
static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
{
int sam_status;
+ int sfmt = (sbp2_status[0] >> 6) & 0x03;
+
+ if (sfmt == 2 || sfmt == 3) {
+ /*
+ * Reserved for future standardization (2) or
+ * Status block format vendor-dependent (3)
+ */
+ return DID_ERROR << 16;
+ }
- sense_data[0] = 0x70;
+ sense_data[0] = 0x70 | sfmt | (sbp2_status[1] & 0x80);
sense_data[1] = 0x0;
- sense_data[2] = sbp2_status[1];
+ sense_data[2] = ((sbp2_status[1] << 1) & 0xe0) | (sbp2_status[1] & 0x0f);
sense_data[3] = sbp2_status[4];
sense_data[4] = sbp2_status[5];
sense_data[5] = sbp2_status[6];
@@ -1325,7 +1356,7 @@ static void complete_command_orb(struct sbp2_orb *base_orb,
{
struct sbp2_command_orb *orb =
container_of(base_orb, struct sbp2_command_orb, base);
- struct fw_device *device = target_device(orb->lu->tgt);
+ struct fw_device *device = target_parent_device(orb->lu->tgt);
int result;
if (status != NULL) {
@@ -1433,7 +1464,7 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost,
struct scsi_cmnd *cmd)
{
struct sbp2_logical_unit *lu = cmd->device->hostdata;
- struct fw_device *device = target_device(lu->tgt);
+ struct fw_device *device = target_parent_device(lu->tgt);
struct sbp2_command_orb *orb;
int generation, retval = SCSI_MLQUEUE_HOST_BUSY;
@@ -1442,7 +1473,7 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost,
* transfer direction not handled.
*/
if (cmd->sc_data_direction == DMA_BIDIRECTIONAL) {
- fw_error("Can't handle DMA_BIDIRECTIONAL, rejecting command\n");
+ dev_err(lu_dev(lu), "cannot handle bidirectional command\n");
cmd->result = DID_ERROR << 16;
cmd->scsi_done(cmd);
return 0;
@@ -1450,7 +1481,7 @@ static int sbp2_scsi_queuecommand(struct Scsi_Host *shost,
orb = kzalloc(sizeof(*orb), GFP_ATOMIC);
if (orb == NULL) {
- fw_notify("failed to alloc orb\n");
+ dev_notice(lu_dev(lu), "failed to alloc ORB\n");
return SCSI_MLQUEUE_HOST_BUSY;
}
@@ -1550,7 +1581,7 @@ static int sbp2_scsi_abort(struct scsi_cmnd *cmd)
{
struct sbp2_logical_unit *lu = cmd->device->hostdata;
- fw_notify("%s: sbp2_scsi_abort\n", lu->tgt->bus_id);
+ dev_notice(lu_dev(lu), "sbp2_scsi_abort\n");
sbp2_agent_reset(lu);
sbp2_cancel_orbs(lu);
@@ -1590,7 +1621,7 @@ static struct device_attribute *sbp2_scsi_sysfs_attrs[] = {
static struct scsi_host_template scsi_driver_template = {
.module = THIS_MODULE,
.name = "SBP-2 IEEE-1394",
- .proc_name = sbp2_driver_name,
+ .proc_name = "sbp2",
.queuecommand = sbp2_scsi_queuecommand,
.slave_alloc = sbp2_scsi_slave_alloc,
.slave_configure = sbp2_scsi_slave_configure,
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 0409cf35adda..edadbdad31d0 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 9a8fb54ae462..007f54bd0081 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 df0d59570a84..3d000169285d 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 1c0fc3756cb1..776b772523e5 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;
}
@@ -378,13 +370,6 @@ static int __devinit ep93xx_gpio_probe(struct platform_device *pdev)
}
ep93xx_gpio->mmio_base = mmio;
- /* Default all ports to GPIO */
- ep93xx_devcfg_set_bits(EP93XX_SYSCON_DEVCFG_KEYS |
- EP93XX_SYSCON_DEVCFG_GONK |
- EP93XX_SYSCON_DEVCFG_EONIDE |
- EP93XX_SYSCON_DEVCFG_GONIDE |
- EP93XX_SYSCON_DEVCFG_HONIDE);
-
for (i = 0; i < ARRAY_SIZE(ep93xx_gpio_banks); i++) {
struct bgpio_chip *bgc = &ep93xx_gpio->bgc[i];
struct ep93xx_gpio_bank *bank = &ep93xx_gpio_banks[i];
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index ddfacc5ce56d..61c2d08d37b6 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 000000000000..2738cc44d636
--- /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 0b0562979171..1adc2ec1e383 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -19,8 +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>
@@ -28,19 +32,36 @@
#include <asm/gpio.h>
#include <asm/mach/irq.h>
+#define OFF_MODE 1
+
+static LIST_HEAD(omap_gpio_list);
+
+struct gpio_regs {
+ u32 irqenable1;
+ u32 irqenable2;
+ u32 wake_en;
+ u32 ctrl;
+ u32 oe;
+ u32 leveldetect0;
+ u32 leveldetect1;
+ u32 risingdetect;
+ u32 fallingdetect;
+ u32 dataout;
+ u32 debounce;
+ u32 debounce_en;
+};
+
struct gpio_bank {
- unsigned long pbase;
+ struct list_head node;
void __iomem *base;
u16 irq;
- u16 virtual_irq_start;
- int method;
+ int irq_base;
+ struct irq_domain *domain;
u32 suspend_wakeup;
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS)
u32 saved_wakeup;
-#endif
u32 non_wakeup_gpios;
u32 enabled_non_wakeup_gpios;
-
+ struct gpio_regs context;
u32 saved_datain;
u32 saved_fallingdetect;
u32 saved_risingdetect;
@@ -51,44 +72,31 @@ struct gpio_bank {
struct clk *dbck;
u32 mod_usage;
u32 dbck_enable_mask;
+ bool dbck_enabled;
struct device *dev;
+ bool is_mpuio;
bool dbck_flag;
+ bool loses_context;
int stride;
u32 width;
+ int context_loss_count;
+ int power_mode;
+ bool workaround_enabled;
void (*set_dataout)(struct gpio_bank *bank, int gpio, int enable);
+ int (*get_context_loss_count)(struct device *dev);
struct omap_gpio_reg_offs *regs;
};
-#ifdef CONFIG_ARCH_OMAP3
-struct omap3_gpio_regs {
- u32 irqenable1;
- u32 irqenable2;
- u32 wake_en;
- u32 ctrl;
- u32 oe;
- u32 leveldetect0;
- u32 leveldetect1;
- u32 risingdetect;
- u32 fallingdetect;
- u32 dataout;
-};
-
-static struct omap3_gpio_regs gpio_context[OMAP34XX_NR_GPIOS];
-#endif
-
-/*
- * TODO: Cleanup gpio_bank usage as it is having information
- * related to all instances of the device
- */
-static struct gpio_bank *gpio_bank;
-
-/* TODO: Analyze removing gpio_bank_count usage from driver code */
-int gpio_bank_count;
-
#define GPIO_INDEX(bank, gpio) (gpio % bank->width)
#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)
{
@@ -102,6 +110,7 @@ static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
else
l &= ~(1 << gpio);
__raw_writel(l, reg);
+ bank->context.oe = l;
}
@@ -111,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);
}
@@ -132,27 +144,28 @@ static void _set_gpio_dataout_mask(struct gpio_bank *bank, int gpio, int enable)
else
l &= ~gpio_bit;
__raw_writel(l, reg);
+ 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;
@@ -160,6 +173,22 @@ static inline void _gpio_rmw(void __iomem *base, u32 reg, u32 mask, bool set)
__raw_writel(l, base + reg);
}
+static inline void _gpio_dbck_enable(struct gpio_bank *bank)
+{
+ if (bank->dbck_enable_mask && !bank->dbck_enabled) {
+ clk_enable(bank->dbck);
+ bank->dbck_enabled = true;
+ }
+}
+
+static inline void _gpio_dbck_disable(struct gpio_bank *bank)
+{
+ if (bank->dbck_enable_mask && bank->dbck_enabled) {
+ clk_disable(bank->dbck);
+ bank->dbck_enabled = false;
+ }
+}
+
/**
* _set_gpio_debounce - low level gpio debounce time
* @bank: the gpio bank we're acting upon
@@ -188,70 +217,74 @@ static void _set_gpio_debounce(struct gpio_bank *bank, unsigned gpio,
l = GPIO_BIT(bank, gpio);
+ clk_enable(bank->dbck);
reg = bank->base + bank->regs->debounce;
__raw_writel(debounce, reg);
reg = bank->base + bank->regs->debounce_en;
val = __raw_readl(reg);
- if (debounce) {
+ if (debounce)
val |= l;
- clk_enable(bank->dbck);
- } else {
+ else
val &= ~l;
- clk_disable(bank->dbck);
- }
bank->dbck_enable_mask = val;
__raw_writel(val, reg);
+ clk_disable(bank->dbck);
+ /*
+ * Enable debounce clock per module.
+ * This call is mandatory because in omap_gpio_request() when
+ * *_runtime_get_sync() is called, _gpio_dbck_enable() within
+ * runtime callbck fails to turn on dbck because dbck_enable_mask
+ * used within _gpio_dbck_enable() is still not initialized at
+ * that point. Therefore we have to enable dbck here.
+ */
+ _gpio_dbck_enable(bank);
+ if (bank->dbck_enable_mask) {
+ bank->context.debounce = debounce;
+ bank->context.debounce_en = val;
+ }
}
-#ifdef CONFIG_ARCH_OMAP2PLUS
-static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
- int trigger)
+static inline void set_gpio_trigger(struct gpio_bank *bank, int gpio,
+ unsigned trigger)
{
void __iomem *base = bank->base;
u32 gpio_bit = 1 << gpio;
- if (cpu_is_omap44xx()) {
- _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT0, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_LOW);
- _gpio_rmw(base, OMAP4_GPIO_LEVELDETECT1, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_HIGH);
- _gpio_rmw(base, OMAP4_GPIO_RISINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_RISING);
- _gpio_rmw(base, OMAP4_GPIO_FALLINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_FALLING);
- } else {
- _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT0, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_LOW);
- _gpio_rmw(base, OMAP24XX_GPIO_LEVELDETECT1, gpio_bit,
- trigger & IRQ_TYPE_LEVEL_HIGH);
- _gpio_rmw(base, OMAP24XX_GPIO_RISINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_RISING);
- _gpio_rmw(base, OMAP24XX_GPIO_FALLINGDETECT, gpio_bit,
- trigger & IRQ_TYPE_EDGE_FALLING);
- }
+ _gpio_rmw(base, bank->regs->leveldetect0, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_LOW);
+ _gpio_rmw(base, bank->regs->leveldetect1, gpio_bit,
+ trigger & IRQ_TYPE_LEVEL_HIGH);
+ _gpio_rmw(base, bank->regs->risingdetect, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_RISING);
+ _gpio_rmw(base, bank->regs->fallingdetect, gpio_bit,
+ trigger & IRQ_TYPE_EDGE_FALLING);
+
+ bank->context.leveldetect0 =
+ __raw_readl(bank->base + bank->regs->leveldetect0);
+ bank->context.leveldetect1 =
+ __raw_readl(bank->base + bank->regs->leveldetect1);
+ bank->context.risingdetect =
+ __raw_readl(bank->base + bank->regs->risingdetect);
+ bank->context.fallingdetect =
+ __raw_readl(bank->base + bank->regs->fallingdetect);
+
if (likely(!(bank->non_wakeup_gpios & gpio_bit))) {
- if (cpu_is_omap44xx()) {
- _gpio_rmw(base, OMAP4_GPIO_IRQWAKEN0, gpio_bit,
- trigger != 0);
- } else {
- /*
- * GPIO wakeup request can only be generated on edge
- * transitions
- */
- if (trigger & IRQ_TYPE_EDGE_BOTH)
- __raw_writel(1 << gpio, bank->base
- + OMAP24XX_GPIO_SETWKUENA);
- else
- __raw_writel(1 << gpio, bank->base
- + OMAP24XX_GPIO_CLEARWKUENA);
- }
+ _gpio_rmw(base, bank->regs->wkup_en, gpio_bit, trigger != 0);
+ bank->context.wake_en =
+ __raw_readl(bank->base + bank->regs->wkup_en);
}
+
/* This part needs to be executed always for OMAP{34xx, 44xx} */
- if (cpu_is_omap34xx() || cpu_is_omap44xx() ||
- (bank->non_wakeup_gpios & gpio_bit)) {
+ if (!bank->regs->irqctrl) {
+ /* On omap24xx proceed only when valid GPIO bit is set */
+ if (bank->non_wakeup_gpios) {
+ if (!(bank->non_wakeup_gpios & gpio_bit))
+ goto exit;
+ }
+
/*
* Log the edge gpio and manually trigger the IRQ
* after resume if the input level changes
@@ -264,17 +297,11 @@ static inline void set_24xx_gpio_triggering(struct gpio_bank *bank, int gpio,
bank->enabled_non_wakeup_gpios &= ~gpio_bit;
}
- if (cpu_is_omap44xx()) {
- bank->level_mask =
- __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT0) |
- __raw_readl(bank->base + OMAP4_GPIO_LEVELDETECT1);
- } else {
- bank->level_mask =
- __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0) |
- __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1);
- }
+exit:
+ bank->level_mask =
+ __raw_readl(bank->base + bank->regs->leveldetect0) |
+ __raw_readl(bank->base + bank->regs->leveldetect1);
}
-#endif
#ifdef CONFIG_ARCH_OMAP1
/*
@@ -286,23 +313,10 @@ static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
void __iomem *reg = bank->base;
u32 l = 0;
- switch (bank->method) {
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride;
- break;
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_INT_CONTROL;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_INT_CONTROL;
- break;
-#endif
- default:
+ if (!bank->regs->irqctrl)
return;
- }
+
+ reg += bank->regs->irqctrl;
l = __raw_readl(reg);
if ((l >> gpio) & 1)
@@ -312,31 +326,22 @@ static void _toggle_gpio_edge_triggering(struct gpio_bank *bank, int gpio)
__raw_writel(l, reg);
}
+#else
+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;
u32 l = 0;
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP1
- case METHOD_MPUIO:
- reg += OMAP_MPUIO_GPIO_INT_EDGE / bank->stride;
- l = __raw_readl(reg);
- if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
- bank->toggle_mask |= 1 << gpio;
- if (trigger & IRQ_TYPE_EDGE_RISING)
- l |= 1 << gpio;
- else if (trigger & IRQ_TYPE_EDGE_FALLING)
- l &= ~(1 << gpio);
- else
- goto bad;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP15XX
- case METHOD_GPIO_1510:
- reg += OMAP1510_GPIO_INT_CONTROL;
+ if (bank->regs->leveldetect0 && bank->regs->wkup_en) {
+ set_gpio_trigger(bank, gpio, trigger);
+ } else if (bank->regs->irqctrl) {
+ reg += bank->regs->irqctrl;
+
l = __raw_readl(reg);
if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
bank->toggle_mask |= 1 << gpio;
@@ -345,15 +350,15 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
else if (trigger & IRQ_TYPE_EDGE_FALLING)
l &= ~(1 << gpio);
else
- goto bad;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
+ return -EINVAL;
+
+ __raw_writel(l, reg);
+ } else if (bank->regs->edgectrl1) {
if (gpio & 0x08)
- reg += OMAP1610_GPIO_EDGE_CTRL2;
+ reg += bank->regs->edgectrl2;
else
- reg += OMAP1610_GPIO_EDGE_CTRL1;
+ reg += bank->regs->edgectrl1;
+
gpio &= 0x07;
l = __raw_readl(reg);
l &= ~(3 << (gpio << 1));
@@ -361,45 +366,19 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, int trigger)
l |= 2 << (gpio << 1);
if (trigger & IRQ_TYPE_EDGE_FALLING)
l |= 1 << (gpio << 1);
- if (trigger)
- /* Enable wake-up during idle for dynamic tick */
- __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_SET_WAKEUPENA);
- else
- __raw_writel(1 << gpio, bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA);
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
- case METHOD_GPIO_7XX:
- reg += OMAP7XX_GPIO_INT_CONTROL;
- l = __raw_readl(reg);
- if ((trigger & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH)
- bank->toggle_mask |= 1 << gpio;
- if (trigger & IRQ_TYPE_EDGE_RISING)
- l |= 1 << gpio;
- else if (trigger & IRQ_TYPE_EDGE_FALLING)
- l &= ~(1 << gpio);
- else
- goto bad;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP2PLUS
- case METHOD_GPIO_24XX:
- case METHOD_GPIO_44XX:
- set_24xx_gpio_triggering(bank, gpio, trigger);
- return 0;
-#endif
- default:
- goto bad;
+
+ /* Enable wake-up during idle for dynamic tick */
+ _gpio_rmw(base, bank->regs->wkup_en, 1 << gpio, trigger);
+ bank->context.wake_en =
+ __raw_readl(bank->base + bank->regs->wkup_en);
+ __raw_writel(l, reg);
}
- __raw_writel(l, reg);
return 0;
-bad:
- return -EINVAL;
}
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;
@@ -407,17 +386,15 @@ 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;
- /* OMAP1 allows only only edge triggering */
- if (!cpu_class_is_omap2()
- && (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
+ if (!bank->regs->leveldetect0 &&
+ (type & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)))
return -EINVAL;
- bank = irq_data_get_irq_chip_data(d);
spin_lock_irqsave(&bank->lock, flags);
retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type);
spin_unlock_irqrestore(&bank->lock, flags);
@@ -474,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);
@@ -481,6 +459,7 @@ 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);
@@ -494,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);
@@ -501,6 +481,7 @@ 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);
@@ -508,7 +489,10 @@ static void _disable_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
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));
}
/*
@@ -525,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;
}
@@ -536,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;
@@ -552,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)
@@ -567,38 +548,39 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
unsigned long flags;
- spin_lock_irqsave(&bank->lock, flags);
+ /*
+ * If this is the first gpio_request for the bank,
+ * enable the bank module.
+ */
+ if (!bank->mod_usage)
+ pm_runtime_get_sync(bank->dev);
+ spin_lock_irqsave(&bank->lock, flags);
/* Set trigger to none. You need to enable the desired trigger with
* request_irq() or set_irq_type().
*/
_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
-#ifdef CONFIG_ARCH_OMAP15XX
- if (bank->method == METHOD_GPIO_1510) {
- void __iomem *reg;
+ if (bank->regs->pinctrl) {
+ void __iomem *reg = bank->base + bank->regs->pinctrl;
/* Claim the pin for MPU */
- reg = bank->base + OMAP1510_GPIO_PIN_CONTROL;
__raw_writel(__raw_readl(reg) | (1 << offset), reg);
}
-#endif
- if (!cpu_class_is_omap1()) {
- if (!bank->mod_usage) {
- void __iomem *reg = bank->base;
- u32 ctrl;
-
- if (cpu_is_omap24xx() || cpu_is_omap34xx())
- reg += OMAP24XX_GPIO_CTRL;
- else if (cpu_is_omap44xx())
- reg += OMAP4_GPIO_CTRL;
- ctrl = __raw_readl(reg);
- /* Module is enabled, clocks are not gated */
- ctrl &= 0xFFFFFFFE;
- __raw_writel(ctrl, reg);
- }
- bank->mod_usage |= 1 << offset;
+
+ if (bank->regs->ctrl && !bank->mod_usage) {
+ void __iomem *reg = bank->base + bank->regs->ctrl;
+ u32 ctrl;
+
+ ctrl = __raw_readl(reg);
+ /* Module is enabled, clocks are not gated */
+ ctrl &= ~GPIO_MOD_CTRL_BIT;
+ __raw_writel(ctrl, reg);
+ bank->context.ctrl = ctrl;
}
+
+ bank->mod_usage |= 1 << offset;
+
spin_unlock_irqrestore(&bank->lock, flags);
return 0;
@@ -607,48 +589,40 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
+ void __iomem *base = bank->base;
unsigned long flags;
spin_lock_irqsave(&bank->lock, flags);
-#ifdef CONFIG_ARCH_OMAP16XX
- if (bank->method == METHOD_GPIO_1610) {
- /* Disable wake-up during idle for dynamic tick */
- void __iomem *reg = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA;
- __raw_writel(1 << offset, reg);
- }
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- if (bank->method == METHOD_GPIO_24XX) {
- /* Disable wake-up during idle for dynamic tick */
- void __iomem *reg = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
- __raw_writel(1 << offset, reg);
- }
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- if (bank->method == METHOD_GPIO_44XX) {
+
+ if (bank->regs->wkup_en) {
/* Disable wake-up during idle for dynamic tick */
- void __iomem *reg = bank->base + OMAP4_GPIO_IRQWAKEN0;
- __raw_writel(1 << offset, reg);
+ _gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
+ bank->context.wake_en =
+ __raw_readl(bank->base + bank->regs->wkup_en);
}
-#endif
- if (!cpu_class_is_omap1()) {
- bank->mod_usage &= ~(1 << offset);
- if (!bank->mod_usage) {
- void __iomem *reg = bank->base;
- u32 ctrl;
-
- if (cpu_is_omap24xx() || cpu_is_omap34xx())
- reg += OMAP24XX_GPIO_CTRL;
- else if (cpu_is_omap44xx())
- reg += OMAP4_GPIO_CTRL;
- ctrl = __raw_readl(reg);
- /* Module is disabled, clocks are gated */
- ctrl |= 1;
- __raw_writel(ctrl, reg);
- }
+
+ bank->mod_usage &= ~(1 << offset);
+
+ if (bank->regs->ctrl && !bank->mod_usage) {
+ void __iomem *reg = bank->base + bank->regs->ctrl;
+ u32 ctrl;
+
+ ctrl = __raw_readl(reg);
+ /* Module is disabled, clocks are gated */
+ ctrl |= GPIO_MOD_CTRL_BIT;
+ __raw_writel(ctrl, reg);
+ bank->context.ctrl = ctrl;
}
+
_reset_gpio(bank, bank->chip.base + offset);
spin_unlock_irqrestore(&bank->lock, flags);
+
+ /*
+ * If this is the last gpio to be freed in the bank,
+ * disable the bank module.
+ */
+ if (!bank->mod_usage)
+ pm_runtime_put(bank->dev);
}
/*
@@ -674,6 +648,7 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
bank = irq_get_handler_data(irq);
isr_reg = bank->base + bank->regs->irqstatus;
+ pm_runtime_get_sync(bank->dev);
if (WARN_ON(!isr_reg))
goto exit;
@@ -685,12 +660,8 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
enabled = _get_gpio_irqbank_mask(bank);
isr_saved = isr = __raw_readl(isr_reg) & enabled;
- if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO))
- isr &= 0x0000ffff;
-
- if (cpu_class_is_omap2()) {
+ if (bank->level_mask)
level_mask = bank->level_mask & enabled;
- }
/* clear edge sensitive interrupts before handler(s) are
called so that we don't miss any interrupt occurred while
@@ -711,14 +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;
-#ifdef CONFIG_ARCH_OMAP1
+ 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
@@ -728,7 +700,6 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
*/
if (bank->toggle_mask & (1 << gpio_index))
_toggle_gpio_edge_triggering(bank, gpio_index);
-#endif
generic_handle_irq(gpio_irq);
}
@@ -740,12 +711,13 @@ static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
exit:
if (!unmasked)
chained_irq_exit(chip, desc);
+ pm_runtime_put(bank->dev);
}
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);
@@ -755,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);
@@ -775,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;
@@ -808,14 +780,6 @@ static struct irq_chip gpio_irq_chip = {
/*---------------------------------------------------------------------*/
-#ifdef CONFIG_ARCH_OMAP1
-
-#define bank_is_mpuio(bank) ((bank)->method == METHOD_MPUIO)
-
-#ifdef CONFIG_ARCH_OMAP16XX
-
-#include <linux/platform_device.h>
-
static int omap_mpuio_suspend_noirq(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -869,32 +833,16 @@ static struct platform_device omap_mpuio_device = {
/* could list the /proc/iomem resources */
};
-static inline void mpuio_init(void)
+static inline void mpuio_init(struct gpio_bank *bank)
{
- struct gpio_bank *bank = &gpio_bank[0];
platform_set_drvdata(&omap_mpuio_device, bank);
if (platform_driver_register(&omap_mpuio_driver) == 0)
(void) platform_device_register(&omap_mpuio_device);
}
-#else
-static inline void mpuio_init(void) {}
-#endif /* 16xx */
-
-#else
-
-#define bank_is_mpuio(bank) 0
-static inline void mpuio_init(void) {}
-
-#endif
-
/*---------------------------------------------------------------------*/
-/* REVISIT these are stupid implementations! replace by ones that
- * don't switch on METHOD_* and which mostly avoid spinlocks
- */
-
static int gpio_input(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;
@@ -917,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)
@@ -982,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;
}
/*---------------------------------------------------------------------*/
@@ -1007,81 +951,35 @@ static void __init omap_gpio_show_rev(struct gpio_bank *bank)
*/
static struct lock_class_key gpio_lock_class;
-static inline int init_gpio_info(struct platform_device *pdev)
+static void omap_gpio_mod_init(struct gpio_bank *bank)
{
- /* TODO: Analyze removing gpio_bank_count usage from driver code */
- gpio_bank = kzalloc(gpio_bank_count * sizeof(struct gpio_bank),
- GFP_KERNEL);
- if (!gpio_bank) {
- dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n");
- return -ENOMEM;
- }
- return 0;
-}
+ void __iomem *base = bank->base;
+ u32 l = 0xffffffff;
-/* TODO: Cleanup cpu_is_* checks */
-static void omap_gpio_mod_init(struct gpio_bank *bank, int id)
-{
- if (cpu_class_is_omap2()) {
- if (cpu_is_omap44xx()) {
- __raw_writel(0xffffffff, bank->base +
- OMAP4_GPIO_IRQSTATUSCLR0);
- __raw_writel(0x00000000, bank->base +
- OMAP4_GPIO_DEBOUNCENABLE);
- /* Initialize interface clk ungated, module enabled */
- __raw_writel(0, bank->base + OMAP4_GPIO_CTRL);
- } else if (cpu_is_omap34xx()) {
- __raw_writel(0x00000000, bank->base +
- OMAP24XX_GPIO_IRQENABLE1);
- __raw_writel(0xffffffff, bank->base +
- OMAP24XX_GPIO_IRQSTATUS1);
- __raw_writel(0x00000000, bank->base +
- OMAP24XX_GPIO_DEBOUNCE_EN);
-
- /* Initialize interface clk ungated, module enabled */
- __raw_writel(0, bank->base + OMAP24XX_GPIO_CTRL);
- } else if (cpu_is_omap24xx()) {
- static const u32 non_wakeup_gpios[] = {
- 0xe203ffc0, 0x08700040
- };
- if (id < ARRAY_SIZE(non_wakeup_gpios))
- bank->non_wakeup_gpios = non_wakeup_gpios[id];
- }
- } else if (cpu_class_is_omap1()) {
- if (bank_is_mpuio(bank))
- __raw_writew(0xffff, bank->base +
- OMAP_MPUIO_GPIO_MASKIT / bank->stride);
- if (cpu_is_omap15xx() && bank->method == METHOD_GPIO_1510) {
- __raw_writew(0xffff, bank->base
- + OMAP1510_GPIO_INT_MASK);
- __raw_writew(0x0000, bank->base
- + OMAP1510_GPIO_INT_STATUS);
- }
- if (cpu_is_omap16xx() && bank->method == METHOD_GPIO_1610) {
- __raw_writew(0x0000, bank->base
- + OMAP1610_GPIO_IRQENABLE1);
- __raw_writew(0xffff, bank->base
- + OMAP1610_GPIO_IRQSTATUS1);
- __raw_writew(0x0014, bank->base
- + OMAP1610_GPIO_SYSCONFIG);
+ if (bank->width == 16)
+ l = 0xffff;
- /*
- * Enable system clock for GPIO module.
- * The CAM_CLK_CTRL *is* really the right place.
- */
- omap_writel(omap_readl(ULPD_CAM_CLK_CTRL) | 0x04,
- ULPD_CAM_CLK_CTRL);
- }
- if (cpu_is_omap7xx() && bank->method == METHOD_GPIO_7XX) {
- __raw_writel(0xffffffff, bank->base
- + OMAP7XX_GPIO_INT_MASK);
- __raw_writel(0x00000000, bank->base
- + OMAP7XX_GPIO_INT_STATUS);
- }
+ if (bank->is_mpuio) {
+ __raw_writel(l, bank->base + bank->regs->irqenable);
+ return;
}
+
+ _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->irqenable_inv);
+ _gpio_rmw(base, bank->regs->irqstatus, l,
+ bank->regs->irqenable_inv == false);
+ _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->debounce_en != 0);
+ _gpio_rmw(base, bank->regs->irqenable, l, bank->regs->ctrl != 0);
+ if (bank->regs->debounce_en)
+ _gpio_rmw(base, bank->regs->debounce_en, 0, 1);
+
+ /* Save OE default value (0xffffffff) in the context */
+ bank->context.oe = __raw_readl(bank->base + bank->regs->direction);
+ /* Initialize interface clk ungated, module enabled */
+ if (bank->regs->ctrl)
+ _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)
{
@@ -1101,8 +999,8 @@ omap_mpuio_alloc_gc(struct gpio_bank *bank, unsigned int irq_start,
ct->chip.irq_mask = irq_gc_mask_set_bit;
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
ct->chip.irq_set_type = gpio_irq_type;
- /* REVISIT: assuming only 16xx supports MPUIO wake events */
- if (cpu_is_omap16xx())
+
+ if (bank->regs->wkup_en)
ct->chip.irq_set_wake = gpio_wake_enable,
ct->regs.mask = OMAP_MPUIO_GPIO_INT / bank->stride;
@@ -1115,7 +1013,6 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
int j;
static int gpio;
- bank->mod_usage = 0;
/*
* REVISIT eventually switch from OMAP-specific gpio structs
* over to the generic ones
@@ -1128,11 +1025,10 @@ static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
bank->chip.set_debounce = gpio_debounce;
bank->chip.set = gpio_set;
bank->chip.to_irq = gpio_2irq;
- if (bank_is_mpuio(bank)) {
+ if (bank->is_mpuio) {
bank->chip.label = "mpuio";
-#ifdef CONFIG_ARCH_OMAP16XX
- bank->chip.dev = &omap_mpuio_device.dev;
-#endif
+ if (bank->regs->wkup_en)
+ bank->chip.dev = &omap_mpuio_device.dev;
bank->chip.base = OMAP_MPUIO(0);
} else {
bank->chip.label = "gpio";
@@ -1143,11 +1039,10 @@ 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(bank)) {
+ if (bank->is_mpuio) {
omap_mpuio_alloc_gc(bank, j, bank->width);
} else {
irq_set_chip(j, &gpio_irq_chip);
@@ -1159,45 +1054,58 @@ 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)
{
- static int gpio_init_done;
+ 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;
- int id;
struct gpio_bank *bank;
+ int ret = 0;
- if (!pdev->dev.platform_data)
- return -EINVAL;
-
- pdata = pdev->dev.platform_data;
+ match = of_match_device(of_match_ptr(omap_gpio_match), dev);
- if (!gpio_init_done) {
- int ret;
+ pdata = match ? match->data : dev->platform_data;
+ if (!pdata)
+ return -EINVAL;
- ret = init_gpio_info(pdev);
- if (ret)
- return ret;
+ bank = devm_kzalloc(&pdev->dev, sizeof(struct gpio_bank), GFP_KERNEL);
+ if (!bank) {
+ dev_err(dev, "Memory alloc failed\n");
+ return -ENOMEM;
}
- id = pdev->id;
- bank = &gpio_bank[id];
-
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (unlikely(!res)) {
- dev_err(&pdev->dev, "GPIO Bank %i Invalid IRQ resource\n", id);
+ dev_err(dev, "Invalid IRQ resource\n");
return -ENODEV;
}
bank->irq = res->start;
- bank->virtual_irq_start = pdata->virtual_irq_start;
- bank->method = pdata->bank_type;
- bank->dev = &pdev->dev;
+ bank->dev = dev;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
bank->width = pdata->bank_width;
-
+ bank->is_mpuio = pdata->is_mpuio;
+ bank->non_wakeup_gpios = pdata->non_wakeup_gpios;
+ 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;
@@ -1209,369 +1117,422 @@ 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", id);
+ 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", id);
+ dev_err(dev, "Could not ioremap\n");
return -ENOMEM;
}
+ platform_set_drvdata(pdev, bank);
+
pm_runtime_enable(bank->dev);
+ pm_runtime_irq_safe(bank->dev);
pm_runtime_get_sync(bank->dev);
- omap_gpio_mod_init(bank, id);
+ if (bank->is_mpuio)
+ mpuio_init(bank);
+
+ omap_gpio_mod_init(bank);
omap_gpio_chip_init(bank);
omap_gpio_show_rev(bank);
- if (!gpio_init_done)
- gpio_init_done = 1;
+ pm_runtime_put(bank->dev);
- return 0;
+ list_add_tail(&bank->node, &omap_gpio_list);
+
+ return ret;
}
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS)
-static int omap_gpio_suspend(void)
+#ifdef CONFIG_ARCH_OMAP2PLUS
+
+#if defined(CONFIG_PM_SLEEP)
+static int omap_gpio_suspend(struct device *dev)
{
- int i;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ void __iomem *base = bank->base;
+ void __iomem *wakeup_enable;
+ unsigned long flags;
- if (!cpu_class_is_omap2() && !cpu_is_omap16xx())
+ if (!bank->mod_usage || !bank->loses_context)
return 0;
- for (i = 0; i < gpio_bank_count; i++) {
- struct gpio_bank *bank = &gpio_bank[i];
- void __iomem *wake_status;
- void __iomem *wake_clear;
- void __iomem *wake_set;
- unsigned long flags;
-
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- wake_status = bank->base + OMAP1610_GPIO_WAKEUPENABLE;
- wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA;
- wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- wake_status = bank->base + OMAP24XX_GPIO_WAKE_EN;
- wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
- wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- case METHOD_GPIO_44XX:
- wake_status = bank->base + OMAP4_GPIO_IRQWAKEN0;
- wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0;
- wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0;
- break;
-#endif
- default:
- continue;
- }
+ if (!bank->regs->wkup_en || !bank->suspend_wakeup)
+ return 0;
- spin_lock_irqsave(&bank->lock, flags);
- bank->saved_wakeup = __raw_readl(wake_status);
- __raw_writel(0xffffffff, wake_clear);
- __raw_writel(bank->suspend_wakeup, wake_set);
- spin_unlock_irqrestore(&bank->lock, flags);
- }
+ wakeup_enable = bank->base + bank->regs->wkup_en;
+
+ spin_lock_irqsave(&bank->lock, flags);
+ bank->saved_wakeup = __raw_readl(wakeup_enable);
+ _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0);
+ _gpio_rmw(base, bank->regs->wkup_en, bank->suspend_wakeup, 1);
+ spin_unlock_irqrestore(&bank->lock, flags);
return 0;
}
-static void omap_gpio_resume(void)
+static int omap_gpio_resume(struct device *dev)
{
- int i;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ void __iomem *base = bank->base;
+ unsigned long flags;
- if (!cpu_class_is_omap2() && !cpu_is_omap16xx())
- return;
+ if (!bank->mod_usage || !bank->loses_context)
+ return 0;
- for (i = 0; i < gpio_bank_count; i++) {
- struct gpio_bank *bank = &gpio_bank[i];
- void __iomem *wake_clear;
- void __iomem *wake_set;
- unsigned long flags;
-
- switch (bank->method) {
-#ifdef CONFIG_ARCH_OMAP16XX
- case METHOD_GPIO_1610:
- wake_clear = bank->base + OMAP1610_GPIO_CLEAR_WAKEUPENA;
- wake_set = bank->base + OMAP1610_GPIO_SET_WAKEUPENA;
- break;
-#endif
-#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
- case METHOD_GPIO_24XX:
- wake_clear = bank->base + OMAP24XX_GPIO_CLEARWKUENA;
- wake_set = bank->base + OMAP24XX_GPIO_SETWKUENA;
- break;
-#endif
-#ifdef CONFIG_ARCH_OMAP4
- case METHOD_GPIO_44XX:
- wake_clear = bank->base + OMAP4_GPIO_IRQWAKEN0;
- wake_set = bank->base + OMAP4_GPIO_IRQWAKEN0;
- break;
-#endif
- default:
- continue;
- }
+ if (!bank->regs->wkup_en || !bank->saved_wakeup)
+ return 0;
- spin_lock_irqsave(&bank->lock, flags);
- __raw_writel(0xffffffff, wake_clear);
- __raw_writel(bank->saved_wakeup, wake_set);
- spin_unlock_irqrestore(&bank->lock, flags);
- }
+ spin_lock_irqsave(&bank->lock, flags);
+ _gpio_rmw(base, bank->regs->wkup_en, 0xffffffff, 0);
+ _gpio_rmw(base, bank->regs->wkup_en, bank->saved_wakeup, 1);
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
}
+#endif /* CONFIG_PM_SLEEP */
-static struct syscore_ops omap_gpio_syscore_ops = {
- .suspend = omap_gpio_suspend,
- .resume = omap_gpio_resume,
-};
+#if defined(CONFIG_PM_RUNTIME)
+static void omap_gpio_restore_context(struct gpio_bank *bank);
-#endif
+static int omap_gpio_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ u32 l1 = 0, l2 = 0;
+ unsigned long flags;
+ u32 wake_low, wake_hi;
-#ifdef CONFIG_ARCH_OMAP2PLUS
+ spin_lock_irqsave(&bank->lock, flags);
-static int workaround_enabled;
+ /*
+ * 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;
+ }
+ /*
+ * If going to OFF, remove triggering for all
+ * non-wakeup GPIOs. Otherwise spurious IRQs will be
+ * generated. See OMAP2420 Errata item 1.101.
+ */
+ bank->saved_datain = __raw_readl(bank->base +
+ bank->regs->datain);
+ l1 = __raw_readl(bank->base + bank->regs->fallingdetect);
+ l2 = __raw_readl(bank->base + bank->regs->risingdetect);
-void omap2_gpio_prepare_for_idle(int off_mode)
-{
- int i, c = 0;
- int min = 0;
+ bank->saved_fallingdetect = l1;
+ bank->saved_risingdetect = l2;
+ l1 &= ~bank->enabled_non_wakeup_gpios;
+ l2 &= ~bank->enabled_non_wakeup_gpios;
- if (cpu_is_omap34xx())
- min = 1;
+ __raw_writel(l1, bank->base + bank->regs->fallingdetect);
+ __raw_writel(l2, bank->base + bank->regs->risingdetect);
- for (i = min; i < gpio_bank_count; i++) {
- struct gpio_bank *bank = &gpio_bank[i];
- u32 l1 = 0, l2 = 0;
- int j;
+ bank->workaround_enabled = true;
- for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++)
- clk_disable(bank->dbck);
+update_gpio_context_count:
+ if (bank->get_context_loss_count)
+ bank->context_loss_count =
+ bank->get_context_loss_count(bank->dev);
- if (!off_mode)
- continue;
+ _gpio_dbck_disable(bank);
+ spin_unlock_irqrestore(&bank->lock, flags);
- /* If going to OFF, remove triggering for all
- * non-wakeup GPIOs. Otherwise spurious IRQs will be
- * generated. See OMAP2420 Errata item 1.101. */
- if (!(bank->enabled_non_wakeup_gpios))
- continue;
+ return 0;
+}
- if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
- bank->saved_datain = __raw_readl(bank->base +
- OMAP24XX_GPIO_DATAIN);
- l1 = __raw_readl(bank->base +
- OMAP24XX_GPIO_FALLINGDETECT);
- l2 = __raw_readl(bank->base +
- OMAP24XX_GPIO_RISINGDETECT);
- }
+static int omap_gpio_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_bank *bank = platform_get_drvdata(pdev);
+ int context_lost_cnt_after;
+ u32 l = 0, gen, gen0, gen1;
+ unsigned long flags;
- if (cpu_is_omap44xx()) {
- bank->saved_datain = __raw_readl(bank->base +
- OMAP4_GPIO_DATAIN);
- l1 = __raw_readl(bank->base +
- OMAP4_GPIO_FALLINGDETECT);
- l2 = __raw_readl(bank->base +
- OMAP4_GPIO_RISINGDETECT);
- }
+ spin_lock_irqsave(&bank->lock, flags);
+ _gpio_dbck_enable(bank);
- bank->saved_fallingdetect = l1;
- bank->saved_risingdetect = l2;
- l1 &= ~bank->enabled_non_wakeup_gpios;
- l2 &= ~bank->enabled_non_wakeup_gpios;
+ /*
+ * 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 (cpu_is_omap24xx() || cpu_is_omap34xx()) {
- __raw_writel(l1, bank->base +
- OMAP24XX_GPIO_FALLINGDETECT);
- __raw_writel(l2, bank->base +
- OMAP24XX_GPIO_RISINGDETECT);
- }
+ if (!bank->workaround_enabled) {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
+ }
- if (cpu_is_omap44xx()) {
- __raw_writel(l1, bank->base + OMAP4_GPIO_FALLINGDETECT);
- __raw_writel(l2, bank->base + OMAP4_GPIO_RISINGDETECT);
+ if (bank->get_context_loss_count) {
+ context_lost_cnt_after =
+ bank->get_context_loss_count(bank->dev);
+ if (context_lost_cnt_after != bank->context_loss_count ||
+ !context_lost_cnt_after) {
+ omap_gpio_restore_context(bank);
+ } else {
+ spin_unlock_irqrestore(&bank->lock, flags);
+ return 0;
}
-
- c++;
- }
- if (!c) {
- workaround_enabled = 0;
- return;
}
- workaround_enabled = 1;
-}
-void omap2_gpio_resume_after_idle(void)
-{
- int i;
- int min = 0;
+ __raw_writel(bank->saved_fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ __raw_writel(bank->saved_risingdetect,
+ bank->base + bank->regs->risingdetect);
+ l = __raw_readl(bank->base + bank->regs->datain);
- if (cpu_is_omap34xx())
- min = 1;
- for (i = min; i < gpio_bank_count; i++) {
- struct gpio_bank *bank = &gpio_bank[i];
- u32 l = 0, gen, gen0, gen1;
- int j;
+ /*
+ * Check if any of the non-wakeup interrupt GPIOs have changed
+ * state. If so, generate an IRQ by software. This is
+ * horribly racy, but it's the best we can do to work around
+ * this silicon bug.
+ */
+ l ^= bank->saved_datain;
+ l &= bank->enabled_non_wakeup_gpios;
- for (j = 0; j < hweight_long(bank->dbck_enable_mask); j++)
- clk_enable(bank->dbck);
+ /*
+ * No need to generate IRQs for the rising edge for gpio IRQs
+ * configured with falling edge only; and vice versa.
+ */
+ gen0 = l & bank->saved_fallingdetect;
+ gen0 &= bank->saved_datain;
- if (!workaround_enabled)
- continue;
+ gen1 = l & bank->saved_risingdetect;
+ gen1 &= ~(bank->saved_datain);
- if (!(bank->enabled_non_wakeup_gpios))
- continue;
+ /* FIXME: Consider GPIO IRQs with level detections properly! */
+ gen = l & (~(bank->saved_fallingdetect) & ~(bank->saved_risingdetect));
+ /* Consider all GPIO IRQs needed to be updated */
+ gen |= gen0 | gen1;
+
+ if (gen) {
+ u32 old0, old1;
+
+ old0 = __raw_readl(bank->base + bank->regs->leveldetect0);
+ old1 = __raw_readl(bank->base + bank->regs->leveldetect1);
if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
- __raw_writel(bank->saved_fallingdetect,
- bank->base + OMAP24XX_GPIO_FALLINGDETECT);
- __raw_writel(bank->saved_risingdetect,
- bank->base + OMAP24XX_GPIO_RISINGDETECT);
- l = __raw_readl(bank->base + OMAP24XX_GPIO_DATAIN);
+ __raw_writel(old0 | gen, bank->base +
+ bank->regs->leveldetect0);
+ __raw_writel(old1 | gen, bank->base +
+ bank->regs->leveldetect1);
}
if (cpu_is_omap44xx()) {
- __raw_writel(bank->saved_fallingdetect,
- bank->base + OMAP4_GPIO_FALLINGDETECT);
- __raw_writel(bank->saved_risingdetect,
- bank->base + OMAP4_GPIO_RISINGDETECT);
- l = __raw_readl(bank->base + OMAP4_GPIO_DATAIN);
- }
-
- /* Check if any of the non-wakeup interrupt GPIOs have changed
- * state. If so, generate an IRQ by software. This is
- * horribly racy, but it's the best we can do to work around
- * this silicon bug. */
- l ^= bank->saved_datain;
- l &= bank->enabled_non_wakeup_gpios;
-
- /*
- * No need to generate IRQs for the rising edge for gpio IRQs
- * configured with falling edge only; and vice versa.
- */
- gen0 = l & bank->saved_fallingdetect;
- gen0 &= bank->saved_datain;
-
- gen1 = l & bank->saved_risingdetect;
- gen1 &= ~(bank->saved_datain);
-
- /* FIXME: Consider GPIO IRQs with level detections properly! */
- gen = l & (~(bank->saved_fallingdetect) &
- ~(bank->saved_risingdetect));
- /* Consider all GPIO IRQs needed to be updated */
- gen |= gen0 | gen1;
-
- if (gen) {
- u32 old0, old1;
-
- if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
- old0 = __raw_readl(bank->base +
- OMAP24XX_GPIO_LEVELDETECT0);
- old1 = __raw_readl(bank->base +
- OMAP24XX_GPIO_LEVELDETECT1);
- __raw_writel(old0 | gen, bank->base +
- OMAP24XX_GPIO_LEVELDETECT0);
- __raw_writel(old1 | gen, bank->base +
- OMAP24XX_GPIO_LEVELDETECT1);
- __raw_writel(old0, bank->base +
- OMAP24XX_GPIO_LEVELDETECT0);
- __raw_writel(old1, bank->base +
- OMAP24XX_GPIO_LEVELDETECT1);
- }
-
- if (cpu_is_omap44xx()) {
- old0 = __raw_readl(bank->base +
- OMAP4_GPIO_LEVELDETECT0);
- old1 = __raw_readl(bank->base +
- OMAP4_GPIO_LEVELDETECT1);
- __raw_writel(old0 | l, bank->base +
- OMAP4_GPIO_LEVELDETECT0);
- __raw_writel(old1 | l, bank->base +
- OMAP4_GPIO_LEVELDETECT1);
- __raw_writel(old0, bank->base +
- OMAP4_GPIO_LEVELDETECT0);
- __raw_writel(old1, bank->base +
- OMAP4_GPIO_LEVELDETECT1);
- }
+ __raw_writel(old0 | l, bank->base +
+ bank->regs->leveldetect0);
+ __raw_writel(old1 | l, bank->base +
+ bank->regs->leveldetect1);
}
+ __raw_writel(old0, bank->base + bank->regs->leveldetect0);
+ __raw_writel(old1, bank->base + bank->regs->leveldetect1);
}
+ bank->workaround_enabled = false;
+ spin_unlock_irqrestore(&bank->lock, flags);
+
+ return 0;
}
+#endif /* CONFIG_PM_RUNTIME */
-#endif
+void omap2_gpio_prepare_for_idle(int pwr_mode)
+{
+ struct gpio_bank *bank;
+
+ list_for_each_entry(bank, &omap_gpio_list, node) {
+ if (!bank->mod_usage || !bank->loses_context)
+ continue;
-#ifdef CONFIG_ARCH_OMAP3
-/* save the registers of bank 2-6 */
-void omap_gpio_save_context(void)
+ bank->power_mode = pwr_mode;
+
+ pm_runtime_put_sync_suspend(bank->dev);
+ }
+}
+
+void omap2_gpio_resume_after_idle(void)
{
- int i;
-
- /* saving banks from 2-6 only since GPIO1 is in WKUP */
- for (i = 1; i < gpio_bank_count; i++) {
- struct gpio_bank *bank = &gpio_bank[i];
- gpio_context[i].irqenable1 =
- __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE1);
- gpio_context[i].irqenable2 =
- __raw_readl(bank->base + OMAP24XX_GPIO_IRQENABLE2);
- gpio_context[i].wake_en =
- __raw_readl(bank->base + OMAP24XX_GPIO_WAKE_EN);
- gpio_context[i].ctrl =
- __raw_readl(bank->base + OMAP24XX_GPIO_CTRL);
- gpio_context[i].oe =
- __raw_readl(bank->base + OMAP24XX_GPIO_OE);
- gpio_context[i].leveldetect0 =
- __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT0);
- gpio_context[i].leveldetect1 =
- __raw_readl(bank->base + OMAP24XX_GPIO_LEVELDETECT1);
- gpio_context[i].risingdetect =
- __raw_readl(bank->base + OMAP24XX_GPIO_RISINGDETECT);
- gpio_context[i].fallingdetect =
- __raw_readl(bank->base + OMAP24XX_GPIO_FALLINGDETECT);
- gpio_context[i].dataout =
- __raw_readl(bank->base + OMAP24XX_GPIO_DATAOUT);
+ struct gpio_bank *bank;
+
+ list_for_each_entry(bank, &omap_gpio_list, node) {
+ if (!bank->mod_usage || !bank->loses_context)
+ continue;
+
+ pm_runtime_get_sync(bank->dev);
}
}
-/* restore the required registers of bank 2-6 */
-void omap_gpio_restore_context(void)
+#if defined(CONFIG_PM_RUNTIME)
+static void omap_gpio_restore_context(struct gpio_bank *bank)
{
- int i;
-
- for (i = 1; i < gpio_bank_count; i++) {
- struct gpio_bank *bank = &gpio_bank[i];
- __raw_writel(gpio_context[i].irqenable1,
- bank->base + OMAP24XX_GPIO_IRQENABLE1);
- __raw_writel(gpio_context[i].irqenable2,
- bank->base + OMAP24XX_GPIO_IRQENABLE2);
- __raw_writel(gpio_context[i].wake_en,
- bank->base + OMAP24XX_GPIO_WAKE_EN);
- __raw_writel(gpio_context[i].ctrl,
- bank->base + OMAP24XX_GPIO_CTRL);
- __raw_writel(gpio_context[i].oe,
- bank->base + OMAP24XX_GPIO_OE);
- __raw_writel(gpio_context[i].leveldetect0,
- bank->base + OMAP24XX_GPIO_LEVELDETECT0);
- __raw_writel(gpio_context[i].leveldetect1,
- bank->base + OMAP24XX_GPIO_LEVELDETECT1);
- __raw_writel(gpio_context[i].risingdetect,
- bank->base + OMAP24XX_GPIO_RISINGDETECT);
- __raw_writel(gpio_context[i].fallingdetect,
- bank->base + OMAP24XX_GPIO_FALLINGDETECT);
- __raw_writel(gpio_context[i].dataout,
- bank->base + OMAP24XX_GPIO_DATAOUT);
+ __raw_writel(bank->context.wake_en,
+ bank->base + bank->regs->wkup_en);
+ __raw_writel(bank->context.ctrl, bank->base + bank->regs->ctrl);
+ __raw_writel(bank->context.leveldetect0,
+ bank->base + bank->regs->leveldetect0);
+ __raw_writel(bank->context.leveldetect1,
+ bank->base + bank->regs->leveldetect1);
+ __raw_writel(bank->context.risingdetect,
+ bank->base + bank->regs->risingdetect);
+ __raw_writel(bank->context.fallingdetect,
+ bank->base + bank->regs->fallingdetect);
+ if (bank->regs->set_dataout && bank->regs->clr_dataout)
+ __raw_writel(bank->context.dataout,
+ bank->base + bank->regs->set_dataout);
+ else
+ __raw_writel(bank->context.dataout,
+ bank->base + bank->regs->dataout);
+ __raw_writel(bank->context.oe, bank->base + bank->regs->direction);
+
+ if (bank->dbck_enable_mask) {
+ __raw_writel(bank->context.debounce, bank->base +
+ bank->regs->debounce);
+ __raw_writel(bank->context.debounce_en,
+ bank->base + bank->regs->debounce_en);
}
+
+ __raw_writel(bank->context.irqenable1,
+ bank->base + bank->regs->irqenable);
+ __raw_writel(bank->context.irqenable2,
+ bank->base + bank->regs->irqenable2);
}
+#endif /* CONFIG_PM_RUNTIME */
+#else
+#define omap_gpio_suspend NULL
+#define omap_gpio_resume NULL
+#define omap_gpio_runtime_suspend NULL
+#define omap_gpio_runtime_resume NULL
+#endif
+
+static const struct dev_pm_ops gpio_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(omap_gpio_suspend, omap_gpio_resume)
+ SET_RUNTIME_PM_OPS(omap_gpio_runtime_suspend, omap_gpio_runtime_resume,
+ 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),
},
};
@@ -1585,17 +1546,3 @@ static int __init omap_gpio_drv_reg(void)
return platform_driver_register(&omap_gpio_driver);
}
postcore_initcall(omap_gpio_drv_reg);
-
-static int __init omap_gpio_sysinit(void)
-{
- mpuio_init();
-
-#if defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP2PLUS)
- if (cpu_is_omap16xx() || cpu_class_is_omap2())
- register_syscore_ops(&omap_gpio_syscore_ops);
-#endif
-
- return 0;
-}
-
-arch_initcall(omap_gpio_sysinit);
diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c
index 77c9cc70fa77..b4b5da4fd2cc 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-sa1100.c b/drivers/gpio/gpio-sa1100.c
index 7eecf69362ee..8ea3b33d4b40 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 0a79a1167a25..46277877b7ec 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 000000000000..9ba15d31d242
--- /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 87a68a896abf..dce34727bbf8 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 bdc293791590..32de6707e3c4 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/irqdomain.h>
#include <asm/mach/irq.h>
@@ -74,9 +75,10 @@ struct tegra_gpio_bank {
#endif
};
-
+static struct irq_domain *irq_domain;
static void __iomem *regs;
-static struct tegra_gpio_bank tegra_gpio_banks[7];
+static u32 tegra_gpio_bank_count;
+static struct tegra_gpio_bank *tegra_gpio_banks;
static inline void tegra_gpio_writel(u32 val, u32 reg)
{
@@ -107,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)
{
@@ -139,7 +143,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
- return TEGRA_GPIO_TO_IRQ(offset);
+ return irq_find_mapping(irq_domain, offset);
}
static struct gpio_chip tegra_gpio_chip = {
@@ -155,28 +159,28 @@ static struct gpio_chip tegra_gpio_chip = {
static void tegra_gpio_irq_ack(struct irq_data *d)
{
- int gpio = d->irq - INT_GPIO_BASE;
+ int gpio = d->hwirq;
tegra_gpio_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
}
static void tegra_gpio_irq_mask(struct irq_data *d)
{
- int gpio = d->irq - INT_GPIO_BASE;
+ int gpio = d->hwirq;
tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 0);
}
static void tegra_gpio_irq_unmask(struct irq_data *d)
{
- int gpio = d->irq - INT_GPIO_BASE;
+ int gpio = d->hwirq;
tegra_gpio_mask_write(GPIO_MSK_INT_ENB(gpio), gpio, 1);
}
static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{
- int gpio = d->irq - INT_GPIO_BASE;
+ int gpio = d->hwirq;
struct tegra_gpio_bank *bank = irq_data_get_irq_chip_data(d);
int port = GPIO_PORT(gpio);
int lvl_type;
@@ -273,7 +277,7 @@ void tegra_gpio_resume(void)
local_irq_save(flags);
- for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+ for (b = 0; b < tegra_gpio_bank_count; b++) {
struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
@@ -296,7 +300,7 @@ void tegra_gpio_suspend(void)
int p;
local_irq_save(flags);
- for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+ for (b = 0; b < tegra_gpio_bank_count; b++) {
struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
@@ -337,13 +341,44 @@ static struct lock_class_key gpio_lock_class;
static int __devinit tegra_gpio_probe(struct platform_device *pdev)
{
+ int irq_base;
struct resource *res;
struct tegra_gpio_bank *bank;
int gpio;
int i;
int j;
- for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
+ for (;;) {
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, tegra_gpio_bank_count);
+ if (!res)
+ break;
+ tegra_gpio_bank_count++;
+ }
+ if (!tegra_gpio_bank_count) {
+ dev_err(&pdev->dev, "Missing IRQ resource\n");
+ return -ENODEV;
+ }
+
+ tegra_gpio_chip.ngpio = tegra_gpio_bank_count * 32;
+
+ tegra_gpio_banks = devm_kzalloc(&pdev->dev,
+ tegra_gpio_bank_count * sizeof(*tegra_gpio_banks),
+ GFP_KERNEL);
+ if (!tegra_gpio_banks) {
+ dev_err(&pdev->dev, "Couldn't allocate bank structure\n");
+ return -ENODEV;
+ }
+
+ irq_base = irq_alloc_descs(-1, 0, tegra_gpio_chip.ngpio, 0);
+ if (irq_base < 0) {
+ dev_err(&pdev->dev, "Couldn't allocate IRQ numbers\n");
+ return -ENODEV;
+ }
+ irq_domain = irq_domain_add_legacy(pdev->dev.of_node,
+ tegra_gpio_chip.ngpio, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
if (!res) {
dev_err(&pdev->dev, "Missing IRQ resource\n");
@@ -380,8 +415,8 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
gpiochip_add(&tegra_gpio_chip);
- for (gpio = 0; gpio < TEGRA_NR_GPIOS; gpio++) {
- int irq = TEGRA_GPIO_TO_IRQ(gpio);
+ for (gpio = 0; gpio < tegra_gpio_chip.ngpio; gpio++) {
+ int irq = irq_find_mapping(irq_domain, gpio);
/* No validity check; all Tegra GPIOs are valid IRQs */
bank = &tegra_gpio_banks[GPIO_BANK(gpio)];
@@ -393,7 +428,7 @@ static int __devinit tegra_gpio_probe(struct platform_device *pdev)
set_irq_flags(irq, IRQF_VALID);
}
- for (i = 0; i < ARRAY_SIZE(tegra_gpio_banks); i++) {
+ for (i = 0; i < tegra_gpio_bank_count; i++) {
bank = &tegra_gpio_banks[i];
irq_set_chained_handler(bank->irq, tegra_gpio_irq_handler);
@@ -426,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 91f45b965d1e..7eef648a3351 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 b8b4f228757c..94256fe7bf36 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 17fdf4b6af93..5a75510d66bb 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/Kconfig b/drivers/gpu/drm/Kconfig
index 2418429a9836..cc1148837e24 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -18,6 +18,11 @@ menuconfig DRM
details. You should also select and configure AGP
(/dev/agpgart) support if it is available for your platform.
+config DRM_USB
+ tristate
+ depends on DRM
+ select USB
+
config DRM_KMS_HELPER
tristate
depends on DRM
@@ -27,6 +32,18 @@ config DRM_KMS_HELPER
help
FB and CRTC helpers for KMS drivers.
+config DRM_LOAD_EDID_FIRMWARE
+ bool "Allow to specify an EDID data set instead of probing for it"
+ depends on DRM_KMS_HELPER
+ help
+ Say Y here, if you want to use EDID data to be loaded from the
+ /lib/firmware directory or one of the provided built-in
+ data sets. This may be necessary, if the graphics adapter or
+ monitor are unable to provide appropriate EDID data. Since this
+ feature is provided as a workaround for broken hardware, the
+ default case is N. Details and instructions how to build your own
+ EDID data are given in Documentation/EDID/HOWTO.txt.
+
config DRM_TTM
tristate
depends on DRM
@@ -71,6 +88,8 @@ config DRM_RADEON
source "drivers/gpu/drm/radeon/Kconfig"
+source "drivers/gpu/drm/nouveau/Kconfig"
+
config DRM_I810
tristate "Intel I810"
# !PREEMPT because of missing ioctl locking
@@ -165,3 +184,4 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"
+source "drivers/gpu/drm/udl/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0cde1b80fdb1..a858532806ae 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -12,17 +12,21 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \
drm_crtc.o drm_modes.o drm_edid.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
- drm_trace_points.o drm_global.o drm_usb.o
+ drm_trace_points.o drm_global.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
+drm-usb-y := drm_usb.o
+
drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
+drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
CFLAGS_drm_trace_points.o := -I$(src)
obj-$(CONFIG_DRM) += drm.o
+obj-$(CONFIG_DRM_USB) += drm_usb.o
obj-$(CONFIG_DRM_TTM) += ttm/
obj-$(CONFIG_DRM_TDFX) += tdfx/
obj-$(CONFIG_DRM_R128) += r128/
@@ -37,4 +41,5 @@ obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_GMA500) += gma500/
+obj-$(CONFIG_DRM_UDL) += udl/
obj-y += i2c/
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 5e818a808ace..d3aaeb6ae236 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -38,11 +38,6 @@
#include "drm_edid.h"
#include "drm_fourcc.h"
-struct drm_prop_enum_list {
- int type;
- char *name;
-};
-
/* Avoid boilerplate. I'm tired of typing. */
#define DRM_ENUM_NAME_FN(fnname, list) \
char *fnname(int val) \
@@ -298,9 +293,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
int ret;
ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
- if (ret) {
+ if (ret)
return ret;
- }
fb->dev = dev;
fb->funcs = funcs;
@@ -370,19 +364,31 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
* Caller must hold mode config lock.
*
* Inits a new object created as base part of an driver crtc object.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
*/
-void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs)
{
+ int ret;
+
crtc->dev = dev;
crtc->funcs = funcs;
mutex_lock(&dev->mode_config.mutex);
- drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+ ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+ if (ret)
+ goto out;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
dev->mode_config.num_crtc++;
+
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_crtc_init);
@@ -442,7 +448,7 @@ void drm_mode_remove(struct drm_connector *connector,
struct drm_display_mode *mode)
{
list_del(&mode->head);
- kfree(mode);
+ drm_mode_destroy(connector->dev, mode);
}
EXPORT_SYMBOL(drm_mode_remove);
@@ -454,21 +460,29 @@ EXPORT_SYMBOL(drm_mode_remove);
* @name: user visible name of the connector
*
* LOCKING:
- * Caller must hold @dev's mode_config lock.
+ * Takes mode config lock.
*
* Initialises a preallocated connector. Connectors should be
* subclassed as part of driver connector objects.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
*/
-void drm_connector_init(struct drm_device *dev,
- struct drm_connector *connector,
- const struct drm_connector_funcs *funcs,
- int connector_type)
+int drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
+ ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ if (ret)
+ goto out;
+
connector->dev = dev;
connector->funcs = funcs;
- drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
connector->connector_type = connector_type;
connector->connector_type_id =
++drm_connector_enum_list[connector_type].count; /* TODO */
@@ -488,7 +502,10 @@ void drm_connector_init(struct drm_device *dev,
drm_connector_attach_property(connector,
dev->mode_config.dpms_property, 0);
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_connector_init);
@@ -497,7 +514,7 @@ EXPORT_SYMBOL(drm_connector_init);
* @connector: connector to cleanup
*
* LOCKING:
- * Caller must hold @dev's mode_config lock.
+ * Takes mode config lock.
*
* Cleans up the connector but doesn't free the object.
*/
@@ -523,23 +540,41 @@ void drm_connector_cleanup(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_connector_cleanup);
-void drm_encoder_init(struct drm_device *dev,
+void drm_connector_unplug_all(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ /* taking the mode config mutex ends up in a clash with sysfs */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ drm_sysfs_connector_remove(connector);
+
+}
+EXPORT_SYMBOL(drm_connector_unplug_all);
+
+int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
- encoder->dev = dev;
+ ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ if (ret)
+ goto out;
- drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
dev->mode_config.num_encoder++;
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_encoder_init);
@@ -560,18 +595,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
const uint32_t *formats, uint32_t format_count,
bool priv)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
+ ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+ if (ret)
+ goto out;
+
plane->dev = dev;
- drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
plane->funcs = funcs;
plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
GFP_KERNEL);
if (!plane->format_types) {
DRM_DEBUG_KMS("out of memory when allocating plane\n");
drm_mode_object_put(dev, &plane->base);
- mutex_unlock(&dev->mode_config.mutex);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out;
}
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
@@ -589,9 +629,10 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
INIT_LIST_HEAD(&plane->head);
}
+ out:
mutex_unlock(&dev->mode_config.mutex);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(drm_plane_init);
@@ -631,7 +672,11 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
if (!nmode)
return NULL;
- drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+ if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
+ kfree(nmode);
+ return NULL;
+ }
+
return nmode;
}
EXPORT_SYMBOL(drm_mode_create);
@@ -648,6 +693,9 @@ EXPORT_SYMBOL(drm_mode_create);
*/
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
{
+ if (!mode)
+ return;
+
drm_mode_object_put(dev, &mode->base);
kfree(mode);
@@ -658,7 +706,6 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
{
struct drm_property *edid;
struct drm_property *dpms;
- int i;
/*
* Standard properties (apply to all connectors)
@@ -668,11 +715,9 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
"EDID", 0);
dev->mode_config.edid_property = edid;
- dpms = drm_property_create(dev, DRM_MODE_PROP_ENUM,
- "DPMS", ARRAY_SIZE(drm_dpms_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dpms_enum_list); i++)
- drm_property_add_enum(dpms, i, drm_dpms_enum_list[i].type,
- drm_dpms_enum_list[i].name);
+ dpms = drm_property_create_enum(dev, 0,
+ "DPMS", drm_dpms_enum_list,
+ ARRAY_SIZE(drm_dpms_enum_list));
dev->mode_config.dpms_property = dpms;
return 0;
@@ -688,30 +733,21 @@ int drm_mode_create_dvi_i_properties(struct drm_device *dev)
{
struct drm_property *dvi_i_selector;
struct drm_property *dvi_i_subconnector;
- int i;
if (dev->mode_config.dvi_i_select_subconnector_property)
return 0;
dvi_i_selector =
- drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ drm_property_create_enum(dev, 0,
"select subconnector",
+ drm_dvi_i_select_enum_list,
ARRAY_SIZE(drm_dvi_i_select_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dvi_i_select_enum_list); i++)
- drm_property_add_enum(dvi_i_selector, i,
- drm_dvi_i_select_enum_list[i].type,
- drm_dvi_i_select_enum_list[i].name);
dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector;
- dvi_i_subconnector =
- drm_property_create(dev, DRM_MODE_PROP_ENUM |
- DRM_MODE_PROP_IMMUTABLE,
+ dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"subconnector",
+ drm_dvi_i_subconnector_enum_list,
ARRAY_SIZE(drm_dvi_i_subconnector_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dvi_i_subconnector_enum_list); i++)
- drm_property_add_enum(dvi_i_subconnector, i,
- drm_dvi_i_subconnector_enum_list[i].type,
- drm_dvi_i_subconnector_enum_list[i].name);
dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector;
return 0;
@@ -742,51 +778,33 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
/*
* Basic connector properties
*/
- tv_selector = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ tv_selector = drm_property_create_enum(dev, 0,
"select subconnector",
+ drm_tv_select_enum_list,
ARRAY_SIZE(drm_tv_select_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_tv_select_enum_list); i++)
- drm_property_add_enum(tv_selector, i,
- drm_tv_select_enum_list[i].type,
- drm_tv_select_enum_list[i].name);
dev->mode_config.tv_select_subconnector_property = tv_selector;
tv_subconnector =
- drm_property_create(dev, DRM_MODE_PROP_ENUM |
- DRM_MODE_PROP_IMMUTABLE, "subconnector",
+ drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
+ "subconnector",
+ drm_tv_subconnector_enum_list,
ARRAY_SIZE(drm_tv_subconnector_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_tv_subconnector_enum_list); i++)
- drm_property_add_enum(tv_subconnector, i,
- drm_tv_subconnector_enum_list[i].type,
- drm_tv_subconnector_enum_list[i].name);
dev->mode_config.tv_subconnector_property = tv_subconnector;
/*
* Other, TV specific properties: margins & TV modes.
*/
dev->mode_config.tv_left_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "left margin", 2);
- dev->mode_config.tv_left_margin_property->values[0] = 0;
- dev->mode_config.tv_left_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "left margin", 0, 100);
dev->mode_config.tv_right_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "right margin", 2);
- dev->mode_config.tv_right_margin_property->values[0] = 0;
- dev->mode_config.tv_right_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "right margin", 0, 100);
dev->mode_config.tv_top_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "top margin", 2);
- dev->mode_config.tv_top_margin_property->values[0] = 0;
- dev->mode_config.tv_top_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "top margin", 0, 100);
dev->mode_config.tv_bottom_margin_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "bottom margin", 2);
- dev->mode_config.tv_bottom_margin_property->values[0] = 0;
- dev->mode_config.tv_bottom_margin_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "bottom margin", 0, 100);
dev->mode_config.tv_mode_property =
drm_property_create(dev, DRM_MODE_PROP_ENUM,
@@ -796,40 +814,22 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes,
i, modes[i]);
dev->mode_config.tv_brightness_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "brightness", 2);
- dev->mode_config.tv_brightness_property->values[0] = 0;
- dev->mode_config.tv_brightness_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "brightness", 0, 100);
dev->mode_config.tv_contrast_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "contrast", 2);
- dev->mode_config.tv_contrast_property->values[0] = 0;
- dev->mode_config.tv_contrast_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "contrast", 0, 100);
dev->mode_config.tv_flicker_reduction_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "flicker reduction", 2);
- dev->mode_config.tv_flicker_reduction_property->values[0] = 0;
- dev->mode_config.tv_flicker_reduction_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "flicker reduction", 0, 100);
dev->mode_config.tv_overscan_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "overscan", 2);
- dev->mode_config.tv_overscan_property->values[0] = 0;
- dev->mode_config.tv_overscan_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "overscan", 0, 100);
dev->mode_config.tv_saturation_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "saturation", 2);
- dev->mode_config.tv_saturation_property->values[0] = 0;
- dev->mode_config.tv_saturation_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "saturation", 0, 100);
dev->mode_config.tv_hue_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "hue", 2);
- dev->mode_config.tv_hue_property->values[0] = 0;
- dev->mode_config.tv_hue_property->values[1] = 100;
+ drm_property_create_range(dev, 0, "hue", 0, 100);
return 0;
}
@@ -845,18 +845,14 @@ EXPORT_SYMBOL(drm_mode_create_tv_properties);
int drm_mode_create_scaling_mode_property(struct drm_device *dev)
{
struct drm_property *scaling_mode;
- int i;
if (dev->mode_config.scaling_mode_property)
return 0;
scaling_mode =
- drm_property_create(dev, DRM_MODE_PROP_ENUM, "scaling mode",
+ drm_property_create_enum(dev, 0, "scaling mode",
+ drm_scaling_mode_enum_list,
ARRAY_SIZE(drm_scaling_mode_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++)
- drm_property_add_enum(scaling_mode, i,
- drm_scaling_mode_enum_list[i].type,
- drm_scaling_mode_enum_list[i].name);
dev->mode_config.scaling_mode_property = scaling_mode;
@@ -874,18 +870,14 @@ EXPORT_SYMBOL(drm_mode_create_scaling_mode_property);
int drm_mode_create_dithering_property(struct drm_device *dev)
{
struct drm_property *dithering_mode;
- int i;
if (dev->mode_config.dithering_mode_property)
return 0;
dithering_mode =
- drm_property_create(dev, DRM_MODE_PROP_ENUM, "dithering",
+ drm_property_create_enum(dev, 0, "dithering",
+ drm_dithering_mode_enum_list,
ARRAY_SIZE(drm_dithering_mode_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++)
- drm_property_add_enum(dithering_mode, i,
- drm_dithering_mode_enum_list[i].type,
- drm_dithering_mode_enum_list[i].name);
dev->mode_config.dithering_mode_property = dithering_mode;
return 0;
@@ -902,20 +894,15 @@ EXPORT_SYMBOL(drm_mode_create_dithering_property);
int drm_mode_create_dirty_info_property(struct drm_device *dev)
{
struct drm_property *dirty_info;
- int i;
if (dev->mode_config.dirty_info_property)
return 0;
dirty_info =
- drm_property_create(dev, DRM_MODE_PROP_ENUM |
- DRM_MODE_PROP_IMMUTABLE,
+ drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE,
"dirty",
+ drm_dirty_info_enum_list,
ARRAY_SIZE(drm_dirty_info_enum_list));
- for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
- drm_property_add_enum(dirty_info, i,
- drm_dirty_info_enum_list[i].type,
- drm_dirty_info_enum_list[i].name);
dev->mode_config.dirty_info_property = dirty_info;
return 0;
@@ -999,6 +986,7 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
return 0;
}
+EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
/**
* drm_mode_config_cleanup - free up DRM mode_config info
@@ -1048,6 +1036,9 @@ void drm_mode_config_cleanup(struct drm_device *dev)
head) {
plane->funcs->destroy(plane);
}
+
+ idr_remove_all(&dev->mode_config.crtc_idr);
+ idr_destroy(&dev->mode_config.crtc_idr);
}
EXPORT_SYMBOL(drm_mode_config_cleanup);
@@ -1062,9 +1053,16 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);
* Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
* the user.
*/
-void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
- struct drm_display_mode *in)
+static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
+ const struct drm_display_mode *in)
{
+ WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX ||
+ in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX ||
+ in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX ||
+ in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX ||
+ in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX,
+ "timing values too large for mode info\n");
+
out->clock = in->clock;
out->hdisplay = in->hdisplay;
out->hsync_start = in->hsync_start;
@@ -1093,10 +1091,16 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
*
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
* the caller.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
*/
-void drm_crtc_convert_umode(struct drm_display_mode *out,
- struct drm_mode_modeinfo *in)
+static int drm_crtc_convert_umode(struct drm_display_mode *out,
+ const struct drm_mode_modeinfo *in)
{
+ if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
+ return -ERANGE;
+
out->clock = in->clock;
out->hdisplay = in->hdisplay;
out->hsync_start = in->hsync_start;
@@ -1113,6 +1117,8 @@ void drm_crtc_convert_umode(struct drm_display_mode *out,
out->type = in->type;
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+
+ return 0;
}
/**
@@ -1311,7 +1317,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Construct a CRTC configuration structure to return to the user.
*
@@ -1371,7 +1377,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Construct a connector configuration structure to return to the user.
*
@@ -1553,6 +1559,9 @@ out:
* @data: ioctl data
* @file_priv: DRM file info
*
+ * LOCKING:
+ * Takes mode config lock.
+ *
* Return an plane count and set of IDs.
*/
int drm_mode_getplane_res(struct drm_device *dev, void *data,
@@ -1599,6 +1608,9 @@ out:
* @data: ioctl data
* @file_priv: DRM file info
*
+ * LOCKING:
+ * Takes mode config lock.
+ *
* Return plane info, including formats supported, gamma size, any
* current fb, etc.
*/
@@ -1664,6 +1676,9 @@ out:
* @data: ioctl data*
* @file_prive: DRM file info
*
+ * LOCKING:
+ * Takes mode config lock.
+ *
* Set plane info, including placement, fb, scaling, and other factors.
* Or pass a NULL fb to disable.
*/
@@ -1794,7 +1809,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Build a new CRTC configuration based on user request.
*
@@ -1809,7 +1824,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
struct drm_mode_config *config = &dev->mode_config;
struct drm_mode_crtc *crtc_req = data;
struct drm_mode_object *obj;
- struct drm_crtc *crtc, *crtcfb;
+ struct drm_crtc *crtc;
struct drm_connector **connector_set = NULL, *connector;
struct drm_framebuffer *fb = NULL;
struct drm_display_mode *mode = NULL;
@@ -1821,6 +1836,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
+ /* For some reason crtc x/y offsets are signed internally. */
+ if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
+ return -ERANGE;
+
mutex_lock(&dev->mode_config.mutex);
obj = drm_mode_object_find(dev, crtc_req->crtc_id,
DRM_MODE_OBJECT_CRTC);
@@ -1836,14 +1855,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
if (crtc_req->fb_id == -1) {
- list_for_each_entry(crtcfb,
- &dev->mode_config.crtc_list, head) {
- if (crtcfb == crtc) {
- DRM_DEBUG_KMS("Using current fb for "
- "setmode\n");
- fb = crtc->fb;
- }
+ if (!crtc->fb) {
+ DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
+ ret = -EINVAL;
+ goto out;
}
+ fb = crtc->fb;
} else {
obj = drm_mode_object_find(dev, crtc_req->fb_id,
DRM_MODE_OBJECT_FB);
@@ -1857,8 +1874,30 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
}
mode = drm_mode_create(dev);
- drm_crtc_convert_umode(mode, &crtc_req->mode);
+ if (!mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ goto out;
+ }
+
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+
+ if (mode->hdisplay > fb->width ||
+ mode->vdisplay > fb->height ||
+ crtc_req->x > fb->width - mode->hdisplay ||
+ crtc_req->y > fb->height - mode->vdisplay) {
+ DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n",
+ mode->hdisplay, mode->vdisplay,
+ crtc_req->x, crtc_req->y,
+ fb->width, fb->height);
+ ret = -ENOSPC;
+ goto out;
+ }
}
if (crtc_req->count_connectors == 0 && mode) {
@@ -1926,6 +1965,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
out:
kfree(connector_set);
+ drm_mode_destroy(dev, mode);
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
@@ -2275,7 +2315,7 @@ out:
* @arg: arg from ioctl
*
* LOCKING:
- * Caller? (FIXME)
+ * Takes mode config lock.
*
* Lookup the FB given its ID and return info about it.
*
@@ -2424,38 +2464,48 @@ void drm_fb_release(struct drm_file *priv)
*
* Add @mode to @connector's user mode list.
*/
-static int drm_mode_attachmode(struct drm_device *dev,
- struct drm_connector *connector,
- struct drm_display_mode *mode)
+static void drm_mode_attachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
{
- int ret = 0;
-
list_add_tail(&mode->head, &connector->user_modes);
- return ret;
}
int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct drm_connector *connector;
int ret = 0;
- struct drm_display_mode *dup_mode;
- int need_dup = 0;
+ struct drm_display_mode *dup_mode, *next;
+ LIST_HEAD(list);
+
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder)
- break;
+ continue;
if (connector->encoder->crtc == crtc) {
- if (need_dup)
- dup_mode = drm_mode_duplicate(dev, mode);
- else
- dup_mode = mode;
- ret = drm_mode_attachmode(dev, connector, dup_mode);
- if (ret)
- return ret;
- need_dup = 1;
+ dup_mode = drm_mode_duplicate(dev, mode);
+ if (!dup_mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ list_add_tail(&dup_mode->head, &list);
}
}
- return 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+ if (connector->encoder->crtc == crtc)
+ list_move_tail(list.next, &connector->user_modes);
+ }
+
+ WARN_ON(!list_empty(&list));
+
+ out:
+ list_for_each_entry_safe(dup_mode, next, &list, head)
+ drm_mode_destroy(dev, dup_mode);
+
+ return ret;
}
EXPORT_SYMBOL(drm_mode_attachmode_crtc);
@@ -2534,9 +2584,14 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
goto out;
}
- drm_crtc_convert_umode(mode, umode);
+ ret = drm_crtc_convert_umode(mode, umode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ drm_mode_destroy(dev, mode);
+ goto out;
+ }
- ret = drm_mode_attachmode(dev, connector, mode);
+ drm_mode_attachmode(dev, connector, mode);
out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
@@ -2577,7 +2632,12 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
}
connector = obj_to_connector(obj);
- drm_crtc_convert_umode(&mode, umode);
+ ret = drm_crtc_convert_umode(&mode, umode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ goto out;
+ }
+
ret = drm_mode_detachmode(dev, connector, &mode);
out:
mutex_unlock(&dev->mode_config.mutex);
@@ -2588,6 +2648,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
const char *name, int num_values)
{
struct drm_property *property = NULL;
+ int ret;
property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
if (!property)
@@ -2599,7 +2660,10 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
goto fail;
}
- drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ if (ret)
+ goto fail;
+
property->flags = flags;
property->num_values = num_values;
INIT_LIST_HEAD(&property->enum_blob_list);
@@ -2612,11 +2676,59 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
list_add_tail(&property->head, &dev->mode_config.property_list);
return property;
fail:
+ kfree(property->values);
kfree(property);
return NULL;
}
EXPORT_SYMBOL(drm_property_create);
+struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
+ const char *name,
+ const struct drm_prop_enum_list *props,
+ int num_values)
+{
+ struct drm_property *property;
+ int i, ret;
+
+ flags |= DRM_MODE_PROP_ENUM;
+
+ property = drm_property_create(dev, flags, name, num_values);
+ if (!property)
+ return NULL;
+
+ for (i = 0; i < num_values; i++) {
+ ret = drm_property_add_enum(property, i,
+ props[i].type,
+ props[i].name);
+ if (ret) {
+ drm_property_destroy(dev, property);
+ return NULL;
+ }
+ }
+
+ return property;
+}
+EXPORT_SYMBOL(drm_property_create_enum);
+
+struct drm_property *drm_property_create_range(struct drm_device *dev, int flags,
+ const char *name,
+ uint64_t min, uint64_t max)
+{
+ struct drm_property *property;
+
+ flags |= DRM_MODE_PROP_RANGE;
+
+ property = drm_property_create(dev, flags, name, 2);
+ if (!property)
+ return NULL;
+
+ property->values[0] = min;
+ property->values[1] = max;
+
+ return property;
+}
+EXPORT_SYMBOL(drm_property_create_range);
+
int drm_property_add_enum(struct drm_property *property, int index,
uint64_t value, const char *name)
{
@@ -2828,6 +2940,7 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
void *data)
{
struct drm_property_blob *blob;
+ int ret;
if (!length || !data)
return NULL;
@@ -2836,13 +2949,16 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
if (!blob)
return NULL;
- blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+ ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+ if (ret) {
+ kfree(blob);
+ return NULL;
+ }
+
blob->length = length;
memcpy(blob->data, data, length);
- drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
-
list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
return blob;
}
@@ -3021,7 +3137,7 @@ void drm_mode_connector_detach_encoder(struct drm_connector *connector,
}
EXPORT_SYMBOL(drm_mode_connector_detach_encoder);
-bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
int gamma_size)
{
crtc->gamma_size = gamma_size;
@@ -3029,10 +3145,10 @@ bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL);
if (!crtc->gamma_store) {
crtc->gamma_size = 0;
- return false;
+ return -ENOMEM;
}
- return true;
+ return 0;
}
EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size);
@@ -3178,6 +3294,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
goto out;
fb = obj_to_fb(obj);
+ if (crtc->mode.hdisplay > fb->width ||
+ crtc->mode.vdisplay > fb->height ||
+ crtc->x > fb->width - crtc->mode.hdisplay ||
+ crtc->y > fb->height - crtc->mode.vdisplay) {
+ DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n",
+ fb->width, fb->height,
+ crtc->mode.hdisplay, crtc->mode.vdisplay,
+ crtc->x, crtc->y);
+ ret = -ENOSPC;
+ goto out;
+ }
+
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
ret = -ENOMEM;
spin_lock_irqsave(&dev->event_lock, flags);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 84a4a809793f..81118893264c 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -37,6 +37,7 @@
#include "drm_fourcc.h"
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
+#include "drm_edid.h"
static bool drm_kms_helper_poll = true;
module_param_named(poll, drm_kms_helper_poll, bool, 0600);
@@ -44,12 +45,12 @@ module_param_named(poll, drm_kms_helper_poll, bool, 0600);
static void drm_mode_validate_flag(struct drm_connector *connector,
int flags)
{
- struct drm_display_mode *mode, *t;
+ struct drm_display_mode *mode;
if (flags == (DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_INTERLACE))
return;
- list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ list_for_each_entry(mode, &connector->modes, head) {
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) &&
!(flags & DRM_MODE_FLAG_INTERLACE))
mode->status = MODE_NO_INTERLACE;
@@ -87,7 +88,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
uint32_t maxX, uint32_t maxY)
{
struct drm_device *dev = connector->dev;
- struct drm_display_mode *mode, *t;
+ struct drm_display_mode *mode;
struct drm_connector_helper_funcs *connector_funcs =
connector->helper_private;
int count = 0;
@@ -96,7 +97,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id,
drm_get_connector_name(connector));
/* set all modes to the unverified state */
- list_for_each_entry_safe(mode, t, &connector->modes, head)
+ list_for_each_entry(mode, &connector->modes, head)
mode->status = MODE_UNVERIFIED;
if (connector->force) {
@@ -118,7 +119,12 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
goto prune;
}
- count = (*connector_funcs->get_modes)(connector);
+#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+ count = drm_load_edid_firmware(connector);
+ if (count == 0)
+#endif
+ count = (*connector_funcs->get_modes)(connector);
+
if (count == 0 && connector->status == connector_status_connected)
count = drm_add_modes_noedid(connector, 1024, 768);
if (count == 0)
@@ -136,7 +142,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
mode_flags |= DRM_MODE_FLAG_DBLSCAN;
drm_mode_validate_flag(connector, mode_flags);
- list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ list_for_each_entry(mode, &connector->modes, head) {
if (mode->status == MODE_OK)
mode->status = connector_funcs->mode_valid(connector,
mode);
@@ -152,7 +158,7 @@ prune:
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] probed modes :\n", connector->base.id,
drm_get_connector_name(connector));
- list_for_each_entry_safe(mode, t, &connector->modes, head) {
+ list_for_each_entry(mode, &connector->modes, head) {
mode->vrefresh = drm_mode_vrefresh(mode);
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
@@ -352,6 +358,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
return true;
adjusted_mode = drm_mode_duplicate(dev, mode);
+ if (!adjusted_mode)
+ return false;
saved_hwmode = crtc->hwmode;
saved_mode = crtc->mode;
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index ebf7d3f68fc4..0b65fbc8a630 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -135,23 +135,23 @@ static struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_GEM_FLINK, drm_gem_flink_ioctl, DRM_AUTH|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETENCODER, drm_mode_getencoder, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
@@ -390,6 +390,10 @@ long drm_ioctl(struct file *filp,
unsigned int usize, asize;
dev = file_priv->minor->dev;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
atomic_inc(&dev->ioctl_count);
atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
++file_priv->ioctl_count;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index ece03fc2d386..5a18b0df8285 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -149,8 +149,7 @@ EXPORT_SYMBOL(drm_edid_header_is_valid);
* Sanity check the EDID block (base or extension). Return 0 if the block
* doesn't check out, or 1 if it's valid.
*/
-static bool
-drm_edid_block_valid(u8 *raw_edid)
+bool drm_edid_block_valid(u8 *raw_edid)
{
int i;
u8 csum = 0;
@@ -203,6 +202,7 @@ bad:
}
return 0;
}
+EXPORT_SYMBOL(drm_edid_block_valid);
/**
* drm_edid_is_valid - sanity check EDID data
@@ -226,7 +226,6 @@ bool drm_edid_is_valid(struct edid *edid)
}
EXPORT_SYMBOL(drm_edid_is_valid);
-#define DDC_ADDR 0x50
#define DDC_SEGMENT_ADDR 0x30
/**
* Get EDID information via I2C.
@@ -266,6 +265,11 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
}
};
ret = i2c_transfer(adapter, msgs, 2);
+ if (ret == -ENXIO) {
+ DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
+ adapter->name);
+ break;
+ }
} while (ret != 2 && --retries);
return ret == 2 ? 0 : -1;
@@ -745,7 +749,7 @@ drm_mode_std(struct drm_connector *connector, struct edid *edid,
*/
mode = drm_gtf_mode(dev, hsize, vsize, vrefresh_rate, 0, 0);
if (drm_mode_hsync(mode) > drm_gtf2_hbreak(edid)) {
- kfree(mode);
+ drm_mode_destroy(dev, mode);
mode = drm_gtf_mode_complex(dev, hsize, vsize,
vrefresh_rate, 0, 0,
drm_gtf2_m(edid),
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
new file mode 100644
index 000000000000..da9acba2dd6c
--- /dev/null
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -0,0 +1,250 @@
+/*
+ drm_edid_load.c: use a built-in EDID data set or load it via the firmware
+ interface
+
+ Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+
+static char edid_firmware[PATH_MAX];
+module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
+MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
+ "from built-in data or /lib/firmware instead. ");
+
+#define GENERIC_EDIDS 4
+static char *generic_edid_name[GENERIC_EDIDS] = {
+ "edid/1024x768.bin",
+ "edid/1280x1024.bin",
+ "edid/1680x1050.bin",
+ "edid/1920x1080.bin",
+};
+
+static u8 generic_edid[GENERIC_EDIDS][128] = {
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
+ 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
+ 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
+ 0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
+ 0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
+ 0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
+ 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
+ 0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
+ 0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
+ 0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
+ 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
+ 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
+ },
+};
+
+static int edid_load(struct drm_connector *connector, char *name,
+ char *connector_name)
+{
+ const struct firmware *fw;
+ struct platform_device *pdev;
+ u8 *fwdata = NULL, *edid;
+ int fwsize, expected;
+ int builtin = 0, err = 0;
+ int i, valid_extensions = 0;
+
+ pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ DRM_ERROR("Failed to register EDID firmware platform device "
+ "for connector \"%s\"\n", connector_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = request_firmware(&fw, name, &pdev->dev);
+ platform_device_unregister(pdev);
+
+ if (err) {
+ i = 0;
+ while (i < GENERIC_EDIDS && strcmp(name, generic_edid_name[i]))
+ i++;
+ if (i < GENERIC_EDIDS) {
+ err = 0;
+ builtin = 1;
+ fwdata = generic_edid[i];
+ fwsize = sizeof(generic_edid[i]);
+ }
+ }
+
+ if (err) {
+ DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
+ name, err);
+ goto out;
+ }
+
+ if (fwdata == NULL) {
+ fwdata = (u8 *) fw->data;
+ fwsize = fw->size;
+ }
+
+ expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
+ if (expected != fwsize) {
+ DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
+ "(expected %d, got %d)\n", name, expected, (int) fwsize);
+ err = -EINVAL;
+ goto relfw_out;
+ }
+
+ edid = kmalloc(fwsize, GFP_KERNEL);
+ if (edid == NULL) {
+ err = -ENOMEM;
+ goto relfw_out;
+ }
+ memcpy(edid, fwdata, fwsize);
+
+ if (!drm_edid_block_valid(edid)) {
+ DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
+ name);
+ kfree(edid);
+ err = -EINVAL;
+ goto relfw_out;
+ }
+
+ for (i = 1; i <= edid[0x7e]; i++) {
+ if (i != valid_extensions + 1)
+ memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
+ edid + i * EDID_LENGTH, EDID_LENGTH);
+ if (drm_edid_block_valid(edid + i * EDID_LENGTH))
+ valid_extensions++;
+ }
+
+ if (valid_extensions != edid[0x7e]) {
+ edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
+ DRM_INFO("Found %d valid extensions instead of %d in EDID data "
+ "\"%s\" for connector \"%s\"\n", valid_extensions,
+ edid[0x7e], name, connector_name);
+ edid[0x7e] = valid_extensions;
+ edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
+ GFP_KERNEL);
+ if (edid == NULL) {
+ err = -ENOMEM;
+ goto relfw_out;
+ }
+ }
+
+ connector->display_info.raw_edid = edid;
+ DRM_INFO("Got %s EDID base block and %d extension%s from "
+ "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
+ "external", valid_extensions, valid_extensions == 1 ? "" : "s",
+ name, connector_name);
+
+relfw_out:
+ release_firmware(fw);
+
+out:
+ return err;
+}
+
+int drm_load_edid_firmware(struct drm_connector *connector)
+{
+ char *connector_name = drm_get_connector_name(connector);
+ char *edidname = edid_firmware, *last, *colon;
+ int ret = 0;
+
+ if (*edidname == '\0')
+ return ret;
+
+ colon = strchr(edidname, ':');
+ if (colon != NULL) {
+ if (strncmp(connector_name, edidname, colon - edidname))
+ return ret;
+ edidname = colon + 1;
+ if (*edidname == '\0')
+ return ret;
+ }
+
+ last = edidname + strlen(edidname) - 1;
+ if (*last == '\n')
+ *last = '\0';
+
+ ret = edid_load(connector, edidname, connector_name);
+ if (ret)
+ return 0;
+
+ drm_mode_connector_update_edid_property(connector,
+ (struct edid *) connector->display_info.raw_edid);
+
+ return drm_add_edid_modes(connector, (struct edid *)
+ connector->display_info.raw_edid);
+}
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index aada26f63dec..7740dd26f007 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -306,91 +306,31 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
#endif
-static void drm_fb_helper_on(struct fb_info *info)
+static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
{
struct drm_fb_helper *fb_helper = info->par;
struct drm_device *dev = fb_helper->dev;
struct drm_crtc *crtc;
- struct drm_crtc_helper_funcs *crtc_funcs;
- struct drm_connector *connector;
- struct drm_encoder *encoder;
- int i, j;
-
- /*
- * For each CRTC in this fb, turn the crtc on then,
- * find all associated encoders and turn them on.
- */
- mutex_lock(&dev->mode_config.mutex);
- for (i = 0; i < fb_helper->crtc_count; i++) {
- crtc = fb_helper->crtc_info[i].mode_set.crtc;
- crtc_funcs = crtc->helper_private;
-
- if (!crtc->enabled)
- continue;
-
- crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-
- /* Walk the connectors & encoders on this fb turning them on */
- for (j = 0; j < fb_helper->connector_count; j++) {
- connector = fb_helper->connector_info[j]->connector;
- connector->dpms = DRM_MODE_DPMS_ON;
- drm_connector_property_set_value(connector,
- dev->mode_config.dpms_property,
- DRM_MODE_DPMS_ON);
- }
- /* Found a CRTC on this fb, now find encoders */
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (encoder->crtc == crtc) {
- struct drm_encoder_helper_funcs *encoder_funcs;
-
- encoder_funcs = encoder->helper_private;
- encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
- }
- }
- }
- mutex_unlock(&dev->mode_config.mutex);
-}
-
-static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
-{
- struct drm_fb_helper *fb_helper = info->par;
- struct drm_device *dev = fb_helper->dev;
- struct drm_crtc *crtc;
- struct drm_crtc_helper_funcs *crtc_funcs;
struct drm_connector *connector;
- struct drm_encoder *encoder;
int i, j;
/*
- * For each CRTC in this fb, find all associated encoders
- * and turn them off, then turn off the CRTC.
+ * For each CRTC in this fb, turn the connectors on/off.
*/
mutex_lock(&dev->mode_config.mutex);
for (i = 0; i < fb_helper->crtc_count; i++) {
crtc = fb_helper->crtc_info[i].mode_set.crtc;
- crtc_funcs = crtc->helper_private;
if (!crtc->enabled)
continue;
- /* Walk the connectors on this fb and mark them off */
+ /* Walk the connectors & encoders on this fb turning them on/off */
for (j = 0; j < fb_helper->connector_count; j++) {
connector = fb_helper->connector_info[j]->connector;
- connector->dpms = dpms_mode;
+ drm_helper_connector_dpms(connector, dpms_mode);
drm_connector_property_set_value(connector,
- dev->mode_config.dpms_property,
- dpms_mode);
- }
- /* Found a CRTC on this fb, now find encoders */
- list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
- if (encoder->crtc == crtc) {
- struct drm_encoder_helper_funcs *encoder_funcs;
-
- encoder_funcs = encoder->helper_private;
- encoder_funcs->dpms(encoder, dpms_mode);
- }
+ dev->mode_config.dpms_property, dpms_mode);
}
- crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}
mutex_unlock(&dev->mode_config.mutex);
}
@@ -400,23 +340,23 @@ int drm_fb_helper_blank(int blank, struct fb_info *info)
switch (blank) {
/* Display: On; HSync: On, VSync: On */
case FB_BLANK_UNBLANK:
- drm_fb_helper_on(info);
+ drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
break;
/* Display: Off; HSync: On, VSync: On */
case FB_BLANK_NORMAL:
- drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+ drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
break;
/* Display: Off; HSync: Off, VSync: On */
case FB_BLANK_HSYNC_SUSPEND:
- drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+ drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
break;
/* Display: Off; HSync: On, VSync: Off */
case FB_BLANK_VSYNC_SUSPEND:
- drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
+ drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
break;
/* Display: Off; HSync: Off, VSync: Off */
case FB_BLANK_POWERDOWN:
- drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
+ drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
break;
}
return 0;
@@ -430,8 +370,11 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
for (i = 0; i < helper->connector_count; i++)
kfree(helper->connector_info[i]);
kfree(helper->connector_info);
- for (i = 0; i < helper->crtc_count; i++)
+ for (i = 0; i < helper->crtc_count; i++) {
kfree(helper->crtc_info[i].mode_set.connectors);
+ if (helper->crtc_info[i].mode_set.mode)
+ drm_mode_destroy(helper->dev, helper->crtc_info[i].mode_set.mode);
+ }
kfree(helper->crtc_info);
}
@@ -474,11 +417,10 @@ int drm_fb_helper_init(struct drm_device *dev,
i = 0;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- fb_helper->crtc_info[i].crtc_id = crtc->base.id;
fb_helper->crtc_info[i].mode_set.crtc = crtc;
i++;
}
- fb_helper->conn_limit = max_conn_count;
+
return 0;
out_free:
drm_fb_helper_crtc_free(fb_helper);
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 6263b0147598..7348a3dab250 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -133,6 +133,9 @@ int drm_open(struct inode *inode, struct file *filp)
if (!(dev = minor->dev))
return -ENODEV;
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
retcode = drm_open_helper(inode, filp, dev);
if (!retcode) {
atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
@@ -181,6 +184,9 @@ int drm_stub_open(struct inode *inode, struct file *filp)
if (!(dev = minor->dev))
goto out;
+ if (drm_device_is_unplugged(dev))
+ goto out;
+
old_fops = filp->f_op;
filp->f_op = fops_get(dev->driver->fops);
if (filp->f_op == NULL) {
@@ -579,6 +585,8 @@ int drm_release(struct inode *inode, struct file *filp)
retcode = -EBUSY;
} else
retcode = drm_lastclose(dev);
+ if (drm_device_is_unplugged(dev))
+ drm_put_dev(dev);
}
mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index f8625e290728..0ef358e53245 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -661,6 +661,9 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_hash_item *hash;
int ret = 0;
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
mutex_lock(&dev->struct_mutex);
if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
@@ -700,7 +703,6 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
*/
drm_gem_object_reference(obj);
- vma->vm_file = filp; /* Needed for drm_vm_open() */
drm_vm_open_locked(vma);
out_unlock:
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 956fd38d7c9e..cf85155da2a0 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -37,6 +37,7 @@
#include "drm_core.h"
#include "linux/pci.h"
+#include "linux/export.h"
/**
* Get the bus id.
@@ -276,6 +277,12 @@ int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
case DRM_CAP_VBLANK_HIGH_CRTC:
req->value = 1;
break;
+ case DRM_CAP_DUMB_PREFERRED_DEPTH:
+ req->value = dev->mode_config.preferred_depth;
+ break;
+ case DRM_CAP_DUMB_PREFER_SHADOW:
+ req->value = dev->mode_config.prefer_shadow;
+ break;
default:
return -EINVAL;
}
@@ -346,3 +353,4 @@ int drm_noop(struct drm_device *dev, void *data,
DRM_DEBUG("\n");
return 0;
}
+EXPORT_SYMBOL(drm_noop);
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 44a5d0ad8b7c..c869436e238a 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -305,7 +305,7 @@ static void drm_irq_vgaarb_nokms(void *cookie, bool state)
* \param dev DRM device.
*
* Initializes the IRQ related data. Installs the handler, calling the driver
- * \c drm_driver_irq_preinstall() and \c drm_driver_irq_postinstall() functions
+ * \c irq_preinstall() and \c irq_postinstall() functions
* before and after the installation.
*/
int drm_irq_install(struct drm_device *dev)
@@ -385,7 +385,7 @@ EXPORT_SYMBOL(drm_irq_install);
*
* \param dev DRM device.
*
- * Calls the driver's \c drm_driver_irq_uninstall() function, and stops the irq.
+ * Calls the driver's \c irq_uninstall() function, and stops the irq.
*/
int drm_irq_uninstall(struct drm_device *dev)
{
diff --git a/drivers/gpu/drm/drm_memory.c b/drivers/gpu/drm/drm_memory.c
index c8b6b66d428d..c86a0f1a435c 100644
--- a/drivers/gpu/drm/drm_memory.c
+++ b/drivers/gpu/drm/drm_memory.c
@@ -37,25 +37,6 @@
#include <linux/export.h>
#include "drmP.h"
-/**
- * Called when "/proc/dri/%dev%/mem" is read.
- *
- * \param buf output buffer.
- * \param start start of output data.
- * \param offset requested start offset.
- * \param len requested number of bytes.
- * \param eof whether there is no more data to return.
- * \param data private data.
- * \return number of written bytes.
- *
- * No-op.
- */
-int drm_mem_info(char *buf, char **start, off_t offset,
- int len, int *eof, void *data)
-{
- return 0;
-}
-
#if __OS_HAS_AGP
static void *agp_remap(unsigned long offset, unsigned long size,
struct drm_device * dev)
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index fb8e46b4e8bc..b7adb4a967fd 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -686,8 +686,6 @@ void drm_mode_set_crtcinfo(struct drm_display_mode *p, int adjust_flags)
p->crtc_vsync_end /= 2;
p->crtc_vtotal /= 2;
}
-
- p->crtc_vtotal |= 1;
}
if (p->flags & DRM_MODE_FLAG_DBLSCAN) {
@@ -716,6 +714,27 @@ EXPORT_SYMBOL(drm_mode_set_crtcinfo);
/**
+ * drm_mode_copy - copy the mode
+ * @dst: mode to overwrite
+ * @src: mode to copy
+ *
+ * LOCKING:
+ * None.
+ *
+ * Copy an existing mode into another mode, preserving the object id
+ * of the destination mode.
+ */
+void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src)
+{
+ int id = dst->base.id;
+
+ *dst = *src;
+ dst->base.id = id;
+ INIT_LIST_HEAD(&dst->head);
+}
+EXPORT_SYMBOL(drm_mode_copy);
+
+/**
* drm_mode_duplicate - allocate and duplicate an existing mode
* @m: mode to duplicate
*
@@ -729,16 +748,13 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
const struct drm_display_mode *mode)
{
struct drm_display_mode *nmode;
- int new_id;
nmode = drm_mode_create(dev);
if (!nmode)
return NULL;
- new_id = nmode->base.id;
- *nmode = *mode;
- nmode->base.id = new_id;
- INIT_LIST_HEAD(&nmode->head);
+ drm_mode_copy(nmode, mode);
+
return nmode;
}
EXPORT_SYMBOL(drm_mode_duplicate);
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c
index d4d10b7880cf..13f3d936472f 100644
--- a/drivers/gpu/drm/drm_pci.c
+++ b/drivers/gpu/drm/drm_pci.c
@@ -324,8 +324,6 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
if (ret)
goto err_g1;
- pci_set_master(pdev);
-
dev->pdev = pdev;
dev->dev = &pdev->dev;
diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c
index ae9db5e2b27c..82431dcae37b 100644
--- a/drivers/gpu/drm/drm_platform.c
+++ b/drivers/gpu/drm/drm_platform.c
@@ -122,7 +122,7 @@ static const char *drm_platform_get_name(struct drm_device *dev)
static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *master)
{
- int len, ret;
+ int len, ret, id;
master->unique_len = 13 + strlen(dev->platformdev->name);
master->unique_size = master->unique_len;
@@ -131,8 +131,16 @@ static int drm_platform_set_busid(struct drm_device *dev, struct drm_master *mas
if (master->unique == NULL)
return -ENOMEM;
+ id = dev->platformdev->id;
+
+ /* if only a single instance of the platform device, id will be
+ * set to -1.. use 0 instead to avoid a funny looking bus-id:
+ */
+ if (id == -1)
+ id = 0;
+
len = snprintf(master->unique, master->unique_len,
- "platform:%s:%02d", dev->platformdev->name, dev->platformdev->id);
+ "platform:%s:%02d", dev->platformdev->name, id);
if (len > master->unique_len) {
DRM_ERROR("Unique buffer overflowed\n");
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 6d7b083c5b77..aa454f80e109 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -319,6 +319,7 @@ int drm_fill_in_dev(struct drm_device *dev,
drm_lastclose(dev);
return retcode;
}
+EXPORT_SYMBOL(drm_fill_in_dev);
/**
@@ -397,6 +398,7 @@ err_idr:
*minor = NULL;
return ret;
}
+EXPORT_SYMBOL(drm_get_minor);
/**
* Put a secondary minor number.
@@ -428,6 +430,12 @@ int drm_put_minor(struct drm_minor **minor_p)
*minor_p = NULL;
return 0;
}
+EXPORT_SYMBOL(drm_put_minor);
+
+static void drm_unplug_minor(struct drm_minor *minor)
+{
+ drm_sysfs_device_remove(minor);
+}
/**
* Called via drm_exit() at module unload time or when pci device is
@@ -492,3 +500,21 @@ void drm_put_dev(struct drm_device *dev)
kfree(dev);
}
EXPORT_SYMBOL(drm_put_dev);
+
+void drm_unplug_dev(struct drm_device *dev)
+{
+ /* for a USB device */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_unplug_minor(dev->control);
+ drm_unplug_minor(dev->primary);
+
+ mutex_lock(&drm_global_mutex);
+
+ drm_device_set_unplugged(dev);
+
+ if (dev->open_count == 0) {
+ drm_put_dev(dev);
+ }
+ mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 62c3675045ac..5a7bd51fc3d8 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -454,6 +454,8 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
{
int i;
+ if (!connector->kdev.parent)
+ return;
DRM_DEBUG("removing \"%s\" from sysfs\n",
drm_get_connector_name(connector));
@@ -461,6 +463,7 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
device_remove_file(&connector->kdev, &connector_attrs[i]);
sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
device_unregister(&connector->kdev);
+ connector->kdev.parent = NULL;
}
EXPORT_SYMBOL(drm_sysfs_connector_remove);
@@ -533,7 +536,9 @@ err_out:
*/
void drm_sysfs_device_remove(struct drm_minor *minor)
{
- device_unregister(&minor->kdev);
+ if (minor->kdev.parent)
+ device_unregister(&minor->kdev);
+ minor->kdev.parent = NULL;
}
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index 445003f4dc93..c8c83dad2ce1 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -2,7 +2,6 @@
#include <linux/usb.h>
#include <linux/export.h>
-#ifdef CONFIG_USB
int drm_get_usb_dev(struct usb_interface *interface,
const struct usb_device_id *id,
struct drm_driver *driver)
@@ -115,4 +114,3 @@ void drm_usb_exit(struct drm_driver *driver,
usb_deregister(udriver);
}
EXPORT_SYMBOL(drm_usb_exit);
-#endif
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index 8c03eaf41448..149561818349 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -519,7 +519,6 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
vma->vm_flags |= VM_RESERVED; /* Don't swap */
vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_file = filp; /* Needed for drm_vm_open() */
drm_vm_open_locked(vma);
return 0;
}
@@ -671,7 +670,6 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
vma->vm_flags |= VM_RESERVED; /* Don't swap */
vma->vm_flags |= VM_DONTEXPAND;
- vma->vm_file = filp; /* Needed for drm_vm_open() */
drm_vm_open_locked(vma);
return 0;
}
@@ -682,6 +680,9 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_device *dev = priv->minor->dev;
int ret;
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
mutex_lock(&dev->struct_mutex);
ret = drm_mmap_locked(filp, vma);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index b9e5266c341b..3343ac437fe5 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -1,7 +1,6 @@
config DRM_EXYNOS
tristate "DRM Support for Samsung SoC EXYNOS Series"
depends on DRM && PLAT_SAMSUNG
- default n
select DRM_KMS_HELPER
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -12,16 +11,19 @@ config DRM_EXYNOS
If M is selected the module will be called exynosdrm.
config DRM_EXYNOS_FIMD
- tristate "Exynos DRM FIMD"
+ bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C
- default n
help
Choose this option if you want to use Exynos FIMD for DRM.
- If M is selected, the module will be called exynos_drm_fimd
config DRM_EXYNOS_HDMI
- tristate "Exynos DRM HDMI"
+ bool "Exynos DRM HDMI"
depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
help
Choose this option if you want to use Exynos HDMI for DRM.
- If M is selected, the module will be called exynos_drm_hdmi
+
+config DRM_EXYNOS_VIDI
+ bool "Exynos DRM Virtual Display"
+ depends on DRM_EXYNOS
+ help
+ Choose this option if you want to use Exynos VIDI for DRM.
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 395e69c9a96e..9e0bff8badf9 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -8,7 +8,10 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
exynos_drm_plane.o
-obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
-obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
-obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \
- exynos_hdmiphy.o exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
+ exynos_ddc.o exynos_hdmiphy.o \
+ exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
+
+obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c
index 84b614fe26fd..7e1051d07f1f 100644
--- a/drivers/gpu/drm/exynos/exynos_ddc.c
+++ b/drivers/gpu/drm/exynos/exynos_ddc.c
@@ -55,4 +55,3 @@ struct i2c_driver ddc_driver = {
.remove = __devexit_p(s5p_ddc_remove),
.command = NULL,
};
-EXPORT_SYMBOL(ddc_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c
index 3cf785c58186..4a3a5f72ed4a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_buf.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c
@@ -25,45 +25,161 @@
#include "drmP.h"
#include "drm.h"
+#include "exynos_drm.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"
static int lowlevel_buffer_allocate(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
+ unsigned int flags, struct exynos_drm_gem_buf *buf)
{
+ dma_addr_t start_addr, end_addr;
+ unsigned int npages, page_size, i = 0;
+ struct scatterlist *sgl;
+ int ret = 0;
+
DRM_DEBUG_KMS("%s\n", __FILE__);
- buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size,
- &buffer->dma_addr, GFP_KERNEL);
- if (!buffer->kvaddr) {
- DRM_ERROR("failed to allocate buffer.\n");
+ if (flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support allocation type.\n");
+ return -EINVAL;
+ }
+
+ if (buf->dma_addr) {
+ DRM_DEBUG_KMS("already allocated.\n");
+ return 0;
+ }
+
+ if (buf->size >= SZ_1M) {
+ npages = (buf->size >> SECTION_SHIFT) + 1;
+ page_size = SECTION_SIZE;
+ } else if (buf->size >= SZ_64K) {
+ npages = (buf->size >> 16) + 1;
+ page_size = SZ_64K;
+ } else {
+ npages = (buf->size >> PAGE_SHIFT) + 1;
+ page_size = PAGE_SIZE;
+ }
+
+ buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!buf->sgt) {
+ DRM_ERROR("failed to allocate sg table.\n");
return -ENOMEM;
}
- DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
- (unsigned long)buffer->kvaddr,
- (unsigned long)buffer->dma_addr,
- buffer->size);
+ ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize sg table.\n");
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+ return -ENOMEM;
+ }
- return 0;
+ buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size,
+ &buf->dma_addr, GFP_KERNEL);
+ if (!buf->kvaddr) {
+ DRM_ERROR("failed to allocate buffer.\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ start_addr = buf->dma_addr;
+ end_addr = buf->dma_addr + buf->size;
+
+ buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
+ if (!buf->pages) {
+ DRM_ERROR("failed to allocate pages.\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ start_addr = buf->dma_addr;
+ end_addr = buf->dma_addr + buf->size;
+
+ buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
+ if (!buf->pages) {
+ DRM_ERROR("failed to allocate pages.\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ sgl = buf->sgt->sgl;
+
+ while (i < npages) {
+ buf->pages[i] = phys_to_page(start_addr);
+ sg_set_page(sgl, buf->pages[i], page_size, 0);
+ sg_dma_address(sgl) = start_addr;
+ start_addr += page_size;
+ if (end_addr - start_addr < page_size)
+ break;
+ sgl = sg_next(sgl);
+ i++;
+ }
+
+ buf->pages[i] = phys_to_page(start_addr);
+
+ sgl = sg_next(sgl);
+ sg_set_page(sgl, buf->pages[i+1], end_addr - start_addr, 0);
+
+ DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)buf->kvaddr,
+ (unsigned long)buf->dma_addr,
+ buf->size);
+
+ return ret;
+err2:
+ dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
+ (dma_addr_t)buf->dma_addr);
+ buf->dma_addr = (dma_addr_t)NULL;
+err1:
+ sg_free_table(buf->sgt);
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ return ret;
}
static void lowlevel_buffer_deallocate(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
+ unsigned int flags, struct exynos_drm_gem_buf *buf)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
- if (buffer->dma_addr && buffer->size)
- dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr,
- (dma_addr_t)buffer->dma_addr);
- else
- DRM_DEBUG_KMS("buffer data are invalid.\n");
+ /*
+ * release only physically continuous memory and
+ * non-continuous memory would be released by exynos
+ * gem framework.
+ */
+ if (flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support allocation type.\n");
+ return;
+ }
+
+ if (!buf->dma_addr) {
+ DRM_DEBUG_KMS("dma_addr is invalid.\n");
+ return;
+ }
+
+ DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)buf->kvaddr,
+ (unsigned long)buf->dma_addr,
+ buf->size);
+
+ sg_free_table(buf->sgt);
+
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ kfree(buf->pages);
+ buf->pages = NULL;
+
+ dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
+ (dma_addr_t)buf->dma_addr);
+ buf->dma_addr = (dma_addr_t)NULL;
}
-struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
- unsigned int size)
+struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
+ unsigned int size)
{
struct exynos_drm_gem_buf *buffer;
@@ -77,21 +193,11 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
}
buffer->size = size;
-
- /*
- * allocate memory region with size and set the memory information
- * to vaddr and dma_addr of a buffer object.
- */
- if (lowlevel_buffer_allocate(dev, buffer) < 0) {
- kfree(buffer);
- return NULL;
- }
-
return buffer;
}
-void exynos_drm_buf_destroy(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
+void exynos_drm_fini_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buffer)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
@@ -100,12 +206,27 @@ void exynos_drm_buf_destroy(struct drm_device *dev,
return;
}
- lowlevel_buffer_deallocate(dev, buffer);
-
kfree(buffer);
buffer = NULL;
}
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module");
-MODULE_LICENSE("GPL");
+int exynos_drm_alloc_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buf, unsigned int flags)
+{
+
+ /*
+ * allocate memory region and set the memory information
+ * to vaddr and dma_addr of a buffer object.
+ */
+ if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void exynos_drm_free_buf(struct drm_device *dev,
+ unsigned int flags, struct exynos_drm_gem_buf *buffer)
+{
+
+ lowlevel_buffer_deallocate(dev, flags, buffer);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h
index c913f2bad760..3388e4eb4ba2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_buf.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h
@@ -26,12 +26,22 @@
#ifndef _EXYNOS_DRM_BUF_H_
#define _EXYNOS_DRM_BUF_H_
-/* allocate physical memory. */
-struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
- unsigned int size);
+/* create and initialize buffer object. */
+struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
+ unsigned int size);
-/* remove allocated physical memory. */
-void exynos_drm_buf_destroy(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer);
+/* destroy buffer object. */
+void exynos_drm_fini_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buffer);
+
+/* allocate physical memory region and setup sgt and pages. */
+int exynos_drm_alloc_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buf,
+ unsigned int flags);
+
+/* release physical memory region, sgt and pages. */
+void exynos_drm_free_buf(struct drm_device *dev,
+ unsigned int flags,
+ struct exynos_drm_gem_buf *buffer);
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index 99d5527b2ca6..bf791fa0e50d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -225,6 +225,29 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.best_encoder = exynos_drm_best_encoder,
};
+static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
+ unsigned int max_width, unsigned int max_height)
+{
+ struct exynos_drm_connector *exynos_connector =
+ to_exynos_connector(connector);
+ struct exynos_drm_manager *manager = exynos_connector->manager;
+ struct exynos_drm_manager_ops *ops = manager->ops;
+ unsigned int width, height;
+
+ width = max_width;
+ height = max_height;
+
+ /*
+ * if specific driver want to find desired_mode using maxmum
+ * resolution then get max width and height from that driver.
+ */
+ if (ops && ops->get_max_resol)
+ ops->get_max_resol(manager->dev, &width, &height);
+
+ return drm_helper_probe_single_connector_modes(connector, width,
+ height);
+}
+
/* get detection status of display device. */
static enum drm_connector_status
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
@@ -262,7 +285,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
static struct drm_connector_funcs exynos_connector_funcs = {
.dpms = drm_helper_connector_dpms,
- .fill_modes = drm_helper_probe_single_connector_modes,
+ .fill_modes = exynos_drm_connector_fill_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
};
@@ -292,6 +315,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
+ case EXYNOS_DISPLAY_TYPE_VIDI:
+ type = DRM_MODE_CONNECTOR_VIRTUAL;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
break;
@@ -325,9 +352,3 @@ err_connector:
kfree(exynos_connector);
return NULL;
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index d08a55896d50..411832e8e17a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -32,7 +32,6 @@
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
-static DEFINE_MUTEX(exynos_drm_mutex);
static LIST_HEAD(exynos_drm_subdrv_list);
static struct drm_device *drm_dev;
@@ -60,6 +59,9 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev,
return ret;
}
+ if (subdrv->is_local)
+ return 0;
+
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, &subdrv->manager,
(1 << MAX_CRTC) - 1);
@@ -116,13 +118,10 @@ int exynos_drm_device_register(struct drm_device *dev)
if (!dev)
return -EINVAL;
- if (drm_dev) {
- DRM_ERROR("Already drm device were registered\n");
- return -EBUSY;
- }
+ drm_dev = dev;
- mutex_lock(&exynos_drm_mutex);
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
+ subdrv->drm_dev = dev;
err = exynos_drm_subdrv_probe(dev, subdrv);
if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n");
@@ -130,9 +129,6 @@ int exynos_drm_device_register(struct drm_device *dev)
}
}
- drm_dev = dev;
- mutex_unlock(&exynos_drm_mutex);
-
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
@@ -143,86 +139,28 @@ int exynos_drm_device_unregister(struct drm_device *dev)
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- if (!dev || dev != drm_dev) {
+ if (!dev) {
WARN(1, "Unexpected drm device unregister!\n");
return -EINVAL;
}
- mutex_lock(&exynos_drm_mutex);
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
exynos_drm_subdrv_remove(dev, subdrv);
drm_dev = NULL;
- mutex_unlock(&exynos_drm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
-static int exynos_drm_mode_group_reinit(struct drm_device *dev)
-{
- struct drm_mode_group *group = &dev->primary->mode_group;
- uint32_t *id_list = group->id_list;
- int ret;
-
- DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
- ret = drm_mode_group_init_legacy_group(dev, group);
- if (ret < 0)
- return ret;
-
- kfree(id_list);
- return 0;
-}
-
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
- int err;
-
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
- mutex_lock(&exynos_drm_mutex);
- if (drm_dev) {
- err = exynos_drm_subdrv_probe(drm_dev, subdrv);
- if (err) {
- DRM_ERROR("failed to probe exynos drm subdrv\n");
- mutex_unlock(&exynos_drm_mutex);
- return err;
- }
-
- /* setup possible_clones. */
- exynos_drm_encoder_setup(drm_dev);
-
- /*
- * if any specific driver such as fimd or hdmi driver called
- * exynos_drm_subdrv_register() later than drm_load(),
- * the fb helper should be re-initialized and re-configured.
- */
- err = exynos_drm_fbdev_reinit(drm_dev);
- if (err) {
- DRM_ERROR("failed to reinitialize exynos drm fbdev\n");
- exynos_drm_subdrv_remove(drm_dev, subdrv);
- mutex_unlock(&exynos_drm_mutex);
- return err;
- }
-
- err = exynos_drm_mode_group_reinit(drm_dev);
- if (err) {
- DRM_ERROR("failed to reinitialize mode group\n");
- exynos_drm_fbdev_fini(drm_dev);
- exynos_drm_subdrv_remove(drm_dev, subdrv);
- mutex_unlock(&exynos_drm_mutex);
- return err;
- }
- }
-
- subdrv->drm_dev = drm_dev;
-
list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
- mutex_unlock(&exynos_drm_mutex);
return 0;
}
@@ -230,46 +168,48 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
- int ret = -EFAULT;
-
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- if (!subdrv) {
- DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n");
- return ret;
- }
+ if (!subdrv)
+ return -EINVAL;
- mutex_lock(&exynos_drm_mutex);
- if (drm_dev) {
- exynos_drm_subdrv_remove(drm_dev, subdrv);
- list_del(&subdrv->list);
+ list_del(&subdrv->list);
- /*
- * fb helper should be updated once a sub driver is released
- * to re-configure crtc and connector and also to re-setup
- * drm framebuffer.
- */
- ret = exynos_drm_fbdev_reinit(drm_dev);
- if (ret < 0) {
- DRM_ERROR("failed fb helper reinit.\n");
- goto fail;
- }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
- ret = exynos_drm_mode_group_reinit(drm_dev);
- if (ret < 0) {
- DRM_ERROR("failed drm mode group reinit.\n");
- goto fail;
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct exynos_drm_subdrv *subdrv;
+ int ret;
+
+ list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
+ if (subdrv->open) {
+ ret = subdrv->open(dev, subdrv->manager.dev, file);
+ if (ret)
+ goto err;
}
}
-fail:
- mutex_unlock(&exynos_drm_mutex);
+ return 0;
+
+err:
+ list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ if (subdrv->close)
+ subdrv->close(dev, subdrv->manager.dev, file);
+ }
return ret;
}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
+
+void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
+{
+ struct exynos_drm_subdrv *subdrv;
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Core Driver");
-MODULE_LICENSE("GPL");
+ list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
+ if (subdrv->close)
+ subdrv->close(dev, subdrv->manager.dev, file);
+ }
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index de818831a511..3486ffed0bf0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
{
DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode;
+ /*
+ * copy the mode data adjusted by mode_fixup() into crtc->mode
+ * so that hardware can be seet to proper mode.
+ */
+ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
return exynos_drm_crtc_update(crtc);
}
@@ -426,9 +430,3 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank);
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 09cc13f791b3..a6819b5f8428 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -38,6 +38,7 @@
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
+#include "exynos_drm_vidi.h"
#define DRIVER_NAME "exynos"
#define DRIVER_DESC "Samsung SoC DRM"
@@ -144,11 +145,34 @@ static int exynos_drm_unload(struct drm_device *dev)
return 0;
}
+static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ return exynos_drm_subdrv_open(dev, file);
+}
+
static void exynos_drm_preclose(struct drm_device *dev,
struct drm_file *file)
{
+ struct exynos_drm_private *private = dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ unsigned long flags;
+
DRM_DEBUG_DRIVER("%s\n", __FILE__);
+ /* release events of current file */
+ spin_lock_irqsave(&dev->event_lock, flags);
+ list_for_each_entry_safe(e, t, &private->pageflip_event_list,
+ base.link) {
+ if (e->base.file_priv == file) {
+ list_del(&e->base.link);
+ e->base.destroy(&e->base);
+ }
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ exynos_drm_subdrv_close(dev, file);
}
static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
@@ -185,6 +209,8 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl,
DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
+ vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
};
static const struct file_operations exynos_drm_driver_fops = {
@@ -202,6 +228,7 @@ static struct drm_driver exynos_drm_driver = {
DRIVER_MODESET | DRIVER_GEM,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
+ .open = exynos_drm_open,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.postclose = exynos_drm_postclose,
@@ -252,9 +279,60 @@ static struct platform_driver exynos_drm_platform_driver = {
static int __init exynos_drm_init(void)
{
+ int ret;
+
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- return platform_driver_register(&exynos_drm_platform_driver);
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ ret = platform_driver_register(&fimd_driver);
+ if (ret < 0)
+ goto out_fimd;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+ ret = platform_driver_register(&hdmi_driver);
+ if (ret < 0)
+ goto out_hdmi;
+ ret = platform_driver_register(&mixer_driver);
+ if (ret < 0)
+ goto out_mixer;
+ ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
+ if (ret < 0)
+ goto out_common_hdmi;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ ret = platform_driver_register(&vidi_driver);
+ if (ret < 0)
+ goto out_vidi;
+#endif
+
+ ret = platform_driver_register(&exynos_drm_platform_driver);
+ if (ret < 0)
+ goto out;
+
+ return 0;
+
+out:
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+out_vidi:
+ platform_driver_unregister(&vidi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+ platform_driver_unregister(&exynos_drm_common_hdmi_driver);
+out_common_hdmi:
+ platform_driver_unregister(&mixer_driver);
+out_mixer:
+ platform_driver_unregister(&hdmi_driver);
+out_hdmi:
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ platform_driver_unregister(&fimd_driver);
+out_fimd:
+#endif
+ return ret;
}
static void __exit exynos_drm_exit(void)
@@ -262,6 +340,20 @@ static void __exit exynos_drm_exit(void)
DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_driver_unregister(&exynos_drm_platform_driver);
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+ platform_driver_unregister(&exynos_drm_common_hdmi_driver);
+ platform_driver_unregister(&mixer_driver);
+ platform_driver_unregister(&hdmi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ platform_driver_unregister(&vidi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ platform_driver_unregister(&fimd_driver);
+#endif
}
module_init(exynos_drm_init);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index 13540de90bfc..fbd0a232c93d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -32,9 +32,9 @@
#include <linux/module.h>
#include "drm.h"
-#define MAX_CRTC 2
+#define MAX_CRTC 3
#define MAX_PLANE 5
-#define MAX_FB_BUFFER 3
+#define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1
struct drm_device;
@@ -50,6 +50,8 @@ enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_LCD,
/* HDMI Interface. */
EXYNOS_DISPLAY_TYPE_HDMI,
+ /* Virtual Display Interface. */
+ EXYNOS_DISPLAY_TYPE_VIDI,
};
/*
@@ -155,8 +157,10 @@ struct exynos_drm_display_ops {
*
* @dpms: control device power.
* @apply: set timing, vblank and overlay data to registers.
+ * @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
+ * @get_max_resol: get maximum resolution to specific hardware.
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
@@ -164,7 +168,13 @@ struct exynos_drm_display_ops {
struct exynos_drm_manager_ops {
void (*dpms)(struct device *subdrv_dev, int mode);
void (*apply)(struct device *subdrv_dev);
+ void (*mode_fixup)(struct device *subdrv_dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct device *subdrv_dev, void *mode);
+ void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
+ unsigned int *height);
void (*commit)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev);
@@ -217,10 +227,13 @@ struct exynos_drm_private {
* @list: sub driver has its own list object to register to exynos drm driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
+ * @is_local: appear encoder and connector disrelated device.
* @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
* by probe callback.
+ * @open: this would be called with drm device file open.
+ * @close: this would be called with drm device file close.
* @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager.
* @encoder: encoder object owned by this sub driver.
@@ -229,9 +242,14 @@ struct exynos_drm_private {
struct exynos_drm_subdrv {
struct list_head list;
struct drm_device *drm_dev;
+ bool is_local;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *dev);
+ int (*open)(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file);
+ void (*close)(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file);
struct exynos_drm_manager manager;
struct drm_encoder *encoder;
@@ -254,15 +272,19 @@ int exynos_drm_device_unregister(struct drm_device *dev);
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
* and when a sub driver is registered to exynos drm driver a probe callback
- * of the sub driver is called and creates its own encoder and connector
- * and then fb helper and drm mode group would be re-initialized.
+ * of the sub driver is called and creates its own encoder and connector.
*/
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
-/*
- * this function removes subdrv list from exynos drm driver and fb helper
- * and drm mode group would be re-initialized.
- */
+/* this function removes subdrv list from exynos drm driver */
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
+void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
+
+extern struct platform_driver fimd_driver;
+extern struct platform_driver hdmi_driver;
+extern struct platform_driver mixer_driver;
+extern struct platform_driver exynos_drm_common_hdmi_driver;
+extern struct platform_driver vidi_driver;
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index ef4754f1519b..6e9ac7bd1dcf 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -111,9 +111,19 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+
DRM_DEBUG_KMS("%s\n", __FILE__);
- /* drm framework doesn't check NULL. */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder)
+ if (manager_ops && manager_ops->mode_fixup)
+ manager_ops->mode_fixup(manager->dev, connector,
+ mode, adjusted_mode);
+ }
return true;
}
@@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode;
-
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
if (manager_ops && manager_ops->mode_set)
- manager_ops->mode_set(manager->dev, mode);
+ manager_ops->mode_set(manager->dev,
+ adjusted_mode);
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
@@ -209,6 +218,7 @@ static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
switch (display_ops->type) {
case EXYNOS_DISPLAY_TYPE_LCD:
case EXYNOS_DISPLAY_TYPE_HDMI:
+ case EXYNOS_DISPLAY_TYPE_VIDI:
clone_mask |= (1 << (cnt++));
break;
default:
@@ -433,9 +443,3 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 3733fe6723d3..c38c8f468fa3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -211,9 +211,3 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM FB Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 54f8f074822f..d5586cc75163 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -125,7 +125,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
}
size = mode_cmd.pitches[0] * mode_cmd.height;
- exynos_gem_obj = exynos_drm_gem_create(dev, size);
+
+ /* 0 means to allocate physically continuous memory */
+ exynos_gem_obj = exynos_drm_gem_create(dev, 0, size);
if (IS_ERR(exynos_gem_obj)) {
ret = PTR_ERR(exynos_gem_obj);
goto out;
@@ -314,89 +316,3 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
drm_fb_helper_restore_fbdev_mode(private->fb_helper);
}
-
-int exynos_drm_fbdev_reinit(struct drm_device *dev)
-{
- struct exynos_drm_private *private = dev->dev_private;
- struct drm_fb_helper *fb_helper;
- int ret;
-
- if (!private)
- return -EINVAL;
-
- /*
- * if all sub drivers were unloaded then num_connector is 0
- * so at this time, the framebuffers also should be destroyed.
- */
- if (!dev->mode_config.num_connector) {
- exynos_drm_fbdev_fini(dev);
- return 0;
- }
-
- fb_helper = private->fb_helper;
-
- if (fb_helper) {
- struct list_head temp_list;
-
- INIT_LIST_HEAD(&temp_list);
-
- /*
- * fb_helper is reintialized but kernel fb is reused
- * so kernel_fb_list need to be backuped and restored
- */
- if (!list_empty(&fb_helper->kernel_fb_list))
- list_replace_init(&fb_helper->kernel_fb_list,
- &temp_list);
-
- drm_fb_helper_fini(fb_helper);
-
- ret = drm_fb_helper_init(dev, fb_helper,
- dev->mode_config.num_crtc, MAX_CONNECTOR);
- if (ret < 0) {
- DRM_ERROR("failed to initialize drm fb helper\n");
- return ret;
- }
-
- if (!list_empty(&temp_list))
- list_replace(&temp_list, &fb_helper->kernel_fb_list);
-
- ret = drm_fb_helper_single_add_all_connectors(fb_helper);
- if (ret < 0) {
- DRM_ERROR("failed to add fb helper to connectors\n");
- goto err;
- }
-
- ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
- if (ret < 0) {
- DRM_ERROR("failed to set up hw configuration.\n");
- goto err;
- }
- } else {
- /*
- * if drm_load() failed whem drm load() was called prior
- * to specific drivers, fb_helper must be NULL and so
- * this fuction should be called again to re-initialize and
- * re-configure the fb helper. it means that this function
- * has been called by the specific drivers.
- */
- ret = exynos_drm_fbdev_init(dev);
- }
-
- return ret;
-
-err:
- /*
- * if drm_load() failed when drm load() was called prior
- * to specific drivers, the fb_helper must be NULL and so check it.
- */
- if (fb_helper)
- drm_fb_helper_fini(fb_helper);
-
- return ret;
-}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index 56458eea0501..ecb6db229700 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -1007,7 +1007,7 @@ static const struct dev_pm_ops fimd_pm_ops = {
SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
};
-static struct platform_driver fimd_driver = {
+struct platform_driver fimd_driver = {
.probe = fimd_probe,
.remove = __devexit_p(fimd_remove),
.driver = {
@@ -1016,21 +1016,3 @@ static struct platform_driver fimd_driver = {
.pm = &fimd_pm_ops,
},
};
-
-static int __init fimd_init(void)
-{
- return platform_driver_register(&fimd_driver);
-}
-
-static void __exit fimd_exit(void)
-{
- platform_driver_unregister(&fimd_driver);
-}
-
-module_init(fimd_init);
-module_exit(fimd_exit);
-
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM FIMD Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 025abb3e3b67..fa1aa94a3d8e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -26,6 +26,7 @@
#include "drmP.h"
#include "drm.h"
+#include <linux/shmem_fs.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
@@ -55,6 +56,178 @@ static unsigned int convert_to_vm_err_msg(int msg)
return out_msg;
}
+static unsigned int mask_gem_flags(unsigned int flags)
+{
+ return flags &= EXYNOS_BO_NONCONTIG;
+}
+
+static struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
+ gfp_t gfpmask)
+{
+ struct inode *inode;
+ struct address_space *mapping;
+ struct page *p, **pages;
+ int i, npages;
+
+ /* This is the shared memory object that backs the GEM resource */
+ inode = obj->filp->f_path.dentry->d_inode;
+ mapping = inode->i_mapping;
+
+ npages = obj->size >> PAGE_SHIFT;
+
+ pages = drm_malloc_ab(npages, sizeof(struct page *));
+ if (pages == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ gfpmask |= mapping_gfp_mask(mapping);
+
+ for (i = 0; i < npages; i++) {
+ p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+ if (IS_ERR(p))
+ goto fail;
+ pages[i] = p;
+ }
+
+ return pages;
+
+fail:
+ while (i--)
+ page_cache_release(pages[i]);
+
+ drm_free_large(pages);
+ return ERR_PTR(PTR_ERR(p));
+}
+
+static void exynos_gem_put_pages(struct drm_gem_object *obj,
+ struct page **pages,
+ bool dirty, bool accessed)
+{
+ int i, npages;
+
+ npages = obj->size >> PAGE_SHIFT;
+
+ for (i = 0; i < npages; i++) {
+ if (dirty)
+ set_page_dirty(pages[i]);
+
+ if (accessed)
+ mark_page_accessed(pages[i]);
+
+ /* Undo the reference we took when populating the table */
+ page_cache_release(pages[i]);
+ }
+
+ drm_free_large(pages);
+}
+
+static int exynos_drm_gem_map_pages(struct drm_gem_object *obj,
+ struct vm_area_struct *vma,
+ unsigned long f_vaddr,
+ pgoff_t page_offset)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+ unsigned long pfn;
+
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ unsigned long usize = buf->size;
+
+ if (!buf->pages)
+ return -EINTR;
+
+ while (usize > 0) {
+ pfn = page_to_pfn(buf->pages[page_offset++]);
+ vm_insert_mixed(vma, f_vaddr, pfn);
+ f_vaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ }
+
+ return 0;
+ }
+
+ pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset;
+
+ return vm_insert_mixed(vma, f_vaddr, pfn);
+}
+
+static int exynos_drm_gem_get_pages(struct drm_gem_object *obj)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+ struct scatterlist *sgl;
+ struct page **pages;
+ unsigned int npages, i = 0;
+ int ret;
+
+ if (buf->pages) {
+ DRM_DEBUG_KMS("already allocated.\n");
+ return -EINVAL;
+ }
+
+ pages = exynos_gem_get_pages(obj, GFP_KERNEL);
+ if (IS_ERR(pages)) {
+ DRM_ERROR("failed to get pages.\n");
+ return PTR_ERR(pages);
+ }
+
+ npages = obj->size >> PAGE_SHIFT;
+
+ buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!buf->sgt) {
+ DRM_ERROR("failed to allocate sg table.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize sg table.\n");
+ ret = -EFAULT;
+ goto err1;
+ }
+
+ sgl = buf->sgt->sgl;
+
+ /* set all pages to sg list. */
+ while (i < npages) {
+ sg_set_page(sgl, pages[i], PAGE_SIZE, 0);
+ sg_dma_address(sgl) = page_to_phys(pages[i]);
+ i++;
+ sgl = sg_next(sgl);
+ }
+
+ /* add some codes for UNCACHED type here. TODO */
+
+ buf->pages = pages;
+ return ret;
+err1:
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+err:
+ exynos_gem_put_pages(obj, pages, true, false);
+ return ret;
+
+}
+
+static void exynos_drm_gem_put_pages(struct drm_gem_object *obj)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+
+ /*
+ * if buffer typs is EXYNOS_BO_NONCONTIG then release all pages
+ * allocated at gem fault handler.
+ */
+ sg_free_table(buf->sgt);
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ exynos_gem_put_pages(obj, buf->pages, true, false);
+ buf->pages = NULL;
+
+ /* add some codes for UNCACHED type here. TODO */
+}
+
static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
struct drm_file *file_priv,
unsigned int *handle)
@@ -90,7 +263,15 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
- exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer);
+ if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) &&
+ exynos_gem_obj->buffer->pages)
+ exynos_drm_gem_put_pages(obj);
+ else
+ exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags,
+ exynos_gem_obj->buffer);
+
+ exynos_drm_fini_buf(obj->dev, exynos_gem_obj->buffer);
+ exynos_gem_obj->buffer = NULL;
if (obj->map_list.map)
drm_gem_free_mmap_offset(obj);
@@ -99,6 +280,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
drm_gem_object_release(obj);
kfree(exynos_gem_obj);
+ exynos_gem_obj = NULL;
}
static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
@@ -114,6 +296,7 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
return NULL;
}
+ exynos_gem_obj->size = size;
obj = &exynos_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size);
@@ -129,27 +312,55 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
}
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
- unsigned long size)
+ unsigned int flags,
+ unsigned long size)
{
- struct exynos_drm_gem_buf *buffer;
struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem_buf *buf;
+ int ret;
size = roundup(size, PAGE_SIZE);
DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
- buffer = exynos_drm_buf_create(dev, size);
- if (!buffer)
+ flags = mask_gem_flags(flags);
+
+ buf = exynos_drm_init_buf(dev, size);
+ if (!buf)
return ERR_PTR(-ENOMEM);
exynos_gem_obj = exynos_drm_gem_init(dev, size);
if (!exynos_gem_obj) {
- exynos_drm_buf_destroy(dev, buffer);
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto err;
}
- exynos_gem_obj->buffer = buffer;
+ exynos_gem_obj->buffer = buf;
+
+ /* set memory type and cache attribute from user side. */
+ exynos_gem_obj->flags = flags;
+
+ /*
+ * allocate all pages as desired size if user wants to allocate
+ * physically non-continuous memory.
+ */
+ if (flags & EXYNOS_BO_NONCONTIG) {
+ ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base);
+ if (ret < 0) {
+ drm_gem_object_release(&exynos_gem_obj->base);
+ goto err;
+ }
+ } else {
+ ret = exynos_drm_alloc_buf(dev, buf, flags);
+ if (ret < 0) {
+ drm_gem_object_release(&exynos_gem_obj->base);
+ goto err;
+ }
+ }
return exynos_gem_obj;
+err:
+ exynos_drm_fini_buf(dev, buf);
+ return ERR_PTR(ret);
}
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
@@ -161,7 +372,7 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
+ exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
@@ -175,6 +386,64 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
return 0;
}
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ exynos_gem_obj = to_exynos_gem_obj(obj);
+
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support NONCONTIG type.\n");
+ drm_gem_object_unreference_unlocked(obj);
+
+ /* TODO */
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &exynos_gem_obj->buffer->dma_addr;
+}
+
+void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return;
+ }
+
+ exynos_gem_obj = to_exynos_gem_obj(obj);
+
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support NONCONTIG type.\n");
+ drm_gem_object_unreference_unlocked(obj);
+
+ /* TODO */
+ return;
+ }
+
+ drm_gem_object_unreference_unlocked(obj);
+
+ /*
+ * decrease obj->refcount one more time because we has already
+ * increased it at exynos_drm_gem_get_dma_addr().
+ */
+ drm_gem_object_unreference_unlocked(obj);
+}
+
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -200,7 +469,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
struct drm_gem_object *obj = filp->private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_gem_buf *buffer;
- unsigned long pfn, vm_size;
+ unsigned long pfn, vm_size, usize, uaddr = vma->vm_start;
+ int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -208,9 +478,9 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
/* in case of direct mapping, always having non-cachable attribute */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vma->vm_file = filp;
- vm_size = vma->vm_end - vma->vm_start;
+ vm_size = usize = vma->vm_end - vma->vm_start;
+
/*
* a buffer contains information to physically continuous memory
* allocated by user request or at framebuffer creation.
@@ -221,18 +491,37 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
if (vm_size > buffer->size)
return -EINVAL;
- /*
- * get page frame number to physical memory to be mapped
- * to user space.
- */
- pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT;
-
- DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
-
- if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
- vma->vm_page_prot)) {
- DRM_ERROR("failed to remap pfn range.\n");
- return -EAGAIN;
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ int i = 0;
+
+ if (!buffer->pages)
+ return -EINVAL;
+
+ do {
+ ret = vm_insert_page(vma, uaddr, buffer->pages[i++]);
+ if (ret) {
+ DRM_ERROR("failed to remap user space.\n");
+ return ret;
+ }
+
+ uaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ } while (usize > 0);
+ } else {
+ /*
+ * get page frame number to physical memory to be mapped
+ * to user space.
+ */
+ pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
+ PAGE_SHIFT;
+
+ DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+ vma->vm_page_prot)) {
+ DRM_ERROR("failed to remap pfn range.\n");
+ return -EAGAIN;
+ }
}
return 0;
@@ -312,9 +601,9 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
*/
args->pitch = args->width * args->bpp >> 3;
- args->size = args->pitch * args->height;
+ args->size = PAGE_ALIGN(args->pitch * args->height);
- exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
+ exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
@@ -398,20 +687,31 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
struct drm_gem_object *obj = vma->vm_private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct drm_device *dev = obj->dev;
- unsigned long pfn;
+ unsigned long f_vaddr;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
+ f_vaddr = (unsigned long)vmf->virtual_address;
mutex_lock(&dev->struct_mutex);
- pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
- PAGE_SHIFT) + page_offset;
+ /*
+ * allocate all pages as desired size if user wants to allocate
+ * physically non-continuous memory.
+ */
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ ret = exynos_drm_gem_get_pages(obj);
+ if (ret < 0)
+ goto err;
+ }
- ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+ ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset);
+ if (ret < 0)
+ DRM_ERROR("failed to map pages.\n");
+err:
mutex_unlock(&dev->struct_mutex);
return convert_to_vm_err_msg(ret);
@@ -435,7 +735,3 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 67cdc9168708..e40fbad8b705 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -36,11 +36,15 @@
* @dma_addr: bus address(accessed by dma) to allocated memory region.
* - this address could be physical address without IOMMU and
* device address with IOMMU.
+ * @sgt: sg table to transfer page data.
+ * @pages: contain all pages to allocated memory region.
* @size: size of allocated memory region.
*/
struct exynos_drm_gem_buf {
void __iomem *kvaddr;
dma_addr_t dma_addr;
+ struct sg_table *sgt;
+ struct page **pages;
unsigned long size;
};
@@ -55,6 +59,8 @@ struct exynos_drm_gem_buf {
* by user request or at framebuffer creation.
* continuous memory region allocated by user request
* or at framebuffer creation.
+ * @size: total memory size to physically non-continuous memory region.
+ * @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transfered to user as kms_bo.handle so
* user can access the buffer through kms_bo.handle.
@@ -62,6 +68,8 @@ struct exynos_drm_gem_buf {
struct exynos_drm_gem_obj {
struct drm_gem_object base;
struct exynos_drm_gem_buf *buffer;
+ unsigned long size;
+ unsigned int flags;
};
/* destroy a buffer with gem object */
@@ -69,7 +77,8 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj);
/* create a new buffer with gem object */
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
- unsigned long size);
+ unsigned int flags,
+ unsigned long size);
/*
* request gem object creation and buffer allocation as the size
@@ -79,6 +88,24 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+/*
+ * get dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be increased.
+ */
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv);
+
+/*
+ * put dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be decreased.
+ */
+void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv);
+
/* get buffer offset to map to user space. */
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
index ed8a319ed84b..14eb26b0ba1c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
@@ -38,7 +38,6 @@ struct drm_hdmi_context {
struct exynos_drm_subdrv subdrv;
struct exynos_drm_hdmi_context *hdmi_ctx;
struct exynos_drm_hdmi_context *mixer_ctx;
- struct work_struct work;
};
void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
@@ -49,7 +48,6 @@ void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
if (display_ops)
hdmi_display_ops = display_ops;
}
-EXPORT_SYMBOL(exynos_drm_display_ops_register);
void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
*manager_ops)
@@ -59,7 +57,6 @@ void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
if (manager_ops)
hdmi_manager_ops = manager_ops;
}
-EXPORT_SYMBOL(exynos_drm_manager_ops_register);
void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
*overlay_ops)
@@ -69,7 +66,6 @@ void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
if (overlay_ops)
hdmi_overlay_ops = overlay_ops;
}
-EXPORT_SYMBOL(exynos_drm_overlay_ops_register);
static bool drm_hdmi_is_connected(struct device *dev)
{
@@ -155,6 +151,20 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx);
}
+static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_manager_ops && hdmi_manager_ops->mode_fixup)
+ hdmi_manager_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector,
+ mode, adjusted_mode);
+}
+
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -165,6 +175,18 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
}
+static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
+ unsigned int *width, unsigned int *height)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_manager_ops && hdmi_manager_ops->get_max_resol)
+ hdmi_manager_ops->get_max_resol(ctx->hdmi_ctx->ctx, width,
+ height);
+}
+
static void drm_hdmi_commit(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -200,7 +222,9 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
.dpms = drm_hdmi_dpms,
.enable_vblank = drm_hdmi_enable_vblank,
.disable_vblank = drm_hdmi_disable_vblank,
+ .mode_fixup = drm_hdmi_mode_fixup,
.mode_set = drm_hdmi_mode_set,
+ .get_max_resol = drm_hdmi_get_max_resol,
.commit = drm_hdmi_commit,
};
@@ -249,7 +273,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
struct drm_hdmi_context *ctx;
struct platform_device *pdev = to_platform_device(dev);
struct exynos_drm_common_hdmi_pd *pd;
- int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -270,26 +293,13 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
return -EFAULT;
}
- ret = platform_driver_register(&hdmi_driver);
- if (ret) {
- DRM_DEBUG_KMS("failed to register hdmi driver.\n");
- return ret;
- }
-
- ret = platform_driver_register(&mixer_driver);
- if (ret) {
- DRM_DEBUG_KMS("failed to register mixer driver.\n");
- goto err_hdmidrv;
- }
-
ctx = get_ctx_from_subdrv(subdrv);
ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
to_context(pd->hdmi_dev);
if (!ctx->hdmi_ctx) {
DRM_DEBUG_KMS("hdmi context is null.\n");
- ret = -EFAULT;
- goto err_mixerdrv;
+ return -EFAULT;
}
ctx->hdmi_ctx->drm_dev = drm_dev;
@@ -298,42 +308,12 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
to_context(pd->mixer_dev);
if (!ctx->mixer_ctx) {
DRM_DEBUG_KMS("mixer context is null.\n");
- ret = -EFAULT;
- goto err_mixerdrv;
+ return -EFAULT;
}
ctx->mixer_ctx->drm_dev = drm_dev;
return 0;
-
-err_mixerdrv:
- platform_driver_unregister(&mixer_driver);
-err_hdmidrv:
- platform_driver_unregister(&hdmi_driver);
- return ret;
-}
-
-static void hdmi_subdrv_remove(struct drm_device *drm_dev)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- platform_driver_unregister(&hdmi_driver);
- platform_driver_unregister(&mixer_driver);
-}
-
-static void exynos_drm_hdmi_late_probe(struct work_struct *work)
-{
- struct drm_hdmi_context *ctx = container_of(work,
- struct drm_hdmi_context, work);
-
- /*
- * this function calls subdrv->probe() so this must be called
- * after probe context.
- *
- * PS. subdrv->probe() will call platform_driver_register() to probe
- * hdmi and mixer driver.
- */
- exynos_drm_subdrv_register(&ctx->subdrv);
}
static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
@@ -353,7 +333,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
subdrv = &ctx->subdrv;
subdrv->probe = hdmi_subdrv_probe;
- subdrv->remove = hdmi_subdrv_remove;
subdrv->manager.pipe = -1;
subdrv->manager.ops = &drm_hdmi_manager_ops;
subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops;
@@ -362,9 +341,7 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, subdrv);
- INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe);
-
- schedule_work(&ctx->work);
+ exynos_drm_subdrv_register(subdrv);
return 0;
}
@@ -400,7 +377,7 @@ static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver exynos_drm_common_hdmi_driver = {
+struct platform_driver exynos_drm_common_hdmi_driver = {
.probe = exynos_drm_hdmi_probe,
.remove = __devexit_p(exynos_drm_hdmi_remove),
.driver = {
@@ -409,31 +386,3 @@ static struct platform_driver exynos_drm_common_hdmi_driver = {
.pm = &hdmi_pm_ops,
},
};
-
-static int __init exynos_drm_hdmi_init(void)
-{
- int ret;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
- if (ret) {
- DRM_DEBUG_KMS("failed to register hdmi common driver.\n");
- return ret;
- }
-
- return ret;
-}
-
-static void __exit exynos_drm_hdmi_exit(void)
-{
- platform_driver_unregister(&exynos_drm_common_hdmi_driver);
-}
-
-module_init(exynos_drm_hdmi_init);
-module_exit(exynos_drm_hdmi_exit);
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
index 3c29f790ee45..44497cfb6c74 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
@@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops {
};
struct exynos_hdmi_manager_ops {
+ void (*mode_fixup)(void *ctx, struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
void (*mode_set)(void *ctx, void *mode);
+ void (*get_max_resol)(void *ctx, unsigned int *width,
+ unsigned int *height);
void (*commit)(void *ctx);
void (*disable)(void *ctx);
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index bdcf770aa22e..c277a3a445f5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -22,6 +22,10 @@ struct exynos_plane {
bool enabled;
};
+static const uint32_t formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
static int
exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -115,9 +119,9 @@ int exynos_plane_init(struct drm_device *dev, unsigned int nr)
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
- /* TODO: format */
return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
- &exynos_plane_funcs, NULL, 0, false);
+ &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
+ false);
}
int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
new file mode 100644
index 000000000000..8e1339f9fe1f
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -0,0 +1,676 @@
+/* exynos_drm_vidi.c
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include "drmP.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/exynos_drm.h>
+
+#include "drm_edid.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_encoder.h"
+
+/* vidi has totally three virtual windows. */
+#define WINDOWS_NR 3
+
+#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev))
+
+struct vidi_win_data {
+ unsigned int offset_x;
+ unsigned int offset_y;
+ unsigned int ovl_width;
+ unsigned int ovl_height;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int bpp;
+ dma_addr_t dma_addr;
+ void __iomem *vaddr;
+ unsigned int buf_offsize;
+ unsigned int line_size; /* bytes */
+ bool enabled;
+};
+
+struct vidi_context {
+ struct exynos_drm_subdrv subdrv;
+ struct drm_crtc *crtc;
+ struct vidi_win_data win_data[WINDOWS_NR];
+ struct edid *raw_edid;
+ unsigned int clkdiv;
+ unsigned int default_win;
+ unsigned long irq_flags;
+ unsigned int connected;
+ bool vblank_on;
+ bool suspended;
+ struct work_struct work;
+ struct mutex lock;
+};
+
+static const char fake_edid_info[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
+ 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd,
+ 0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00,
+ 0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e,
+ 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
+ 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
+ 0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1,
+ 0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83,
+ 0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00,
+ 0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c,
+ 0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a,
+ 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00,
+ 0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06
+};
+
+static void vidi_fake_vblank_handler(struct work_struct *work);
+
+static bool vidi_display_is_connected(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * connection request would come from user side
+ * to do hotplug through specific ioctl.
+ */
+ return ctx->connected ? true : false;
+}
+
+static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
+ u8 *edid, int len)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct edid *raw_edid;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * the edid data comes from user side and it would be set
+ * to ctx->raw_edid through specific ioctl.
+ */
+ if (!ctx->raw_edid) {
+ DRM_DEBUG_KMS("raw_edid is null.\n");
+ return -EFAULT;
+ }
+
+ raw_edid = kzalloc(len, GFP_KERNEL);
+ if (!raw_edid) {
+ DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
+ return -ENOMEM;
+ }
+
+ memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
+ * EDID_LENGTH, len));
+
+ /* attach the edid data to connector. */
+ connector->display_info.raw_edid = (char *)raw_edid;
+
+ memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
+ * EDID_LENGTH, len));
+
+ return 0;
+}
+
+static void *vidi_get_panel(struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return NULL;
+}
+
+static int vidi_check_timing(struct device *dev, void *timing)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return 0;
+}
+
+static int vidi_display_power_on(struct device *dev, int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static struct exynos_drm_display_ops vidi_display_ops = {
+ .type = EXYNOS_DISPLAY_TYPE_VIDI,
+ .is_connected = vidi_display_is_connected,
+ .get_edid = vidi_get_edid,
+ .get_panel = vidi_get_panel,
+ .check_timing = vidi_check_timing,
+ .power_on = vidi_display_power_on,
+};
+
+static void vidi_dpms(struct device *subdrv_dev, int mode)
+{
+ struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+
+ mutex_lock(&ctx->lock);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ /* TODO. */
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ /* TODO. */
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&ctx->lock);
+}
+
+static void vidi_apply(struct device *subdrv_dev)
+{
+ struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+ struct exynos_drm_manager *mgr = &ctx->subdrv.manager;
+ struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
+ struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
+ struct vidi_win_data *win_data;
+ int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ if (win_data->enabled && (ovl_ops && ovl_ops->commit))
+ ovl_ops->commit(subdrv_dev, i);
+ }
+
+ if (mgr_ops && mgr_ops->commit)
+ mgr_ops->commit(subdrv_dev);
+}
+
+static void vidi_commit(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+}
+
+static int vidi_enable_vblank(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return -EPERM;
+
+ if (!test_and_set_bit(0, &ctx->irq_flags))
+ ctx->vblank_on = true;
+
+ return 0;
+}
+
+static void vidi_disable_vblank(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ ctx->vblank_on = false;
+}
+
+static struct exynos_drm_manager_ops vidi_manager_ops = {
+ .dpms = vidi_dpms,
+ .apply = vidi_apply,
+ .commit = vidi_commit,
+ .enable_vblank = vidi_enable_vblank,
+ .disable_vblank = vidi_disable_vblank,
+};
+
+static void vidi_win_mode_set(struct device *dev,
+ struct exynos_drm_overlay *overlay)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_win_data *win_data;
+ int win;
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!overlay) {
+ dev_err(dev, "overlay is NULL\n");
+ return;
+ }
+
+ win = overlay->zpos;
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ offset = overlay->fb_x * (overlay->bpp >> 3);
+ offset += overlay->fb_y * overlay->pitch;
+
+ DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
+
+ win_data = &ctx->win_data[win];
+
+ win_data->offset_x = overlay->crtc_x;
+ win_data->offset_y = overlay->crtc_y;
+ win_data->ovl_width = overlay->crtc_width;
+ win_data->ovl_height = overlay->crtc_height;
+ win_data->fb_width = overlay->fb_width;
+ win_data->fb_height = overlay->fb_height;
+ win_data->dma_addr = overlay->dma_addr[0] + offset;
+ win_data->vaddr = overlay->vaddr[0] + offset;
+ win_data->bpp = overlay->bpp;
+ win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
+ (overlay->bpp >> 3);
+ win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
+
+ /*
+ * some parts of win_data should be transferred to user side
+ * through specific ioctl.
+ */
+
+ DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+ win_data->offset_x, win_data->offset_y);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+ DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
+ (unsigned long)win_data->dma_addr,
+ (unsigned long)win_data->vaddr);
+ DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
+ overlay->fb_width, overlay->crtc_width);
+}
+
+static void vidi_win_commit(struct device *dev, int zpos)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_win_data *win_data;
+ int win = zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+
+ win_data->enabled = true;
+
+ DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr);
+
+ if (ctx->vblank_on)
+ schedule_work(&ctx->work);
+}
+
+static void vidi_win_disable(struct device *dev, int zpos)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_win_data *win_data;
+ int win = zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+ win_data->enabled = false;
+
+ /* TODO. */
+}
+
+static struct exynos_drm_overlay_ops vidi_overlay_ops = {
+ .mode_set = vidi_win_mode_set,
+ .commit = vidi_win_commit,
+ .disable = vidi_win_disable,
+};
+
+static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc)
+{
+ struct exynos_drm_private *dev_priv = drm_dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ struct timeval now;
+ unsigned long flags;
+ bool is_checked = false;
+
+ spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+ list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
+ base.link) {
+ /* if event's pipe isn't same as crtc then ignore it. */
+ if (crtc != e->pipe)
+ continue;
+
+ is_checked = true;
+
+ do_gettimeofday(&now);
+ e->event.sequence = 0;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+ }
+
+ if (is_checked) {
+ /*
+ * call drm_vblank_put only in case that drm_vblank_get was
+ * called.
+ */
+ if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+ drm_vblank_put(drm_dev, crtc);
+
+ /*
+ * don't off vblank if vblank_disable_allowed is 1,
+ * because vblank would be off by timer handler.
+ */
+ if (!drm_dev->vblank_disable_allowed)
+ drm_vblank_off(drm_dev, crtc);
+ }
+
+ spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static void vidi_fake_vblank_handler(struct work_struct *work)
+{
+ struct vidi_context *ctx = container_of(work, struct vidi_context,
+ work);
+ struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+ struct exynos_drm_manager *manager = &subdrv->manager;
+
+ if (manager->pipe < 0)
+ return;
+
+ /* refresh rate is about 50Hz. */
+ usleep_range(16000, 20000);
+
+ drm_handle_vblank(subdrv->drm_dev, manager->pipe);
+ vidi_finish_pageflip(subdrv->drm_dev, manager->pipe);
+}
+
+static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ drm_dev->irq_enabled = 1;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = 1;
+
+ return 0;
+}
+
+static void vidi_subdrv_remove(struct drm_device *drm_dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+}
+
+static int vidi_power_on(struct vidi_context *ctx, bool enable)
+{
+ struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+ struct device *dev = subdrv->manager.dev;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (enable != false && enable != true)
+ return -EINVAL;
+
+ if (enable) {
+ ctx->suspended = false;
+
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ vidi_enable_vblank(dev);
+
+ vidi_apply(dev);
+ } else {
+ ctx->suspended = true;
+ }
+
+ return 0;
+}
+
+static int vidi_show_connection(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ mutex_lock(&ctx->lock);
+
+ rc = sprintf(buf, "%d\n", ctx->connected);
+
+ mutex_unlock(&ctx->lock);
+
+ return rc;
+}
+
+static int vidi_store_connection(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ret = kstrtoint(buf, 0, &ctx->connected);
+ if (ret)
+ return ret;
+
+ if (ctx->connected > 1)
+ return -EINVAL;
+
+ DRM_DEBUG_KMS("requested connection.\n");
+
+ drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+
+ return len;
+}
+
+static DEVICE_ATTR(connection, 0644, vidi_show_connection,
+ vidi_store_connection);
+
+int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct vidi_context *ctx = NULL;
+ struct drm_encoder *encoder;
+ struct exynos_drm_manager *manager;
+ struct exynos_drm_display_ops *display_ops;
+ struct drm_exynos_vidi_connection *vidi = data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!vidi) {
+ DRM_DEBUG_KMS("user data for vidi is null.\n");
+ return -EINVAL;
+ }
+
+ if (!vidi->edid) {
+ DRM_DEBUG_KMS("edid data is null.\n");
+ return -EINVAL;
+ }
+
+ if (vidi->connection > 1) {
+ DRM_DEBUG_KMS("connection should be 0 or 1.\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
+ head) {
+ manager = exynos_drm_get_manager(encoder);
+ display_ops = manager->display_ops;
+
+ if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
+ ctx = get_vidi_context(manager->dev);
+ break;
+ }
+ }
+
+ if (!ctx) {
+ DRM_DEBUG_KMS("not found virtual device type encoder.\n");
+ return -EINVAL;
+ }
+
+ if (ctx->connected == vidi->connection) {
+ DRM_DEBUG_KMS("same connection request.\n");
+ return -EINVAL;
+ }
+
+ if (vidi->connection)
+ ctx->raw_edid = (struct edid *)vidi->edid;
+
+ ctx->connected = vidi->connection;
+ drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+
+ return 0;
+}
+
+static int __devinit vidi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vidi_context *ctx;
+ struct exynos_drm_subdrv *subdrv;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->default_win = 0;
+
+ INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
+
+ /* for test */
+ ctx->raw_edid = (struct edid *)fake_edid_info;
+
+ subdrv = &ctx->subdrv;
+ subdrv->probe = vidi_subdrv_probe;
+ subdrv->remove = vidi_subdrv_remove;
+ subdrv->manager.pipe = -1;
+ subdrv->manager.ops = &vidi_manager_ops;
+ subdrv->manager.overlay_ops = &vidi_overlay_ops;
+ subdrv->manager.display_ops = &vidi_display_ops;
+ subdrv->manager.dev = dev;
+
+ mutex_init(&ctx->lock);
+
+ platform_set_drvdata(pdev, ctx);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_connection);
+ if (ret < 0)
+ DRM_INFO("failed to create connection sysfs.\n");
+
+ exynos_drm_subdrv_register(subdrv);
+
+ return 0;
+}
+
+static int __devexit vidi_remove(struct platform_device *pdev)
+{
+ struct vidi_context *ctx = platform_get_drvdata(pdev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_drm_subdrv_unregister(&ctx->subdrv);
+
+ kfree(ctx);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vidi_suspend(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ return vidi_power_on(ctx, false);
+}
+
+static int vidi_resume(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ return vidi_power_on(ctx, true);
+}
+#endif
+
+static const struct dev_pm_ops vidi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
+};
+
+struct platform_driver vidi_driver = {
+ .probe = vidi_probe,
+ .remove = __devexit_p(vidi_remove),
+ .driver = {
+ .name = "exynos-drm-vidi",
+ .owner = THIS_MODULE,
+ .pm = &vidi_pm_ops,
+ },
+};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.h b/drivers/gpu/drm/exynos/exynos_drm_vidi.h
new file mode 100644
index 000000000000..a4babe4e65d7
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.h
@@ -0,0 +1,36 @@
+/* exynos_drm_vidi.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Author: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_VIDI_H_
+#define _EXYNOS_DRM_VIDI_H_
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
+ struct drm_file *file_priv);
+#else
+#define vidi_connection_ioctl NULL
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 3429d3fd93f3..575a8cbd3533 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -41,44 +41,83 @@
#include "exynos_hdmi.h"
#define HDMI_OVERLAY_NUMBER 3
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1080
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
-static const u8 hdmiphy_conf27[32] = {
+struct hdmi_resources {
+ struct clk *hdmi;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_pixel;
+ struct clk *sclk_hdmiphy;
+ struct clk *hdmiphy;
+ struct regulator_bulk_data *regul_bulk;
+ int regul_count;
+};
+
+struct hdmi_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct fb_videomode *default_timing;
+ unsigned int is_v13:1;
+ unsigned int default_win;
+ unsigned int default_bpp;
+ bool hpd_handle;
+ bool enabled;
+
+ struct resource *regs_res;
+ void __iomem *regs;
+ unsigned int irq;
+ struct workqueue_struct *wq;
+ struct work_struct hotplug_work;
+
+ struct i2c_client *ddc_port;
+ struct i2c_client *hdmiphy_port;
+
+ /* current hdmiphy conf index */
+ int cur_conf;
+
+ struct hdmi_resources res;
+ void *parent_ctx;
+};
+
+/* HDMI Version 1.3 */
+static const u8 hdmiphy_v13_conf27[32] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf27_027[32] = {
+static const u8 hdmiphy_v13_conf27_027[32] = {
0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf74_175[32] = {
+static const u8 hdmiphy_v13_conf74_175[32] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf74_25[32] = {
+static const u8 hdmiphy_v13_conf74_25[32] = {
0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf148_5[32] = {
+static const u8 hdmiphy_v13_conf148_5[32] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
};
-struct hdmi_tg_regs {
+struct hdmi_v13_tg_regs {
u8 cmd;
u8 h_fsz_l;
u8 h_fsz_h;
@@ -110,7 +149,7 @@ struct hdmi_tg_regs {
u8 field_bot_hdmi_h;
};
-struct hdmi_core_regs {
+struct hdmi_v13_core_regs {
u8 h_blank[2];
u8 v_blank[3];
u8 h_v_line[3];
@@ -123,12 +162,21 @@ struct hdmi_core_regs {
u8 v_sync_gen3[3];
};
-struct hdmi_preset_conf {
- struct hdmi_core_regs core;
- struct hdmi_tg_regs tg;
+struct hdmi_v13_preset_conf {
+ struct hdmi_v13_core_regs core;
+ struct hdmi_v13_tg_regs tg;
};
-static const struct hdmi_preset_conf hdmi_conf_480p = {
+struct hdmi_v13_conf {
+ int width;
+ int height;
+ int vrefresh;
+ bool interlace;
+ const u8 *hdmiphy_data;
+ const struct hdmi_v13_preset_conf *conf;
+};
+
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = {
.core = {
.h_blank = {0x8a, 0x00},
.v_blank = {0x0d, 0x6a, 0x01},
@@ -154,7 +202,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = {
.core = {
.h_blank = {0x72, 0x01},
.v_blank = {0xee, 0xf2, 0x00},
@@ -182,7 +230,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = {
.core = {
.h_blank = {0xd0, 0x02},
.v_blank = {0x32, 0xB2, 0x00},
@@ -210,7 +258,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = {
.core = {
.h_blank = {0xd0, 0x02},
.v_blank = {0x65, 0x6c, 0x01},
@@ -238,7 +286,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = {
.core = {
.h_blank = {0x18, 0x01},
.v_blank = {0x32, 0xB2, 0x00},
@@ -266,7 +314,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = {
.core = {
.h_blank = {0x18, 0x01},
.v_blank = {0x65, 0x6c, 0x01},
@@ -294,13 +342,530 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
},
};
+static const struct hdmi_v13_conf hdmi_v13_confs[] = {
+ { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 },
+ { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 },
+ { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p },
+ { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 },
+ { 1920, 1080, 50, false, hdmiphy_v13_conf148_5,
+ &hdmi_v13_conf_1080p50 },
+ { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 },
+ { 1920, 1080, 60, false, hdmiphy_v13_conf148_5,
+ &hdmi_v13_conf_1080p60 },
+};
+
+/* HDMI Version 1.4 */
+static const u8 hdmiphy_conf27_027[32] = {
+ 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
+ 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
+};
+
+static const u8 hdmiphy_conf74_25[32] = {
+ 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
+ 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
+};
+
+static const u8 hdmiphy_conf148_5[32] = {
+ 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
+ 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+};
+
+struct hdmi_tg_regs {
+ u8 cmd;
+ u8 h_fsz_l;
+ u8 h_fsz_h;
+ u8 hact_st_l;
+ u8 hact_st_h;
+ u8 hact_sz_l;
+ u8 hact_sz_h;
+ u8 v_fsz_l;
+ u8 v_fsz_h;
+ u8 vsync_l;
+ u8 vsync_h;
+ u8 vsync2_l;
+ u8 vsync2_h;
+ u8 vact_st_l;
+ u8 vact_st_h;
+ u8 vact_sz_l;
+ u8 vact_sz_h;
+ u8 field_chg_l;
+ u8 field_chg_h;
+ u8 vact_st2_l;
+ u8 vact_st2_h;
+ u8 vact_st3_l;
+ u8 vact_st3_h;
+ u8 vact_st4_l;
+ u8 vact_st4_h;
+ u8 vsync_top_hdmi_l;
+ u8 vsync_top_hdmi_h;
+ u8 vsync_bot_hdmi_l;
+ u8 vsync_bot_hdmi_h;
+ u8 field_top_hdmi_l;
+ u8 field_top_hdmi_h;
+ u8 field_bot_hdmi_l;
+ u8 field_bot_hdmi_h;
+ u8 tg_3d;
+};
+
+struct hdmi_core_regs {
+ u8 h_blank[2];
+ u8 v2_blank[2];
+ u8 v1_blank[2];
+ u8 v_line[2];
+ u8 h_line[2];
+ u8 hsync_pol[1];
+ u8 vsync_pol[1];
+ u8 int_pro_mode[1];
+ u8 v_blank_f0[2];
+ u8 v_blank_f1[2];
+ u8 h_sync_start[2];
+ u8 h_sync_end[2];
+ u8 v_sync_line_bef_2[2];
+ u8 v_sync_line_bef_1[2];
+ u8 v_sync_line_aft_2[2];
+ u8 v_sync_line_aft_1[2];
+ u8 v_sync_line_aft_pxl_2[2];
+ u8 v_sync_line_aft_pxl_1[2];
+ u8 v_blank_f2[2]; /* for 3D mode */
+ u8 v_blank_f3[2]; /* for 3D mode */
+ u8 v_blank_f4[2]; /* for 3D mode */
+ u8 v_blank_f5[2]; /* for 3D mode */
+ u8 v_sync_line_aft_3[2];
+ u8 v_sync_line_aft_4[2];
+ u8 v_sync_line_aft_5[2];
+ u8 v_sync_line_aft_6[2];
+ u8 v_sync_line_aft_pxl_3[2];
+ u8 v_sync_line_aft_pxl_4[2];
+ u8 v_sync_line_aft_pxl_5[2];
+ u8 v_sync_line_aft_pxl_6[2];
+ u8 vact_space_1[2];
+ u8 vact_space_2[2];
+ u8 vact_space_3[2];
+ u8 vact_space_4[2];
+ u8 vact_space_5[2];
+ u8 vact_space_6[2];
+};
+
+struct hdmi_preset_conf {
+ struct hdmi_core_regs core;
+ struct hdmi_tg_regs tg;
+};
+
+struct hdmi_conf {
+ int width;
+ int height;
+ int vrefresh;
+ bool interlace;
+ const u8 *hdmiphy_data;
+ const struct hdmi_preset_conf *conf;
+};
+
+static const struct hdmi_preset_conf hdmi_conf_480p60 = {
+ .core = {
+ .h_blank = {0x8a, 0x00},
+ .v2_blank = {0x0d, 0x02},
+ .v1_blank = {0x2d, 0x00},
+ .v_line = {0x0d, 0x02},
+ .h_line = {0x5a, 0x03},
+ .hsync_pol = {0x01},
+ .vsync_pol = {0x01},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x0e, 0x00},
+ .h_sync_end = {0x4c, 0x00},
+ .v_sync_line_bef_2 = {0x0f, 0x00},
+ .v_sync_line_bef_1 = {0x09, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x5a, 0x03, /* h_fsz */
+ 0x8a, 0x00, 0xd0, 0x02, /* hact */
+ 0x0d, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0xe0, 0x01, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p50 = {
+ .core = {
+ .h_blank = {0xbc, 0x02},
+ .v2_blank = {0xee, 0x02},
+ .v1_blank = {0x1e, 0x00},
+ .v_line = {0xee, 0x02},
+ .h_line = {0xbc, 0x07},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0xb6, 0x01},
+ .h_sync_end = {0xde, 0x01},
+ .v_sync_line_bef_2 = {0x0a, 0x00},
+ .v_sync_line_bef_1 = {0x05, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0xbc, 0x07, /* h_fsz */
+ 0xbc, 0x02, 0x00, 0x05, /* hact */
+ 0xee, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x1e, 0x00, 0xd0, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+ .core = {
+ .h_blank = {0x72, 0x01},
+ .v2_blank = {0xee, 0x02},
+ .v1_blank = {0x1e, 0x00},
+ .v_line = {0xee, 0x02},
+ .h_line = {0x72, 0x06},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x6c, 0x00},
+ .h_sync_end = {0x94, 0x00},
+ .v_sync_line_bef_2 = {0x0a, 0x00},
+ .v_sync_line_bef_1 = {0x05, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x72, 0x06, /* h_fsz */
+ 0x72, 0x01, 0x00, 0x05, /* hact */
+ 0xee, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x1e, 0x00, 0xd0, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
+ .core = {
+ .h_blank = {0xd0, 0x02},
+ .v2_blank = {0x32, 0x02},
+ .v1_blank = {0x16, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x50, 0x0a},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x01},
+ .v_blank_f0 = {0x49, 0x02},
+ .v_blank_f1 = {0x65, 0x04},
+ .h_sync_start = {0x0e, 0x02},
+ .h_sync_end = {0x3a, 0x02},
+ .v_sync_line_bef_2 = {0x07, 0x00},
+ .v_sync_line_bef_1 = {0x02, 0x00},
+ .v_sync_line_aft_2 = {0x39, 0x02},
+ .v_sync_line_aft_1 = {0x34, 0x02},
+ .v_sync_line_aft_pxl_2 = {0x38, 0x07},
+ .v_sync_line_aft_pxl_1 = {0x38, 0x07},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x50, 0x0a, /* h_fsz */
+ 0xd0, 0x02, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x16, 0x00, 0x1c, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
+ .core = {
+ .h_blank = {0x18, 0x01},
+ .v2_blank = {0x32, 0x02},
+ .v1_blank = {0x16, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x98, 0x08},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x01},
+ .v_blank_f0 = {0x49, 0x02},
+ .v_blank_f1 = {0x65, 0x04},
+ .h_sync_start = {0x56, 0x00},
+ .h_sync_end = {0x82, 0x00},
+ .v_sync_line_bef_2 = {0x07, 0x00},
+ .v_sync_line_bef_1 = {0x02, 0x00},
+ .v_sync_line_aft_2 = {0x39, 0x02},
+ .v_sync_line_aft_1 = {0x34, 0x02},
+ .v_sync_line_aft_pxl_2 = {0xa4, 0x04},
+ .v_sync_line_aft_pxl_1 = {0xa4, 0x04},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x16, 0x00, 0x1c, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+ .core = {
+ .h_blank = {0xd0, 0x02},
+ .v2_blank = {0x65, 0x04},
+ .v1_blank = {0x2d, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x50, 0x0a},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x0e, 0x02},
+ .h_sync_end = {0x3a, 0x02},
+ .v_sync_line_bef_2 = {0x09, 0x00},
+ .v_sync_line_bef_1 = {0x04, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x50, 0x0a, /* h_fsz */
+ 0xd0, 0x02, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+ .core = {
+ .h_blank = {0x18, 0x01},
+ .v2_blank = {0x65, 0x04},
+ .v1_blank = {0x2d, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x98, 0x08},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x56, 0x00},
+ .h_sync_end = {0x82, 0x00},
+ .v_sync_line_bef_2 = {0x09, 0x00},
+ .v_sync_line_bef_1 = {0x04, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
static const struct hdmi_conf hdmi_confs[] = {
+ { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 },
+ { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 },
{ 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
- { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
- { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p },
{ 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 },
- { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
{ 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 },
+ { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
{ 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 },
};
@@ -324,7 +889,7 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
writel(value, hdata->regs + reg_id);
}
-static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
+static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
{
#define DUMPREG(reg_id) \
DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
@@ -333,6 +898,101 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_INTC_FLAG);
DUMPREG(HDMI_INTC_CON);
DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_V13_PHY_RSTOUT);
+ DUMPREG(HDMI_V13_PHY_VPLL);
+ DUMPREG(HDMI_V13_PHY_CMU);
+ DUMPREG(HDMI_V13_CORE_RSTOUT);
+
+ DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_CON_0);
+ DUMPREG(HDMI_CON_1);
+ DUMPREG(HDMI_CON_2);
+ DUMPREG(HDMI_SYS_STATUS);
+ DUMPREG(HDMI_V13_PHY_STATUS);
+ DUMPREG(HDMI_STATUS_EN);
+ DUMPREG(HDMI_HPD);
+ DUMPREG(HDMI_MODE_SEL);
+ DUMPREG(HDMI_V13_HPD_GEN);
+ DUMPREG(HDMI_V13_DC_CONTROL);
+ DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN);
+
+ DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_H_BLANK_0);
+ DUMPREG(HDMI_H_BLANK_1);
+ DUMPREG(HDMI_V13_V_BLANK_0);
+ DUMPREG(HDMI_V13_V_BLANK_1);
+ DUMPREG(HDMI_V13_V_BLANK_2);
+ DUMPREG(HDMI_V13_H_V_LINE_0);
+ DUMPREG(HDMI_V13_H_V_LINE_1);
+ DUMPREG(HDMI_V13_H_V_LINE_2);
+ DUMPREG(HDMI_VSYNC_POL);
+ DUMPREG(HDMI_INT_PRO_MODE);
+ DUMPREG(HDMI_V13_V_BLANK_F_0);
+ DUMPREG(HDMI_V13_V_BLANK_F_1);
+ DUMPREG(HDMI_V13_V_BLANK_F_2);
+ DUMPREG(HDMI_V13_H_SYNC_GEN_0);
+ DUMPREG(HDMI_V13_H_SYNC_GEN_1);
+ DUMPREG(HDMI_V13_H_SYNC_GEN_2);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_1_0);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_1_1);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_1_2);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_2_0);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_2_1);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_2_2);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_3_0);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_3_1);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_3_2);
+
+ DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_TG_CMD);
+ DUMPREG(HDMI_TG_H_FSZ_L);
+ DUMPREG(HDMI_TG_H_FSZ_H);
+ DUMPREG(HDMI_TG_HACT_ST_L);
+ DUMPREG(HDMI_TG_HACT_ST_H);
+ DUMPREG(HDMI_TG_HACT_SZ_L);
+ DUMPREG(HDMI_TG_HACT_SZ_H);
+ DUMPREG(HDMI_TG_V_FSZ_L);
+ DUMPREG(HDMI_TG_V_FSZ_H);
+ DUMPREG(HDMI_TG_VSYNC_L);
+ DUMPREG(HDMI_TG_VSYNC_H);
+ DUMPREG(HDMI_TG_VSYNC2_L);
+ DUMPREG(HDMI_TG_VSYNC2_H);
+ DUMPREG(HDMI_TG_VACT_ST_L);
+ DUMPREG(HDMI_TG_VACT_ST_H);
+ DUMPREG(HDMI_TG_VACT_SZ_L);
+ DUMPREG(HDMI_TG_VACT_SZ_H);
+ DUMPREG(HDMI_TG_FIELD_CHG_L);
+ DUMPREG(HDMI_TG_FIELD_CHG_H);
+ DUMPREG(HDMI_TG_VACT_ST2_L);
+ DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+#undef DUMPREG
+}
+
+static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)
+{
+ int i;
+
+#define DUMPREG(reg_id) \
+ DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
+ readl(hdata->regs + reg_id))
+
+ DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_INTC_CON);
+ DUMPREG(HDMI_INTC_FLAG);
+ DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_INTC_CON_1);
+ DUMPREG(HDMI_INTC_FLAG_1);
+ DUMPREG(HDMI_PHY_STATUS_0);
+ DUMPREG(HDMI_PHY_STATUS_PLL);
+ DUMPREG(HDMI_PHY_CON_0);
DUMPREG(HDMI_PHY_RSTOUT);
DUMPREG(HDMI_PHY_VPLL);
DUMPREG(HDMI_PHY_CMU);
@@ -343,40 +1003,93 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_CON_1);
DUMPREG(HDMI_CON_2);
DUMPREG(HDMI_SYS_STATUS);
- DUMPREG(HDMI_PHY_STATUS);
+ DUMPREG(HDMI_PHY_STATUS_0);
DUMPREG(HDMI_STATUS_EN);
DUMPREG(HDMI_HPD);
DUMPREG(HDMI_MODE_SEL);
- DUMPREG(HDMI_HPD_GEN);
+ DUMPREG(HDMI_ENC_EN);
DUMPREG(HDMI_DC_CONTROL);
DUMPREG(HDMI_VIDEO_PATTERN_GEN);
DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
DUMPREG(HDMI_H_BLANK_0);
DUMPREG(HDMI_H_BLANK_1);
- DUMPREG(HDMI_V_BLANK_0);
- DUMPREG(HDMI_V_BLANK_1);
- DUMPREG(HDMI_V_BLANK_2);
- DUMPREG(HDMI_H_V_LINE_0);
- DUMPREG(HDMI_H_V_LINE_1);
- DUMPREG(HDMI_H_V_LINE_2);
+ DUMPREG(HDMI_V2_BLANK_0);
+ DUMPREG(HDMI_V2_BLANK_1);
+ DUMPREG(HDMI_V1_BLANK_0);
+ DUMPREG(HDMI_V1_BLANK_1);
+ DUMPREG(HDMI_V_LINE_0);
+ DUMPREG(HDMI_V_LINE_1);
+ DUMPREG(HDMI_H_LINE_0);
+ DUMPREG(HDMI_H_LINE_1);
+ DUMPREG(HDMI_HSYNC_POL);
+
DUMPREG(HDMI_VSYNC_POL);
DUMPREG(HDMI_INT_PRO_MODE);
- DUMPREG(HDMI_V_BLANK_F_0);
- DUMPREG(HDMI_V_BLANK_F_1);
- DUMPREG(HDMI_V_BLANK_F_2);
- DUMPREG(HDMI_H_SYNC_GEN_0);
- DUMPREG(HDMI_H_SYNC_GEN_1);
- DUMPREG(HDMI_H_SYNC_GEN_2);
- DUMPREG(HDMI_V_SYNC_GEN_1_0);
- DUMPREG(HDMI_V_SYNC_GEN_1_1);
- DUMPREG(HDMI_V_SYNC_GEN_1_2);
- DUMPREG(HDMI_V_SYNC_GEN_2_0);
- DUMPREG(HDMI_V_SYNC_GEN_2_1);
- DUMPREG(HDMI_V_SYNC_GEN_2_2);
- DUMPREG(HDMI_V_SYNC_GEN_3_0);
- DUMPREG(HDMI_V_SYNC_GEN_3_1);
- DUMPREG(HDMI_V_SYNC_GEN_3_2);
+ DUMPREG(HDMI_V_BLANK_F0_0);
+ DUMPREG(HDMI_V_BLANK_F0_1);
+ DUMPREG(HDMI_V_BLANK_F1_0);
+ DUMPREG(HDMI_V_BLANK_F1_1);
+
+ DUMPREG(HDMI_H_SYNC_START_0);
+ DUMPREG(HDMI_H_SYNC_START_1);
+ DUMPREG(HDMI_H_SYNC_END_0);
+ DUMPREG(HDMI_H_SYNC_END_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0);
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1);
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0);
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1);
+
+ DUMPREG(HDMI_V_BLANK_F2_0);
+ DUMPREG(HDMI_V_BLANK_F2_1);
+ DUMPREG(HDMI_V_BLANK_F3_0);
+ DUMPREG(HDMI_V_BLANK_F3_1);
+ DUMPREG(HDMI_V_BLANK_F4_0);
+ DUMPREG(HDMI_V_BLANK_F4_1);
+ DUMPREG(HDMI_V_BLANK_F5_0);
+ DUMPREG(HDMI_V_BLANK_F5_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1);
+
+ DUMPREG(HDMI_VACT_SPACE_1_0);
+ DUMPREG(HDMI_VACT_SPACE_1_1);
+ DUMPREG(HDMI_VACT_SPACE_2_0);
+ DUMPREG(HDMI_VACT_SPACE_2_1);
+ DUMPREG(HDMI_VACT_SPACE_3_0);
+ DUMPREG(HDMI_VACT_SPACE_3_1);
+ DUMPREG(HDMI_VACT_SPACE_4_0);
+ DUMPREG(HDMI_VACT_SPACE_4_1);
+ DUMPREG(HDMI_VACT_SPACE_5_0);
+ DUMPREG(HDMI_VACT_SPACE_5_1);
+ DUMPREG(HDMI_VACT_SPACE_6_0);
+ DUMPREG(HDMI_VACT_SPACE_6_1);
DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
DUMPREG(HDMI_TG_CMD);
@@ -400,6 +1113,10 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_TG_FIELD_CHG_H);
DUMPREG(HDMI_TG_VACT_ST2_L);
DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VACT_ST3_L);
+ DUMPREG(HDMI_TG_VACT_ST3_H);
+ DUMPREG(HDMI_TG_VACT_ST4_L);
+ DUMPREG(HDMI_TG_VACT_ST4_H);
DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
@@ -408,10 +1125,49 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_3D);
+
+ DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_AVI_CON);
+ DUMPREG(HDMI_AVI_HEADER0);
+ DUMPREG(HDMI_AVI_HEADER1);
+ DUMPREG(HDMI_AVI_HEADER2);
+ DUMPREG(HDMI_AVI_CHECK_SUM);
+ DUMPREG(HDMI_VSI_CON);
+ DUMPREG(HDMI_VSI_HEADER0);
+ DUMPREG(HDMI_VSI_HEADER1);
+ DUMPREG(HDMI_VSI_HEADER2);
+ for (i = 0; i < 7; ++i)
+ DUMPREG(HDMI_VSI_DATA(i));
+
#undef DUMPREG
}
-static int hdmi_conf_index(struct drm_display_mode *mode)
+static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
+{
+ if (hdata->is_v13)
+ hdmi_v13_regs_dump(hdata, prefix);
+ else
+ hdmi_v14_regs_dump(hdata, prefix);
+}
+
+static int hdmi_v13_conf_index(struct drm_display_mode *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
+ if (hdmi_v13_confs[i].width == mode->hdisplay &&
+ hdmi_v13_confs[i].height == mode->vdisplay &&
+ hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
+ hdmi_v13_confs[i].interlace ==
+ ((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
+ true : false))
+ return i;
+
+ return -EINVAL;
+}
+
+static int hdmi_v14_conf_index(struct drm_display_mode *mode)
{
int i;
@@ -424,7 +1180,16 @@ static int hdmi_conf_index(struct drm_display_mode *mode)
true : false))
return i;
- return -1;
+ return -EINVAL;
+}
+
+static int hdmi_conf_index(struct hdmi_context *hdata,
+ struct drm_display_mode *mode)
+{
+ if (hdata->is_v13)
+ return hdmi_v13_conf_index(mode);
+
+ return hdmi_v14_conf_index(mode);
}
static bool hdmi_is_connected(void *ctx)
@@ -462,29 +1227,69 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
return 0;
}
-static int hdmi_check_timing(void *ctx, void *timing)
+static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
{
- struct fb_videomode *check_timing = timing;
int i;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
+ check_timing->xres, check_timing->yres,
+ check_timing->refresh, (check_timing->vmode &
+ FB_VMODE_INTERLACED) ? true : false);
- DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
- check_timing->yres, check_timing->refresh,
- check_timing->vmode);
+ for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
+ if (hdmi_v13_confs[i].width == check_timing->xres &&
+ hdmi_v13_confs[i].height == check_timing->yres &&
+ hdmi_v13_confs[i].vrefresh == check_timing->refresh &&
+ hdmi_v13_confs[i].interlace ==
+ ((check_timing->vmode & FB_VMODE_INTERLACED) ?
+ true : false))
+ return 0;
- for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
+ /* TODO */
+
+ return -EINVAL;
+}
+
+static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
+{
+ int i;
+
+ DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
+ check_timing->xres, check_timing->yres,
+ check_timing->refresh, (check_timing->vmode &
+ FB_VMODE_INTERLACED) ? true : false);
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++)
if (hdmi_confs[i].width == check_timing->xres &&
hdmi_confs[i].height == check_timing->yres &&
hdmi_confs[i].vrefresh == check_timing->refresh &&
hdmi_confs[i].interlace ==
((check_timing->vmode & FB_VMODE_INTERLACED) ?
true : false))
- return 0;
+ return 0;
+
+ /* TODO */
return -EINVAL;
}
+static int hdmi_check_timing(void *ctx, void *timing)
+{
+ struct hdmi_context *hdata = (struct hdmi_context *)ctx;
+ struct fb_videomode *check_timing = timing;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
+ check_timing->yres, check_timing->refresh,
+ check_timing->vmode);
+
+ if (hdata->is_v13)
+ return hdmi_v13_check_timing(check_timing);
+ else
+ return hdmi_v14_check_timing(check_timing);
+}
+
static int hdmi_display_power_on(void *ctx, int mode)
{
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
@@ -514,15 +1319,185 @@ static struct exynos_hdmi_display_ops display_ops = {
.power_on = hdmi_display_power_on,
};
+static void hdmi_set_acr(u32 freq, u8 *acr)
+{
+ u32 n, cts;
+
+ switch (freq) {
+ case 32000:
+ n = 4096;
+ cts = 27000;
+ break;
+ case 44100:
+ n = 6272;
+ cts = 30000;
+ break;
+ case 88200:
+ n = 12544;
+ cts = 30000;
+ break;
+ case 176400:
+ n = 25088;
+ cts = 30000;
+ break;
+ case 48000:
+ n = 6144;
+ cts = 27000;
+ break;
+ case 96000:
+ n = 12288;
+ cts = 27000;
+ break;
+ case 192000:
+ n = 24576;
+ cts = 27000;
+ break;
+ default:
+ n = 0;
+ cts = 0;
+ break;
+ }
+
+ acr[1] = cts >> 16;
+ acr[2] = cts >> 8 & 0xff;
+ acr[3] = cts & 0xff;
+
+ acr[4] = n >> 16;
+ acr[5] = n >> 8 & 0xff;
+ acr[6] = n & 0xff;
+}
+
+static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
+{
+ hdmi_reg_writeb(hdata, HDMI_ACR_N0, acr[6]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_N1, acr[5]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_N2, acr[4]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_MCTS0, acr[3]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_MCTS1, acr[2]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_MCTS2, acr[1]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CTS0, acr[3]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]);
+
+ if (hdata->is_v13)
+ hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4);
+ else
+ hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
+}
+
+static void hdmi_audio_init(struct hdmi_context *hdata)
+{
+ u32 sample_rate, bits_per_sample, frame_size_code;
+ u32 data_num, bit_ch, sample_frq;
+ u32 val;
+ u8 acr[7];
+
+ sample_rate = 44100;
+ bits_per_sample = 16;
+ frame_size_code = 0;
+
+ switch (bits_per_sample) {
+ case 20:
+ data_num = 2;
+ bit_ch = 1;
+ break;
+ case 24:
+ data_num = 3;
+ bit_ch = 1;
+ break;
+ default:
+ data_num = 1;
+ bit_ch = 0;
+ break;
+ }
+
+ hdmi_set_acr(sample_rate, acr);
+ hdmi_reg_acr(hdata, acr);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
+ | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
+ | HDMI_I2S_MUX_ENABLE);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
+ | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
+
+ sample_frq = (sample_rate == 44100) ? 0 :
+ (sample_rate == 48000) ? 2 :
+ (sample_rate == 32000) ? 3 :
+ (sample_rate == 96000) ? 0xa : 0x0;
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
+
+ val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
+ hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
+
+ /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
+ | HDMI_I2S_SEL_LRCK(6));
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
+ | HDMI_I2S_SEL_SDATA2(4));
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
+ | HDMI_I2S_SEL_SDATA2(2));
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
+
+ /* I2S_CON_1 & 2 */
+ hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
+ | HDMI_I2S_L_CH_LOW_POL);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
+ | HDMI_I2S_SET_BIT_CH(bit_ch)
+ | HDMI_I2S_SET_SDATA_BIT(data_num)
+ | HDMI_I2S_BASIC_FORMAT);
+
+ /* Configure register related to CUV information */
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
+ | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
+ | HDMI_I2S_COPYRIGHT
+ | HDMI_I2S_LINEAR_PCM
+ | HDMI_I2S_CONSUMER_FORMAT);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
+ | HDMI_I2S_SET_SMP_FREQ(sample_frq));
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
+ HDMI_I2S_ORG_SMP_FREQ_44_1
+ | HDMI_I2S_WORD_LEN_MAX24_24BITS
+ | HDMI_I2S_WORD_LEN_MAX_24BITS);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
+}
+
+static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
+{
+ u32 mod;
+
+ mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
+ if (mod & HDMI_DVI_MODE_EN)
+ return;
+
+ hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
+ hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
+ HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
+}
+
static void hdmi_conf_reset(struct hdmi_context *hdata)
{
+ u32 reg;
+
/* disable hpd handle for drm */
hdata->hpd_handle = false;
+ if (hdata->is_v13)
+ reg = HDMI_V13_CORE_RSTOUT;
+ else
+ reg = HDMI_CORE_RSTOUT;
+
/* resetting HDMI core */
- hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT);
mdelay(10);
- hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
mdelay(10);
/* enable hpd handle for drm */
@@ -546,27 +1521,126 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
/* disable bluescreen */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
- /* choose bluescreen (fecal) color */
- hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12);
- hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34);
- hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56);
- /* enable AVI packet every vsync, fixes purple line problem */
- hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
- /* force RGB, look to CEA-861-D, table 7 for more detail */
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5);
- hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
-
- hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02);
- hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02);
- hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04);
+
+ if (hdata->is_v13) {
+ /* choose bluescreen (fecal) color */
+ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
+ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
+ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
+
+ /* enable AVI packet every vsync, fixes purple line problem */
+ hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
+ /* force RGB, look to CEA-861-D, table 7 for more detail */
+ hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
+ hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
+
+ hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
+ hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
+ hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
+ } else {
+ /* enable AVI packet every vsync, fixes purple line problem */
+ hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5);
+ hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
+ }
/* enable hpd handle for drm */
hdata->hpd_handle = true;
}
-static void hdmi_timing_apply(struct hdmi_context *hdata,
- const struct hdmi_preset_conf *conf)
+static void hdmi_v13_timing_apply(struct hdmi_context *hdata)
{
+ const struct hdmi_v13_preset_conf *conf =
+ hdmi_v13_confs[hdata->cur_conf].conf;
+ const struct hdmi_v13_core_regs *core = &conf->core;
+ const struct hdmi_v13_tg_regs *tg = &conf->tg;
+ int tries;
+
+ /* setting core registers */
+ hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]);
+ hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
+ hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ /* Timing generator registers */
+ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+
+ /* waiting for HDMIPHY's PLL to get to steady state */
+ for (tries = 100; tries; --tries) {
+ u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS);
+ if (val & HDMI_PHY_STATUS_READY)
+ break;
+ mdelay(1);
+ }
+ /* steady state not achieved */
+ if (tries == 0) {
+ DRM_ERROR("hdmiphy's pll could not reach steady state.\n");
+ hdmi_regs_dump(hdata, "timing apply");
+ }
+
+ clk_disable(hdata->res.sclk_hdmi);
+ clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy);
+ clk_enable(hdata->res.sclk_hdmi);
+
+ /* enable HDMI and timing generator */
+ hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
+ if (core->int_pro_mode[0])
+ hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
+ HDMI_FIELD_EN);
+ else
+ hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+}
+
+static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
+{
+ const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf;
const struct hdmi_core_regs *core = &conf->core;
const struct hdmi_tg_regs *tg = &conf->tg;
int tries;
@@ -574,29 +1648,102 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
/* setting core registers */
hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]);
- hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]);
- hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]);
- hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]);
+ hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]);
+ hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]);
+ hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]);
hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0,
+ core->v_sync_line_bef_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1,
+ core->v_sync_line_bef_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0,
+ core->v_sync_line_bef_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1,
+ core->v_sync_line_bef_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0,
+ core->v_sync_line_aft_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1,
+ core->v_sync_line_aft_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0,
+ core->v_sync_line_aft_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1,
+ core->v_sync_line_aft_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0,
+ core->v_sync_line_aft_pxl_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1,
+ core->v_sync_line_aft_pxl_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0,
+ core->v_sync_line_aft_pxl_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1,
+ core->v_sync_line_aft_pxl_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0,
+ core->v_sync_line_aft_3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1,
+ core->v_sync_line_aft_3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0,
+ core->v_sync_line_aft_4[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1,
+ core->v_sync_line_aft_4[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0,
+ core->v_sync_line_aft_5[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1,
+ core->v_sync_line_aft_5[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0,
+ core->v_sync_line_aft_6[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1,
+ core->v_sync_line_aft_6[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0,
+ core->v_sync_line_aft_pxl_3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1,
+ core->v_sync_line_aft_pxl_3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0,
+ core->v_sync_line_aft_pxl_4[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1,
+ core->v_sync_line_aft_pxl_4[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0,
+ core->v_sync_line_aft_pxl_5[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1,
+ core->v_sync_line_aft_pxl_5[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0,
+ core->v_sync_line_aft_pxl_6[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1,
+ core->v_sync_line_aft_pxl_6[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]);
+
/* Timing generator registers */
hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
@@ -618,6 +1765,10 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h);
hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
@@ -626,10 +1777,11 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d);
/* waiting for HDMIPHY's PLL to get to steady state */
for (tries = 100; tries; --tries) {
- u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
+ u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0);
if (val & HDMI_PHY_STATUS_READY)
break;
mdelay(1);
@@ -653,9 +1805,18 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
}
+static void hdmi_timing_apply(struct hdmi_context *hdata)
+{
+ if (hdata->is_v13)
+ hdmi_v13_timing_apply(hdata);
+ else
+ hdmi_v14_timing_apply(hdata);
+}
+
static void hdmiphy_conf_reset(struct hdmi_context *hdata)
{
u8 buffer[2];
+ u32 reg;
clk_disable(hdata->res.sclk_hdmi);
clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
@@ -668,15 +1829,21 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
if (hdata->hdmiphy_port)
i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+ if (hdata->is_v13)
+ reg = HDMI_V13_PHY_RSTOUT;
+ else
+ reg = HDMI_PHY_RSTOUT;
+
/* reset hdmiphy */
- hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT);
mdelay(10);
- hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT);
mdelay(10);
}
static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{
+ const u8 *hdmiphy_data;
u8 buffer[32];
u8 operation[2];
u8 read_buffer[32] = {0, };
@@ -689,7 +1856,12 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
}
/* pixel clock */
- memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32);
+ if (hdata->is_v13)
+ hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data;
+ else
+ hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data;
+
+ memcpy(buffer, hdmiphy_data, 32);
ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
if (ret != 32) {
DRM_ERROR("failed to configure HDMIPHY via I2C\n");
@@ -721,9 +1893,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
static void hdmi_conf_apply(struct hdmi_context *hdata)
{
- const struct hdmi_preset_conf *conf =
- hdmi_confs[hdata->cur_conf].conf;
-
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmiphy_conf_reset(hdata);
@@ -731,13 +1900,55 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmi_conf_reset(hdata);
hdmi_conf_init(hdata);
+ hdmi_audio_init(hdata);
/* setting core registers */
- hdmi_timing_apply(hdata, conf);
+ hdmi_timing_apply(hdata);
+ hdmi_audio_control(hdata, true);
hdmi_regs_dump(hdata, "start");
}
+static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_display_mode *m;
+ struct hdmi_context *hdata = (struct hdmi_context *)ctx;
+ int index;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ if (hdata->is_v13)
+ index = hdmi_v13_conf_index(adjusted_mode);
+ else
+ index = hdmi_v14_conf_index(adjusted_mode);
+
+ /* just return if user desired mode exists. */
+ if (index >= 0)
+ return;
+
+ /*
+ * otherwise, find the most suitable mode among modes and change it
+ * to adjusted_mode.
+ */
+ list_for_each_entry(m, &connector->modes, head) {
+ if (hdata->is_v13)
+ index = hdmi_v13_conf_index(m);
+ else
+ index = hdmi_v14_conf_index(m);
+
+ if (index >= 0) {
+ DRM_INFO("desired mode doesn't exist so\n");
+ DRM_INFO("use the most suitable mode among modes.\n");
+ memcpy(adjusted_mode, m, sizeof(*m));
+ break;
+ }
+ }
+}
+
static void hdmi_mode_set(void *ctx, void *mode)
{
struct hdmi_context *hdata = (struct hdmi_context *)ctx;
@@ -745,13 +1956,22 @@ static void hdmi_mode_set(void *ctx, void *mode)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- conf_idx = hdmi_conf_index(mode);
- if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs))
+ conf_idx = hdmi_conf_index(hdata, mode);
+ if (conf_idx >= 0)
hdata->cur_conf = conf_idx;
else
DRM_DEBUG_KMS("not supported mode\n");
}
+static void hdmi_get_max_resol(void *ctx, unsigned int *width,
+ unsigned int *height)
+{
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ *width = MAX_WIDTH;
+ *height = MAX_HEIGHT;
+}
+
static void hdmi_commit(void *ctx)
{
struct hdmi_context *hdata = (struct hdmi_context *)ctx;
@@ -770,13 +1990,16 @@ static void hdmi_disable(void *ctx)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (hdata->enabled) {
+ hdmi_audio_control(hdata, false);
hdmiphy_conf_reset(hdata);
hdmi_conf_reset(hdata);
}
}
static struct exynos_hdmi_manager_ops manager_ops = {
+ .mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
+ .get_max_resol = hdmi_get_max_resol,
.commit = hdmi_commit,
.disable = hdmi_disable,
};
@@ -926,7 +2149,7 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata)
hdmiphy_conf_reset(hdata);
hdmi_conf_reset(hdata);
hdmi_conf_init(hdata);
-
+ hdmi_audio_init(hdata);
}
static void hdmi_resource_poweroff(struct hdmi_context *hdata)
@@ -978,14 +2201,12 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
if (ddc)
hdmi_ddc = ddc;
}
-EXPORT_SYMBOL(hdmi_attach_ddc_client);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
{
if (hdmiphy)
hdmi_hdmiphy = hdmiphy;
}
-EXPORT_SYMBOL(hdmi_attach_hdmiphy_client);
static int __devinit hdmi_probe(struct platform_device *pdev)
{
@@ -1022,6 +2243,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, drm_hdmi_ctx);
+ hdata->is_v13 = pdata->is_v13;
hdata->default_win = pdata->default_win;
hdata->default_timing = &pdata->timing;
hdata->default_bpp = pdata->bpp;
@@ -1167,10 +2389,3 @@ struct platform_driver hdmi_driver = {
.pm = &hdmi_pm_ops,
},
};
-EXPORT_SYMBOL(hdmi_driver);
-
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM HDMI core Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
index 31d6cf84c1aa..1c3b6d8f1fe7 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
@@ -28,56 +28,6 @@
#ifndef _EXYNOS_HDMI_H_
#define _EXYNOS_HDMI_H_
-struct hdmi_conf {
- int width;
- int height;
- int vrefresh;
- bool interlace;
- const u8 *hdmiphy_data;
- const struct hdmi_preset_conf *conf;
-};
-
-struct hdmi_resources {
- struct clk *hdmi;
- struct clk *sclk_hdmi;
- struct clk *sclk_pixel;
- struct clk *sclk_hdmiphy;
- struct clk *hdmiphy;
- struct regulator_bulk_data *regul_bulk;
- int regul_count;
-};
-
-struct hdmi_context {
- struct device *dev;
- struct drm_device *drm_dev;
- struct fb_videomode *default_timing;
- unsigned int default_win;
- unsigned int default_bpp;
- bool hpd_handle;
- bool enabled;
-
- struct resource *regs_res;
- /** base address of HDMI registers */
- void __iomem *regs;
- /** HDMI hotplug interrupt */
- unsigned int irq;
- /** workqueue for delayed work */
- struct workqueue_struct *wq;
- /** hotplug handling work */
- struct work_struct hotplug_work;
-
- struct i2c_client *ddc_port;
- struct i2c_client *hdmiphy_port;
-
- /** current hdmiphy conf index */
- int cur_conf;
- /** other resources */
- struct hdmi_resources res;
-
- void *parent_ctx;
-};
-
-
void hdmi_attach_ddc_client(struct i2c_client *ddc);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index 93846e810e38..4d5f41e19527 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -36,11 +36,57 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_hdmi.h"
-#include "exynos_hdmi.h"
-#include "exynos_mixer.h"
+
+#define HDMI_OVERLAY_NUMBER 3
#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
+struct hdmi_win_data {
+ dma_addr_t dma_addr;
+ void __iomem *vaddr;
+ dma_addr_t chroma_dma_addr;
+ void __iomem *chroma_vaddr;
+ uint32_t pixel_format;
+ unsigned int bpp;
+ unsigned int crtc_x;
+ unsigned int crtc_y;
+ unsigned int crtc_width;
+ unsigned int crtc_height;
+ unsigned int fb_x;
+ unsigned int fb_y;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int mode_width;
+ unsigned int mode_height;
+ unsigned int scan_flags;
+};
+
+struct mixer_resources {
+ struct device *dev;
+ int irq;
+ void __iomem *mixer_regs;
+ void __iomem *vp_regs;
+ spinlock_t reg_slock;
+ struct clk *mixer;
+ struct clk *vp;
+ struct clk *sclk_mixer;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_dac;
+};
+
+struct mixer_context {
+ struct fb_videomode *default_timing;
+ unsigned int default_win;
+ unsigned int default_bpp;
+ unsigned int irq;
+ int pipe;
+ bool interlace;
+ bool vp_enabled;
+
+ struct mixer_resources mixer_res;
+ struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER];
+};
+
static const u8 filter_y_horiz_tap8[] = {
0, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 0, 0, 0,
@@ -1066,10 +1112,3 @@ struct platform_driver mixer_driver = {
.probe = mixer_probe,
.remove = __devexit_p(mixer_remove),
};
-EXPORT_SYMBOL(mixer_driver);
-
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.h b/drivers/gpu/drm/exynos/exynos_mixer.h
deleted file mode 100644
index cebacfefc077..000000000000
--- a/drivers/gpu/drm/exynos/exynos_mixer.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- * Inki Dae <inki.dae@samsung.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef _EXYNOS_MIXER_H_
-#define _EXYNOS_MIXER_H_
-
-#define HDMI_OVERLAY_NUMBER 3
-
-struct hdmi_win_data {
- dma_addr_t dma_addr;
- void __iomem *vaddr;
- dma_addr_t chroma_dma_addr;
- void __iomem *chroma_vaddr;
- uint32_t pixel_format;
- unsigned int bpp;
- unsigned int crtc_x;
- unsigned int crtc_y;
- unsigned int crtc_width;
- unsigned int crtc_height;
- unsigned int fb_x;
- unsigned int fb_y;
- unsigned int fb_width;
- unsigned int fb_height;
- unsigned int mode_width;
- unsigned int mode_height;
- unsigned int scan_flags;
-};
-
-struct mixer_resources {
- struct device *dev;
- /** interrupt index */
- int irq;
- /** pointer to Mixer registers */
- void __iomem *mixer_regs;
- /** pointer to Video Processor registers */
- void __iomem *vp_regs;
- /** spinlock for protection of registers */
- spinlock_t reg_slock;
- /** other resources */
- struct clk *mixer;
- struct clk *vp;
- struct clk *sclk_mixer;
- struct clk *sclk_hdmi;
- struct clk *sclk_dac;
-};
-
-struct mixer_context {
- unsigned int default_win;
- struct fb_videomode *default_timing;
- unsigned int default_bpp;
-
- /** mixer interrupt */
- unsigned int irq;
- /** current crtc pipe for vblank */
- int pipe;
- /** interlace scan mode */
- bool interlace;
- /** vp enabled status */
- bool vp_enabled;
-
- /** mixer and vp resources */
- struct mixer_resources mixer_res;
-
- /** overlay window data */
- struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER];
-};
-
-#endif
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h
index 72e6b52be740..3c04bea842ce 100644
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -19,64 +19,67 @@
* Register part
*/
+/* HDMI Version 1.3 & Common */
#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
+#define HDMI_I2S_BASE(x) ((x) + 0x00040000)
#define HDMI_TG_BASE(x) ((x) + 0x00050000)
/* Control registers */
#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000)
#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004)
#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C)
-#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
-#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018)
-#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C)
-#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
+#define HDMI_V13_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
+#define HDMI_V13_PHY_VPLL HDMI_CTRL_BASE(0x0018)
+#define HDMI_V13_PHY_CMU HDMI_CTRL_BASE(0x001C)
+#define HDMI_V13_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
/* Core registers */
#define HDMI_CON_0 HDMI_CORE_BASE(0x0000)
#define HDMI_CON_1 HDMI_CORE_BASE(0x0004)
#define HDMI_CON_2 HDMI_CORE_BASE(0x0008)
#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010)
-#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014)
+#define HDMI_V13_PHY_STATUS HDMI_CORE_BASE(0x0014)
#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020)
#define HDMI_HPD HDMI_CORE_BASE(0x0030)
#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040)
-#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
-#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
-#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
+#define HDMI_ENC_EN HDMI_CORE_BASE(0x0044)
+#define HDMI_V13_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
+#define HDMI_V13_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
+#define HDMI_V13_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0)
#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4)
-#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
-#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
-#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
-#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
-#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
-#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
+#define HDMI_V13_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
+#define HDMI_V13_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
+#define HDMI_V13_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
+#define HDMI_V13_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
+#define HDMI_V13_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
+#define HDMI_V13_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4)
#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8)
-#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
-#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
-#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
-#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
-#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
-#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
-#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
-#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
-#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
-#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
-#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
-#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
-#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
-#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
-#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
-#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180)
-#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300)
-#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
-#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0)
-#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
-#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8)
-#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360)
-#define HDMI_SPD_CON HDMI_CORE_BASE(0x0400)
+#define HDMI_V13_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
+#define HDMI_V13_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
+#define HDMI_V13_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
+#define HDMI_V13_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
+#define HDMI_V13_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
+#define HDMI_V13_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
+#define HDMI_V13_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
+#define HDMI_V13_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
+#define HDMI_V13_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
+#define HDMI_V13_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
+#define HDMI_V13_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
+#define HDMI_V13_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
+#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
+#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
+#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
+#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180)
+#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300)
+#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
+#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0)
+#define HDMI_V13_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
+#define HDMI_V13_HPD_GEN HDMI_CORE_BASE(0x05C8)
+#define HDMI_V13_AUI_CON HDMI_CORE_BASE(0x0360)
+#define HDMI_V13_SPD_CON HDMI_CORE_BASE(0x0400)
/* Timing generator registers */
#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
@@ -130,6 +133,9 @@
/* HDMI_CON_0 */
#define HDMI_BLUE_SCR_EN (1 << 5)
+#define HDMI_ASP_EN (1 << 2)
+#define HDMI_ASP_DIS (0 << 2)
+#define HDMI_ASP_MASK (1 << 2)
#define HDMI_EN (1 << 0)
/* HDMI_PHY_STATUS */
@@ -138,10 +144,418 @@
/* HDMI_MODE_SEL */
#define HDMI_MODE_HDMI_EN (1 << 1)
#define HDMI_MODE_DVI_EN (1 << 0)
+#define HDMI_DVI_MODE_EN (1)
+#define HDMI_DVI_MODE_DIS (0)
#define HDMI_MODE_MASK (3 << 0)
/* HDMI_TG_CMD */
#define HDMI_TG_EN (1 << 0)
#define HDMI_FIELD_EN (1 << 1)
+
+/* HDMI Version 1.4 */
+/* Control registers */
+/* #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) */
+/* #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) */
+#define HDMI_HDCP_KEY_LOAD HDMI_CTRL_BASE(0x0008)
+/* #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) */
+#define HDMI_INTC_CON_1 HDMI_CTRL_BASE(0x0010)
+#define HDMI_INTC_FLAG_1 HDMI_CTRL_BASE(0x0014)
+#define HDMI_PHY_STATUS_0 HDMI_CTRL_BASE(0x0020)
+#define HDMI_PHY_STATUS_CMU HDMI_CTRL_BASE(0x0024)
+#define HDMI_PHY_STATUS_PLL HDMI_CTRL_BASE(0x0028)
+#define HDMI_PHY_CON_0 HDMI_CTRL_BASE(0x0030)
+#define HDMI_HPD_CTRL HDMI_CTRL_BASE(0x0040)
+#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044)
+#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050)
+#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070)
+#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074)
+#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078)
+#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C)
+#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080)
+
+/* Video related registers */
+#define HDMI_YMAX HDMI_CORE_BASE(0x0060)
+#define HDMI_YMIN HDMI_CORE_BASE(0x0064)
+#define HDMI_CMAX HDMI_CORE_BASE(0x0068)
+#define HDMI_CMIN HDMI_CORE_BASE(0x006C)
+
+#define HDMI_V2_BLANK_0 HDMI_CORE_BASE(0x00B0)
+#define HDMI_V2_BLANK_1 HDMI_CORE_BASE(0x00B4)
+#define HDMI_V1_BLANK_0 HDMI_CORE_BASE(0x00B8)
+#define HDMI_V1_BLANK_1 HDMI_CORE_BASE(0x00BC)
+
+#define HDMI_V_LINE_0 HDMI_CORE_BASE(0x00C0)
+#define HDMI_V_LINE_1 HDMI_CORE_BASE(0x00C4)
+#define HDMI_H_LINE_0 HDMI_CORE_BASE(0x00C8)
+#define HDMI_H_LINE_1 HDMI_CORE_BASE(0x00CC)
+
+#define HDMI_HSYNC_POL HDMI_CORE_BASE(0x00E0)
+
+#define HDMI_V_BLANK_F0_0 HDMI_CORE_BASE(0x0110)
+#define HDMI_V_BLANK_F0_1 HDMI_CORE_BASE(0x0114)
+#define HDMI_V_BLANK_F1_0 HDMI_CORE_BASE(0x0118)
+#define HDMI_V_BLANK_F1_1 HDMI_CORE_BASE(0x011C)
+
+#define HDMI_H_SYNC_START_0 HDMI_CORE_BASE(0x0120)
+#define HDMI_H_SYNC_START_1 HDMI_CORE_BASE(0x0124)
+#define HDMI_H_SYNC_END_0 HDMI_CORE_BASE(0x0128)
+#define HDMI_H_SYNC_END_1 HDMI_CORE_BASE(0x012C)
+
+#define HDMI_V_SYNC_LINE_BEF_2_0 HDMI_CORE_BASE(0x0130)
+#define HDMI_V_SYNC_LINE_BEF_2_1 HDMI_CORE_BASE(0x0134)
+#define HDMI_V_SYNC_LINE_BEF_1_0 HDMI_CORE_BASE(0x0138)
+#define HDMI_V_SYNC_LINE_BEF_1_1 HDMI_CORE_BASE(0x013C)
+
+#define HDMI_V_SYNC_LINE_AFT_2_0 HDMI_CORE_BASE(0x0140)
+#define HDMI_V_SYNC_LINE_AFT_2_1 HDMI_CORE_BASE(0x0144)
+#define HDMI_V_SYNC_LINE_AFT_1_0 HDMI_CORE_BASE(0x0148)
+#define HDMI_V_SYNC_LINE_AFT_1_1 HDMI_CORE_BASE(0x014C)
+
+#define HDMI_V_SYNC_LINE_AFT_PXL_2_0 HDMI_CORE_BASE(0x0150)
+#define HDMI_V_SYNC_LINE_AFT_PXL_2_1 HDMI_CORE_BASE(0x0154)
+#define HDMI_V_SYNC_LINE_AFT_PXL_1_0 HDMI_CORE_BASE(0x0158)
+#define HDMI_V_SYNC_LINE_AFT_PXL_1_1 HDMI_CORE_BASE(0x015C)
+
+#define HDMI_V_BLANK_F2_0 HDMI_CORE_BASE(0x0160)
+#define HDMI_V_BLANK_F2_1 HDMI_CORE_BASE(0x0164)
+#define HDMI_V_BLANK_F3_0 HDMI_CORE_BASE(0x0168)
+#define HDMI_V_BLANK_F3_1 HDMI_CORE_BASE(0x016C)
+#define HDMI_V_BLANK_F4_0 HDMI_CORE_BASE(0x0170)
+#define HDMI_V_BLANK_F4_1 HDMI_CORE_BASE(0x0174)
+#define HDMI_V_BLANK_F5_0 HDMI_CORE_BASE(0x0178)
+#define HDMI_V_BLANK_F5_1 HDMI_CORE_BASE(0x017C)
+
+#define HDMI_V_SYNC_LINE_AFT_3_0 HDMI_CORE_BASE(0x0180)
+#define HDMI_V_SYNC_LINE_AFT_3_1 HDMI_CORE_BASE(0x0184)
+#define HDMI_V_SYNC_LINE_AFT_4_0 HDMI_CORE_BASE(0x0188)
+#define HDMI_V_SYNC_LINE_AFT_4_1 HDMI_CORE_BASE(0x018C)
+#define HDMI_V_SYNC_LINE_AFT_5_0 HDMI_CORE_BASE(0x0190)
+#define HDMI_V_SYNC_LINE_AFT_5_1 HDMI_CORE_BASE(0x0194)
+#define HDMI_V_SYNC_LINE_AFT_6_0 HDMI_CORE_BASE(0x0198)
+#define HDMI_V_SYNC_LINE_AFT_6_1 HDMI_CORE_BASE(0x019C)
+
+#define HDMI_V_SYNC_LINE_AFT_PXL_3_0 HDMI_CORE_BASE(0x01A0)
+#define HDMI_V_SYNC_LINE_AFT_PXL_3_1 HDMI_CORE_BASE(0x01A4)
+#define HDMI_V_SYNC_LINE_AFT_PXL_4_0 HDMI_CORE_BASE(0x01A8)
+#define HDMI_V_SYNC_LINE_AFT_PXL_4_1 HDMI_CORE_BASE(0x01AC)
+#define HDMI_V_SYNC_LINE_AFT_PXL_5_0 HDMI_CORE_BASE(0x01B0)
+#define HDMI_V_SYNC_LINE_AFT_PXL_5_1 HDMI_CORE_BASE(0x01B4)
+#define HDMI_V_SYNC_LINE_AFT_PXL_6_0 HDMI_CORE_BASE(0x01B8)
+#define HDMI_V_SYNC_LINE_AFT_PXL_6_1 HDMI_CORE_BASE(0x01BC)
+
+#define HDMI_VACT_SPACE_1_0 HDMI_CORE_BASE(0x01C0)
+#define HDMI_VACT_SPACE_1_1 HDMI_CORE_BASE(0x01C4)
+#define HDMI_VACT_SPACE_2_0 HDMI_CORE_BASE(0x01C8)
+#define HDMI_VACT_SPACE_2_1 HDMI_CORE_BASE(0x01CC)
+#define HDMI_VACT_SPACE_3_0 HDMI_CORE_BASE(0x01D0)
+#define HDMI_VACT_SPACE_3_1 HDMI_CORE_BASE(0x01D4)
+#define HDMI_VACT_SPACE_4_0 HDMI_CORE_BASE(0x01D8)
+#define HDMI_VACT_SPACE_4_1 HDMI_CORE_BASE(0x01DC)
+#define HDMI_VACT_SPACE_5_0 HDMI_CORE_BASE(0x01E0)
+#define HDMI_VACT_SPACE_5_1 HDMI_CORE_BASE(0x01E4)
+#define HDMI_VACT_SPACE_6_0 HDMI_CORE_BASE(0x01E8)
+#define HDMI_VACT_SPACE_6_1 HDMI_CORE_BASE(0x01EC)
+
+#define HDMI_GCP_CON HDMI_CORE_BASE(0x0200)
+#define HDMI_GCP_BYTE1 HDMI_CORE_BASE(0x0210)
+#define HDMI_GCP_BYTE2 HDMI_CORE_BASE(0x0214)
+#define HDMI_GCP_BYTE3 HDMI_CORE_BASE(0x0218)
+
+/* Audio related registers */
+#define HDMI_ASP_CON HDMI_CORE_BASE(0x0300)
+#define HDMI_ASP_SP_FLAT HDMI_CORE_BASE(0x0304)
+#define HDMI_ASP_CHCFG0 HDMI_CORE_BASE(0x0310)
+#define HDMI_ASP_CHCFG1 HDMI_CORE_BASE(0x0314)
+#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318)
+#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C)
+
+#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400)
+#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410)
+#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414)
+#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418)
+#define HDMI_ACR_CTS0 HDMI_CORE_BASE(0x0420)
+#define HDMI_ACR_CTS1 HDMI_CORE_BASE(0x0424)
+#define HDMI_ACR_CTS2 HDMI_CORE_BASE(0x0428)
+#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430)
+#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434)
+#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438)
+
+/* Packet related registers */
+#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500)
+#define HDMI_ACP_TYPE HDMI_CORE_BASE(0x0514)
+#define HDMI_ACP_DATA(n) HDMI_CORE_BASE(0x0520 + 4 * (n))
+
+#define HDMI_ISRC_CON HDMI_CORE_BASE(0x0600)
+#define HDMI_ISRC1_HEADER1 HDMI_CORE_BASE(0x0614)
+#define HDMI_ISRC1_DATA(n) HDMI_CORE_BASE(0x0620 + 4 * (n))
+#define HDMI_ISRC2_DATA(n) HDMI_CORE_BASE(0x06A0 + 4 * (n))
+
+#define HDMI_AVI_CON HDMI_CORE_BASE(0x0700)
+#define HDMI_AVI_HEADER0 HDMI_CORE_BASE(0x0710)
+#define HDMI_AVI_HEADER1 HDMI_CORE_BASE(0x0714)
+#define HDMI_AVI_HEADER2 HDMI_CORE_BASE(0x0718)
+#define HDMI_AVI_CHECK_SUM HDMI_CORE_BASE(0x071C)
+#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0720 + 4 * (n))
+
+#define HDMI_AUI_CON HDMI_CORE_BASE(0x0800)
+#define HDMI_AUI_HEADER0 HDMI_CORE_BASE(0x0810)
+#define HDMI_AUI_HEADER1 HDMI_CORE_BASE(0x0814)
+#define HDMI_AUI_HEADER2 HDMI_CORE_BASE(0x0818)
+#define HDMI_AUI_CHECK_SUM HDMI_CORE_BASE(0x081C)
+#define HDMI_AUI_BYTE(n) HDMI_CORE_BASE(0x0820 + 4 * (n))
+
+#define HDMI_MPG_CON HDMI_CORE_BASE(0x0900)
+#define HDMI_MPG_CHECK_SUM HDMI_CORE_BASE(0x091C)
+#define HDMI_MPG_DATA(n) HDMI_CORE_BASE(0x0920 + 4 * (n))
+
+#define HDMI_SPD_CON HDMI_CORE_BASE(0x0A00)
+#define HDMI_SPD_HEADER0 HDMI_CORE_BASE(0x0A10)
+#define HDMI_SPD_HEADER1 HDMI_CORE_BASE(0x0A14)
+#define HDMI_SPD_HEADER2 HDMI_CORE_BASE(0x0A18)
+#define HDMI_SPD_DATA(n) HDMI_CORE_BASE(0x0A20 + 4 * (n))
+
+#define HDMI_GAMUT_CON HDMI_CORE_BASE(0x0B00)
+#define HDMI_GAMUT_HEADER0 HDMI_CORE_BASE(0x0B10)
+#define HDMI_GAMUT_HEADER1 HDMI_CORE_BASE(0x0B14)
+#define HDMI_GAMUT_HEADER2 HDMI_CORE_BASE(0x0B18)
+#define HDMI_GAMUT_METADATA(n) HDMI_CORE_BASE(0x0B20 + 4 * (n))
+
+#define HDMI_VSI_CON HDMI_CORE_BASE(0x0C00)
+#define HDMI_VSI_HEADER0 HDMI_CORE_BASE(0x0C10)
+#define HDMI_VSI_HEADER1 HDMI_CORE_BASE(0x0C14)
+#define HDMI_VSI_HEADER2 HDMI_CORE_BASE(0x0C18)
+#define HDMI_VSI_DATA(n) HDMI_CORE_BASE(0x0C20 + 4 * (n))
+
+#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x0D00)
+#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x0D04)
+
+#define HDMI_AN_SEED_SEL HDMI_CORE_BASE(0x0E48)
+#define HDMI_AN_SEED_0 HDMI_CORE_BASE(0x0E58)
+#define HDMI_AN_SEED_1 HDMI_CORE_BASE(0x0E5C)
+#define HDMI_AN_SEED_2 HDMI_CORE_BASE(0x0E60)
+#define HDMI_AN_SEED_3 HDMI_CORE_BASE(0x0E64)
+
+/* HDCP related registers */
+#define HDMI_HDCP_SHA1(n) HDMI_CORE_BASE(0x7000 + 4 * (n))
+#define HDMI_HDCP_KSV_LIST(n) HDMI_CORE_BASE(0x7050 + 4 * (n))
+
+#define HDMI_HDCP_KSV_LIST_CON HDMI_CORE_BASE(0x7064)
+#define HDMI_HDCP_SHA_RESULT HDMI_CORE_BASE(0x7070)
+#define HDMI_HDCP_CTRL1 HDMI_CORE_BASE(0x7080)
+#define HDMI_HDCP_CTRL2 HDMI_CORE_BASE(0x7084)
+#define HDMI_HDCP_CHECK_RESULT HDMI_CORE_BASE(0x7090)
+#define HDMI_HDCP_BKSV(n) HDMI_CORE_BASE(0x70A0 + 4 * (n))
+#define HDMI_HDCP_AKSV(n) HDMI_CORE_BASE(0x70C0 + 4 * (n))
+#define HDMI_HDCP_AN(n) HDMI_CORE_BASE(0x70E0 + 4 * (n))
+
+#define HDMI_HDCP_BCAPS HDMI_CORE_BASE(0x7100)
+#define HDMI_HDCP_BSTATUS_0 HDMI_CORE_BASE(0x7110)
+#define HDMI_HDCP_BSTATUS_1 HDMI_CORE_BASE(0x7114)
+#define HDMI_HDCP_RI_0 HDMI_CORE_BASE(0x7140)
+#define HDMI_HDCP_RI_1 HDMI_CORE_BASE(0x7144)
+#define HDMI_HDCP_I2C_INT HDMI_CORE_BASE(0x7180)
+#define HDMI_HDCP_AN_INT HDMI_CORE_BASE(0x7190)
+#define HDMI_HDCP_WDT_INT HDMI_CORE_BASE(0x71A0)
+#define HDMI_HDCP_RI_INT HDMI_CORE_BASE(0x71B0)
+#define HDMI_HDCP_RI_COMPARE_0 HDMI_CORE_BASE(0x71D0)
+#define HDMI_HDCP_RI_COMPARE_1 HDMI_CORE_BASE(0x71D4)
+#define HDMI_HDCP_FRAME_COUNT HDMI_CORE_BASE(0x71E0)
+
+#define HDMI_RGB_ROUND_EN HDMI_CORE_BASE(0xD500)
+#define HDMI_VACT_SPACE_R_0 HDMI_CORE_BASE(0xD504)
+#define HDMI_VACT_SPACE_R_1 HDMI_CORE_BASE(0xD508)
+#define HDMI_VACT_SPACE_G_0 HDMI_CORE_BASE(0xD50C)
+#define HDMI_VACT_SPACE_G_1 HDMI_CORE_BASE(0xD510)
+#define HDMI_VACT_SPACE_B_0 HDMI_CORE_BASE(0xD514)
+#define HDMI_VACT_SPACE_B_1 HDMI_CORE_BASE(0xD518)
+
+#define HDMI_BLUE_SCREEN_B_0 HDMI_CORE_BASE(0xD520)
+#define HDMI_BLUE_SCREEN_B_1 HDMI_CORE_BASE(0xD524)
+#define HDMI_BLUE_SCREEN_G_0 HDMI_CORE_BASE(0xD528)
+#define HDMI_BLUE_SCREEN_G_1 HDMI_CORE_BASE(0xD52C)
+#define HDMI_BLUE_SCREEN_R_0 HDMI_CORE_BASE(0xD530)
+#define HDMI_BLUE_SCREEN_R_1 HDMI_CORE_BASE(0xD534)
+
+/* HDMI I2S register */
+#define HDMI_I2S_CLK_CON HDMI_I2S_BASE(0x000)
+#define HDMI_I2S_CON_1 HDMI_I2S_BASE(0x004)
+#define HDMI_I2S_CON_2 HDMI_I2S_BASE(0x008)
+#define HDMI_I2S_PIN_SEL_0 HDMI_I2S_BASE(0x00c)
+#define HDMI_I2S_PIN_SEL_1 HDMI_I2S_BASE(0x010)
+#define HDMI_I2S_PIN_SEL_2 HDMI_I2S_BASE(0x014)
+#define HDMI_I2S_PIN_SEL_3 HDMI_I2S_BASE(0x018)
+#define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c)
+#define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020)
+#define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024)
+#define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028)
+#define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c)
+#define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030)
+#define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034)
+#define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038)
+#define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c)
+#define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040)
+#define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044)
+#define HDMI_I2S_CH_ST_SH_3 HDMI_I2S_BASE(0x048)
+#define HDMI_I2S_CH_ST_SH_4 HDMI_I2S_BASE(0x04c)
+#define HDMI_I2S_MUX_CH HDMI_I2S_BASE(0x054)
+#define HDMI_I2S_MUX_CUV HDMI_I2S_BASE(0x058)
+
+/* I2S bit definition */
+
+/* I2S_CLK_CON */
+#define HDMI_I2S_CLK_DIS (0)
+#define HDMI_I2S_CLK_EN (1)
+
+/* I2S_CON_1 */
+#define HDMI_I2S_SCLK_FALLING_EDGE (0 << 1)
+#define HDMI_I2S_SCLK_RISING_EDGE (1 << 1)
+#define HDMI_I2S_L_CH_LOW_POL (0)
+#define HDMI_I2S_L_CH_HIGH_POL (1)
+
+/* I2S_CON_2 */
+#define HDMI_I2S_MSB_FIRST_MODE (0 << 6)
+#define HDMI_I2S_LSB_FIRST_MODE (1 << 6)
+#define HDMI_I2S_BIT_CH_32FS (0 << 4)
+#define HDMI_I2S_BIT_CH_48FS (1 << 4)
+#define HDMI_I2S_BIT_CH_RESERVED (2 << 4)
+#define HDMI_I2S_SDATA_16BIT (1 << 2)
+#define HDMI_I2S_SDATA_20BIT (2 << 2)
+#define HDMI_I2S_SDATA_24BIT (3 << 2)
+#define HDMI_I2S_BASIC_FORMAT (0)
+#define HDMI_I2S_L_JUST_FORMAT (2)
+#define HDMI_I2S_R_JUST_FORMAT (3)
+#define HDMI_I2S_CON_2_CLR (~(0xFF))
+#define HDMI_I2S_SET_BIT_CH(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SET_SDATA_BIT(x) (((x) & 0x7) << 2)
+
+/* I2S_PIN_SEL_0 */
+#define HDMI_I2S_SEL_SCLK(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SEL_LRCK(x) ((x) & 0x7)
+
+/* I2S_PIN_SEL_1 */
+#define HDMI_I2S_SEL_SDATA1(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7)
+
+/* I2S_PIN_SEL_2 */
+#define HDMI_I2S_SEL_SDATA3(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7)
+
+/* I2S_PIN_SEL_3 */
+#define HDMI_I2S_SEL_DSD(x) ((x) & 0x7)
+
+/* I2S_DSD_CON */
+#define HDMI_I2S_DSD_CLK_RI_EDGE (1 << 1)
+#define HDMI_I2S_DSD_CLK_FA_EDGE (0 << 1)
+#define HDMI_I2S_DSD_ENABLE (1)
+#define HDMI_I2S_DSD_DISABLE (0)
+
+/* I2S_MUX_CON */
+#define HDMI_I2S_NOISE_FILTER_ZERO (0 << 5)
+#define HDMI_I2S_NOISE_FILTER_2_STAGE (1 << 5)
+#define HDMI_I2S_NOISE_FILTER_3_STAGE (2 << 5)
+#define HDMI_I2S_NOISE_FILTER_4_STAGE (3 << 5)
+#define HDMI_I2S_NOISE_FILTER_5_STAGE (4 << 5)
+#define HDMI_I2S_IN_DISABLE (1 << 4)
+#define HDMI_I2S_IN_ENABLE (0 << 4)
+#define HDMI_I2S_AUD_SPDIF (0 << 2)
+#define HDMI_I2S_AUD_I2S (1 << 2)
+#define HDMI_I2S_AUD_DSD (2 << 2)
+#define HDMI_I2S_CUV_SPDIF_ENABLE (0 << 1)
+#define HDMI_I2S_CUV_I2S_ENABLE (1 << 1)
+#define HDMI_I2S_MUX_DISABLE (0)
+#define HDMI_I2S_MUX_ENABLE (1)
+#define HDMI_I2S_MUX_CON_CLR (~(0xFF))
+
+/* I2S_CH_ST_CON */
+#define HDMI_I2S_CH_STATUS_RELOAD (1)
+#define HDMI_I2S_CH_ST_CON_CLR (~(1))
+
+/* I2S_CH_ST_0 / I2S_CH_ST_SH_0 */
+#define HDMI_I2S_CH_STATUS_MODE_0 (0 << 6)
+#define HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH (0 << 3)
+#define HDMI_I2S_2AUD_CH_WITH_PREEMPH (1 << 3)
+#define HDMI_I2S_DEFAULT_EMPHASIS (0 << 3)
+#define HDMI_I2S_COPYRIGHT (0 << 2)
+#define HDMI_I2S_NO_COPYRIGHT (1 << 2)
+#define HDMI_I2S_LINEAR_PCM (0 << 1)
+#define HDMI_I2S_NO_LINEAR_PCM (1 << 1)
+#define HDMI_I2S_CONSUMER_FORMAT (0)
+#define HDMI_I2S_PROF_FORMAT (1)
+#define HDMI_I2S_CH_ST_0_CLR (~(0xFF))
+
+/* I2S_CH_ST_1 / I2S_CH_ST_SH_1 */
+#define HDMI_I2S_CD_PLAYER (0x00)
+#define HDMI_I2S_DAT_PLAYER (0x03)
+#define HDMI_I2S_DCC_PLAYER (0x43)
+#define HDMI_I2S_MINI_DISC_PLAYER (0x49)
+
+/* I2S_CH_ST_2 / I2S_CH_ST_SH_2 */
+#define HDMI_I2S_CHANNEL_NUM_MASK (0xF << 4)
+#define HDMI_I2S_SOURCE_NUM_MASK (0xF)
+#define HDMI_I2S_SET_CHANNEL_NUM(x) (((x) & (0xF)) << 4)
+#define HDMI_I2S_SET_SOURCE_NUM(x) ((x) & (0xF))
+
+/* I2S_CH_ST_3 / I2S_CH_ST_SH_3 */
+#define HDMI_I2S_CLK_ACCUR_LEVEL_1 (1 << 4)
+#define HDMI_I2S_CLK_ACCUR_LEVEL_2 (0 << 4)
+#define HDMI_I2S_CLK_ACCUR_LEVEL_3 (2 << 4)
+#define HDMI_I2S_SMP_FREQ_44_1 (0x0)
+#define HDMI_I2S_SMP_FREQ_48 (0x2)
+#define HDMI_I2S_SMP_FREQ_32 (0x3)
+#define HDMI_I2S_SMP_FREQ_96 (0xA)
+#define HDMI_I2S_SET_SMP_FREQ(x) ((x) & (0xF))
+
+/* I2S_CH_ST_4 / I2S_CH_ST_SH_4 */
+#define HDMI_I2S_ORG_SMP_FREQ_44_1 (0xF << 4)
+#define HDMI_I2S_ORG_SMP_FREQ_88_2 (0x7 << 4)
+#define HDMI_I2S_ORG_SMP_FREQ_22_05 (0xB << 4)
+#define HDMI_I2S_ORG_SMP_FREQ_176_4 (0x3 << 4)
+#define HDMI_I2S_WORD_LEN_NOT_DEFINE (0x0 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_20BITS (0x1 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_22BITS (0x2 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_23BITS (0x4 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_24BITS (0x5 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_21BITS (0x6 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_16BITS (0x1 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_18BITS (0x2 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_19BITS (0x4 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_20BITS (0x5 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_17BITS (0x6 << 1)
+#define HDMI_I2S_WORD_LEN_MAX_24BITS (1)
+#define HDMI_I2S_WORD_LEN_MAX_20BITS (0)
+
+/* I2S_MUX_CH */
+#define HDMI_I2S_CH3_R_EN (1 << 7)
+#define HDMI_I2S_CH3_L_EN (1 << 6)
+#define HDMI_I2S_CH3_EN (3 << 6)
+#define HDMI_I2S_CH2_R_EN (1 << 5)
+#define HDMI_I2S_CH2_L_EN (1 << 4)
+#define HDMI_I2S_CH2_EN (3 << 4)
+#define HDMI_I2S_CH1_R_EN (1 << 3)
+#define HDMI_I2S_CH1_L_EN (1 << 2)
+#define HDMI_I2S_CH1_EN (3 << 2)
+#define HDMI_I2S_CH0_R_EN (1 << 1)
+#define HDMI_I2S_CH0_L_EN (1)
+#define HDMI_I2S_CH0_EN (3)
+#define HDMI_I2S_CH_ALL_EN (0xFF)
+#define HDMI_I2S_MUX_CH_CLR (~HDMI_I2S_CH_ALL_EN)
+
+/* I2S_MUX_CUV */
+#define HDMI_I2S_CUV_R_EN (1 << 1)
+#define HDMI_I2S_CUV_L_EN (1)
+#define HDMI_I2S_CUV_RL_EN (0x03)
+
+/* I2S_CUV_L_R */
+#define HDMI_I2S_CUV_R_DATA_MASK (0x7 << 4)
+#define HDMI_I2S_CUV_L_DATA_MASK (0x7)
+
+/* Timing generator registers */
+/* TG configure/status registers */
+#define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068)
+#define HDMI_TG_VACT_ST3_H HDMI_TG_BASE(0x006c)
+#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070)
+#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
+#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
+
#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/gpu/drm/gma500/Kconfig b/drivers/gpu/drm/gma500/Kconfig
index 754e14bdc801..42e665c7e90a 100644
--- a/drivers/gpu/drm/gma500/Kconfig
+++ b/drivers/gpu/drm/gma500/Kconfig
@@ -16,8 +16,7 @@ config DRM_GMA600
depends on DRM_GMA500
help
Say yes to include support for GMA600 (Intel Moorestown/Oaktrail)
- platforms with LVDS ports. HDMI and MIPI are not currently
- supported.
+ platforms with LVDS ports. MIPI is not currently supported.
config DRM_GMA3600
bool "Intel GMA3600/3650 support (Experimental)"
@@ -25,3 +24,10 @@ config DRM_GMA3600
help
Say yes to include basic support for Intel GMA3600/3650 (Intel
Cedar Trail) platforms.
+
+config DRM_MEDFIELD
+ bool "Intel Medfield support (Experimental)"
+ depends on DRM_GMA500 && X86_INTEL_MID
+ help
+ Say yes to include support for the Intel Medfield platform.
+
diff --git a/drivers/gpu/drm/gma500/Makefile b/drivers/gpu/drm/gma500/Makefile
index 81c103be5e21..1583982917ce 100644
--- a/drivers/gpu/drm/gma500/Makefile
+++ b/drivers/gpu/drm/gma500/Makefile
@@ -37,4 +37,14 @@ gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \
oaktrail_hdmi.o \
oaktrail_hdmi_i2c.o
+gma500_gfx-$(CONFIG_DRM_MEDFIELD) += mdfld_device.o \
+ mdfld_output.o \
+ mdfld_intel_display.o \
+ mdfld_dsi_output.o \
+ mdfld_dsi_dpi.o \
+ mdfld_dsi_pkg_sender.o \
+ mdfld_tpo_vid.o \
+ mdfld_tmd_vid.o \
+ tc35876x-dsi-lvds.o
+
obj-$(CONFIG_DRM_GMA500) += gma500_gfx.o
diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c
index 53404af2e748..a54cc738926a 100644
--- a/drivers/gpu/drm/gma500/cdv_device.c
+++ b/drivers/gpu/drm/gma500/cdv_device.c
@@ -202,13 +202,12 @@ static inline void CDV_MSG_WRITE32(uint port, uint offset, u32 value)
pci_dev_put(pci_root);
}
-#define PSB_APM_CMD 0x0
-#define PSB_APM_STS 0x04
#define PSB_PM_SSC 0x20
#define PSB_PM_SSS 0x30
-#define PSB_PWRGT_GFX_MASK 0x3
-#define CDV_PWRGT_DISPLAY_CNTR 0x000fc00c
-#define CDV_PWRGT_DISPLAY_STS 0x000fc00c
+#define PSB_PWRGT_GFX_ON 0x02
+#define PSB_PWRGT_GFX_OFF 0x01
+#define PSB_PWRGT_GFX_D0 0x00
+#define PSB_PWRGT_GFX_D3 0x03
static void cdv_init_pm(struct drm_device *dev)
{
@@ -221,26 +220,22 @@ static void cdv_init_pm(struct drm_device *dev)
dev_priv->ospm_base = CDV_MSG_READ32(PSB_PUNIT_PORT,
PSB_OSPMBA) & 0xFFFF;
- /* Force power on for now */
+ /* Power status */
pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
- pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ /* Enable the GPU */
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_cnt |= PSB_PWRGT_GFX_ON;
outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
+
+ /* Wait for the GPU power */
for (i = 0; i < 5; i++) {
u32 pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
if ((pwr_sts & PSB_PWRGT_GFX_MASK) == 0)
- break;
- udelay(10);
- }
- pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC);
- pwr_cnt &= ~CDV_PWRGT_DISPLAY_CNTR;
- outl(pwr_cnt, dev_priv->ospm_base + PSB_PM_SSC);
- for (i = 0; i < 5; i++) {
- u32 pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS);
- if ((pwr_sts & CDV_PWRGT_DISPLAY_STS) == 0)
- break;
+ return;
udelay(10);
}
+ dev_err(dev->dev, "GPU: power management timed out.\n");
}
/**
@@ -249,11 +244,50 @@ static void cdv_init_pm(struct drm_device *dev)
*
* Save the state we need in order to be able to restore the interface
* upon resume from suspend
- *
- * FIXME: review
*/
static int cdv_save_display_registers(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_save_area *regs = &dev_priv->regs;
+ struct drm_connector *connector;
+
+ dev_info(dev->dev, "Saving GPU registers.\n");
+
+ pci_read_config_byte(dev->pdev, 0xF4, &regs->cdv.saveLBB);
+
+ regs->cdv.saveDSPCLK_GATE_D = REG_READ(DSPCLK_GATE_D);
+ regs->cdv.saveRAMCLK_GATE_D = REG_READ(RAMCLK_GATE_D);
+
+ regs->cdv.saveDSPARB = REG_READ(DSPARB);
+ regs->cdv.saveDSPFW[0] = REG_READ(DSPFW1);
+ regs->cdv.saveDSPFW[1] = REG_READ(DSPFW2);
+ regs->cdv.saveDSPFW[2] = REG_READ(DSPFW3);
+ regs->cdv.saveDSPFW[3] = REG_READ(DSPFW4);
+ regs->cdv.saveDSPFW[4] = REG_READ(DSPFW5);
+ regs->cdv.saveDSPFW[5] = REG_READ(DSPFW6);
+
+ regs->cdv.saveADPA = REG_READ(ADPA);
+
+ regs->cdv.savePP_CONTROL = REG_READ(PP_CONTROL);
+ regs->cdv.savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS);
+ regs->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
+ regs->saveBLC_PWM_CTL2 = REG_READ(BLC_PWM_CTL2);
+ regs->cdv.saveLVDS = REG_READ(LVDS);
+
+ regs->cdv.savePFIT_CONTROL = REG_READ(PFIT_CONTROL);
+
+ regs->cdv.savePP_ON_DELAYS = REG_READ(PP_ON_DELAYS);
+ regs->cdv.savePP_OFF_DELAYS = REG_READ(PP_OFF_DELAYS);
+ regs->cdv.savePP_CYCLE = REG_READ(PP_CYCLE);
+
+ regs->cdv.saveVGACNTRL = REG_READ(VGACNTRL);
+
+ regs->cdv.saveIER = REG_READ(PSB_INT_ENABLE_R);
+ regs->cdv.saveIMR = REG_READ(PSB_INT_MASK_R);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
return 0;
}
@@ -267,16 +301,113 @@ static int cdv_save_display_registers(struct drm_device *dev)
*/
static int cdv_restore_display_registers(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_save_area *regs = &dev_priv->regs;
+ struct drm_connector *connector;
+ u32 temp;
+
+ pci_write_config_byte(dev->pdev, 0xF4, regs->cdv.saveLBB);
+
+ REG_WRITE(DSPCLK_GATE_D, regs->cdv.saveDSPCLK_GATE_D);
+ REG_WRITE(RAMCLK_GATE_D, regs->cdv.saveRAMCLK_GATE_D);
+
+ /* BIOS does below anyway */
+ REG_WRITE(DPIO_CFG, 0);
+ REG_WRITE(DPIO_CFG, DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N);
+
+ temp = REG_READ(DPLL_A);
+ if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) {
+ REG_WRITE(DPLL_A, temp | DPLL_SYNCLOCK_ENABLE);
+ REG_READ(DPLL_A);
+ }
+
+ temp = REG_READ(DPLL_B);
+ if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) {
+ REG_WRITE(DPLL_B, temp | DPLL_SYNCLOCK_ENABLE);
+ REG_READ(DPLL_B);
+ }
+
+ udelay(500);
+
+ REG_WRITE(DSPFW1, regs->cdv.saveDSPFW[0]);
+ REG_WRITE(DSPFW2, regs->cdv.saveDSPFW[1]);
+ REG_WRITE(DSPFW3, regs->cdv.saveDSPFW[2]);
+ REG_WRITE(DSPFW4, regs->cdv.saveDSPFW[3]);
+ REG_WRITE(DSPFW5, regs->cdv.saveDSPFW[4]);
+ REG_WRITE(DSPFW6, regs->cdv.saveDSPFW[5]);
+
+ REG_WRITE(DSPARB, regs->cdv.saveDSPARB);
+ REG_WRITE(ADPA, regs->cdv.saveADPA);
+
+ REG_WRITE(BLC_PWM_CTL2, regs->saveBLC_PWM_CTL2);
+ REG_WRITE(LVDS, regs->cdv.saveLVDS);
+ REG_WRITE(PFIT_CONTROL, regs->cdv.savePFIT_CONTROL);
+ REG_WRITE(PFIT_PGM_RATIOS, regs->cdv.savePFIT_PGM_RATIOS);
+ REG_WRITE(BLC_PWM_CTL, regs->saveBLC_PWM_CTL);
+ REG_WRITE(PP_ON_DELAYS, regs->cdv.savePP_ON_DELAYS);
+ REG_WRITE(PP_OFF_DELAYS, regs->cdv.savePP_OFF_DELAYS);
+ REG_WRITE(PP_CYCLE, regs->cdv.savePP_CYCLE);
+ REG_WRITE(PP_CONTROL, regs->cdv.savePP_CONTROL);
+
+ REG_WRITE(VGACNTRL, regs->cdv.saveVGACNTRL);
+
+ REG_WRITE(PSB_INT_ENABLE_R, regs->cdv.saveIER);
+ REG_WRITE(PSB_INT_MASK_R, regs->cdv.saveIMR);
+
+ /* Fix arbitration bug */
+ CDV_MSG_WRITE32(3, 0x30, 0x08027108);
+
+ drm_mode_config_reset(dev);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_ON);
+
+ /* Resume the modeset for every activated CRTC */
+ drm_helper_resume_force_mode(dev);
return 0;
}
static int cdv_power_down(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 pwr_cnt, pwr_mask, pwr_sts;
+ int tries = 5;
+
+ pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_cnt |= PSB_PWRGT_GFX_OFF;
+ pwr_mask = PSB_PWRGT_GFX_MASK;
+
+ outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
+
+ while (tries--) {
+ pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
+ if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D3)
+ return 0;
+ udelay(10);
+ }
return 0;
}
static int cdv_power_up(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 pwr_cnt, pwr_mask, pwr_sts;
+ int tries = 5;
+
+ pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_cnt |= PSB_PWRGT_GFX_ON;
+ pwr_mask = PSB_PWRGT_GFX_MASK;
+
+ outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
+
+ while (tries--) {
+ pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
+ if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D0)
+ return 0;
+ udelay(10);
+ }
return 0;
}
diff --git a/drivers/gpu/drm/gma500/cdv_device.h b/drivers/gpu/drm/gma500/cdv_device.h
index 2a88b7beb551..9561e17621b3 100644
--- a/drivers/gpu/drm/gma500/cdv_device.h
+++ b/drivers/gpu/drm/gma500/cdv_device.h
@@ -26,7 +26,7 @@ extern void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *
extern struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev,
struct drm_crtc *crtc);
-extern inline void cdv_intel_wait_for_vblank(struct drm_device *dev)
+static inline void cdv_intel_wait_for_vblank(struct drm_device *dev)
{
/* Wait for 20ms, i.e. one cycle at 50hz. */
/* FIXME: msleep ?? */
diff --git a/drivers/gpu/drm/gma500/cdv_intel_crt.c b/drivers/gpu/drm/gma500/cdv_intel_crt.c
index c100f3e9c920..a71a6cd95bdd 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_crt.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_crt.c
@@ -32,6 +32,7 @@
#include "psb_intel_drv.h"
#include "psb_intel_reg.h"
#include "power.h"
+#include "cdv_device.h"
#include <linux/pm_runtime.h>
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
index 18d11525095e..be8455919b33 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -344,7 +344,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
/*
* Returns whether any encoder on the specified pipe is of the specified type
*/
-bool cdv_intel_pipe_has_type(struct drm_crtc *crtc, int type)
+static bool cdv_intel_pipe_has_type(struct drm_crtc *crtc, int type)
{
struct drm_device *dev = crtc->dev;
struct drm_mode_config *mode_config = &dev->mode_config;
@@ -476,7 +476,7 @@ static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target,
return err != target;
}
-int cdv_intel_pipe_set_base(struct drm_crtc *crtc,
+static int cdv_intel_pipe_set_base(struct drm_crtc *crtc,
int x, int y, struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
@@ -569,7 +569,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE;
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
u32 temp;
- bool enabled;
/* XXX: When our outputs are all unaware of DPMS modes other than off
* and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -663,7 +662,6 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
udelay(150);
break;
}
- enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
/*Set FIFO Watermarks*/
REG_WRITE(DSPARB, 0x3F3E);
}
@@ -680,22 +678,6 @@ static void cdv_intel_crtc_commit(struct drm_crtc *crtc)
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
}
-void cdv_intel_encoder_prepare(struct drm_encoder *encoder)
-{
- struct drm_encoder_helper_funcs *encoder_funcs =
- encoder->helper_private;
- /* lvds has its own version of prepare see cdv_intel_lvds_prepare */
- encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
-}
-
-void cdv_intel_encoder_commit(struct drm_encoder *encoder)
-{
- struct drm_encoder_helper_funcs *encoder_funcs =
- encoder->helper_private;
- /* lvds has its own version of commit see cdv_intel_lvds_commit */
- encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
-}
-
static bool cdv_intel_crtc_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
@@ -745,7 +727,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
int refclk;
struct cdv_intel_clock_t clock;
u32 dpll = 0, dspcntr, pipeconf;
- bool ok, is_sdvo = false, is_dvo = false;
+ bool ok;
bool is_crt = false, is_lvds = false, is_tv = false;
bool is_hdmi = false;
struct drm_mode_config *mode_config = &dev->mode_config;
@@ -763,12 +745,6 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
case INTEL_OUTPUT_LVDS:
is_lvds = true;
break;
- case INTEL_OUTPUT_SDVO:
- is_sdvo = true;
- break;
- case INTEL_OUTPUT_DVO:
- is_dvo = true;
- break;
case INTEL_OUTPUT_TVOUT:
is_tv = true;
break;
@@ -928,7 +904,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
}
/** Loads the palette/gamma unit for the CRTC with the prepared values */
-void cdv_intel_crtc_load_lut(struct drm_crtc *crtc)
+static void cdv_intel_crtc_load_lut(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_psb_private *dev_priv =
@@ -968,7 +944,7 @@ void cdv_intel_crtc_load_lut(struct drm_crtc *crtc)
gma_power_end(dev);
} else {
for (i = 0; i < 256; i++) {
- dev_priv->save_palette_a[i] =
+ dev_priv->regs.psb.save_palette_a[i] =
((psb_intel_crtc->lut_r[i] +
psb_intel_crtc->lut_adj[i]) << 16) |
((psb_intel_crtc->lut_g[i] +
@@ -1338,18 +1314,20 @@ static int cdv_intel_crtc_clock_get(struct drm_device *dev,
gma_power_end(dev);
} else {
dpll = (pipe == 0) ?
- dev_priv->saveDPLL_A : dev_priv->saveDPLL_B;
+ dev_priv->regs.psb.saveDPLL_A :
+ dev_priv->regs.psb.saveDPLL_B;
if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
fp = (pipe == 0) ?
- dev_priv->saveFPA0 :
- dev_priv->saveFPB0;
+ dev_priv->regs.psb.saveFPA0 :
+ dev_priv->regs.psb.saveFPB0;
else
fp = (pipe == 0) ?
- dev_priv->saveFPA1 :
- dev_priv->saveFPB1;
+ dev_priv->regs.psb.saveFPA1 :
+ dev_priv->regs.psb.saveFPB1;
- is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN);
+ is_lvds = (pipe == 1) &&
+ (dev_priv->regs.psb.saveLVDS & LVDS_PORT_EN);
}
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
@@ -1419,13 +1397,17 @@ struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev,
gma_power_end(dev);
} else {
htot = (pipe == 0) ?
- dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B;
+ dev_priv->regs.psb.saveHTOTAL_A :
+ dev_priv->regs.psb.saveHTOTAL_B;
hsync = (pipe == 0) ?
- dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B;
+ dev_priv->regs.psb.saveHSYNC_A :
+ dev_priv->regs.psb.saveHSYNC_B;
vtot = (pipe == 0) ?
- dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B;
+ dev_priv->regs.psb.saveVTOTAL_A :
+ dev_priv->regs.psb.saveVTOTAL_B;
vsync = (pipe == 0) ?
- dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B;
+ dev_priv->regs.psb.saveVSYNC_A :
+ dev_priv->regs.psb.saveVSYNC_B;
}
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
@@ -1475,34 +1457,3 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
.set_config = cdv_crtc_set_config,
.destroy = cdv_intel_crtc_destroy,
};
-
-/*
- * Set the default value of cursor control and base register
- * to zero. This is a workaround for h/w defect on oaktrail
- */
-void cdv_intel_cursor_init(struct drm_device *dev, int pipe)
-{
- uint32_t control;
- uint32_t base;
-
- switch (pipe) {
- case 0:
- control = CURACNTR;
- base = CURABASE;
- break;
- case 1:
- control = CURBCNTR;
- base = CURBBASE;
- break;
- case 2:
- control = CURCCNTR;
- base = CURCBASE;
- break;
- default:
- return;
- }
-
- REG_WRITE(control, 0);
- REG_WRITE(base, 0);
-}
-
diff --git a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
index de25560e629d..8d5269555005 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_hdmi.c
@@ -34,6 +34,7 @@
#include "psb_intel_drv.h"
#include "psb_drv.h"
#include "psb_intel_reg.h"
+#include "cdv_device.h"
#include <linux/pm_runtime.h>
/* hdmi control bits */
diff --git a/drivers/gpu/drm/gma500/cdv_intel_lvds.c b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
index 50e744be9852..8359c1a3f45f 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_lvds.c
@@ -78,13 +78,14 @@ static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev)
gma_power_end(dev);
} else
- retval = ((dev_priv->saveBLC_PWM_CTL &
+ retval = ((dev_priv->regs.saveBLC_PWM_CTL &
BACKLIGHT_MODULATION_FREQ_MASK) >>
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
return retval;
}
+#if 0
/*
* Set LVDS backlight level by I2C command
*/
@@ -165,6 +166,7 @@ void cdv_intel_lvds_set_brightness(struct drm_device *dev, int level)
else
cdv_lvds_pwm_set_brightness(dev, level);
}
+#endif
/**
* Sets the backlight level.
@@ -184,9 +186,9 @@ static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level)
(level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
gma_power_end(dev);
} else {
- blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL &
+ blc_pwm_ctl = dev_priv->regs.saveBLC_PWM_CTL &
~BACKLIGHT_DUTY_CYCLE_MASK;
- dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
+ dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl |
(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
}
}
@@ -242,7 +244,7 @@ static void cdv_intel_lvds_restore(struct drm_connector *connector)
{
}
-int cdv_intel_lvds_mode_valid(struct drm_connector *connector,
+static int cdv_intel_lvds_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct drm_device *dev = connector->dev;
@@ -267,7 +269,7 @@ int cdv_intel_lvds_mode_valid(struct drm_connector *connector,
return MODE_OK;
}
-bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder,
+static bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
@@ -436,7 +438,7 @@ static int cdv_intel_lvds_get_modes(struct drm_connector *connector)
* Unregister the DDC bus for this connector then free the driver private
* structure.
*/
-void cdv_intel_lvds_destroy(struct drm_connector *connector)
+static void cdv_intel_lvds_destroy(struct drm_connector *connector)
{
struct psb_intel_encoder *psb_intel_encoder =
psb_intel_attached_encoder(connector);
@@ -448,7 +450,7 @@ void cdv_intel_lvds_destroy(struct drm_connector *connector)
kfree(connector);
}
-int cdv_intel_lvds_set_property(struct drm_connector *connector,
+static int cdv_intel_lvds_set_property(struct drm_connector *connector,
struct drm_property *property,
uint64_t value)
{
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index be616735ec91..8ea202f1ba50 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -111,39 +111,6 @@ static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info)
return 0;
}
-void psbfb_suspend(struct drm_device *dev)
-{
- struct drm_framebuffer *fb;
-
- console_lock();
- mutex_lock(&dev->mode_config.mutex);
- list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
- struct psb_framebuffer *psbfb = to_psb_fb(fb);
- struct fb_info *info = psbfb->fbdev;
- fb_set_suspend(info, 1);
- drm_fb_helper_blank(FB_BLANK_POWERDOWN, info);
- }
- mutex_unlock(&dev->mode_config.mutex);
- console_unlock();
-}
-
-void psbfb_resume(struct drm_device *dev)
-{
- struct drm_framebuffer *fb;
-
- console_lock();
- mutex_lock(&dev->mode_config.mutex);
- list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
- struct psb_framebuffer *psbfb = to_psb_fb(fb);
- struct fb_info *info = psbfb->fbdev;
- fb_set_suspend(info, 0);
- drm_fb_helper_blank(FB_BLANK_UNBLANK, info);
- }
- mutex_unlock(&dev->mode_config.mutex);
- console_unlock();
- drm_helper_disable_unused_functions(dev);
-}
-
static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct psb_framebuffer *psbfb = vma->vm_private_data;
@@ -158,7 +125,7 @@ static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
unsigned long phys_addr = (unsigned long)dev_priv->stolen_base;
page_num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
- address = (unsigned long)vmf->virtual_address;
+ address = (unsigned long)vmf->virtual_address - (vmf->pgoff << PAGE_SHIFT);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
@@ -390,6 +357,7 @@ static int psbfb_create(struct psb_fbdev *fbdev,
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
bpp = sizes->surface_bpp;
+ depth = sizes->surface_depth;
/* No 24bit packed */
if (bpp == 24)
@@ -402,7 +370,6 @@ static int psbfb_create(struct psb_fbdev *fbdev,
* is ok with some fonts
*/
mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096 >> pitch_lines);
- depth = sizes->surface_depth;
size = mode_cmd.pitches[0] * mode_cmd.height;
size = ALIGN(size, PAGE_SIZE);
@@ -462,6 +429,7 @@ static int psbfb_create(struct psb_fbdev *fbdev,
fbdev->psb_fb_helper.fb = fb;
fbdev->psb_fb_helper.fbdev = info;
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
strcpy(info->fix.id, "psbfb");
info->flags = FBINFO_DEFAULT;
@@ -499,18 +467,13 @@ static int psbfb_create(struct psb_fbdev *fbdev,
info->apertures->ranges[0].size = dev_priv->gtt.stolen_size;
}
- drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(info, &fbdev->psb_fb_helper,
sizes->fb_width, sizes->fb_height);
info->fix.mmio_start = pci_resource_start(dev->pdev, 0);
info->fix.mmio_len = pci_resource_len(dev->pdev, 0);
- info->pixmap.size = 64 * 1024;
- info->pixmap.buf_align = 8;
- info->pixmap.access_align = 32;
- info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->pixmap.scan_align = 1;
+ /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
dev_info(dev->dev, "allocated %dx%d fb\n",
psbfb->base.width, psbfb->base.height);
@@ -559,11 +522,21 @@ static struct drm_framebuffer *psb_user_framebuffer_create
static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno)
{
+ struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc);
+
+ intel_crtc->lut_r[regno] = red >> 8;
+ intel_crtc->lut_g[regno] = green >> 8;
+ intel_crtc->lut_b[regno] = blue >> 8;
}
static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red,
u16 *green, u16 *blue, int regno)
{
+ struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc);
+
+ *red = intel_crtc->lut_r[regno] << 8;
+ *green = intel_crtc->lut_g[regno] << 8;
+ *blue = intel_crtc->lut_b[regno] << 8;
}
static int psbfb_probe(struct drm_fb_helper *helper,
@@ -588,7 +561,7 @@ struct drm_fb_helper_funcs psb_fb_helper_funcs = {
.fb_probe = psbfb_probe,
};
-int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev)
+static int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev)
{
struct fb_info *info;
struct psb_framebuffer *psbfb = &fbdev->pfb;
@@ -630,7 +603,7 @@ int psb_fbdev_init(struct drm_device *dev)
return 0;
}
-void psb_fbdev_fini(struct drm_device *dev)
+static void psb_fbdev_fini(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
@@ -724,10 +697,7 @@ static int psb_create_backlight_property(struct drm_device *dev)
if (dev_priv->backlight_property)
return 0;
- backlight = drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "backlight", 2);
- backlight->values[0] = 0;
- backlight->values[1] = 100;
+ backlight = drm_property_create_range(dev, 0, "backlight", 0, 100);
dev_priv->backlight_property = backlight;
diff --git a/drivers/gpu/drm/gma500/gem_glue.c b/drivers/gpu/drm/gma500/gem_glue.c
index daac12120653..3c17634f6061 100644
--- a/drivers/gpu/drm/gma500/gem_glue.c
+++ b/drivers/gpu/drm/gma500/gem_glue.c
@@ -19,6 +19,7 @@
#include <drm/drmP.h>
#include <drm/drm.h>
+#include "gem_glue.h"
void drm_gem_object_release_wrap(struct drm_gem_object *obj)
{
diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c
index aff194fbe9f3..c6465b40090f 100644
--- a/drivers/gpu/drm/gma500/gtt.c
+++ b/drivers/gpu/drm/gma500/gtt.c
@@ -57,7 +57,7 @@ static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type)
* Given a gtt_range object return the GTT offset of the page table
* entries for this gtt_range
*/
-u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r)
+static u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r)
{
struct drm_psb_private *dev_priv = dev->dev_private;
unsigned long offset;
@@ -378,7 +378,7 @@ void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt)
kfree(gt);
}
-void psb_gtt_alloc(struct drm_device *dev)
+static void psb_gtt_alloc(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
init_rwsem(&dev_priv->gtt.sem);
diff --git a/drivers/gpu/drm/gma500/intel_gmbus.c b/drivers/gpu/drm/gma500/intel_gmbus.c
index 147584ac8d02..9db90527bf0f 100644
--- a/drivers/gpu/drm/gma500/intel_gmbus.c
+++ b/drivers/gpu/drm/gma500/intel_gmbus.c
@@ -395,7 +395,7 @@ int gma_intel_setup_gmbus(struct drm_device *dev)
struct drm_psb_private *dev_priv = dev->dev_private;
int ret, i;
- dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS,
+ dev_priv->gmbus = kcalloc(GMBUS_NUM_PORTS, sizeof(struct intel_gmbus),
GFP_KERNEL);
if (dev_priv->gmbus == NULL)
return -ENOMEM;
diff --git a/drivers/gpu/drm/gma500/mdfld_device.c b/drivers/gpu/drm/gma500/mdfld_device.c
new file mode 100644
index 000000000000..af656787db0f
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_device.c
@@ -0,0 +1,691 @@
+/**************************************************************************
+ * Copyright (c) 2011, Intel Corporation.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ **************************************************************************/
+
+#include "psb_drv.h"
+#include "mid_bios.h"
+#include "mdfld_output.h"
+#include "mdfld_dsi_output.h"
+#include "tc35876x-dsi-lvds.h"
+
+#include <asm/intel_scu_ipc.h>
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+
+#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
+#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */
+#define BLC_PWM_FREQ_CALC_CONSTANT 32
+#define MHz 1000000
+#define BRIGHTNESS_MIN_LEVEL 1
+#define BRIGHTNESS_MAX_LEVEL 100
+#define BRIGHTNESS_MASK 0xFF
+#define BLC_POLARITY_NORMAL 0
+#define BLC_POLARITY_INVERSE 1
+#define BLC_ADJUSTMENT_MAX 100
+
+#define MDFLD_BLC_PWM_PRECISION_FACTOR 10
+#define MDFLD_BLC_MAX_PWM_REG_FREQ 0xFFFE
+#define MDFLD_BLC_MIN_PWM_REG_FREQ 0x2
+
+#define MDFLD_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
+#define MDFLD_BACKLIGHT_PWM_CTL_SHIFT (16)
+
+static struct backlight_device *mdfld_backlight_device;
+
+int mdfld_set_brightness(struct backlight_device *bd)
+{
+ struct drm_device *dev =
+ (struct drm_device *)bl_get_data(mdfld_backlight_device);
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ int level = bd->props.brightness;
+
+ DRM_DEBUG_DRIVER("backlight level set to %d\n", level);
+
+ /* Perform value bounds checking */
+ if (level < BRIGHTNESS_MIN_LEVEL)
+ level = BRIGHTNESS_MIN_LEVEL;
+
+ if (gma_power_begin(dev, false)) {
+ u32 adjusted_level = 0;
+
+ /*
+ * Adjust the backlight level with the percent in
+ * dev_priv->blc_adj2
+ */
+ adjusted_level = level * dev_priv->blc_adj2;
+ adjusted_level = adjusted_level / BLC_ADJUSTMENT_MAX;
+ dev_priv->brightness_adjusted = adjusted_level;
+
+ if (mdfld_get_panel_type(dev, 0) == TC35876X) {
+ if (dev_priv->dpi_panel_on[0] ||
+ dev_priv->dpi_panel_on[2])
+ tc35876x_brightness_control(dev,
+ dev_priv->brightness_adjusted);
+ } else {
+ if (dev_priv->dpi_panel_on[0])
+ mdfld_dsi_brightness_control(dev, 0,
+ dev_priv->brightness_adjusted);
+ }
+
+ if (dev_priv->dpi_panel_on[2])
+ mdfld_dsi_brightness_control(dev, 2,
+ dev_priv->brightness_adjusted);
+ gma_power_end(dev);
+ }
+
+ /* cache the brightness for later use */
+ dev_priv->brightness = level;
+ return 0;
+}
+
+static int mdfld_get_brightness(struct backlight_device *bd)
+{
+ struct drm_device *dev =
+ (struct drm_device *)bl_get_data(mdfld_backlight_device);
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ DRM_DEBUG_DRIVER("brightness = 0x%x \n", dev_priv->brightness);
+
+ /* return locally cached var instead of HW read (due to DPST etc.) */
+ return dev_priv->brightness;
+}
+
+static const struct backlight_ops mdfld_ops = {
+ .get_brightness = mdfld_get_brightness,
+ .update_status = mdfld_set_brightness,
+};
+
+static int device_backlight_init(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = (struct drm_psb_private *)
+ dev->dev_private;
+
+ dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX;
+ dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX;
+
+ return 0;
+}
+
+static int mdfld_backlight_init(struct drm_device *dev)
+{
+ struct backlight_properties props;
+ int ret = 0;
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = BRIGHTNESS_MAX_LEVEL;
+ props.type = BACKLIGHT_PLATFORM;
+ mdfld_backlight_device = backlight_device_register("mdfld-bl",
+ NULL, (void *)dev, &mdfld_ops, &props);
+
+ if (IS_ERR(mdfld_backlight_device))
+ return PTR_ERR(mdfld_backlight_device);
+
+ ret = device_backlight_init(dev);
+ if (ret)
+ return ret;
+
+ mdfld_backlight_device->props.brightness = BRIGHTNESS_MAX_LEVEL;
+ mdfld_backlight_device->props.max_brightness = BRIGHTNESS_MAX_LEVEL;
+ backlight_update_status(mdfld_backlight_device);
+ return 0;
+}
+#endif
+
+struct backlight_device *mdfld_get_backlight_device(void)
+{
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+ return mdfld_backlight_device;
+#else
+ return NULL;
+#endif
+}
+
+/*
+ * mdfld_save_display_registers
+ *
+ * Description: We are going to suspend so save current display
+ * register state.
+ *
+ * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
+ */
+static int mdfld_save_display_registers(struct drm_device *dev, int pipe)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct medfield_state *regs = &dev_priv->regs.mdfld;
+ int i;
+
+ /* register */
+ u32 dpll_reg = MRST_DPLL_A;
+ u32 fp_reg = MRST_FPA0;
+ u32 pipeconf_reg = PIPEACONF;
+ u32 htot_reg = HTOTAL_A;
+ u32 hblank_reg = HBLANK_A;
+ u32 hsync_reg = HSYNC_A;
+ u32 vtot_reg = VTOTAL_A;
+ u32 vblank_reg = VBLANK_A;
+ u32 vsync_reg = VSYNC_A;
+ u32 pipesrc_reg = PIPEASRC;
+ u32 dspstride_reg = DSPASTRIDE;
+ u32 dsplinoff_reg = DSPALINOFF;
+ u32 dsptileoff_reg = DSPATILEOFF;
+ u32 dspsize_reg = DSPASIZE;
+ u32 dsppos_reg = DSPAPOS;
+ u32 dspsurf_reg = DSPASURF;
+ u32 mipi_reg = MIPI;
+ u32 dspcntr_reg = DSPACNTR;
+ u32 dspstatus_reg = PIPEASTAT;
+ u32 palette_reg = PALETTE_A;
+
+ /* pointer to values */
+ u32 *dpll_val = &regs->saveDPLL_A;
+ u32 *fp_val = &regs->saveFPA0;
+ u32 *pipeconf_val = &regs->savePIPEACONF;
+ u32 *htot_val = &regs->saveHTOTAL_A;
+ u32 *hblank_val = &regs->saveHBLANK_A;
+ u32 *hsync_val = &regs->saveHSYNC_A;
+ u32 *vtot_val = &regs->saveVTOTAL_A;
+ u32 *vblank_val = &regs->saveVBLANK_A;
+ u32 *vsync_val = &regs->saveVSYNC_A;
+ u32 *pipesrc_val = &regs->savePIPEASRC;
+ u32 *dspstride_val = &regs->saveDSPASTRIDE;
+ u32 *dsplinoff_val = &regs->saveDSPALINOFF;
+ u32 *dsptileoff_val = &regs->saveDSPATILEOFF;
+ u32 *dspsize_val = &regs->saveDSPASIZE;
+ u32 *dsppos_val = &regs->saveDSPAPOS;
+ u32 *dspsurf_val = &regs->saveDSPASURF;
+ u32 *mipi_val = &regs->saveMIPI;
+ u32 *dspcntr_val = &regs->saveDSPACNTR;
+ u32 *dspstatus_val = &regs->saveDSPASTATUS;
+ u32 *palette_val = regs->save_palette_a;
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ /* regester */
+ dpll_reg = MDFLD_DPLL_B;
+ fp_reg = MDFLD_DPLL_DIV0;
+ pipeconf_reg = PIPEBCONF;
+ htot_reg = HTOTAL_B;
+ hblank_reg = HBLANK_B;
+ hsync_reg = HSYNC_B;
+ vtot_reg = VTOTAL_B;
+ vblank_reg = VBLANK_B;
+ vsync_reg = VSYNC_B;
+ pipesrc_reg = PIPEBSRC;
+ dspstride_reg = DSPBSTRIDE;
+ dsplinoff_reg = DSPBLINOFF;
+ dsptileoff_reg = DSPBTILEOFF;
+ dspsize_reg = DSPBSIZE;
+ dsppos_reg = DSPBPOS;
+ dspsurf_reg = DSPBSURF;
+ dspcntr_reg = DSPBCNTR;
+ dspstatus_reg = PIPEBSTAT;
+ palette_reg = PALETTE_B;
+
+ /* values */
+ dpll_val = &regs->saveDPLL_B;
+ fp_val = &regs->saveFPB0;
+ pipeconf_val = &regs->savePIPEBCONF;
+ htot_val = &regs->saveHTOTAL_B;
+ hblank_val = &regs->saveHBLANK_B;
+ hsync_val = &regs->saveHSYNC_B;
+ vtot_val = &regs->saveVTOTAL_B;
+ vblank_val = &regs->saveVBLANK_B;
+ vsync_val = &regs->saveVSYNC_B;
+ pipesrc_val = &regs->savePIPEBSRC;
+ dspstride_val = &regs->saveDSPBSTRIDE;
+ dsplinoff_val = &regs->saveDSPBLINOFF;
+ dsptileoff_val = &regs->saveDSPBTILEOFF;
+ dspsize_val = &regs->saveDSPBSIZE;
+ dsppos_val = &regs->saveDSPBPOS;
+ dspsurf_val = &regs->saveDSPBSURF;
+ dspcntr_val = &regs->saveDSPBCNTR;
+ dspstatus_val = &regs->saveDSPBSTATUS;
+ palette_val = regs->save_palette_b;
+ break;
+ case 2:
+ /* register */
+ pipeconf_reg = PIPECCONF;
+ htot_reg = HTOTAL_C;
+ hblank_reg = HBLANK_C;
+ hsync_reg = HSYNC_C;
+ vtot_reg = VTOTAL_C;
+ vblank_reg = VBLANK_C;
+ vsync_reg = VSYNC_C;
+ pipesrc_reg = PIPECSRC;
+ dspstride_reg = DSPCSTRIDE;
+ dsplinoff_reg = DSPCLINOFF;
+ dsptileoff_reg = DSPCTILEOFF;
+ dspsize_reg = DSPCSIZE;
+ dsppos_reg = DSPCPOS;
+ dspsurf_reg = DSPCSURF;
+ mipi_reg = MIPI_C;
+ dspcntr_reg = DSPCCNTR;
+ dspstatus_reg = PIPECSTAT;
+ palette_reg = PALETTE_C;
+
+ /* pointer to values */
+ pipeconf_val = &regs->savePIPECCONF;
+ htot_val = &regs->saveHTOTAL_C;
+ hblank_val = &regs->saveHBLANK_C;
+ hsync_val = &regs->saveHSYNC_C;
+ vtot_val = &regs->saveVTOTAL_C;
+ vblank_val = &regs->saveVBLANK_C;
+ vsync_val = &regs->saveVSYNC_C;
+ pipesrc_val = &regs->savePIPECSRC;
+ dspstride_val = &regs->saveDSPCSTRIDE;
+ dsplinoff_val = &regs->saveDSPCLINOFF;
+ dsptileoff_val = &regs->saveDSPCTILEOFF;
+ dspsize_val = &regs->saveDSPCSIZE;
+ dsppos_val = &regs->saveDSPCPOS;
+ dspsurf_val = &regs->saveDSPCSURF;
+ mipi_val = &regs->saveMIPI_C;
+ dspcntr_val = &regs->saveDSPCCNTR;
+ dspstatus_val = &regs->saveDSPCSTATUS;
+ palette_val = regs->save_palette_c;
+ break;
+ default:
+ DRM_ERROR("%s, invalid pipe number.\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Pipe & plane A info */
+ *dpll_val = PSB_RVDC32(dpll_reg);
+ *fp_val = PSB_RVDC32(fp_reg);
+ *pipeconf_val = PSB_RVDC32(pipeconf_reg);
+ *htot_val = PSB_RVDC32(htot_reg);
+ *hblank_val = PSB_RVDC32(hblank_reg);
+ *hsync_val = PSB_RVDC32(hsync_reg);
+ *vtot_val = PSB_RVDC32(vtot_reg);
+ *vblank_val = PSB_RVDC32(vblank_reg);
+ *vsync_val = PSB_RVDC32(vsync_reg);
+ *pipesrc_val = PSB_RVDC32(pipesrc_reg);
+ *dspstride_val = PSB_RVDC32(dspstride_reg);
+ *dsplinoff_val = PSB_RVDC32(dsplinoff_reg);
+ *dsptileoff_val = PSB_RVDC32(dsptileoff_reg);
+ *dspsize_val = PSB_RVDC32(dspsize_reg);
+ *dsppos_val = PSB_RVDC32(dsppos_reg);
+ *dspsurf_val = PSB_RVDC32(dspsurf_reg);
+ *dspcntr_val = PSB_RVDC32(dspcntr_reg);
+ *dspstatus_val = PSB_RVDC32(dspstatus_reg);
+
+ /*save palette (gamma) */
+ for (i = 0; i < 256; i++)
+ palette_val[i] = PSB_RVDC32(palette_reg + (i << 2));
+
+ if (pipe == 1) {
+ regs->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
+ regs->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
+
+ regs->saveHDMIPHYMISCCTL = PSB_RVDC32(HDMIPHYMISCCTL);
+ regs->saveHDMIB_CONTROL = PSB_RVDC32(HDMIB_CONTROL);
+ return 0;
+ }
+
+ *mipi_val = PSB_RVDC32(mipi_reg);
+ return 0;
+}
+
+/*
+ * mdfld_restore_display_registers
+ *
+ * Description: We are going to resume so restore display register state.
+ *
+ * Notes: FIXME_JLIU7 need to add the support for DPI MIPI & HDMI audio
+ */
+static int mdfld_restore_display_registers(struct drm_device *dev, int pipe)
+{
+ /* To get panel out of ULPS mode. */
+ u32 temp = 0;
+ u32 device_ready_reg = DEVICE_READY_REG;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct mdfld_dsi_config *dsi_config = NULL;
+ struct medfield_state *regs = &dev_priv->regs.mdfld;
+ u32 i = 0;
+ u32 dpll = 0;
+ u32 timeout = 0;
+
+ /* regester */
+ u32 dpll_reg = MRST_DPLL_A;
+ u32 fp_reg = MRST_FPA0;
+ u32 pipeconf_reg = PIPEACONF;
+ u32 htot_reg = HTOTAL_A;
+ u32 hblank_reg = HBLANK_A;
+ u32 hsync_reg = HSYNC_A;
+ u32 vtot_reg = VTOTAL_A;
+ u32 vblank_reg = VBLANK_A;
+ u32 vsync_reg = VSYNC_A;
+ u32 pipesrc_reg = PIPEASRC;
+ u32 dspstride_reg = DSPASTRIDE;
+ u32 dsplinoff_reg = DSPALINOFF;
+ u32 dsptileoff_reg = DSPATILEOFF;
+ u32 dspsize_reg = DSPASIZE;
+ u32 dsppos_reg = DSPAPOS;
+ u32 dspsurf_reg = DSPASURF;
+ u32 dspstatus_reg = PIPEASTAT;
+ u32 mipi_reg = MIPI;
+ u32 dspcntr_reg = DSPACNTR;
+ u32 palette_reg = PALETTE_A;
+
+ /* values */
+ u32 dpll_val = regs->saveDPLL_A & ~DPLL_VCO_ENABLE;
+ u32 fp_val = regs->saveFPA0;
+ u32 pipeconf_val = regs->savePIPEACONF;
+ u32 htot_val = regs->saveHTOTAL_A;
+ u32 hblank_val = regs->saveHBLANK_A;
+ u32 hsync_val = regs->saveHSYNC_A;
+ u32 vtot_val = regs->saveVTOTAL_A;
+ u32 vblank_val = regs->saveVBLANK_A;
+ u32 vsync_val = regs->saveVSYNC_A;
+ u32 pipesrc_val = regs->savePIPEASRC;
+ u32 dspstride_val = regs->saveDSPASTRIDE;
+ u32 dsplinoff_val = regs->saveDSPALINOFF;
+ u32 dsptileoff_val = regs->saveDSPATILEOFF;
+ u32 dspsize_val = regs->saveDSPASIZE;
+ u32 dsppos_val = regs->saveDSPAPOS;
+ u32 dspsurf_val = regs->saveDSPASURF;
+ u32 dspstatus_val = regs->saveDSPASTATUS;
+ u32 mipi_val = regs->saveMIPI;
+ u32 dspcntr_val = regs->saveDSPACNTR;
+ u32 *palette_val = regs->save_palette_a;
+
+ switch (pipe) {
+ case 0:
+ dsi_config = dev_priv->dsi_configs[0];
+ break;
+ case 1:
+ /* regester */
+ dpll_reg = MDFLD_DPLL_B;
+ fp_reg = MDFLD_DPLL_DIV0;
+ pipeconf_reg = PIPEBCONF;
+ htot_reg = HTOTAL_B;
+ hblank_reg = HBLANK_B;
+ hsync_reg = HSYNC_B;
+ vtot_reg = VTOTAL_B;
+ vblank_reg = VBLANK_B;
+ vsync_reg = VSYNC_B;
+ pipesrc_reg = PIPEBSRC;
+ dspstride_reg = DSPBSTRIDE;
+ dsplinoff_reg = DSPBLINOFF;
+ dsptileoff_reg = DSPBTILEOFF;
+ dspsize_reg = DSPBSIZE;
+ dsppos_reg = DSPBPOS;
+ dspsurf_reg = DSPBSURF;
+ dspcntr_reg = DSPBCNTR;
+ dspstatus_reg = PIPEBSTAT;
+ palette_reg = PALETTE_B;
+
+ /* values */
+ dpll_val = regs->saveDPLL_B & ~DPLL_VCO_ENABLE;
+ fp_val = regs->saveFPB0;
+ pipeconf_val = regs->savePIPEBCONF;
+ htot_val = regs->saveHTOTAL_B;
+ hblank_val = regs->saveHBLANK_B;
+ hsync_val = regs->saveHSYNC_B;
+ vtot_val = regs->saveVTOTAL_B;
+ vblank_val = regs->saveVBLANK_B;
+ vsync_val = regs->saveVSYNC_B;
+ pipesrc_val = regs->savePIPEBSRC;
+ dspstride_val = regs->saveDSPBSTRIDE;
+ dsplinoff_val = regs->saveDSPBLINOFF;
+ dsptileoff_val = regs->saveDSPBTILEOFF;
+ dspsize_val = regs->saveDSPBSIZE;
+ dsppos_val = regs->saveDSPBPOS;
+ dspsurf_val = regs->saveDSPBSURF;
+ dspcntr_val = regs->saveDSPBCNTR;
+ dspstatus_val = regs->saveDSPBSTATUS;
+ palette_val = regs->save_palette_b;
+ break;
+ case 2:
+ /* regester */
+ pipeconf_reg = PIPECCONF;
+ htot_reg = HTOTAL_C;
+ hblank_reg = HBLANK_C;
+ hsync_reg = HSYNC_C;
+ vtot_reg = VTOTAL_C;
+ vblank_reg = VBLANK_C;
+ vsync_reg = VSYNC_C;
+ pipesrc_reg = PIPECSRC;
+ dspstride_reg = DSPCSTRIDE;
+ dsplinoff_reg = DSPCLINOFF;
+ dsptileoff_reg = DSPCTILEOFF;
+ dspsize_reg = DSPCSIZE;
+ dsppos_reg = DSPCPOS;
+ dspsurf_reg = DSPCSURF;
+ mipi_reg = MIPI_C;
+ dspcntr_reg = DSPCCNTR;
+ dspstatus_reg = PIPECSTAT;
+ palette_reg = PALETTE_C;
+
+ /* values */
+ pipeconf_val = regs->savePIPECCONF;
+ htot_val = regs->saveHTOTAL_C;
+ hblank_val = regs->saveHBLANK_C;
+ hsync_val = regs->saveHSYNC_C;
+ vtot_val = regs->saveVTOTAL_C;
+ vblank_val = regs->saveVBLANK_C;
+ vsync_val = regs->saveVSYNC_C;
+ pipesrc_val = regs->savePIPECSRC;
+ dspstride_val = regs->saveDSPCSTRIDE;
+ dsplinoff_val = regs->saveDSPCLINOFF;
+ dsptileoff_val = regs->saveDSPCTILEOFF;
+ dspsize_val = regs->saveDSPCSIZE;
+ dsppos_val = regs->saveDSPCPOS;
+ dspsurf_val = regs->saveDSPCSURF;
+ mipi_val = regs->saveMIPI_C;
+ dspcntr_val = regs->saveDSPCCNTR;
+ dspstatus_val = regs->saveDSPCSTATUS;
+ palette_val = regs->save_palette_c;
+
+ dsi_config = dev_priv->dsi_configs[1];
+ break;
+ default:
+ DRM_ERROR("%s, invalid pipe number.\n", __func__);
+ return -EINVAL;
+ }
+
+ /*make sure VGA plane is off. it initializes to on after reset!*/
+ PSB_WVDC32(0x80000000, VGACNTRL);
+
+ if (pipe == 1) {
+ PSB_WVDC32(dpll_val & ~DPLL_VCO_ENABLE, dpll_reg);
+ PSB_RVDC32(dpll_reg);
+
+ PSB_WVDC32(fp_val, fp_reg);
+ } else {
+
+ dpll = PSB_RVDC32(dpll_reg);
+
+ if (!(dpll & DPLL_VCO_ENABLE)) {
+
+ /* When ungating power of DPLL, needs to wait 0.5us
+ before enable the VCO */
+ if (dpll & MDFLD_PWR_GATE_EN) {
+ dpll &= ~MDFLD_PWR_GATE_EN;
+ PSB_WVDC32(dpll, dpll_reg);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+ }
+
+ PSB_WVDC32(fp_val, fp_reg);
+ PSB_WVDC32(dpll_val, dpll_reg);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+
+ dpll_val |= DPLL_VCO_ENABLE;
+ PSB_WVDC32(dpll_val, dpll_reg);
+ PSB_RVDC32(dpll_reg);
+
+ /* wait for DSI PLL to lock */
+ while (timeout < 20000 &&
+ !(PSB_RVDC32(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) {
+ udelay(150);
+ timeout++;
+ }
+
+ if (timeout == 20000) {
+ DRM_ERROR("%s, can't lock DSIPLL.\n",
+ __func__);
+ return -EINVAL;
+ }
+ }
+ }
+ /* Restore mode */
+ PSB_WVDC32(htot_val, htot_reg);
+ PSB_WVDC32(hblank_val, hblank_reg);
+ PSB_WVDC32(hsync_val, hsync_reg);
+ PSB_WVDC32(vtot_val, vtot_reg);
+ PSB_WVDC32(vblank_val, vblank_reg);
+ PSB_WVDC32(vsync_val, vsync_reg);
+ PSB_WVDC32(pipesrc_val, pipesrc_reg);
+ PSB_WVDC32(dspstatus_val, dspstatus_reg);
+
+ /*set up the plane*/
+ PSB_WVDC32(dspstride_val, dspstride_reg);
+ PSB_WVDC32(dsplinoff_val, dsplinoff_reg);
+ PSB_WVDC32(dsptileoff_val, dsptileoff_reg);
+ PSB_WVDC32(dspsize_val, dspsize_reg);
+ PSB_WVDC32(dsppos_val, dsppos_reg);
+ PSB_WVDC32(dspsurf_val, dspsurf_reg);
+
+ if (pipe == 1) {
+ /* restore palette (gamma) */
+ /*DRM_UDELAY(50000); */
+ for (i = 0; i < 256; i++)
+ PSB_WVDC32(palette_val[i], palette_reg + (i << 2));
+
+ PSB_WVDC32(regs->savePFIT_CONTROL, PFIT_CONTROL);
+ PSB_WVDC32(regs->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
+
+ /*TODO: resume HDMI port */
+
+ /*TODO: resume pipe*/
+
+ /*enable the plane*/
+ PSB_WVDC32(dspcntr_val & ~DISPLAY_PLANE_ENABLE, dspcntr_reg);
+
+ return 0;
+ }
+
+ /*set up pipe related registers*/
+ PSB_WVDC32(mipi_val, mipi_reg);
+
+ /*setup MIPI adapter + MIPI IP registers*/
+ if (dsi_config)
+ mdfld_dsi_controller_init(dsi_config, pipe);
+
+ if (in_atomic() || in_interrupt())
+ mdelay(20);
+ else
+ msleep(20);
+
+ /*enable the plane*/
+ PSB_WVDC32(dspcntr_val, dspcntr_reg);
+
+ if (in_atomic() || in_interrupt())
+ mdelay(20);
+ else
+ msleep(20);
+
+ /* LP Hold Release */
+ temp = REG_READ(mipi_reg);
+ temp |= LP_OUTPUT_HOLD_RELEASE;
+ REG_WRITE(mipi_reg, temp);
+ mdelay(1);
+
+
+ /* Set DSI host to exit from Utra Low Power State */
+ temp = REG_READ(device_ready_reg);
+ temp &= ~ULPS_MASK;
+ temp |= 0x3;
+ temp |= EXIT_ULPS_DEV_READY;
+ REG_WRITE(device_ready_reg, temp);
+ mdelay(1);
+
+ temp = REG_READ(device_ready_reg);
+ temp &= ~ULPS_MASK;
+ temp |= EXITING_ULPS;
+ REG_WRITE(device_ready_reg, temp);
+ mdelay(1);
+
+ /*enable the pipe*/
+ PSB_WVDC32(pipeconf_val, pipeconf_reg);
+
+ /* restore palette (gamma) */
+ /*DRM_UDELAY(50000); */
+ for (i = 0; i < 256; i++)
+ PSB_WVDC32(palette_val[i], palette_reg + (i << 2));
+
+ return 0;
+}
+
+static int mdfld_save_registers(struct drm_device *dev)
+{
+ /* mdfld_save_cursor_overlay_registers(dev); */
+ mdfld_save_display_registers(dev, 0);
+ mdfld_save_display_registers(dev, 2);
+ mdfld_disable_crtc(dev, 0);
+ mdfld_disable_crtc(dev, 2);
+
+ return 0;
+}
+
+static int mdfld_restore_registers(struct drm_device *dev)
+{
+ mdfld_restore_display_registers(dev, 2);
+ mdfld_restore_display_registers(dev, 0);
+ /* mdfld_restore_cursor_overlay_registers(dev); */
+
+ return 0;
+}
+
+static int mdfld_power_down(struct drm_device *dev)
+{
+ /* FIXME */
+ return 0;
+}
+
+static int mdfld_power_up(struct drm_device *dev)
+{
+ /* FIXME */
+ return 0;
+}
+
+const struct psb_ops mdfld_chip_ops = {
+ .name = "mdfld",
+ .accel_2d = 0,
+ .pipes = 3,
+ .crtcs = 3,
+ .sgx_offset = MRST_SGX_OFFSET,
+
+ .chip_setup = mid_chip_setup,
+ .crtc_helper = &mdfld_helper_funcs,
+ .crtc_funcs = &psb_intel_crtc_funcs,
+
+ .output_init = mdfld_output_init,
+
+#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
+ .backlight_init = mdfld_backlight_init,
+#endif
+
+ .save_regs = mdfld_save_registers,
+ .restore_regs = mdfld_restore_registers,
+ .power_down = mdfld_power_down,
+ .power_up = mdfld_power_up,
+};
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c
new file mode 100644
index 000000000000..d52358b744a0
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.c
@@ -0,0 +1,1017 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * jim liu <jim.liu@intel.com>
+ * Jackie Li<yaodong.li@intel.com>
+ */
+
+#include "mdfld_dsi_dpi.h"
+#include "mdfld_output.h"
+#include "mdfld_dsi_pkg_sender.h"
+#include "psb_drv.h"
+#include "tc35876x-dsi-lvds.h"
+
+static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output,
+ int pipe);
+
+static void mdfld_wait_for_HS_DATA_FIFO(struct drm_device *dev, u32 pipe)
+{
+ u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
+ int timeout = 0;
+
+ udelay(500);
+
+ /* This will time out after approximately 2+ seconds */
+ while ((timeout < 20000) &&
+ (REG_READ(gen_fifo_stat_reg) & DSI_FIFO_GEN_HS_DATA_FULL)) {
+ udelay(100);
+ timeout++;
+ }
+
+ if (timeout == 20000)
+ DRM_INFO("MIPI: HS Data FIFO was never cleared!\n");
+}
+
+static void mdfld_wait_for_HS_CTRL_FIFO(struct drm_device *dev, u32 pipe)
+{
+ u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
+ int timeout = 0;
+
+ udelay(500);
+
+ /* This will time out after approximately 2+ seconds */
+ while ((timeout < 20000) && (REG_READ(gen_fifo_stat_reg)
+ & DSI_FIFO_GEN_HS_CTRL_FULL)) {
+ udelay(100);
+ timeout++;
+ }
+ if (timeout == 20000)
+ DRM_INFO("MIPI: HS CMD FIFO was never cleared!\n");
+}
+
+static void mdfld_wait_for_DPI_CTRL_FIFO(struct drm_device *dev, u32 pipe)
+{
+ u32 gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
+ int timeout = 0;
+
+ udelay(500);
+
+ /* This will time out after approximately 2+ seconds */
+ while ((timeout < 20000) && ((REG_READ(gen_fifo_stat_reg) &
+ DPI_FIFO_EMPTY) != DPI_FIFO_EMPTY)) {
+ udelay(100);
+ timeout++;
+ }
+
+ if (timeout == 20000)
+ DRM_ERROR("MIPI: DPI FIFO was never cleared\n");
+}
+
+static void mdfld_wait_for_SPL_PKG_SENT(struct drm_device *dev, u32 pipe)
+{
+ u32 intr_stat_reg = MIPI_INTR_STAT_REG(pipe);
+ int timeout = 0;
+
+ udelay(500);
+
+ /* This will time out after approximately 2+ seconds */
+ while ((timeout < 20000) && (!(REG_READ(intr_stat_reg)
+ & DSI_INTR_STATE_SPL_PKG_SENT))) {
+ udelay(100);
+ timeout++;
+ }
+
+ if (timeout == 20000)
+ DRM_ERROR("MIPI: SPL_PKT_SENT_INTERRUPT was not sent successfully!\n");
+}
+
+/* For TC35876X */
+
+static void dsi_set_device_ready_state(struct drm_device *dev, int state,
+ int pipe)
+{
+ REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), !!state, 0, 0);
+}
+
+static void dsi_set_pipe_plane_enable_state(struct drm_device *dev,
+ int state, int pipe)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 pipeconf_reg = PIPEACONF;
+ u32 dspcntr_reg = DSPACNTR;
+
+ u32 dspcntr = dev_priv->dspcntr[pipe];
+ u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
+
+ if (pipe) {
+ pipeconf_reg = PIPECCONF;
+ dspcntr_reg = DSPCCNTR;
+ } else
+ mipi &= (~0x03);
+
+ if (state) {
+ /*Set up pipe */
+ REG_WRITE(pipeconf_reg, BIT(31));
+
+ if (REG_BIT_WAIT(pipeconf_reg, 1, 30))
+ dev_err(&dev->pdev->dev, "%s: Pipe enable timeout\n",
+ __func__);
+
+ /*Set up display plane */
+ REG_WRITE(dspcntr_reg, dspcntr);
+ } else {
+ u32 dspbase_reg = pipe ? MDFLD_DSPCBASE : MRST_DSPABASE;
+
+ /* Put DSI lanes to ULPS to disable pipe */
+ REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 2, 2, 1);
+ REG_READ(MIPI_DEVICE_READY_REG(pipe)); /* posted write? */
+
+ /* LP Hold */
+ REG_FLD_MOD(MIPI_PORT_CONTROL(pipe), 0, 16, 16);
+ REG_READ(MIPI_PORT_CONTROL(pipe)); /* posted write? */
+
+ /* Disable display plane */
+ REG_FLD_MOD(dspcntr_reg, 0, 31, 31);
+
+ /* Flush the plane changes ??? posted write? */
+ REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
+ REG_READ(dspbase_reg);
+
+ /* Disable PIPE */
+ REG_FLD_MOD(pipeconf_reg, 0, 31, 31);
+
+ if (REG_BIT_WAIT(pipeconf_reg, 0, 30))
+ dev_err(&dev->pdev->dev, "%s: Pipe disable timeout\n",
+ __func__);
+
+ if (REG_BIT_WAIT(MIPI_GEN_FIFO_STAT_REG(pipe), 1, 28))
+ dev_err(&dev->pdev->dev, "%s: FIFO not empty\n",
+ __func__);
+ }
+}
+
+static void mdfld_dsi_configure_down(struct mdfld_dsi_encoder *dsi_encoder,
+ int pipe)
+{
+ struct mdfld_dsi_dpi_output *dpi_output =
+ MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_encoder_get_config(dsi_encoder);
+ struct drm_device *dev = dsi_config->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (!dev_priv->dpi_panel_on[pipe]) {
+ dev_err(dev->dev, "DPI panel is already off\n");
+ return;
+ }
+ tc35876x_toshiba_bridge_panel_off(dev);
+ tc35876x_set_bridge_reset_state(dev, 1);
+ dsi_set_pipe_plane_enable_state(dev, 0, pipe);
+ mdfld_dsi_dpi_shut_down(dpi_output, pipe);
+ dsi_set_device_ready_state(dev, 0, pipe);
+}
+
+static void mdfld_dsi_configure_up(struct mdfld_dsi_encoder *dsi_encoder,
+ int pipe)
+{
+ struct mdfld_dsi_dpi_output *dpi_output =
+ MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_encoder_get_config(dsi_encoder);
+ struct drm_device *dev = dsi_config->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->dpi_panel_on[pipe]) {
+ dev_err(dev->dev, "DPI panel is already on\n");
+ return;
+ }
+
+ /* For resume path sequence */
+ mdfld_dsi_dpi_shut_down(dpi_output, pipe);
+ dsi_set_device_ready_state(dev, 0, pipe);
+
+ dsi_set_device_ready_state(dev, 1, pipe);
+ tc35876x_set_bridge_reset_state(dev, 0);
+ tc35876x_configure_lvds_bridge(dev);
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe); /* Send turn on command */
+ dsi_set_pipe_plane_enable_state(dev, 1, pipe);
+}
+/* End for TC35876X */
+
+/* ************************************************************************* *\
+ * FUNCTION: mdfld_dsi_tpo_ic_init
+ *
+ * DESCRIPTION: This function is called only by mrst_dsi_mode_set and
+ * restore_display_registers. since this function does not
+ * acquire the mutex, it is important that the calling function
+ * does!
+\* ************************************************************************* */
+static void mdfld_dsi_tpo_ic_init(struct mdfld_dsi_config *dsi_config, u32 pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ u32 dcsChannelNumber = dsi_config->channel_num;
+ u32 gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe);
+ u32 gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe);
+ u32 gen_ctrl_val = GEN_LONG_WRITE;
+
+ DRM_INFO("Enter mrst init TPO MIPI display.\n");
+
+ gen_ctrl_val |= dcsChannelNumber << DCS_CHANNEL_NUMBER_POS;
+
+ /* Flip page order */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00008036);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS));
+
+ /* 0xF0 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x005a5af0);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
+
+ /* Write protection key */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x005a5af1);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
+
+ /* 0xFC */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x005a5afc);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
+
+ /* 0xB7 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x770000b7);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00000044);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x05 << WORD_COUNTS_POS));
+
+ /* 0xB6 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x000a0ab6);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
+
+ /* 0xF2 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x081010f2);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x4a070708);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x000000c5);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS));
+
+ /* 0xF8 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x024003f8);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x01030a04);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x0e020220);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00000004);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x0d << WORD_COUNTS_POS));
+
+ /* 0xE2 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x398fc3e2);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x0000916f);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x06 << WORD_COUNTS_POS));
+
+ /* 0xB0 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x000000b0);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x02 << WORD_COUNTS_POS));
+
+ /* 0xF4 */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x240242f4);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x78ee2002);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x2a071050);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x507fee10);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x10300710);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x14 << WORD_COUNTS_POS));
+
+ /* 0xBA */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x19fe07ba);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x101c0a31);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00000010);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS));
+
+ /* 0xBB */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x28ff07bb);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x24280a31);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00000034);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x09 << WORD_COUNTS_POS));
+
+ /* 0xFB */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x535d05fb);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x1b1a2130);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x221e180e);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x131d2120);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x535d0508);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x1c1a2131);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x231f160d);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x111b2220);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x535c2008);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x1f1d2433);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x2c251a10);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x2c34372d);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00000023);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS));
+
+ /* 0xFA */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x525c0bfa);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x1c1c232f);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x2623190e);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x18212625);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x545d0d0e);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x1e1d2333);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x26231a10);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x1a222725);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x545d280f);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x21202635);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x31292013);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x31393d33);
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x00000029);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x31 << WORD_COUNTS_POS));
+
+ /* Set DM */
+ mdfld_wait_for_HS_DATA_FIFO(dev, pipe);
+ REG_WRITE(gen_data_reg, 0x000100f7);
+ mdfld_wait_for_HS_CTRL_FIFO(dev, pipe);
+ REG_WRITE(gen_ctrl_reg, gen_ctrl_val | (0x03 << WORD_COUNTS_POS));
+}
+
+static u16 mdfld_dsi_dpi_to_byte_clock_count(int pixel_clock_count,
+ int num_lane, int bpp)
+{
+ return (u16)((pixel_clock_count * bpp) / (num_lane * 8));
+}
+
+/*
+ * Calculate the dpi time basing on a given drm mode @mode
+ * return 0 on success.
+ * FIXME: I was using proposed mode value for calculation, may need to
+ * use crtc mode values later
+ */
+int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode,
+ struct mdfld_dsi_dpi_timing *dpi_timing,
+ int num_lane, int bpp)
+{
+ int pclk_hsync, pclk_hfp, pclk_hbp, pclk_hactive;
+ int pclk_vsync, pclk_vfp, pclk_vbp;
+
+ pclk_hactive = mode->hdisplay;
+ pclk_hfp = mode->hsync_start - mode->hdisplay;
+ pclk_hsync = mode->hsync_end - mode->hsync_start;
+ pclk_hbp = mode->htotal - mode->hsync_end;
+
+ pclk_vfp = mode->vsync_start - mode->vdisplay;
+ pclk_vsync = mode->vsync_end - mode->vsync_start;
+ pclk_vbp = mode->vtotal - mode->vsync_end;
+
+ /*
+ * byte clock counts were calculated by following formula
+ * bclock_count = pclk_count * bpp / num_lane / 8
+ */
+ dpi_timing->hsync_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_hsync, num_lane, bpp);
+ dpi_timing->hbp_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_hbp, num_lane, bpp);
+ dpi_timing->hfp_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_hfp, num_lane, bpp);
+ dpi_timing->hactive_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_hactive, num_lane, bpp);
+ dpi_timing->vsync_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_vsync, num_lane, bpp);
+ dpi_timing->vbp_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_vbp, num_lane, bpp);
+ dpi_timing->vfp_count = mdfld_dsi_dpi_to_byte_clock_count(
+ pclk_vfp, num_lane, bpp);
+
+ return 0;
+}
+
+void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config,
+ int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ int lane_count = dsi_config->lane_count;
+ struct mdfld_dsi_dpi_timing dpi_timing;
+ struct drm_display_mode *mode = dsi_config->mode;
+ u32 val;
+
+ /*un-ready device*/
+ REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 0, 0, 0);
+
+ /*init dsi adapter before kicking off*/
+ REG_WRITE(MIPI_CTRL_REG(pipe), 0x00000018);
+
+ /*enable all interrupts*/
+ REG_WRITE(MIPI_INTR_EN_REG(pipe), 0xffffffff);
+
+ /*set up func_prg*/
+ val = lane_count;
+ val |= dsi_config->channel_num << DSI_DPI_VIRT_CHANNEL_OFFSET;
+
+ switch (dsi_config->bpp) {
+ case 16:
+ val |= DSI_DPI_COLOR_FORMAT_RGB565;
+ break;
+ case 18:
+ val |= DSI_DPI_COLOR_FORMAT_RGB666;
+ break;
+ case 24:
+ val |= DSI_DPI_COLOR_FORMAT_RGB888;
+ break;
+ default:
+ DRM_ERROR("unsupported color format, bpp = %d\n",
+ dsi_config->bpp);
+ }
+ REG_WRITE(MIPI_DSI_FUNC_PRG_REG(pipe), val);
+
+ REG_WRITE(MIPI_HS_TX_TIMEOUT_REG(pipe),
+ (mode->vtotal * mode->htotal * dsi_config->bpp /
+ (8 * lane_count)) & DSI_HS_TX_TIMEOUT_MASK);
+ REG_WRITE(MIPI_LP_RX_TIMEOUT_REG(pipe),
+ 0xffff & DSI_LP_RX_TIMEOUT_MASK);
+
+ /*max value: 20 clock cycles of txclkesc*/
+ REG_WRITE(MIPI_TURN_AROUND_TIMEOUT_REG(pipe),
+ 0x14 & DSI_TURN_AROUND_TIMEOUT_MASK);
+
+ /*min 21 txclkesc, max: ffffh*/
+ REG_WRITE(MIPI_DEVICE_RESET_TIMER_REG(pipe),
+ 0xffff & DSI_RESET_TIMER_MASK);
+
+ REG_WRITE(MIPI_DPI_RESOLUTION_REG(pipe),
+ mode->vdisplay << 16 | mode->hdisplay);
+
+ /*set DPI timing registers*/
+ mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing,
+ dsi_config->lane_count, dsi_config->bpp);
+
+ REG_WRITE(MIPI_HSYNC_COUNT_REG(pipe),
+ dpi_timing.hsync_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_HBP_COUNT_REG(pipe),
+ dpi_timing.hbp_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_HFP_COUNT_REG(pipe),
+ dpi_timing.hfp_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_HACTIVE_COUNT_REG(pipe),
+ dpi_timing.hactive_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_VSYNC_COUNT_REG(pipe),
+ dpi_timing.vsync_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_VBP_COUNT_REG(pipe),
+ dpi_timing.vbp_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_VFP_COUNT_REG(pipe),
+ dpi_timing.vfp_count & DSI_DPI_TIMING_MASK);
+
+ REG_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe), 0x46);
+
+ /*min: 7d0 max: 4e20*/
+ REG_WRITE(MIPI_INIT_COUNT_REG(pipe), 0x000007d0);
+
+ /*set up video mode*/
+ val = dsi_config->video_mode | DSI_DPI_COMPLETE_LAST_LINE;
+ REG_WRITE(MIPI_VIDEO_MODE_FORMAT_REG(pipe), val);
+
+ REG_WRITE(MIPI_EOT_DISABLE_REG(pipe), 0x00000000);
+
+ REG_WRITE(MIPI_LP_BYTECLK_REG(pipe), 0x00000004);
+
+ /*TODO: figure out how to setup these registers*/
+ if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x2A0c6008);
+ else
+ REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x150c3408);
+
+ REG_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe), (0xa << 16) | 0x14);
+
+ if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ tc35876x_set_bridge_reset_state(dev, 0); /*Pull High Reset */
+
+ /*set device ready*/
+ REG_FLD_MOD(MIPI_DEVICE_READY_REG(pipe), 1, 0, 0);
+}
+
+void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output, int pipe)
+{
+ struct drm_device *dev = output->dev;
+
+ /* clear special packet sent bit */
+ if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT)
+ REG_WRITE(MIPI_INTR_STAT_REG(pipe),
+ DSI_INTR_STATE_SPL_PKG_SENT);
+
+ /*send turn on package*/
+ REG_WRITE(MIPI_DPI_CONTROL_REG(pipe), DSI_DPI_CTRL_HS_TURN_ON);
+
+ /*wait for SPL_PKG_SENT interrupt*/
+ mdfld_wait_for_SPL_PKG_SENT(dev, pipe);
+
+ if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT)
+ REG_WRITE(MIPI_INTR_STAT_REG(pipe),
+ DSI_INTR_STATE_SPL_PKG_SENT);
+
+ output->panel_on = 1;
+
+ /* FIXME the following is disabled to WA the X slow start issue
+ for TMD panel
+ if (pipe == 2)
+ dev_priv->dpi_panel_on2 = true;
+ else if (pipe == 0)
+ dev_priv->dpi_panel_on = true; */
+}
+
+static void mdfld_dsi_dpi_shut_down(struct mdfld_dsi_dpi_output *output,
+ int pipe)
+{
+ struct drm_device *dev = output->dev;
+
+ /*if output is on, or mode setting didn't happen, ignore this*/
+ if ((!output->panel_on) || output->first_boot) {
+ output->first_boot = 0;
+ return;
+ }
+
+ /* Wait for dpi fifo to empty */
+ mdfld_wait_for_DPI_CTRL_FIFO(dev, pipe);
+
+ /* Clear the special packet interrupt bit if set */
+ if (REG_READ(MIPI_INTR_STAT_REG(pipe)) & DSI_INTR_STATE_SPL_PKG_SENT)
+ REG_WRITE(MIPI_INTR_STAT_REG(pipe),
+ DSI_INTR_STATE_SPL_PKG_SENT);
+
+ if (REG_READ(MIPI_DPI_CONTROL_REG(pipe)) == DSI_DPI_CTRL_HS_SHUTDOWN)
+ goto shutdown_out;
+
+ REG_WRITE(MIPI_DPI_CONTROL_REG(pipe), DSI_DPI_CTRL_HS_SHUTDOWN);
+
+shutdown_out:
+ output->panel_on = 0;
+ output->first_boot = 0;
+
+ /* FIXME the following is disabled to WA the X slow start issue
+ for TMD panel
+ if (pipe == 2)
+ dev_priv->dpi_panel_on2 = false;
+ else if (pipe == 0)
+ dev_priv->dpi_panel_on = false; */
+}
+
+static void mdfld_dsi_dpi_set_power(struct drm_encoder *encoder, bool on)
+{
+ struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder);
+ struct mdfld_dsi_dpi_output *dpi_output =
+ MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_encoder_get_config(dsi_encoder);
+ int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder);
+ struct drm_device *dev = dsi_config->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ /*start up display island if it was shutdown*/
+ if (!gma_power_begin(dev, true))
+ return;
+
+ if (on) {
+ if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe);
+ else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ mdfld_dsi_configure_up(dsi_encoder, pipe);
+ else {
+ /*enable mipi port*/
+ REG_WRITE(MIPI_PORT_CONTROL(pipe),
+ REG_READ(MIPI_PORT_CONTROL(pipe)) | BIT(31));
+ REG_READ(MIPI_PORT_CONTROL(pipe));
+
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe);
+ mdfld_dsi_tpo_ic_init(dsi_config, pipe);
+ }
+ dev_priv->dpi_panel_on[pipe] = true;
+ } else {
+ if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
+ mdfld_dsi_dpi_shut_down(dpi_output, pipe);
+ else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ mdfld_dsi_configure_down(dsi_encoder, pipe);
+ else {
+ mdfld_dsi_dpi_shut_down(dpi_output, pipe);
+
+ /*disable mipi port*/
+ REG_WRITE(MIPI_PORT_CONTROL(pipe),
+ REG_READ(MIPI_PORT_CONTROL(pipe)) & ~BIT(31));
+ REG_READ(MIPI_PORT_CONTROL(pipe));
+ }
+ dev_priv->dpi_panel_on[pipe] = false;
+ }
+ gma_power_end(dev);
+}
+
+void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode)
+{
+ mdfld_dsi_dpi_set_power(encoder, mode == DRM_MODE_DPMS_ON);
+}
+
+bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_encoder_get_config(dsi_encoder);
+ struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
+
+ if (fixed_mode) {
+ adjusted_mode->hdisplay = fixed_mode->hdisplay;
+ adjusted_mode->hsync_start = fixed_mode->hsync_start;
+ adjusted_mode->hsync_end = fixed_mode->hsync_end;
+ adjusted_mode->htotal = fixed_mode->htotal;
+ adjusted_mode->vdisplay = fixed_mode->vdisplay;
+ adjusted_mode->vsync_start = fixed_mode->vsync_start;
+ adjusted_mode->vsync_end = fixed_mode->vsync_end;
+ adjusted_mode->vtotal = fixed_mode->vtotal;
+ adjusted_mode->clock = fixed_mode->clock;
+ drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+ }
+ return true;
+}
+
+void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder)
+{
+ mdfld_dsi_dpi_set_power(encoder, false);
+}
+
+void mdfld_dsi_dpi_commit(struct drm_encoder *encoder)
+{
+ mdfld_dsi_dpi_set_power(encoder, true);
+}
+
+/* For TC35876X */
+/* This functionality was implemented in FW in iCDK */
+/* But removed in DV0 and later. So need to add here. */
+static void mipi_set_properties(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+
+ REG_WRITE(MIPI_CTRL_REG(pipe), 0x00000018);
+ REG_WRITE(MIPI_INTR_EN_REG(pipe), 0xffffffff);
+ REG_WRITE(MIPI_HS_TX_TIMEOUT_REG(pipe), 0xffffff);
+ REG_WRITE(MIPI_LP_RX_TIMEOUT_REG(pipe), 0xffffff);
+ REG_WRITE(MIPI_TURN_AROUND_TIMEOUT_REG(pipe), 0x14);
+ REG_WRITE(MIPI_DEVICE_RESET_TIMER_REG(pipe), 0xff);
+ REG_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe), 0x25);
+ REG_WRITE(MIPI_INIT_COUNT_REG(pipe), 0xf0);
+ REG_WRITE(MIPI_EOT_DISABLE_REG(pipe), 0x00000000);
+ REG_WRITE(MIPI_LP_BYTECLK_REG(pipe), 0x00000004);
+ REG_WRITE(MIPI_DBI_BW_CTRL_REG(pipe), 0x00000820);
+ REG_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe), (0xa << 16) | 0x14);
+}
+
+static void mdfld_mipi_set_video_timing(struct mdfld_dsi_config *dsi_config,
+ int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ struct mdfld_dsi_dpi_timing dpi_timing;
+ struct drm_display_mode *mode = dsi_config->mode;
+
+ mdfld_dsi_dpi_timing_calculation(mode, &dpi_timing,
+ dsi_config->lane_count,
+ dsi_config->bpp);
+
+ REG_WRITE(MIPI_DPI_RESOLUTION_REG(pipe),
+ mode->vdisplay << 16 | mode->hdisplay);
+ REG_WRITE(MIPI_HSYNC_COUNT_REG(pipe),
+ dpi_timing.hsync_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_HBP_COUNT_REG(pipe),
+ dpi_timing.hbp_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_HFP_COUNT_REG(pipe),
+ dpi_timing.hfp_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_HACTIVE_COUNT_REG(pipe),
+ dpi_timing.hactive_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_VSYNC_COUNT_REG(pipe),
+ dpi_timing.vsync_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_VBP_COUNT_REG(pipe),
+ dpi_timing.vbp_count & DSI_DPI_TIMING_MASK);
+ REG_WRITE(MIPI_VFP_COUNT_REG(pipe),
+ dpi_timing.vfp_count & DSI_DPI_TIMING_MASK);
+}
+
+static void mdfld_mipi_config(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ int lane_count = dsi_config->lane_count;
+
+ if (pipe) {
+ REG_WRITE(MIPI_PORT_CONTROL(0), 0x00000002);
+ REG_WRITE(MIPI_PORT_CONTROL(2), 0x80000000);
+ } else {
+ REG_WRITE(MIPI_PORT_CONTROL(0), 0x80010000);
+ REG_WRITE(MIPI_PORT_CONTROL(2), 0x00);
+ }
+
+ REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x150A600F);
+ REG_WRITE(MIPI_VIDEO_MODE_FORMAT_REG(pipe), 0x0000000F);
+
+ /* lane_count = 3 */
+ REG_WRITE(MIPI_DSI_FUNC_PRG_REG(pipe), 0x00000200 | lane_count);
+
+ mdfld_mipi_set_video_timing(dsi_config, pipe);
+}
+
+static void mdfld_set_pipe_timing(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ struct drm_device *dev = dsi_config->dev;
+ struct drm_display_mode *mode = dsi_config->mode;
+
+ REG_WRITE(HTOTAL_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1));
+ REG_WRITE(HBLANK_A, ((mode->htotal - 1) << 16) | (mode->hdisplay - 1));
+ REG_WRITE(HSYNC_A,
+ ((mode->hsync_end - 1) << 16) | (mode->hsync_start - 1));
+
+ REG_WRITE(VTOTAL_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1));
+ REG_WRITE(VBLANK_A, ((mode->vtotal - 1) << 16) | (mode->vdisplay - 1));
+ REG_WRITE(VSYNC_A,
+ ((mode->vsync_end - 1) << 16) | (mode->vsync_start - 1));
+
+ REG_WRITE(PIPEASRC,
+ ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+}
+/* End for TC35876X */
+
+void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct mdfld_dsi_encoder *dsi_encoder = mdfld_dsi_encoder(encoder);
+ struct mdfld_dsi_dpi_output *dpi_output =
+ MDFLD_DSI_DPI_OUTPUT(dsi_encoder);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_encoder_get_config(dsi_encoder);
+ struct drm_device *dev = dsi_config->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ int pipe = mdfld_dsi_encoder_get_pipe(dsi_encoder);
+
+ u32 pipeconf_reg = PIPEACONF;
+ u32 dspcntr_reg = DSPACNTR;
+
+ u32 pipeconf = dev_priv->pipeconf[pipe];
+ u32 dspcntr = dev_priv->dspcntr[pipe];
+ u32 mipi = MIPI_PORT_EN | PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
+
+ if (pipe) {
+ pipeconf_reg = PIPECCONF;
+ dspcntr_reg = DSPCCNTR;
+ } else {
+ if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ mipi &= (~0x03); /* Use all four lanes */
+ else
+ mipi |= 2;
+ }
+
+ /*start up display island if it was shutdown*/
+ if (!gma_power_begin(dev, true))
+ return;
+
+ if (mdfld_get_panel_type(dev, pipe) == TC35876X) {
+ /*
+ * The following logic is required to reset the bridge and
+ * configure. This also starts the DSI clock at 200MHz.
+ */
+ tc35876x_set_bridge_reset_state(dev, 0); /*Pull High Reset */
+ tc35876x_toshiba_bridge_panel_on(dev);
+ udelay(100);
+ /* Now start the DSI clock */
+ REG_WRITE(MRST_DPLL_A, 0x00);
+ REG_WRITE(MRST_FPA0, 0xC1);
+ REG_WRITE(MRST_DPLL_A, 0x00800000);
+ udelay(500);
+ REG_WRITE(MRST_DPLL_A, 0x80800000);
+
+ if (REG_BIT_WAIT(pipeconf_reg, 1, 29))
+ dev_err(&dev->pdev->dev, "%s: DSI PLL lock timeout\n",
+ __func__);
+
+ REG_WRITE(MIPI_DPHY_PARAM_REG(pipe), 0x2A0c6008);
+
+ mipi_set_properties(dsi_config, pipe);
+ mdfld_mipi_config(dsi_config, pipe);
+ mdfld_set_pipe_timing(dsi_config, pipe);
+
+ REG_WRITE(DSPABASE, 0x00);
+ REG_WRITE(DSPASTRIDE, (mode->hdisplay * 4));
+ REG_WRITE(DSPASIZE,
+ ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
+
+ REG_WRITE(DSPACNTR, 0x98000000);
+ REG_WRITE(DSPASURF, 0x00);
+
+ REG_WRITE(VGACNTRL, 0x80000000);
+ REG_WRITE(DEVICE_READY_REG, 0x00000001);
+
+ REG_WRITE(MIPI_PORT_CONTROL(pipe), 0x80810000);
+ } else {
+ /*set up mipi port FIXME: do at init time */
+ REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi);
+ }
+ REG_READ(MIPI_PORT_CONTROL(pipe));
+
+ if (mdfld_get_panel_type(dev, pipe) == TMD_VID) {
+ /* NOP */
+ } else if (mdfld_get_panel_type(dev, pipe) == TC35876X) {
+ /* set up DSI controller DPI interface */
+ mdfld_dsi_dpi_controller_init(dsi_config, pipe);
+
+ /* Configure MIPI Bridge and Panel */
+ tc35876x_configure_lvds_bridge(dev);
+ dev_priv->dpi_panel_on[pipe] = true;
+ } else {
+ /*turn on DPI interface*/
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe);
+ }
+
+ /*set up pipe*/
+ REG_WRITE(pipeconf_reg, pipeconf);
+ REG_READ(pipeconf_reg);
+
+ /*set up display plane*/
+ REG_WRITE(dspcntr_reg, dspcntr);
+ REG_READ(dspcntr_reg);
+
+ msleep(20); /* FIXME: this should wait for vblank */
+
+ if (mdfld_get_panel_type(dev, pipe) == TMD_VID) {
+ /* NOP */
+ } else if (mdfld_get_panel_type(dev, pipe) == TC35876X) {
+ mdfld_dsi_dpi_turn_on(dpi_output, pipe);
+ } else {
+ /* init driver ic */
+ mdfld_dsi_tpo_ic_init(dsi_config, pipe);
+ /*init backlight*/
+ mdfld_dsi_brightness_init(dsi_config, pipe);
+ }
+
+ gma_power_end(dev);
+}
+
+/*
+ * Init DSI DPI encoder.
+ * Allocate an mdfld_dsi_encoder and attach it to given @dsi_connector
+ * return pointer of newly allocated DPI encoder, NULL on error
+ */
+struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
+ struct mdfld_dsi_connector *dsi_connector,
+ const struct panel_funcs *p_funcs)
+{
+ struct mdfld_dsi_dpi_output *dpi_output = NULL;
+ struct mdfld_dsi_config *dsi_config;
+ struct drm_connector *connector = NULL;
+ struct drm_encoder *encoder = NULL;
+ int pipe;
+ u32 data;
+ int ret;
+
+ pipe = dsi_connector->pipe;
+
+ if (mdfld_get_panel_type(dev, pipe) != TC35876X) {
+ dsi_config = mdfld_dsi_get_config(dsi_connector);
+
+ /* panel hard-reset */
+ if (p_funcs->reset) {
+ ret = p_funcs->reset(pipe);
+ if (ret) {
+ DRM_ERROR("Panel %d hard-reset failed\n", pipe);
+ return NULL;
+ }
+ }
+
+ /* panel drvIC init */
+ if (p_funcs->drv_ic_init)
+ p_funcs->drv_ic_init(dsi_config, pipe);
+
+ /* panel power mode detect */
+ ret = mdfld_dsi_get_power_mode(dsi_config, &data, false);
+ if (ret) {
+ DRM_ERROR("Panel %d get power mode failed\n", pipe);
+ dsi_connector->status = connector_status_disconnected;
+ } else {
+ DRM_INFO("pipe %d power mode 0x%x\n", pipe, data);
+ dsi_connector->status = connector_status_connected;
+ }
+ }
+
+ dpi_output = kzalloc(sizeof(struct mdfld_dsi_dpi_output), GFP_KERNEL);
+ if (!dpi_output) {
+ DRM_ERROR("No memory\n");
+ return NULL;
+ }
+
+ if (dsi_connector->pipe)
+ dpi_output->panel_on = 0;
+ else
+ dpi_output->panel_on = 0;
+
+ dpi_output->dev = dev;
+ if (mdfld_get_panel_type(dev, pipe) != TC35876X)
+ dpi_output->p_funcs = p_funcs;
+ dpi_output->first_boot = 1;
+
+ /*get fixed mode*/
+ dsi_config = mdfld_dsi_get_config(dsi_connector);
+
+ /*create drm encoder object*/
+ connector = &dsi_connector->base.base;
+ encoder = &dpi_output->base.base.base;
+ drm_encoder_init(dev,
+ encoder,
+ p_funcs->encoder_funcs,
+ DRM_MODE_ENCODER_LVDS);
+ drm_encoder_helper_add(encoder,
+ p_funcs->encoder_helper_funcs);
+
+ /*attach to given connector*/
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ /*set possible crtcs and clones*/
+ if (dsi_connector->pipe) {
+ encoder->possible_crtcs = (1 << 2);
+ encoder->possible_clones = (1 << 1);
+ } else {
+ encoder->possible_crtcs = (1 << 0);
+ encoder->possible_clones = (1 << 0);
+ }
+
+ dsi_connector->base.encoder = &dpi_output->base.base;
+
+ return &dpi_output->base;
+}
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_dpi.h b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.h
new file mode 100644
index 000000000000..6f762478b959
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_dpi.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * jim liu <jim.liu@intel.com>
+ * Jackie Li<yaodong.li@intel.com>
+ */
+
+#ifndef __MDFLD_DSI_DPI_H__
+#define __MDFLD_DSI_DPI_H__
+
+#include "mdfld_dsi_output.h"
+#include "mdfld_output.h"
+
+struct mdfld_dsi_dpi_timing {
+ u16 hsync_count;
+ u16 hbp_count;
+ u16 hfp_count;
+ u16 hactive_count;
+ u16 vsync_count;
+ u16 vbp_count;
+ u16 vfp_count;
+};
+
+struct mdfld_dsi_dpi_output {
+ struct mdfld_dsi_encoder base;
+ struct drm_device *dev;
+
+ int panel_on;
+ int first_boot;
+
+ const struct panel_funcs *p_funcs;
+};
+
+#define MDFLD_DSI_DPI_OUTPUT(dsi_encoder)\
+ container_of(dsi_encoder, struct mdfld_dsi_dpi_output, base)
+
+/* Export functions */
+extern int mdfld_dsi_dpi_timing_calculation(struct drm_display_mode *mode,
+ struct mdfld_dsi_dpi_timing *dpi_timing,
+ int num_lane, int bpp);
+extern struct mdfld_dsi_encoder *mdfld_dsi_dpi_init(struct drm_device *dev,
+ struct mdfld_dsi_connector *dsi_connector,
+ const struct panel_funcs *p_funcs);
+
+/* MDFLD DPI helper functions */
+extern void mdfld_dsi_dpi_dpms(struct drm_encoder *encoder, int mode);
+extern bool mdfld_dsi_dpi_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+extern void mdfld_dsi_dpi_prepare(struct drm_encoder *encoder);
+extern void mdfld_dsi_dpi_commit(struct drm_encoder *encoder);
+extern void mdfld_dsi_dpi_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
+extern void mdfld_dsi_dpi_turn_on(struct mdfld_dsi_dpi_output *output,
+ int pipe);
+extern void mdfld_dsi_dpi_controller_init(struct mdfld_dsi_config *dsi_config,
+ int pipe);
+#endif /*__MDFLD_DSI_DPI_H__*/
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.c b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
new file mode 100644
index 000000000000..5675d93b4205
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * jim liu <jim.liu@intel.com>
+ * Jackie Li<yaodong.li@intel.com>
+ */
+
+#include <linux/module.h>
+
+#include "mdfld_dsi_output.h"
+#include "mdfld_dsi_dpi.h"
+#include "mdfld_output.h"
+#include "mdfld_dsi_pkg_sender.h"
+#include "tc35876x-dsi-lvds.h"
+#include <linux/pm_runtime.h>
+#include <asm/intel_scu_ipc.h>
+
+/* get the LABC from command line. */
+static int LABC_control = 1;
+
+#ifdef MODULE
+module_param(LABC_control, int, 0644);
+#else
+
+static int __init parse_LABC_control(char *arg)
+{
+ /* LABC control can be passed in as a cmdline parameter */
+ /* to enable this feature add LABC=1 to cmdline */
+ /* to disable this feature add LABC=0 to cmdline */
+ if (!arg)
+ return -EINVAL;
+
+ if (!strcasecmp(arg, "0"))
+ LABC_control = 0;
+ else if (!strcasecmp(arg, "1"))
+ LABC_control = 1;
+
+ return 0;
+}
+early_param("LABC", parse_LABC_control);
+#endif
+
+/**
+ * Check and see if the generic control or data buffer is empty and ready.
+ */
+void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg,
+ u32 fifo_stat)
+{
+ u32 GEN_BF_time_out_count;
+
+ /* Check MIPI Adatper command registers */
+ for (GEN_BF_time_out_count = 0;
+ GEN_BF_time_out_count < GEN_FB_TIME_OUT;
+ GEN_BF_time_out_count++) {
+ if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat)
+ break;
+ udelay(100);
+ }
+
+ if (GEN_BF_time_out_count == GEN_FB_TIME_OUT)
+ DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n",
+ gen_fifo_stat_reg);
+}
+
+/**
+ * Manage the DSI MIPI keyboard and display brightness.
+ * FIXME: this is exported to OSPM code. should work out an specific
+ * display interface to OSPM.
+ */
+
+void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ struct mdfld_dsi_pkg_sender *sender =
+ mdfld_dsi_get_pkg_sender(dsi_config);
+ struct drm_device *dev = sender->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 gen_ctrl_val;
+
+ if (!sender) {
+ DRM_ERROR("No sender found\n");
+ return;
+ }
+
+ /* Set default display backlight value to 85% (0xd8)*/
+ mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1,
+ true);
+
+ /* Set minimum brightness setting of CABC function to 20% (0x33)*/
+ mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true);
+
+ /* Enable backlight or/and LABC */
+ gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
+ BACKLIGHT_ON;
+ if (LABC_control == 1)
+ gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
+ | GAMMA_AUTO;
+
+ if (LABC_control == 1)
+ gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON;
+
+ dev_priv->mipi_ctrl_display = gen_ctrl_val;
+
+ mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val,
+ 1, true);
+
+ mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true);
+}
+
+void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level)
+{
+ struct mdfld_dsi_pkg_sender *sender;
+ struct drm_psb_private *dev_priv;
+ struct mdfld_dsi_config *dsi_config;
+ u32 gen_ctrl_val = 0;
+ int p_type = TMD_VID;
+
+ if (!dev || (pipe != 0 && pipe != 2)) {
+ DRM_ERROR("Invalid parameter\n");
+ return;
+ }
+
+ p_type = mdfld_get_panel_type(dev, 0);
+
+ dev_priv = dev->dev_private;
+
+ if (pipe)
+ dsi_config = dev_priv->dsi_configs[1];
+ else
+ dsi_config = dev_priv->dsi_configs[0];
+
+ sender = mdfld_dsi_get_pkg_sender(dsi_config);
+
+ if (!sender) {
+ DRM_ERROR("No sender found\n");
+ return;
+ }
+
+ gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
+
+ dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n",
+ pipe, gen_ctrl_val);
+
+ if (p_type == TMD_VID) {
+ /* Set display backlight value */
+ mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness,
+ (u8)gen_ctrl_val, 1, true);
+ } else {
+ /* Set display backlight value */
+ mdfld_dsi_send_mcs_short(sender, write_display_brightness,
+ (u8)gen_ctrl_val, 1, true);
+
+ /* Enable backlight control */
+ if (level == 0)
+ gen_ctrl_val = 0;
+ else
+ gen_ctrl_val = dev_priv->mipi_ctrl_display;
+
+ mdfld_dsi_send_mcs_short(sender, write_ctrl_display,
+ (u8)gen_ctrl_val, 1, true);
+ }
+}
+
+static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
+ u8 dcs, u32 *data, bool hs)
+{
+ struct mdfld_dsi_pkg_sender *sender
+ = mdfld_dsi_get_pkg_sender(dsi_config);
+
+ if (!sender || !data) {
+ DRM_ERROR("Invalid parameter\n");
+ return -EINVAL;
+ }
+
+ return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs);
+}
+
+int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode,
+ bool hs)
+{
+ if (!dsi_config || !mode) {
+ DRM_ERROR("Invalid parameter\n");
+ return -EINVAL;
+ }
+
+ return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs);
+}
+
+/*
+ * NOTE: this function was used by OSPM.
+ * TODO: will be removed later, should work out display interfaces for OSPM
+ */
+void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe)
+{
+ if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
+ DRM_ERROR("Invalid parameters\n");
+ return;
+ }
+
+ mdfld_dsi_dpi_controller_init(dsi_config, pipe);
+}
+
+static void mdfld_dsi_connector_save(struct drm_connector *connector)
+{
+}
+
+static void mdfld_dsi_connector_restore(struct drm_connector *connector)
+{
+}
+
+/* FIXME: start using the force parameter */
+static enum drm_connector_status
+mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
+{
+ struct mdfld_dsi_connector *dsi_connector
+ = mdfld_dsi_connector(connector);
+
+ dsi_connector->status = connector_status_connected;
+
+ return dsi_connector->status;
+}
+
+static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
+ struct drm_property *property,
+ uint64_t value)
+{
+ struct drm_encoder *encoder = connector->encoder;
+
+ if (!strcmp(property->name, "scaling mode") && encoder) {
+ struct psb_intel_crtc *psb_crtc =
+ to_psb_intel_crtc(encoder->crtc);
+ bool centerechange;
+ uint64_t val;
+
+ if (!psb_crtc)
+ goto set_prop_error;
+
+ switch (value) {
+ case DRM_MODE_SCALE_FULLSCREEN:
+ break;
+ case DRM_MODE_SCALE_NO_SCALE:
+ break;
+ case DRM_MODE_SCALE_ASPECT:
+ break;
+ default:
+ goto set_prop_error;
+ }
+
+ if (drm_connector_property_get_value(connector, property, &val))
+ goto set_prop_error;
+
+ if (val == value)
+ goto set_prop_done;
+
+ if (drm_connector_property_set_value(connector,
+ property, value))
+ goto set_prop_error;
+
+ centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
+ (value == DRM_MODE_SCALE_NO_SCALE);
+
+ if (psb_crtc->saved_mode.hdisplay != 0 &&
+ psb_crtc->saved_mode.vdisplay != 0) {
+ if (centerechange) {
+ if (!drm_crtc_helper_set_mode(encoder->crtc,
+ &psb_crtc->saved_mode,
+ encoder->crtc->x,
+ encoder->crtc->y,
+ encoder->crtc->fb))
+ goto set_prop_error;
+ } else {
+ struct drm_encoder_helper_funcs *funcs =
+ encoder->helper_private;
+ funcs->mode_set(encoder,
+ &psb_crtc->saved_mode,
+ &psb_crtc->saved_adjusted_mode);
+ }
+ }
+ } else if (!strcmp(property->name, "backlight") && encoder) {
+ if (drm_connector_property_set_value(connector, property,
+ 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:
+ return 0;
+set_prop_error:
+ return -1;
+}
+
+static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
+{
+ struct mdfld_dsi_connector *dsi_connector =
+ mdfld_dsi_connector(connector);
+ struct mdfld_dsi_pkg_sender *sender;
+
+ if (!dsi_connector)
+ return;
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ sender = dsi_connector->pkg_sender;
+ mdfld_dsi_pkg_sender_destroy(sender);
+ kfree(dsi_connector);
+}
+
+static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
+{
+ struct mdfld_dsi_connector *dsi_connector =
+ mdfld_dsi_connector(connector);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_get_config(dsi_connector);
+ struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
+ struct drm_display_mode *dup_mode = NULL;
+ struct drm_device *dev = connector->dev;
+
+ connector->display_info.min_vfreq = 0;
+ connector->display_info.max_vfreq = 200;
+ connector->display_info.min_hfreq = 0;
+ connector->display_info.max_hfreq = 200;
+
+ if (fixed_mode) {
+ dev_dbg(dev->dev, "fixed_mode %dx%d\n",
+ fixed_mode->hdisplay, fixed_mode->vdisplay);
+ dup_mode = drm_mode_duplicate(dev, fixed_mode);
+ drm_mode_probed_add(connector, dup_mode);
+ return 1;
+ }
+ DRM_ERROR("Didn't get any modes!\n");
+ return 0;
+}
+
+static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct mdfld_dsi_connector *dsi_connector =
+ mdfld_dsi_connector(connector);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_get_config(dsi_connector);
+ struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ return MODE_NO_INTERLACE;
+
+ /**
+ * FIXME: current DC has no fitting unit, reject any mode setting
+ * request
+ * Will figure out a way to do up-scaling(pannel fitting) later.
+ **/
+ if (fixed_mode) {
+ if (mode->hdisplay != fixed_mode->hdisplay)
+ return MODE_PANEL;
+
+ if (mode->vdisplay != fixed_mode->vdisplay)
+ return MODE_PANEL;
+ }
+
+ return MODE_OK;
+}
+
+static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
+{
+ if (mode == connector->dpms)
+ return;
+
+ /*first, execute dpms*/
+
+ drm_helper_connector_dpms(connector, mode);
+}
+
+static struct drm_encoder *mdfld_dsi_connector_best_encoder(
+ struct drm_connector *connector)
+{
+ struct mdfld_dsi_connector *dsi_connector =
+ mdfld_dsi_connector(connector);
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_get_config(dsi_connector);
+ return &dsi_config->encoder->base.base;
+}
+
+/*DSI connector funcs*/
+static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
+ .dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
+ .save = mdfld_dsi_connector_save,
+ .restore = mdfld_dsi_connector_restore,
+ .detect = mdfld_dsi_connector_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .set_property = mdfld_dsi_connector_set_property,
+ .destroy = mdfld_dsi_connector_destroy,
+};
+
+/*DSI connector helper funcs*/
+static const struct drm_connector_helper_funcs
+ mdfld_dsi_connector_helper_funcs = {
+ .get_modes = mdfld_dsi_connector_get_modes,
+ .mode_valid = mdfld_dsi_connector_mode_valid,
+ .best_encoder = mdfld_dsi_connector_best_encoder,
+};
+
+static int mdfld_dsi_get_default_config(struct drm_device *dev,
+ struct mdfld_dsi_config *config, int pipe)
+{
+ if (!dev || !config) {
+ DRM_ERROR("Invalid parameters");
+ return -EINVAL;
+ }
+
+ config->bpp = 24;
+ if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ config->lane_count = 4;
+ else
+ config->lane_count = 2;
+ config->channel_num = 0;
+
+ if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
+ config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
+ else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
+ config->video_mode =
+ MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
+ else
+ config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
+
+ return 0;
+}
+
+int mdfld_dsi_panel_reset(int pipe)
+{
+ unsigned gpio;
+ int ret = 0;
+
+ switch (pipe) {
+ case 0:
+ gpio = 128;
+ break;
+ case 2:
+ gpio = 34;
+ break;
+ default:
+ DRM_ERROR("Invalid output\n");
+ return -EINVAL;
+ }
+
+ ret = gpio_request(gpio, "gfx");
+ if (ret) {
+ DRM_ERROR("gpio_rqueset failed\n");
+ return ret;
+ }
+
+ ret = gpio_direction_output(gpio, 1);
+ if (ret) {
+ DRM_ERROR("gpio_direction_output failed\n");
+ goto gpio_error;
+ }
+
+ gpio_get_value(128);
+
+gpio_error:
+ if (gpio_is_valid(gpio))
+ gpio_free(gpio);
+
+ return ret;
+}
+
+/*
+ * MIPI output init
+ * @dev drm device
+ * @pipe pipe number. 0 or 2
+ * @config
+ *
+ * Do the initialization of a MIPI output, including create DRM mode objects
+ * initialization of DSI output on @pipe
+ */
+void mdfld_dsi_output_init(struct drm_device *dev,
+ int pipe,
+ const struct panel_funcs *p_vid_funcs)
+{
+ struct mdfld_dsi_config *dsi_config;
+ struct mdfld_dsi_connector *dsi_connector;
+ struct drm_connector *connector;
+ struct mdfld_dsi_encoder *encoder;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct panel_info dsi_panel_info;
+ u32 width_mm, height_mm;
+
+ dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
+
+ if (!dev || ((pipe != 0) && (pipe != 2))) {
+ DRM_ERROR("Invalid parameter\n");
+ return;
+ }
+
+ /*create a new connetor*/
+ dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
+ if (!dsi_connector) {
+ DRM_ERROR("No memory");
+ return;
+ }
+
+ dsi_connector->pipe = pipe;
+
+ dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
+ GFP_KERNEL);
+ if (!dsi_config) {
+ DRM_ERROR("cannot allocate memory for DSI config\n");
+ goto dsi_init_err0;
+ }
+ mdfld_dsi_get_default_config(dev, dsi_config, pipe);
+
+ dsi_connector->private = dsi_config;
+
+ dsi_config->changed = 1;
+ dsi_config->dev = dev;
+
+ dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
+ if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
+ goto dsi_init_err0;
+
+ width_mm = dsi_panel_info.width_mm;
+ height_mm = dsi_panel_info.height_mm;
+
+ dsi_config->mode = dsi_config->fixed_mode;
+ dsi_config->connector = dsi_connector;
+
+ if (!dsi_config->fixed_mode) {
+ DRM_ERROR("No pannel fixed mode was found\n");
+ goto dsi_init_err0;
+ }
+
+ if (pipe && dev_priv->dsi_configs[0]) {
+ dsi_config->dvr_ic_inited = 0;
+ dev_priv->dsi_configs[1] = dsi_config;
+ } else if (pipe == 0) {
+ dsi_config->dvr_ic_inited = 1;
+ dev_priv->dsi_configs[0] = dsi_config;
+ } else {
+ DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
+ goto dsi_init_err0;
+ }
+
+
+ connector = &dsi_connector->base.base;
+ drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
+ DRM_MODE_CONNECTOR_LVDS);
+ drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
+
+ connector->display_info.subpixel_order = SubPixelHorizontalRGB;
+ connector->display_info.width_mm = width_mm;
+ connector->display_info.height_mm = height_mm;
+ connector->interlace_allowed = false;
+ connector->doublescan_allowed = false;
+
+ /*attach properties*/
+ drm_connector_attach_property(connector,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_FULLSCREEN);
+ drm_connector_attach_property(connector,
+ dev_priv->backlight_property,
+ MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
+
+ /*init DSI package sender on this output*/
+ if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
+ DRM_ERROR("Package Sender initialization failed on pipe %d\n",
+ pipe);
+ goto dsi_init_err0;
+ }
+
+ encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
+ if (!encoder) {
+ DRM_ERROR("Create DPI encoder failed\n");
+ goto dsi_init_err1;
+ }
+ encoder->private = dsi_config;
+ dsi_config->encoder = encoder;
+ encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
+ INTEL_OUTPUT_MIPI2;
+ drm_sysfs_connector_add(connector);
+ return;
+
+ /*TODO: add code to destroy outputs on error*/
+dsi_init_err1:
+ /*destroy sender*/
+ mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
+
+ drm_connector_cleanup(connector);
+
+ kfree(dsi_config->fixed_mode);
+ kfree(dsi_config);
+dsi_init_err0:
+ kfree(dsi_connector);
+}
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_output.h b/drivers/gpu/drm/gma500/mdfld_dsi_output.h
new file mode 100644
index 000000000000..21071cef92a4
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_output.h
@@ -0,0 +1,378 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * jim liu <jim.liu@intel.com>
+ * Jackie Li<yaodong.li@intel.com>
+ */
+
+#ifndef __MDFLD_DSI_OUTPUT_H__
+#define __MDFLD_DSI_OUTPUT_H__
+
+#include <linux/backlight.h>
+#include <linux/version.h>
+#include <drm/drmP.h>
+#include <drm/drm.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+
+#include "psb_drv.h"
+#include "psb_intel_drv.h"
+#include "psb_intel_reg.h"
+#include "mdfld_output.h"
+
+#include <asm/mrst.h>
+
+#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end))
+#define FLD_MOD(orig, val, start, end) \
+ (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end))
+
+#define REG_FLD_MOD(reg, val, start, end) \
+ REG_WRITE(reg, FLD_MOD(REG_READ(reg), val, start, end))
+
+static inline int REGISTER_FLD_WAIT(struct drm_device *dev, u32 reg,
+ u32 val, int start, int end)
+{
+ int t = 100000;
+
+ while (FLD_GET(REG_READ(reg), start, end) != val) {
+ if (--t == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+#define REG_FLD_WAIT(reg, val, start, end) \
+ REGISTER_FLD_WAIT(dev, reg, val, start, end)
+
+#define REG_BIT_WAIT(reg, val, bitnum) \
+ REGISTER_FLD_WAIT(dev, reg, val, bitnum, bitnum)
+
+#define MDFLD_DSI_BRIGHTNESS_MAX_LEVEL 100
+
+#ifdef DEBUG
+#define CHECK_PIPE(pipe) ({ \
+ const typeof(pipe) __pipe = (pipe); \
+ BUG_ON(__pipe != 0 && __pipe != 2); \
+ __pipe; })
+#else
+#define CHECK_PIPE(pipe) (pipe)
+#endif
+
+/*
+ * Actual MIPIA->MIPIC reg offset is 0x800, value 0x400 is valid for 0 and 2
+ */
+#define REG_OFFSET(pipe) (CHECK_PIPE(pipe) * 0x400)
+
+/* mdfld DSI controller registers */
+#define MIPI_DEVICE_READY_REG(pipe) (0xb000 + REG_OFFSET(pipe))
+#define MIPI_INTR_STAT_REG(pipe) (0xb004 + REG_OFFSET(pipe))
+#define MIPI_INTR_EN_REG(pipe) (0xb008 + REG_OFFSET(pipe))
+#define MIPI_DSI_FUNC_PRG_REG(pipe) (0xb00c + REG_OFFSET(pipe))
+#define MIPI_HS_TX_TIMEOUT_REG(pipe) (0xb010 + REG_OFFSET(pipe))
+#define MIPI_LP_RX_TIMEOUT_REG(pipe) (0xb014 + REG_OFFSET(pipe))
+#define MIPI_TURN_AROUND_TIMEOUT_REG(pipe) (0xb018 + REG_OFFSET(pipe))
+#define MIPI_DEVICE_RESET_TIMER_REG(pipe) (0xb01c + REG_OFFSET(pipe))
+#define MIPI_DPI_RESOLUTION_REG(pipe) (0xb020 + REG_OFFSET(pipe))
+#define MIPI_DBI_FIFO_THROTTLE_REG(pipe) (0xb024 + REG_OFFSET(pipe))
+#define MIPI_HSYNC_COUNT_REG(pipe) (0xb028 + REG_OFFSET(pipe))
+#define MIPI_HBP_COUNT_REG(pipe) (0xb02c + REG_OFFSET(pipe))
+#define MIPI_HFP_COUNT_REG(pipe) (0xb030 + REG_OFFSET(pipe))
+#define MIPI_HACTIVE_COUNT_REG(pipe) (0xb034 + REG_OFFSET(pipe))
+#define MIPI_VSYNC_COUNT_REG(pipe) (0xb038 + REG_OFFSET(pipe))
+#define MIPI_VBP_COUNT_REG(pipe) (0xb03c + REG_OFFSET(pipe))
+#define MIPI_VFP_COUNT_REG(pipe) (0xb040 + REG_OFFSET(pipe))
+#define MIPI_HIGH_LOW_SWITCH_COUNT_REG(pipe) (0xb044 + REG_OFFSET(pipe))
+#define MIPI_DPI_CONTROL_REG(pipe) (0xb048 + REG_OFFSET(pipe))
+#define MIPI_DPI_DATA_REG(pipe) (0xb04c + REG_OFFSET(pipe))
+#define MIPI_INIT_COUNT_REG(pipe) (0xb050 + REG_OFFSET(pipe))
+#define MIPI_MAX_RETURN_PACK_SIZE_REG(pipe) (0xb054 + REG_OFFSET(pipe))
+#define MIPI_VIDEO_MODE_FORMAT_REG(pipe) (0xb058 + REG_OFFSET(pipe))
+#define MIPI_EOT_DISABLE_REG(pipe) (0xb05c + REG_OFFSET(pipe))
+#define MIPI_LP_BYTECLK_REG(pipe) (0xb060 + REG_OFFSET(pipe))
+#define MIPI_LP_GEN_DATA_REG(pipe) (0xb064 + REG_OFFSET(pipe))
+#define MIPI_HS_GEN_DATA_REG(pipe) (0xb068 + REG_OFFSET(pipe))
+#define MIPI_LP_GEN_CTRL_REG(pipe) (0xb06c + REG_OFFSET(pipe))
+#define MIPI_HS_GEN_CTRL_REG(pipe) (0xb070 + REG_OFFSET(pipe))
+#define MIPI_GEN_FIFO_STAT_REG(pipe) (0xb074 + REG_OFFSET(pipe))
+#define MIPI_HS_LS_DBI_ENABLE_REG(pipe) (0xb078 + REG_OFFSET(pipe))
+#define MIPI_DPHY_PARAM_REG(pipe) (0xb080 + REG_OFFSET(pipe))
+#define MIPI_DBI_BW_CTRL_REG(pipe) (0xb084 + REG_OFFSET(pipe))
+#define MIPI_CLK_LANE_SWITCH_TIME_CNT_REG(pipe) (0xb088 + REG_OFFSET(pipe))
+
+#define MIPI_CTRL_REG(pipe) (0xb104 + REG_OFFSET(pipe))
+#define MIPI_DATA_ADD_REG(pipe) (0xb108 + REG_OFFSET(pipe))
+#define MIPI_DATA_LEN_REG(pipe) (0xb10c + REG_OFFSET(pipe))
+#define MIPI_CMD_ADD_REG(pipe) (0xb110 + REG_OFFSET(pipe))
+#define MIPI_CMD_LEN_REG(pipe) (0xb114 + REG_OFFSET(pipe))
+
+/* non-uniform reg offset */
+#define MIPI_PORT_CONTROL(pipe) (CHECK_PIPE(pipe) ? MIPI_C : MIPI)
+
+#define DSI_DEVICE_READY (0x1)
+#define DSI_POWER_STATE_ULPS_ENTER (0x2 << 1)
+#define DSI_POWER_STATE_ULPS_EXIT (0x1 << 1)
+#define DSI_POWER_STATE_ULPS_OFFSET (0x1)
+
+
+#define DSI_ONE_DATA_LANE (0x1)
+#define DSI_TWO_DATA_LANE (0x2)
+#define DSI_THREE_DATA_LANE (0X3)
+#define DSI_FOUR_DATA_LANE (0x4)
+#define DSI_DPI_VIRT_CHANNEL_OFFSET (0x3)
+#define DSI_DBI_VIRT_CHANNEL_OFFSET (0x5)
+#define DSI_DPI_COLOR_FORMAT_RGB565 (0x01 << 7)
+#define DSI_DPI_COLOR_FORMAT_RGB666 (0x02 << 7)
+#define DSI_DPI_COLOR_FORMAT_RGB666_UNPACK (0x03 << 7)
+#define DSI_DPI_COLOR_FORMAT_RGB888 (0x04 << 7)
+#define DSI_DBI_COLOR_FORMAT_OPTION2 (0x05 << 13)
+
+#define DSI_INTR_STATE_RXSOTERROR BIT(0)
+
+#define DSI_INTR_STATE_SPL_PKG_SENT BIT(30)
+#define DSI_INTR_STATE_TE BIT(31)
+
+#define DSI_HS_TX_TIMEOUT_MASK (0xffffff)
+
+#define DSI_LP_RX_TIMEOUT_MASK (0xffffff)
+
+#define DSI_TURN_AROUND_TIMEOUT_MASK (0x3f)
+
+#define DSI_RESET_TIMER_MASK (0xffff)
+
+#define DSI_DBI_FIFO_WM_HALF (0x0)
+#define DSI_DBI_FIFO_WM_QUARTER (0x1)
+#define DSI_DBI_FIFO_WM_LOW (0x2)
+
+#define DSI_DPI_TIMING_MASK (0xffff)
+
+#define DSI_INIT_TIMER_MASK (0xffff)
+
+#define DSI_DBI_RETURN_PACK_SIZE_MASK (0x3ff)
+
+#define DSI_LP_BYTECLK_MASK (0x0ffff)
+
+#define DSI_HS_CTRL_GEN_SHORT_W0 (0x03)
+#define DSI_HS_CTRL_GEN_SHORT_W1 (0x13)
+#define DSI_HS_CTRL_GEN_SHORT_W2 (0x23)
+#define DSI_HS_CTRL_GEN_R0 (0x04)
+#define DSI_HS_CTRL_GEN_R1 (0x14)
+#define DSI_HS_CTRL_GEN_R2 (0x24)
+#define DSI_HS_CTRL_GEN_LONG_W (0x29)
+#define DSI_HS_CTRL_MCS_SHORT_W0 (0x05)
+#define DSI_HS_CTRL_MCS_SHORT_W1 (0x15)
+#define DSI_HS_CTRL_MCS_R0 (0x06)
+#define DSI_HS_CTRL_MCS_LONG_W (0x39)
+#define DSI_HS_CTRL_VC_OFFSET (0x06)
+#define DSI_HS_CTRL_WC_OFFSET (0x08)
+
+#define DSI_FIFO_GEN_HS_DATA_FULL BIT(0)
+#define DSI_FIFO_GEN_HS_DATA_HALF_EMPTY BIT(1)
+#define DSI_FIFO_GEN_HS_DATA_EMPTY BIT(2)
+#define DSI_FIFO_GEN_LP_DATA_FULL BIT(8)
+#define DSI_FIFO_GEN_LP_DATA_HALF_EMPTY BIT(9)
+#define DSI_FIFO_GEN_LP_DATA_EMPTY BIT(10)
+#define DSI_FIFO_GEN_HS_CTRL_FULL BIT(16)
+#define DSI_FIFO_GEN_HS_CTRL_HALF_EMPTY BIT(17)
+#define DSI_FIFO_GEN_HS_CTRL_EMPTY BIT(18)
+#define DSI_FIFO_GEN_LP_CTRL_FULL BIT(24)
+#define DSI_FIFO_GEN_LP_CTRL_HALF_EMPTY BIT(25)
+#define DSI_FIFO_GEN_LP_CTRL_EMPTY BIT(26)
+#define DSI_FIFO_DBI_EMPTY BIT(27)
+#define DSI_FIFO_DPI_EMPTY BIT(28)
+
+#define DSI_DBI_HS_LP_SWITCH_MASK (0x1)
+
+#define DSI_HS_LP_SWITCH_COUNTER_OFFSET (0x0)
+#define DSI_LP_HS_SWITCH_COUNTER_OFFSET (0x16)
+
+#define DSI_DPI_CTRL_HS_SHUTDOWN (0x00000001)
+#define DSI_DPI_CTRL_HS_TURN_ON (0x00000002)
+
+/*dsi power modes*/
+#define DSI_POWER_MODE_DISPLAY_ON BIT(2)
+#define DSI_POWER_MODE_NORMAL_ON BIT(3)
+#define DSI_POWER_MODE_SLEEP_OUT BIT(4)
+#define DSI_POWER_MODE_PARTIAL_ON BIT(5)
+#define DSI_POWER_MODE_IDLE_ON BIT(6)
+
+enum {
+ MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE = 1,
+ MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS = 2,
+ MDFLD_DSI_VIDEO_BURST_MODE = 3,
+};
+
+#define DSI_DPI_COMPLETE_LAST_LINE BIT(2)
+#define DSI_DPI_DISABLE_BTA BIT(3)
+
+struct mdfld_dsi_connector {
+ struct psb_intel_connector base;
+
+ int pipe;
+ void *private;
+ void *pkg_sender;
+
+ /* Connection status */
+ enum drm_connector_status status;
+};
+
+struct mdfld_dsi_encoder {
+ struct psb_intel_encoder base;
+ void *private;
+};
+
+/*
+ * DSI config, consists of one DSI connector, two DSI encoders.
+ * DRM will pick up on DSI encoder basing on differents configs.
+ */
+struct mdfld_dsi_config {
+ struct drm_device *dev;
+ struct drm_display_mode *fixed_mode;
+ struct drm_display_mode *mode;
+
+ struct mdfld_dsi_connector *connector;
+ struct mdfld_dsi_encoder *encoder;
+
+ int changed;
+
+ int bpp;
+ int lane_count;
+ /*Virtual channel number for this encoder*/
+ int channel_num;
+ /*video mode configure*/
+ int video_mode;
+
+ int dvr_ic_inited;
+};
+
+static inline struct mdfld_dsi_connector *mdfld_dsi_connector(
+ struct drm_connector *connector)
+{
+ struct psb_intel_connector *psb_connector;
+
+ psb_connector = to_psb_intel_connector(connector);
+
+ return container_of(psb_connector, struct mdfld_dsi_connector, base);
+}
+
+static inline struct mdfld_dsi_encoder *mdfld_dsi_encoder(
+ struct drm_encoder *encoder)
+{
+ struct psb_intel_encoder *psb_encoder;
+
+ psb_encoder = to_psb_intel_encoder(encoder);
+
+ return container_of(psb_encoder, struct mdfld_dsi_encoder, base);
+}
+
+static inline struct mdfld_dsi_config *
+ mdfld_dsi_get_config(struct mdfld_dsi_connector *connector)
+{
+ if (!connector)
+ return NULL;
+ return (struct mdfld_dsi_config *)connector->private;
+}
+
+static inline void *mdfld_dsi_get_pkg_sender(struct mdfld_dsi_config *config)
+{
+ struct mdfld_dsi_connector *dsi_connector;
+
+ if (!config)
+ return NULL;
+
+ dsi_connector = config->connector;
+
+ if (!dsi_connector)
+ return NULL;
+
+ return dsi_connector->pkg_sender;
+}
+
+static inline struct mdfld_dsi_config *
+ mdfld_dsi_encoder_get_config(struct mdfld_dsi_encoder *encoder)
+{
+ if (!encoder)
+ return NULL;
+ return (struct mdfld_dsi_config *)encoder->private;
+}
+
+static inline struct mdfld_dsi_connector *
+ mdfld_dsi_encoder_get_connector(struct mdfld_dsi_encoder *encoder)
+{
+ struct mdfld_dsi_config *config;
+
+ if (!encoder)
+ return NULL;
+
+ config = mdfld_dsi_encoder_get_config(encoder);
+ if (!config)
+ return NULL;
+
+ return config->connector;
+}
+
+static inline void *mdfld_dsi_encoder_get_pkg_sender(
+ struct mdfld_dsi_encoder *encoder)
+{
+ struct mdfld_dsi_config *dsi_config;
+
+ dsi_config = mdfld_dsi_encoder_get_config(encoder);
+ if (!dsi_config)
+ return NULL;
+
+ return mdfld_dsi_get_pkg_sender(dsi_config);
+}
+
+static inline int mdfld_dsi_encoder_get_pipe(struct mdfld_dsi_encoder *encoder)
+{
+ struct mdfld_dsi_connector *connector;
+
+ if (!encoder)
+ return -1;
+
+ connector = mdfld_dsi_encoder_get_connector(encoder);
+ if (!connector)
+ return -1;
+ return connector->pipe;
+}
+
+/* Export functions */
+extern void mdfld_dsi_gen_fifo_ready(struct drm_device *dev,
+ u32 gen_fifo_stat_reg, u32 fifo_stat);
+extern void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config,
+ int pipe);
+extern void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe,
+ int level);
+extern void mdfld_dsi_output_init(struct drm_device *dev,
+ int pipe,
+ const struct panel_funcs *p_vid_funcs);
+extern void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config,
+ int pipe);
+
+extern int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config,
+ u32 *mode, bool hs);
+extern int mdfld_dsi_panel_reset(int pipe);
+
+#endif /*__MDFLD_DSI_OUTPUT_H__*/
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
new file mode 100644
index 000000000000..baa0e14165e0
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.c
@@ -0,0 +1,694 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Jackie Li<yaodong.li@intel.com>
+ */
+
+#include <linux/freezer.h>
+
+#include "mdfld_dsi_output.h"
+#include "mdfld_dsi_pkg_sender.h"
+#include "mdfld_dsi_dpi.h"
+
+#define MDFLD_DSI_READ_MAX_COUNT 5000
+
+enum data_type {
+ DSI_DT_GENERIC_SHORT_WRITE_0 = 0x03,
+ DSI_DT_GENERIC_SHORT_WRITE_1 = 0x13,
+ DSI_DT_GENERIC_SHORT_WRITE_2 = 0x23,
+ DSI_DT_GENERIC_READ_0 = 0x04,
+ DSI_DT_GENERIC_READ_1 = 0x14,
+ DSI_DT_GENERIC_READ_2 = 0x24,
+ DSI_DT_GENERIC_LONG_WRITE = 0x29,
+ DSI_DT_DCS_SHORT_WRITE_0 = 0x05,
+ DSI_DT_DCS_SHORT_WRITE_1 = 0x15,
+ DSI_DT_DCS_READ = 0x06,
+ DSI_DT_DCS_LONG_WRITE = 0x39,
+};
+
+enum {
+ MDFLD_DSI_PANEL_MODE_SLEEP = 0x1,
+};
+
+enum {
+ MDFLD_DSI_PKG_SENDER_FREE = 0x0,
+ MDFLD_DSI_PKG_SENDER_BUSY = 0x1,
+};
+
+static const char *const dsi_errors[] = {
+ "RX SOT Error",
+ "RX SOT Sync Error",
+ "RX EOT Sync Error",
+ "RX Escape Mode Entry Error",
+ "RX LP TX Sync Error",
+ "RX HS Receive Timeout Error",
+ "RX False Control Error",
+ "RX ECC Single Bit Error",
+ "RX ECC Multibit Error",
+ "RX Checksum Error",
+ "RX DSI Data Type Not Recognised",
+ "RX DSI VC ID Invalid",
+ "TX False Control Error",
+ "TX ECC Single Bit Error",
+ "TX ECC Multibit Error",
+ "TX Checksum Error",
+ "TX DSI Data Type Not Recognised",
+ "TX DSI VC ID invalid",
+ "High Contention",
+ "Low contention",
+ "DPI FIFO Under run",
+ "HS TX Timeout",
+ "LP RX Timeout",
+ "Turn Around ACK Timeout",
+ "ACK With No Error",
+ "RX Invalid TX Length",
+ "RX Prot Violation",
+ "HS Generic Write FIFO Full",
+ "LP Generic Write FIFO Full",
+ "Generic Read Data Avail"
+ "Special Packet Sent",
+ "Tearing Effect",
+};
+
+static inline int wait_for_gen_fifo_empty(struct mdfld_dsi_pkg_sender *sender,
+ u32 mask)
+{
+ struct drm_device *dev = sender->dev;
+ u32 gen_fifo_stat_reg = sender->mipi_gen_fifo_stat_reg;
+ int retry = 0xffff;
+
+ while (retry--) {
+ if ((mask & REG_READ(gen_fifo_stat_reg)) == mask)
+ return 0;
+ udelay(100);
+ }
+ DRM_ERROR("fifo is NOT empty 0x%08x\n", REG_READ(gen_fifo_stat_reg));
+ return -EIO;
+}
+
+static int wait_for_all_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
+{
+ return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(10) | BIT(18) |
+ BIT(26) | BIT(27) | BIT(28)));
+}
+
+static int wait_for_lp_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
+{
+ return wait_for_gen_fifo_empty(sender, (BIT(10) | BIT(26)));
+}
+
+static int wait_for_hs_fifos_empty(struct mdfld_dsi_pkg_sender *sender)
+{
+ return wait_for_gen_fifo_empty(sender, (BIT(2) | BIT(18)));
+}
+
+static int handle_dsi_error(struct mdfld_dsi_pkg_sender *sender, u32 mask)
+{
+ u32 intr_stat_reg = sender->mipi_intr_stat_reg;
+ struct drm_device *dev = sender->dev;
+
+ dev_dbg(sender->dev->dev, "Handling error 0x%08x\n", mask);
+
+ switch (mask) {
+ case BIT(0):
+ case BIT(1):
+ case BIT(2):
+ case BIT(3):
+ case BIT(4):
+ case BIT(5):
+ case BIT(6):
+ case BIT(7):
+ case BIT(8):
+ case BIT(9):
+ case BIT(10):
+ case BIT(11):
+ case BIT(12):
+ case BIT(13):
+ dev_dbg(sender->dev->dev, "No Action required\n");
+ break;
+ case BIT(14):
+ /*wait for all fifo empty*/
+ /*wait_for_all_fifos_empty(sender)*/;
+ break;
+ case BIT(15):
+ dev_dbg(sender->dev->dev, "No Action required\n");
+ break;
+ case BIT(16):
+ break;
+ case BIT(17):
+ break;
+ case BIT(18):
+ case BIT(19):
+ dev_dbg(sender->dev->dev, "High/Low contention detected\n");
+ /*wait for contention recovery time*/
+ /*mdelay(10);*/
+ /*wait for all fifo empty*/
+ if (0)
+ wait_for_all_fifos_empty(sender);
+ break;
+ case BIT(20):
+ dev_dbg(sender->dev->dev, "No Action required\n");
+ break;
+ case BIT(21):
+ /*wait for all fifo empty*/
+ /*wait_for_all_fifos_empty(sender);*/
+ break;
+ case BIT(22):
+ break;
+ case BIT(23):
+ case BIT(24):
+ case BIT(25):
+ case BIT(26):
+ case BIT(27):
+ dev_dbg(sender->dev->dev, "HS Gen fifo full\n");
+ REG_WRITE(intr_stat_reg, mask);
+ wait_for_hs_fifos_empty(sender);
+ break;
+ case BIT(28):
+ dev_dbg(sender->dev->dev, "LP Gen fifo full\n");
+ REG_WRITE(intr_stat_reg, mask);
+ wait_for_lp_fifos_empty(sender);
+ break;
+ case BIT(29):
+ case BIT(30):
+ case BIT(31):
+ dev_dbg(sender->dev->dev, "No Action required\n");
+ break;
+ }
+
+ if (mask & REG_READ(intr_stat_reg))
+ dev_dbg(sender->dev->dev,
+ "Cannot clean interrupt 0x%08x\n", mask);
+ return 0;
+}
+
+static int dsi_error_handler(struct mdfld_dsi_pkg_sender *sender)
+{
+ struct drm_device *dev = sender->dev;
+ u32 intr_stat_reg = sender->mipi_intr_stat_reg;
+ u32 mask;
+ u32 intr_stat;
+ int i;
+ int err = 0;
+
+ intr_stat = REG_READ(intr_stat_reg);
+
+ for (i = 0; i < 32; i++) {
+ mask = (0x00000001UL) << i;
+ if (intr_stat & mask) {
+ dev_dbg(sender->dev->dev, "[DSI]: %s\n", dsi_errors[i]);
+ err = handle_dsi_error(sender, mask);
+ if (err)
+ DRM_ERROR("Cannot handle error\n");
+ }
+ }
+ return err;
+}
+
+static int send_short_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
+ u8 cmd, u8 param, bool hs)
+{
+ struct drm_device *dev = sender->dev;
+ u32 ctrl_reg;
+ u32 val;
+ u8 virtual_channel = 0;
+
+ if (hs) {
+ ctrl_reg = sender->mipi_hs_gen_ctrl_reg;
+
+ /* FIXME: wait_for_hs_fifos_empty(sender); */
+ } else {
+ ctrl_reg = sender->mipi_lp_gen_ctrl_reg;
+
+ /* FIXME: wait_for_lp_fifos_empty(sender); */
+ }
+
+ val = FLD_VAL(param, 23, 16) | FLD_VAL(cmd, 15, 8) |
+ FLD_VAL(virtual_channel, 7, 6) | FLD_VAL(data_type, 5, 0);
+
+ REG_WRITE(ctrl_reg, val);
+
+ return 0;
+}
+
+static int send_long_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
+ u8 *data, int len, bool hs)
+{
+ struct drm_device *dev = sender->dev;
+ u32 ctrl_reg;
+ u32 data_reg;
+ u32 val;
+ u8 *p;
+ u8 b1, b2, b3, b4;
+ u8 virtual_channel = 0;
+ int i;
+
+ if (hs) {
+ ctrl_reg = sender->mipi_hs_gen_ctrl_reg;
+ data_reg = sender->mipi_hs_gen_data_reg;
+
+ /* FIXME: wait_for_hs_fifos_empty(sender); */
+ } else {
+ ctrl_reg = sender->mipi_lp_gen_ctrl_reg;
+ data_reg = sender->mipi_lp_gen_data_reg;
+
+ /* FIXME: wait_for_lp_fifos_empty(sender); */
+ }
+
+ p = data;
+ for (i = 0; i < len / 4; i++) {
+ b1 = *p++;
+ b2 = *p++;
+ b3 = *p++;
+ b4 = *p++;
+
+ REG_WRITE(data_reg, b4 << 24 | b3 << 16 | b2 << 8 | b1);
+ }
+
+ i = len % 4;
+ if (i) {
+ b1 = 0; b2 = 0; b3 = 0;
+
+ switch (i) {
+ case 3:
+ b1 = *p++;
+ b2 = *p++;
+ b3 = *p++;
+ break;
+ case 2:
+ b1 = *p++;
+ b2 = *p++;
+ break;
+ case 1:
+ b1 = *p++;
+ break;
+ }
+
+ REG_WRITE(data_reg, b3 << 16 | b2 << 8 | b1);
+ }
+
+ val = FLD_VAL(len, 23, 8) | FLD_VAL(virtual_channel, 7, 6) |
+ FLD_VAL(data_type, 5, 0);
+
+ REG_WRITE(ctrl_reg, val);
+
+ return 0;
+}
+
+static int send_pkg_prepare(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
+ u8 *data, u16 len)
+{
+ u8 cmd;
+
+ switch (data_type) {
+ case DSI_DT_DCS_SHORT_WRITE_0:
+ case DSI_DT_DCS_SHORT_WRITE_1:
+ case DSI_DT_DCS_LONG_WRITE:
+ cmd = *data;
+ break;
+ default:
+ return 0;
+ }
+
+ /*this prevents other package sending while doing msleep*/
+ sender->status = MDFLD_DSI_PKG_SENDER_BUSY;
+
+ /*wait for 120 milliseconds in case exit_sleep_mode just be sent*/
+ if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) {
+ /*TODO: replace it with msleep later*/
+ mdelay(120);
+ }
+
+ if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) {
+ /*TODO: replace it with msleep later*/
+ mdelay(120);
+ }
+ return 0;
+}
+
+static int send_pkg_done(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
+ u8 *data, u16 len)
+{
+ u8 cmd;
+
+ switch (data_type) {
+ case DSI_DT_DCS_SHORT_WRITE_0:
+ case DSI_DT_DCS_SHORT_WRITE_1:
+ case DSI_DT_DCS_LONG_WRITE:
+ cmd = *data;
+ break;
+ default:
+ return 0;
+ }
+
+ /*update panel status*/
+ if (unlikely(cmd == DCS_ENTER_SLEEP_MODE)) {
+ sender->panel_mode |= MDFLD_DSI_PANEL_MODE_SLEEP;
+ /*TODO: replace it with msleep later*/
+ mdelay(120);
+ } else if (unlikely(cmd == DCS_EXIT_SLEEP_MODE)) {
+ sender->panel_mode &= ~MDFLD_DSI_PANEL_MODE_SLEEP;
+ /*TODO: replace it with msleep later*/
+ mdelay(120);
+ } else if (unlikely(cmd == DCS_SOFT_RESET)) {
+ /*TODO: replace it with msleep later*/
+ mdelay(5);
+ }
+
+ sender->status = MDFLD_DSI_PKG_SENDER_FREE;
+
+ return 0;
+}
+
+static int send_pkg(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
+ u8 *data, u16 len, bool hs)
+{
+ int ret;
+
+ /*handle DSI error*/
+ ret = dsi_error_handler(sender);
+ if (ret) {
+ DRM_ERROR("Error handling failed\n");
+ return -EAGAIN;
+ }
+
+ /* send pkg */
+ if (sender->status == MDFLD_DSI_PKG_SENDER_BUSY) {
+ DRM_ERROR("sender is busy\n");
+ return -EAGAIN;
+ }
+
+ ret = send_pkg_prepare(sender, data_type, data, len);
+ if (ret) {
+ DRM_ERROR("send_pkg_prepare error\n");
+ return ret;
+ }
+
+ switch (data_type) {
+ case DSI_DT_GENERIC_SHORT_WRITE_0:
+ case DSI_DT_GENERIC_SHORT_WRITE_1:
+ case DSI_DT_GENERIC_SHORT_WRITE_2:
+ case DSI_DT_GENERIC_READ_0:
+ case DSI_DT_GENERIC_READ_1:
+ case DSI_DT_GENERIC_READ_2:
+ case DSI_DT_DCS_SHORT_WRITE_0:
+ case DSI_DT_DCS_SHORT_WRITE_1:
+ case DSI_DT_DCS_READ:
+ ret = send_short_pkg(sender, data_type, data[0], data[1], hs);
+ break;
+ case DSI_DT_GENERIC_LONG_WRITE:
+ case DSI_DT_DCS_LONG_WRITE:
+ ret = send_long_pkg(sender, data_type, data, len, hs);
+ break;
+ }
+
+ send_pkg_done(sender, data_type, data, len);
+
+ /*FIXME: should I query complete and fifo empty here?*/
+
+ return ret;
+}
+
+int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
+ u32 len, bool hs)
+{
+ unsigned long flags;
+
+ if (!sender || !data || !len) {
+ DRM_ERROR("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&sender->lock, flags);
+ send_pkg(sender, DSI_DT_DCS_LONG_WRITE, data, len, hs);
+ spin_unlock_irqrestore(&sender->lock, flags);
+
+ return 0;
+}
+
+int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
+ u8 param, u8 param_num, bool hs)
+{
+ u8 data[2];
+ unsigned long flags;
+ u8 data_type;
+
+ if (!sender) {
+ DRM_ERROR("Invalid parameter\n");
+ return -EINVAL;
+ }
+
+ data[0] = cmd;
+
+ if (param_num) {
+ data_type = DSI_DT_DCS_SHORT_WRITE_1;
+ data[1] = param;
+ } else {
+ data_type = DSI_DT_DCS_SHORT_WRITE_0;
+ data[1] = 0;
+ }
+
+ spin_lock_irqsave(&sender->lock, flags);
+ send_pkg(sender, data_type, data, sizeof(data), hs);
+ spin_unlock_irqrestore(&sender->lock, flags);
+
+ return 0;
+}
+
+int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0,
+ u8 param1, u8 param_num, bool hs)
+{
+ u8 data[2];
+ unsigned long flags;
+ u8 data_type;
+
+ if (!sender || param_num > 2) {
+ DRM_ERROR("Invalid parameter\n");
+ return -EINVAL;
+ }
+
+ switch (param_num) {
+ case 0:
+ data_type = DSI_DT_GENERIC_SHORT_WRITE_0;
+ data[0] = 0;
+ data[1] = 0;
+ break;
+ case 1:
+ data_type = DSI_DT_GENERIC_SHORT_WRITE_1;
+ data[0] = param0;
+ data[1] = 0;
+ break;
+ case 2:
+ data_type = DSI_DT_GENERIC_SHORT_WRITE_2;
+ data[0] = param0;
+ data[1] = param1;
+ break;
+ }
+
+ spin_lock_irqsave(&sender->lock, flags);
+ send_pkg(sender, data_type, data, sizeof(data), hs);
+ spin_unlock_irqrestore(&sender->lock, flags);
+
+ return 0;
+}
+
+int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
+ u32 len, bool hs)
+{
+ unsigned long flags;
+
+ if (!sender || !data || !len) {
+ DRM_ERROR("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&sender->lock, flags);
+ send_pkg(sender, DSI_DT_GENERIC_LONG_WRITE, data, len, hs);
+ spin_unlock_irqrestore(&sender->lock, flags);
+
+ return 0;
+}
+
+static int __read_panel_data(struct mdfld_dsi_pkg_sender *sender, u8 data_type,
+ u8 *data, u16 len, u32 *data_out, u16 len_out, bool hs)
+{
+ unsigned long flags;
+ struct drm_device *dev = sender->dev;
+ int i;
+ u32 gen_data_reg;
+ int retry = MDFLD_DSI_READ_MAX_COUNT;
+
+ if (!sender || !data_out || !len_out) {
+ DRM_ERROR("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ /**
+ * do reading.
+ * 0) send out generic read request
+ * 1) polling read data avail interrupt
+ * 2) read data
+ */
+ spin_lock_irqsave(&sender->lock, flags);
+
+ REG_WRITE(sender->mipi_intr_stat_reg, BIT(29));
+
+ if ((REG_READ(sender->mipi_intr_stat_reg) & BIT(29)))
+ DRM_ERROR("Can NOT clean read data valid interrupt\n");
+
+ /*send out read request*/
+ send_pkg(sender, data_type, data, len, hs);
+
+ /*polling read data avail interrupt*/
+ while (retry && !(REG_READ(sender->mipi_intr_stat_reg) & BIT(29))) {
+ udelay(100);
+ retry--;
+ }
+
+ if (!retry) {
+ spin_unlock_irqrestore(&sender->lock, flags);
+ return -ETIMEDOUT;
+ }
+
+ REG_WRITE(sender->mipi_intr_stat_reg, BIT(29));
+
+ /*read data*/
+ if (hs)
+ gen_data_reg = sender->mipi_hs_gen_data_reg;
+ else
+ gen_data_reg = sender->mipi_lp_gen_data_reg;
+
+ for (i = 0; i < len_out; i++)
+ *(data_out + i) = REG_READ(gen_data_reg);
+
+ spin_unlock_irqrestore(&sender->lock, flags);
+
+ return 0;
+}
+
+int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
+ u32 *data, u16 len, bool hs)
+{
+ if (!sender || !data || !len) {
+ DRM_ERROR("Invalid parameters\n");
+ return -EINVAL;
+ }
+
+ return __read_panel_data(sender, DSI_DT_DCS_READ, &cmd, 1,
+ data, len, hs);
+}
+
+int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector,
+ int pipe)
+{
+ struct mdfld_dsi_pkg_sender *pkg_sender;
+ struct mdfld_dsi_config *dsi_config =
+ mdfld_dsi_get_config(dsi_connector);
+ struct drm_device *dev = dsi_config->dev;
+ u32 mipi_val = 0;
+
+ if (!dsi_connector) {
+ DRM_ERROR("Invalid parameter\n");
+ return -EINVAL;
+ }
+
+ pkg_sender = dsi_connector->pkg_sender;
+
+ if (!pkg_sender || IS_ERR(pkg_sender)) {
+ pkg_sender = kzalloc(sizeof(struct mdfld_dsi_pkg_sender),
+ GFP_KERNEL);
+ if (!pkg_sender) {
+ DRM_ERROR("Create DSI pkg sender failed\n");
+ return -ENOMEM;
+ }
+ dsi_connector->pkg_sender = (void *)pkg_sender;
+ }
+
+ pkg_sender->dev = dev;
+ pkg_sender->dsi_connector = dsi_connector;
+ pkg_sender->pipe = pipe;
+ pkg_sender->pkg_num = 0;
+ pkg_sender->panel_mode = 0;
+ pkg_sender->status = MDFLD_DSI_PKG_SENDER_FREE;
+
+ /*init regs*/
+ if (pipe == 0) {
+ pkg_sender->dpll_reg = MRST_DPLL_A;
+ pkg_sender->dspcntr_reg = DSPACNTR;
+ pkg_sender->pipeconf_reg = PIPEACONF;
+ pkg_sender->dsplinoff_reg = DSPALINOFF;
+ pkg_sender->dspsurf_reg = DSPASURF;
+ pkg_sender->pipestat_reg = PIPEASTAT;
+ } else if (pipe == 2) {
+ pkg_sender->dpll_reg = MRST_DPLL_A;
+ pkg_sender->dspcntr_reg = DSPCCNTR;
+ pkg_sender->pipeconf_reg = PIPECCONF;
+ pkg_sender->dsplinoff_reg = DSPCLINOFF;
+ pkg_sender->dspsurf_reg = DSPCSURF;
+ pkg_sender->pipestat_reg = PIPECSTAT;
+ }
+
+ pkg_sender->mipi_intr_stat_reg = MIPI_INTR_STAT_REG(pipe);
+ pkg_sender->mipi_lp_gen_data_reg = MIPI_LP_GEN_DATA_REG(pipe);
+ pkg_sender->mipi_hs_gen_data_reg = MIPI_HS_GEN_DATA_REG(pipe);
+ pkg_sender->mipi_lp_gen_ctrl_reg = MIPI_LP_GEN_CTRL_REG(pipe);
+ pkg_sender->mipi_hs_gen_ctrl_reg = MIPI_HS_GEN_CTRL_REG(pipe);
+ pkg_sender->mipi_gen_fifo_stat_reg = MIPI_GEN_FIFO_STAT_REG(pipe);
+ pkg_sender->mipi_data_addr_reg = MIPI_DATA_ADD_REG(pipe);
+ pkg_sender->mipi_data_len_reg = MIPI_DATA_LEN_REG(pipe);
+ pkg_sender->mipi_cmd_addr_reg = MIPI_CMD_ADD_REG(pipe);
+ pkg_sender->mipi_cmd_len_reg = MIPI_CMD_LEN_REG(pipe);
+
+ /*init lock*/
+ spin_lock_init(&pkg_sender->lock);
+
+ if (mdfld_get_panel_type(dev, pipe) != TC35876X) {
+ /**
+ * For video mode, don't enable DPI timing output here,
+ * will init the DPI timing output during mode setting.
+ */
+ mipi_val = PASS_FROM_SPHY_TO_AFE | SEL_FLOPPED_HSTX;
+
+ if (pipe == 0)
+ mipi_val |= 0x2;
+
+ REG_WRITE(MIPI_PORT_CONTROL(pipe), mipi_val);
+ REG_READ(MIPI_PORT_CONTROL(pipe));
+
+ /* do dsi controller init */
+ mdfld_dsi_controller_init(dsi_config, pipe);
+ }
+
+ return 0;
+}
+
+void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender)
+{
+ if (!sender || IS_ERR(sender))
+ return;
+
+ /*free*/
+ kfree(sender);
+}
+
+
diff --git a/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h
new file mode 100644
index 000000000000..459cd7ea8b81
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_dsi_pkg_sender.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Jackie Li<yaodong.li@intel.com>
+ */
+#ifndef __MDFLD_DSI_PKG_SENDER_H__
+#define __MDFLD_DSI_PKG_SENDER_H__
+
+#include <linux/kthread.h>
+
+#define MDFLD_MAX_DCS_PARAM 8
+
+struct mdfld_dsi_pkg_sender {
+ struct drm_device *dev;
+ struct mdfld_dsi_connector *dsi_connector;
+ u32 status;
+ u32 panel_mode;
+
+ int pipe;
+
+ spinlock_t lock;
+
+ u32 pkg_num;
+
+ /* Registers */
+ u32 dpll_reg;
+ u32 dspcntr_reg;
+ u32 pipeconf_reg;
+ u32 pipestat_reg;
+ u32 dsplinoff_reg;
+ u32 dspsurf_reg;
+
+ u32 mipi_intr_stat_reg;
+ u32 mipi_lp_gen_data_reg;
+ u32 mipi_hs_gen_data_reg;
+ u32 mipi_lp_gen_ctrl_reg;
+ u32 mipi_hs_gen_ctrl_reg;
+ u32 mipi_gen_fifo_stat_reg;
+ u32 mipi_data_addr_reg;
+ u32 mipi_data_len_reg;
+ u32 mipi_cmd_addr_reg;
+ u32 mipi_cmd_len_reg;
+};
+
+/* DCS definitions */
+#define DCS_SOFT_RESET 0x01
+#define DCS_ENTER_SLEEP_MODE 0x10
+#define DCS_EXIT_SLEEP_MODE 0x11
+#define DCS_SET_DISPLAY_OFF 0x28
+#define DCS_SET_DISPLAY_ON 0x29
+#define DCS_SET_COLUMN_ADDRESS 0x2a
+#define DCS_SET_PAGE_ADDRESS 0x2b
+#define DCS_WRITE_MEM_START 0x2c
+#define DCS_SET_TEAR_OFF 0x34
+#define DCS_SET_TEAR_ON 0x35
+
+extern int mdfld_dsi_pkg_sender_init(struct mdfld_dsi_connector *dsi_connector,
+ int pipe);
+extern void mdfld_dsi_pkg_sender_destroy(struct mdfld_dsi_pkg_sender *sender);
+int mdfld_dsi_send_mcs_short(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
+ u8 param, u8 param_num, bool hs);
+int mdfld_dsi_send_mcs_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
+ u32 len, bool hs);
+int mdfld_dsi_send_gen_short(struct mdfld_dsi_pkg_sender *sender, u8 param0,
+ u8 param1, u8 param_num, bool hs);
+int mdfld_dsi_send_gen_long(struct mdfld_dsi_pkg_sender *sender, u8 *data,
+ u32 len, bool hs);
+/* Read interfaces */
+int mdfld_dsi_read_mcs(struct mdfld_dsi_pkg_sender *sender, u8 cmd,
+ u32 *data, u16 len, bool hs);
+
+#endif
diff --git a/drivers/gpu/drm/gma500/mdfld_intel_display.c b/drivers/gpu/drm/gma500/mdfld_intel_display.c
new file mode 100644
index 000000000000..a35a2921bdf7
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_intel_display.c
@@ -0,0 +1,1180 @@
+/*
+ * Copyright © 2006-2007 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Authors:
+ * Eric Anholt <eric@anholt.net>
+ */
+
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+
+#include <drm/drmP.h>
+#include "psb_intel_reg.h"
+#include "psb_intel_display.h"
+#include "framebuffer.h"
+#include "mdfld_output.h"
+#include "mdfld_dsi_output.h"
+
+/* Hardcoded currently */
+static int ksel = KSEL_CRYSTAL_19;
+
+struct psb_intel_range_t {
+ int min, max;
+};
+
+struct mrst_limit_t {
+ struct psb_intel_range_t dot, m, p1;
+};
+
+struct mrst_clock_t {
+ /* derived values */
+ int dot;
+ int m;
+ int p1;
+};
+
+#define COUNT_MAX 0x10000000
+
+void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe)
+{
+ int count, temp;
+ u32 pipeconf_reg = PIPEACONF;
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ pipeconf_reg = PIPEBCONF;
+ break;
+ case 2:
+ pipeconf_reg = PIPECCONF;
+ break;
+ default:
+ DRM_ERROR("Illegal Pipe Number.\n");
+ return;
+ }
+
+ /* FIXME JLIU7_PO */
+ psb_intel_wait_for_vblank(dev);
+ return;
+
+ /* Wait for for the pipe disable to take effect. */
+ for (count = 0; count < COUNT_MAX; count++) {
+ temp = REG_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_PIPE_STATE) == 0)
+ break;
+ }
+}
+
+void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe)
+{
+ int count, temp;
+ u32 pipeconf_reg = PIPEACONF;
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ pipeconf_reg = PIPEBCONF;
+ break;
+ case 2:
+ pipeconf_reg = PIPECCONF;
+ break;
+ default:
+ DRM_ERROR("Illegal Pipe Number.\n");
+ return;
+ }
+
+ /* FIXME JLIU7_PO */
+ psb_intel_wait_for_vblank(dev);
+ return;
+
+ /* Wait for for the pipe enable to take effect. */
+ for (count = 0; count < COUNT_MAX; count++) {
+ temp = REG_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_PIPE_STATE) == 1)
+ break;
+ }
+}
+
+static void psb_intel_crtc_prepare(struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+}
+
+static void psb_intel_crtc_commit(struct drm_crtc *crtc)
+{
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static bool psb_intel_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+/**
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+static int psb_intel_panel_fitter_pipe(struct drm_device *dev)
+{
+ u32 pfit_control;
+
+ pfit_control = REG_READ(PFIT_CONTROL);
+
+ /* See if the panel fitter is in use */
+ if ((pfit_control & PFIT_ENABLE) == 0)
+ return -1;
+
+ /* 965 can place panel fitter on either pipe */
+ return (pfit_control >> 29) & 0x3;
+}
+
+static struct drm_device globle_dev;
+
+void mdfld__intel_plane_set_alpha(int enable)
+{
+ struct drm_device *dev = &globle_dev;
+ int dspcntr_reg = DSPACNTR;
+ u32 dspcntr;
+
+ dspcntr = REG_READ(dspcntr_reg);
+
+ if (enable) {
+ dspcntr &= ~DISPPLANE_32BPP_NO_ALPHA;
+ dspcntr |= DISPPLANE_32BPP;
+ } else {
+ dspcntr &= ~DISPPLANE_32BPP;
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ }
+
+ REG_WRITE(dspcntr_reg, dspcntr);
+}
+
+static int check_fb(struct drm_framebuffer *fb)
+{
+ if (!fb)
+ return 0;
+
+ switch (fb->bits_per_pixel) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ return 0;
+ default:
+ DRM_ERROR("Unknown color depth\n");
+ return -EINVAL;
+ }
+}
+
+static int mdfld__intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ /* struct drm_i915_master_private *master_priv; */
+ struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+ struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
+ int pipe = psb_intel_crtc->pipe;
+ unsigned long start, offset;
+ int dsplinoff = DSPALINOFF;
+ int dspsurf = DSPASURF;
+ int dspstride = DSPASTRIDE;
+ int dspcntr_reg = DSPACNTR;
+ u32 dspcntr;
+ int ret;
+
+ memcpy(&globle_dev, dev, sizeof(struct drm_device));
+
+ dev_dbg(dev->dev, "pipe = 0x%x.\n", pipe);
+
+ /* no fb bound */
+ if (!crtc->fb) {
+ dev_dbg(dev->dev, "No FB bound\n");
+ return 0;
+ }
+
+ ret = check_fb(crtc->fb);
+ if (ret)
+ return ret;
+
+ switch (pipe) {
+ case 0:
+ dsplinoff = DSPALINOFF;
+ break;
+ case 1:
+ dsplinoff = DSPBLINOFF;
+ dspsurf = DSPBSURF;
+ dspstride = DSPBSTRIDE;
+ dspcntr_reg = DSPBCNTR;
+ break;
+ case 2:
+ dsplinoff = DSPCLINOFF;
+ dspsurf = DSPCSURF;
+ dspstride = DSPCSTRIDE;
+ dspcntr_reg = DSPCCNTR;
+ break;
+ default:
+ DRM_ERROR("Illegal Pipe Number.\n");
+ return -EINVAL;
+ }
+
+ if (!gma_power_begin(dev, true))
+ return 0;
+
+ start = psbfb->gtt->offset;
+ offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
+
+ REG_WRITE(dspstride, crtc->fb->pitches[0]);
+ dspcntr = REG_READ(dspcntr_reg);
+ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+
+ switch (crtc->fb->bits_per_pixel) {
+ case 8:
+ dspcntr |= DISPPLANE_8BPP;
+ break;
+ case 16:
+ if (crtc->fb->depth == 15)
+ dspcntr |= DISPPLANE_15_16BPP;
+ else
+ dspcntr |= DISPPLANE_16BPP;
+ break;
+ case 24:
+ case 32:
+ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ }
+ REG_WRITE(dspcntr_reg, dspcntr);
+
+ dev_dbg(dev->dev, "Writing base %08lX %08lX %d %d\n",
+ start, offset, x, y);
+ REG_WRITE(dsplinoff, offset);
+ REG_READ(dsplinoff);
+ REG_WRITE(dspsurf, start);
+ REG_READ(dspsurf);
+
+ gma_power_end(dev);
+
+ return 0;
+}
+
+/*
+ * Disable the pipe, plane and pll.
+ *
+ */
+void mdfld_disable_crtc(struct drm_device *dev, int pipe)
+{
+ int dpll_reg = MRST_DPLL_A;
+ int dspcntr_reg = DSPACNTR;
+ int dspbase_reg = MRST_DSPABASE;
+ int pipeconf_reg = PIPEACONF;
+ u32 temp;
+
+ dev_dbg(dev->dev, "pipe = %d\n", pipe);
+
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ dpll_reg = MDFLD_DPLL_B;
+ dspcntr_reg = DSPBCNTR;
+ dspbase_reg = DSPBSURF;
+ pipeconf_reg = PIPEBCONF;
+ break;
+ case 2:
+ dpll_reg = MRST_DPLL_A;
+ dspcntr_reg = DSPCCNTR;
+ dspbase_reg = MDFLD_DSPCBASE;
+ pipeconf_reg = PIPECCONF;
+ break;
+ default:
+ DRM_ERROR("Illegal Pipe Number.\n");
+ return;
+ }
+
+ if (pipe != 1)
+ mdfld_dsi_gen_fifo_ready(dev, MIPI_GEN_FIFO_STAT_REG(pipe),
+ HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY);
+
+ /* Disable display plane */
+ temp = REG_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ REG_WRITE(dspcntr_reg,
+ temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
+ REG_READ(dspbase_reg);
+ }
+
+ /* FIXME_JLIU7 MDFLD_PO revisit */
+
+ /* Next, disable display pipes */
+ temp = REG_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ temp &= ~PIPEACONF_ENABLE;
+ temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF;
+ REG_WRITE(pipeconf_reg, temp);
+ REG_READ(pipeconf_reg);
+
+ /* Wait for for the pipe disable to take effect. */
+ mdfldWaitForPipeDisable(dev, pipe);
+ }
+
+ temp = REG_READ(dpll_reg);
+ if (temp & DPLL_VCO_ENABLE) {
+ if ((pipe != 1 &&
+ !((REG_READ(PIPEACONF) | REG_READ(PIPECCONF))
+ & PIPEACONF_ENABLE)) || pipe == 1) {
+ temp &= ~(DPLL_VCO_ENABLE);
+ REG_WRITE(dpll_reg, temp);
+ REG_READ(dpll_reg);
+ /* Wait for the clocks to turn off. */
+ /* FIXME_MDFLD PO may need more delay */
+ udelay(500);
+
+ if (!(temp & MDFLD_PWR_GATE_EN)) {
+ /* gating power of DPLL */
+ REG_WRITE(dpll_reg, temp | MDFLD_PWR_GATE_EN);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(5000);
+ }
+ }
+ }
+
+}
+
+/**
+ * Sets the power management mode of the pipe and plane.
+ *
+ * This code should probably grow support for turning the cursor off and back
+ * on appropriately at the same time as we're turning the pipe off/on.
+ */
+static void mdfld_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+ int pipe = psb_intel_crtc->pipe;
+ int dpll_reg = MRST_DPLL_A;
+ int dspcntr_reg = DSPACNTR;
+ int dspbase_reg = MRST_DSPABASE;
+ int pipeconf_reg = PIPEACONF;
+ u32 pipestat_reg = PIPEASTAT;
+ u32 pipeconf = dev_priv->pipeconf[pipe];
+ u32 temp;
+ int timeout = 0;
+
+ dev_dbg(dev->dev, "mode = %d, pipe = %d\n", mode, pipe);
+
+/* FIXME_JLIU7 MDFLD_PO replaced w/ the following function */
+/* mdfld_dbi_dpms (struct drm_device *dev, int pipe, bool enabled) */
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ dpll_reg = DPLL_B;
+ dspcntr_reg = DSPBCNTR;
+ dspbase_reg = MRST_DSPBBASE;
+ pipeconf_reg = PIPEBCONF;
+ dpll_reg = MDFLD_DPLL_B;
+ break;
+ case 2:
+ dpll_reg = MRST_DPLL_A;
+ dspcntr_reg = DSPCCNTR;
+ dspbase_reg = MDFLD_DSPCBASE;
+ pipeconf_reg = PIPECCONF;
+ pipestat_reg = PIPECSTAT;
+ break;
+ default:
+ DRM_ERROR("Illegal Pipe Number.\n");
+ return;
+ }
+
+ if (!gma_power_begin(dev, true))
+ return;
+
+ /* XXX: When our outputs are all unaware of DPMS modes other than off
+ * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
+ */
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ /* Enable the DPLL */
+ temp = REG_READ(dpll_reg);
+
+ if ((temp & DPLL_VCO_ENABLE) == 0) {
+ /* When ungating power of DPLL, needs to wait 0.5us
+ before enable the VCO */
+ if (temp & MDFLD_PWR_GATE_EN) {
+ temp &= ~MDFLD_PWR_GATE_EN;
+ REG_WRITE(dpll_reg, temp);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+ }
+
+ REG_WRITE(dpll_reg, temp);
+ REG_READ(dpll_reg);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+
+ REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+ REG_READ(dpll_reg);
+
+ /**
+ * wait for DSI PLL to lock
+ * NOTE: only need to poll status of pipe 0 and pipe 1,
+ * since both MIPI pipes share the same PLL.
+ */
+ while ((pipe != 2) && (timeout < 20000) &&
+ !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) {
+ udelay(150);
+ timeout++;
+ }
+ }
+
+ /* Enable the plane */
+ temp = REG_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+ REG_WRITE(dspcntr_reg,
+ temp | DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
+ }
+
+ /* Enable the pipe */
+ temp = REG_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) == 0) {
+ REG_WRITE(pipeconf_reg, pipeconf);
+
+ /* Wait for for the pipe enable to take effect. */
+ mdfldWaitForPipeEnable(dev, pipe);
+ }
+
+ /*workaround for sighting 3741701 Random X blank display*/
+ /*perform w/a in video mode only on pipe A or C*/
+ if (pipe == 0 || pipe == 2) {
+ REG_WRITE(pipestat_reg, REG_READ(pipestat_reg));
+ msleep(100);
+ if (PIPE_VBLANK_STATUS & REG_READ(pipestat_reg))
+ dev_dbg(dev->dev, "OK");
+ else {
+ dev_dbg(dev->dev, "STUCK!!!!");
+ /*shutdown controller*/
+ temp = REG_READ(dspcntr_reg);
+ REG_WRITE(dspcntr_reg,
+ temp & ~DISPLAY_PLANE_ENABLE);
+ REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
+ /*mdfld_dsi_dpi_shut_down(dev, pipe);*/
+ REG_WRITE(0xb048, 1);
+ msleep(100);
+ temp = REG_READ(pipeconf_reg);
+ temp &= ~PIPEACONF_ENABLE;
+ REG_WRITE(pipeconf_reg, temp);
+ msleep(100); /*wait for pipe disable*/
+ REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 0);
+ msleep(100);
+ REG_WRITE(0xb004, REG_READ(0xb004));
+ /* try to bring the controller back up again*/
+ REG_WRITE(MIPI_DEVICE_READY_REG(pipe), 1);
+ temp = REG_READ(dspcntr_reg);
+ REG_WRITE(dspcntr_reg,
+ temp | DISPLAY_PLANE_ENABLE);
+ REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
+ /*mdfld_dsi_dpi_turn_on(dev, pipe);*/
+ REG_WRITE(0xb048, 2);
+ msleep(100);
+ temp = REG_READ(pipeconf_reg);
+ temp |= PIPEACONF_ENABLE;
+ REG_WRITE(pipeconf_reg, temp);
+ }
+ }
+
+ psb_intel_crtc_load_lut(crtc);
+
+ /* Give the overlay scaler a chance to enable
+ if it's on this pipe */
+ /* psb_intel_crtc_dpms_video(crtc, true); TODO */
+
+ break;
+ case DRM_MODE_DPMS_OFF:
+ /* Give the overlay scaler a chance to disable
+ * if it's on this pipe */
+ /* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */
+ if (pipe != 1)
+ mdfld_dsi_gen_fifo_ready(dev,
+ MIPI_GEN_FIFO_STAT_REG(pipe),
+ HS_CTRL_FIFO_EMPTY | HS_DATA_FIFO_EMPTY);
+
+ /* Disable the VGA plane that we never use */
+ REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable display plane */
+ temp = REG_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+ REG_WRITE(dspcntr_reg,
+ temp & ~DISPLAY_PLANE_ENABLE);
+ /* Flush the plane changes */
+ REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
+ REG_READ(dspbase_reg);
+ }
+
+ /* Next, disable display pipes */
+ temp = REG_READ(pipeconf_reg);
+ if ((temp & PIPEACONF_ENABLE) != 0) {
+ temp &= ~PIPEACONF_ENABLE;
+ temp |= PIPECONF_PLANE_OFF | PIPECONF_CURSOR_OFF;
+ REG_WRITE(pipeconf_reg, temp);
+ REG_READ(pipeconf_reg);
+
+ /* Wait for for the pipe disable to take effect. */
+ mdfldWaitForPipeDisable(dev, pipe);
+ }
+
+ temp = REG_READ(dpll_reg);
+ if (temp & DPLL_VCO_ENABLE) {
+ if ((pipe != 1 && !((REG_READ(PIPEACONF)
+ | REG_READ(PIPECCONF)) & PIPEACONF_ENABLE))
+ || pipe == 1) {
+ temp &= ~(DPLL_VCO_ENABLE);
+ REG_WRITE(dpll_reg, temp);
+ REG_READ(dpll_reg);
+ /* Wait for the clocks to turn off. */
+ /* FIXME_MDFLD PO may need more delay */
+ udelay(500);
+ }
+ }
+ break;
+ }
+ gma_power_end(dev);
+}
+
+
+#define MDFLD_LIMT_DPLL_19 0
+#define MDFLD_LIMT_DPLL_25 1
+#define MDFLD_LIMT_DPLL_83 2
+#define MDFLD_LIMT_DPLL_100 3
+#define MDFLD_LIMT_DSIPLL_19 4
+#define MDFLD_LIMT_DSIPLL_25 5
+#define MDFLD_LIMT_DSIPLL_83 6
+#define MDFLD_LIMT_DSIPLL_100 7
+
+#define MDFLD_DOT_MIN 19750
+#define MDFLD_DOT_MAX 120000
+#define MDFLD_DPLL_M_MIN_19 113
+#define MDFLD_DPLL_M_MAX_19 155
+#define MDFLD_DPLL_P1_MIN_19 2
+#define MDFLD_DPLL_P1_MAX_19 10
+#define MDFLD_DPLL_M_MIN_25 101
+#define MDFLD_DPLL_M_MAX_25 130
+#define MDFLD_DPLL_P1_MIN_25 2
+#define MDFLD_DPLL_P1_MAX_25 10
+#define MDFLD_DPLL_M_MIN_83 64
+#define MDFLD_DPLL_M_MAX_83 64
+#define MDFLD_DPLL_P1_MIN_83 2
+#define MDFLD_DPLL_P1_MAX_83 2
+#define MDFLD_DPLL_M_MIN_100 64
+#define MDFLD_DPLL_M_MAX_100 64
+#define MDFLD_DPLL_P1_MIN_100 2
+#define MDFLD_DPLL_P1_MAX_100 2
+#define MDFLD_DSIPLL_M_MIN_19 131
+#define MDFLD_DSIPLL_M_MAX_19 175
+#define MDFLD_DSIPLL_P1_MIN_19 3
+#define MDFLD_DSIPLL_P1_MAX_19 8
+#define MDFLD_DSIPLL_M_MIN_25 97
+#define MDFLD_DSIPLL_M_MAX_25 140
+#define MDFLD_DSIPLL_P1_MIN_25 3
+#define MDFLD_DSIPLL_P1_MAX_25 9
+#define MDFLD_DSIPLL_M_MIN_83 33
+#define MDFLD_DSIPLL_M_MAX_83 92
+#define MDFLD_DSIPLL_P1_MIN_83 2
+#define MDFLD_DSIPLL_P1_MAX_83 3
+#define MDFLD_DSIPLL_M_MIN_100 97
+#define MDFLD_DSIPLL_M_MAX_100 140
+#define MDFLD_DSIPLL_P1_MIN_100 3
+#define MDFLD_DSIPLL_P1_MAX_100 9
+
+static const struct mrst_limit_t mdfld_limits[] = {
+ { /* MDFLD_LIMT_DPLL_19 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DPLL_M_MIN_19, .max = MDFLD_DPLL_M_MAX_19},
+ .p1 = {.min = MDFLD_DPLL_P1_MIN_19, .max = MDFLD_DPLL_P1_MAX_19},
+ },
+ { /* MDFLD_LIMT_DPLL_25 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DPLL_M_MIN_25, .max = MDFLD_DPLL_M_MAX_25},
+ .p1 = {.min = MDFLD_DPLL_P1_MIN_25, .max = MDFLD_DPLL_P1_MAX_25},
+ },
+ { /* MDFLD_LIMT_DPLL_83 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DPLL_M_MIN_83, .max = MDFLD_DPLL_M_MAX_83},
+ .p1 = {.min = MDFLD_DPLL_P1_MIN_83, .max = MDFLD_DPLL_P1_MAX_83},
+ },
+ { /* MDFLD_LIMT_DPLL_100 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DPLL_M_MIN_100, .max = MDFLD_DPLL_M_MAX_100},
+ .p1 = {.min = MDFLD_DPLL_P1_MIN_100, .max = MDFLD_DPLL_P1_MAX_100},
+ },
+ { /* MDFLD_LIMT_DSIPLL_19 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DSIPLL_M_MIN_19, .max = MDFLD_DSIPLL_M_MAX_19},
+ .p1 = {.min = MDFLD_DSIPLL_P1_MIN_19, .max = MDFLD_DSIPLL_P1_MAX_19},
+ },
+ { /* MDFLD_LIMT_DSIPLL_25 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DSIPLL_M_MIN_25, .max = MDFLD_DSIPLL_M_MAX_25},
+ .p1 = {.min = MDFLD_DSIPLL_P1_MIN_25, .max = MDFLD_DSIPLL_P1_MAX_25},
+ },
+ { /* MDFLD_LIMT_DSIPLL_83 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DSIPLL_M_MIN_83, .max = MDFLD_DSIPLL_M_MAX_83},
+ .p1 = {.min = MDFLD_DSIPLL_P1_MIN_83, .max = MDFLD_DSIPLL_P1_MAX_83},
+ },
+ { /* MDFLD_LIMT_DSIPLL_100 */
+ .dot = {.min = MDFLD_DOT_MIN, .max = MDFLD_DOT_MAX},
+ .m = {.min = MDFLD_DSIPLL_M_MIN_100, .max = MDFLD_DSIPLL_M_MAX_100},
+ .p1 = {.min = MDFLD_DSIPLL_P1_MIN_100, .max = MDFLD_DSIPLL_P1_MAX_100},
+ },
+};
+
+#define MDFLD_M_MIN 21
+#define MDFLD_M_MAX 180
+static const u32 mdfld_m_converts[] = {
+/* M configuration table from 9-bit LFSR table */
+ 224, 368, 440, 220, 366, 439, 219, 365, 182, 347, /* 21 - 30 */
+ 173, 342, 171, 85, 298, 149, 74, 37, 18, 265, /* 31 - 40 */
+ 388, 194, 353, 432, 216, 108, 310, 155, 333, 166, /* 41 - 50 */
+ 83, 41, 276, 138, 325, 162, 337, 168, 340, 170, /* 51 - 60 */
+ 341, 426, 469, 234, 373, 442, 221, 110, 311, 411, /* 61 - 70 */
+ 461, 486, 243, 377, 188, 350, 175, 343, 427, 213, /* 71 - 80 */
+ 106, 53, 282, 397, 354, 227, 113, 56, 284, 142, /* 81 - 90 */
+ 71, 35, 273, 136, 324, 418, 465, 488, 500, 506, /* 91 - 100 */
+ 253, 126, 63, 287, 399, 455, 483, 241, 376, 444, /* 101 - 110 */
+ 478, 495, 503, 251, 381, 446, 479, 239, 375, 443, /* 111 - 120 */
+ 477, 238, 119, 315, 157, 78, 295, 147, 329, 420, /* 121 - 130 */
+ 210, 105, 308, 154, 77, 38, 275, 137, 68, 290, /* 131 - 140 */
+ 145, 328, 164, 82, 297, 404, 458, 485, 498, 249, /* 141 - 150 */
+ 380, 190, 351, 431, 471, 235, 117, 314, 413, 206, /* 151 - 160 */
+ 103, 51, 25, 12, 262, 387, 193, 96, 48, 280, /* 161 - 170 */
+ 396, 198, 99, 305, 152, 76, 294, 403, 457, 228, /* 171 - 180 */
+};
+
+static const struct mrst_limit_t *mdfld_limit(struct drm_crtc *crtc)
+{
+ const struct mrst_limit_t *limit = NULL;
+ struct drm_device *dev = crtc->dev;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)
+ || psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI2)) {
+ if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19))
+ limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_19];
+ else if (ksel == KSEL_BYPASS_25)
+ limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_25];
+ else if ((ksel == KSEL_BYPASS_83_100) &&
+ (dev_priv->core_freq == 166))
+ limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_83];
+ else if ((ksel == KSEL_BYPASS_83_100) &&
+ (dev_priv->core_freq == 100 ||
+ dev_priv->core_freq == 200))
+ limit = &mdfld_limits[MDFLD_LIMT_DSIPLL_100];
+ } else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI)) {
+ if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19))
+ limit = &mdfld_limits[MDFLD_LIMT_DPLL_19];
+ else if (ksel == KSEL_BYPASS_25)
+ limit = &mdfld_limits[MDFLD_LIMT_DPLL_25];
+ else if ((ksel == KSEL_BYPASS_83_100) &&
+ (dev_priv->core_freq == 166))
+ limit = &mdfld_limits[MDFLD_LIMT_DPLL_83];
+ else if ((ksel == KSEL_BYPASS_83_100) &&
+ (dev_priv->core_freq == 100 ||
+ dev_priv->core_freq == 200))
+ limit = &mdfld_limits[MDFLD_LIMT_DPLL_100];
+ } else {
+ limit = NULL;
+ dev_dbg(dev->dev, "mdfld_limit Wrong display type.\n");
+ }
+
+ return limit;
+}
+
+/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
+static void mdfld_clock(int refclk, struct mrst_clock_t *clock)
+{
+ clock->dot = (refclk * clock->m) / clock->p1;
+}
+
+/**
+ * Returns a set of divisors for the desired target clock with the given refclk,
+ * or FALSE. Divisor values are the actual divisors for
+ */
+static bool
+mdfldFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
+ struct mrst_clock_t *best_clock)
+{
+ struct mrst_clock_t clock;
+ const struct mrst_limit_t *limit = mdfld_limit(crtc);
+ int err = target;
+
+ memset(best_clock, 0, sizeof(*best_clock));
+
+ for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
+ for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
+ clock.p1++) {
+ int this_err;
+
+ mdfld_clock(refclk, &clock);
+
+ this_err = abs(clock.dot - target);
+ if (this_err < err) {
+ *best_clock = clock;
+ err = this_err;
+ }
+ }
+ }
+ return err != target;
+}
+
+static int mdfld_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ int pipe = psb_intel_crtc->pipe;
+ int fp_reg = MRST_FPA0;
+ int dpll_reg = MRST_DPLL_A;
+ int dspcntr_reg = DSPACNTR;
+ int pipeconf_reg = PIPEACONF;
+ int htot_reg = HTOTAL_A;
+ int hblank_reg = HBLANK_A;
+ int hsync_reg = HSYNC_A;
+ int vtot_reg = VTOTAL_A;
+ int vblank_reg = VBLANK_A;
+ int vsync_reg = VSYNC_A;
+ int dspsize_reg = DSPASIZE;
+ int dsppos_reg = DSPAPOS;
+ int pipesrc_reg = PIPEASRC;
+ u32 *pipeconf = &dev_priv->pipeconf[pipe];
+ u32 *dspcntr = &dev_priv->dspcntr[pipe];
+ int refclk = 0;
+ int clk_n = 0, clk_p2 = 0, clk_byte = 1, clk = 0, m_conv = 0,
+ clk_tmp = 0;
+ struct mrst_clock_t clock;
+ bool ok;
+ u32 dpll = 0, fp = 0;
+ bool is_mipi = false, is_mipi2 = false, is_hdmi = false;
+ struct drm_mode_config *mode_config = &dev->mode_config;
+ struct psb_intel_encoder *psb_intel_encoder = NULL;
+ uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int timeout = 0;
+ int ret;
+
+ dev_dbg(dev->dev, "pipe = 0x%x\n", pipe);
+
+#if 0
+ if (pipe == 1) {
+ if (!gma_power_begin(dev, true))
+ return 0;
+ android_hdmi_crtc_mode_set(crtc, mode, adjusted_mode,
+ x, y, old_fb);
+ goto mrst_crtc_mode_set_exit;
+ }
+#endif
+
+ switch (pipe) {
+ case 0:
+ break;
+ case 1:
+ fp_reg = FPB0;
+ dpll_reg = DPLL_B;
+ dspcntr_reg = DSPBCNTR;
+ pipeconf_reg = PIPEBCONF;
+ htot_reg = HTOTAL_B;
+ hblank_reg = HBLANK_B;
+ hsync_reg = HSYNC_B;
+ vtot_reg = VTOTAL_B;
+ vblank_reg = VBLANK_B;
+ vsync_reg = VSYNC_B;
+ dspsize_reg = DSPBSIZE;
+ dsppos_reg = DSPBPOS;
+ pipesrc_reg = PIPEBSRC;
+ fp_reg = MDFLD_DPLL_DIV0;
+ dpll_reg = MDFLD_DPLL_B;
+ break;
+ case 2:
+ dpll_reg = MRST_DPLL_A;
+ dspcntr_reg = DSPCCNTR;
+ pipeconf_reg = PIPECCONF;
+ htot_reg = HTOTAL_C;
+ hblank_reg = HBLANK_C;
+ hsync_reg = HSYNC_C;
+ vtot_reg = VTOTAL_C;
+ vblank_reg = VBLANK_C;
+ vsync_reg = VSYNC_C;
+ dspsize_reg = DSPCSIZE;
+ dsppos_reg = DSPCPOS;
+ pipesrc_reg = PIPECSRC;
+ break;
+ default:
+ DRM_ERROR("Illegal Pipe Number.\n");
+ return 0;
+ }
+
+ ret = check_fb(crtc->fb);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev->dev, "adjusted_hdisplay = %d\n",
+ adjusted_mode->hdisplay);
+ dev_dbg(dev->dev, "adjusted_vdisplay = %d\n",
+ adjusted_mode->vdisplay);
+ dev_dbg(dev->dev, "adjusted_hsync_start = %d\n",
+ adjusted_mode->hsync_start);
+ dev_dbg(dev->dev, "adjusted_hsync_end = %d\n",
+ adjusted_mode->hsync_end);
+ dev_dbg(dev->dev, "adjusted_htotal = %d\n",
+ adjusted_mode->htotal);
+ dev_dbg(dev->dev, "adjusted_vsync_start = %d\n",
+ adjusted_mode->vsync_start);
+ dev_dbg(dev->dev, "adjusted_vsync_end = %d\n",
+ adjusted_mode->vsync_end);
+ dev_dbg(dev->dev, "adjusted_vtotal = %d\n",
+ adjusted_mode->vtotal);
+ dev_dbg(dev->dev, "adjusted_clock = %d\n",
+ adjusted_mode->clock);
+ dev_dbg(dev->dev, "hdisplay = %d\n",
+ mode->hdisplay);
+ dev_dbg(dev->dev, "vdisplay = %d\n",
+ mode->vdisplay);
+
+ if (!gma_power_begin(dev, true))
+ return 0;
+
+ memcpy(&psb_intel_crtc->saved_mode, mode,
+ sizeof(struct drm_display_mode));
+ memcpy(&psb_intel_crtc->saved_adjusted_mode, adjusted_mode,
+ sizeof(struct drm_display_mode));
+
+ list_for_each_entry(connector, &mode_config->connector_list, head) {
+ if (!connector)
+ continue;
+
+ encoder = connector->encoder;
+
+ if (!encoder)
+ continue;
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ psb_intel_encoder = psb_intel_attached_encoder(connector);
+
+ switch (psb_intel_encoder->type) {
+ case INTEL_OUTPUT_MIPI:
+ is_mipi = true;
+ break;
+ case INTEL_OUTPUT_MIPI2:
+ is_mipi2 = true;
+ break;
+ case INTEL_OUTPUT_HDMI:
+ is_hdmi = true;
+ break;
+ }
+ }
+
+ /* Disable the VGA plane that we never use */
+ REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+
+ /* Disable the panel fitter if it was on our pipe */
+ if (psb_intel_panel_fitter_pipe(dev) == pipe)
+ REG_WRITE(PFIT_CONTROL, 0);
+
+ /* pipesrc and dspsize control the size that is scaled from,
+ * which should always be the user's requested size.
+ */
+ if (pipe == 1) {
+ /* FIXME: To make HDMI display with 864x480 (TPO), 480x864
+ * (PYR) or 480x854 (TMD), set the sprite width/height and
+ * souce image size registers with the adjusted mode for
+ * pipe B.
+ */
+
+ /*
+ * The defined sprite rectangle must always be completely
+ * contained within the displayable area of the screen image
+ * (frame buffer).
+ */
+ REG_WRITE(dspsize_reg, ((min(mode->crtc_vdisplay, adjusted_mode->crtc_vdisplay) - 1) << 16)
+ | (min(mode->crtc_hdisplay, adjusted_mode->crtc_hdisplay) - 1));
+ /* Set the CRTC with encoder mode. */
+ REG_WRITE(pipesrc_reg, ((mode->crtc_hdisplay - 1) << 16)
+ | (mode->crtc_vdisplay - 1));
+ } else {
+ REG_WRITE(dspsize_reg,
+ ((mode->crtc_vdisplay - 1) << 16) |
+ (mode->crtc_hdisplay - 1));
+ REG_WRITE(pipesrc_reg,
+ ((mode->crtc_hdisplay - 1) << 16) |
+ (mode->crtc_vdisplay - 1));
+ }
+
+ REG_WRITE(dsppos_reg, 0);
+
+ if (psb_intel_encoder)
+ drm_connector_property_get_value(connector,
+ dev->mode_config.scaling_mode_property, &scalingType);
+
+ if (scalingType == DRM_MODE_SCALE_NO_SCALE) {
+ /* Medfield doesn't have register support for centering so we
+ * need to mess with the h/vblank and h/vsync start and ends
+ * to get centering
+ */
+ int offsetX = 0, offsetY = 0;
+
+ offsetX = (adjusted_mode->crtc_hdisplay -
+ mode->crtc_hdisplay) / 2;
+ offsetY = (adjusted_mode->crtc_vdisplay -
+ mode->crtc_vdisplay) / 2;
+
+ REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start -
+ offsetX - 1) |
+ ((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16));
+ REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start -
+ offsetX - 1) |
+ ((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16));
+ REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start -
+ offsetY - 1) |
+ ((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16));
+ REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start -
+ offsetY - 1) |
+ ((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16));
+ } else {
+ REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
+ ((adjusted_mode->crtc_htotal - 1) << 16));
+ REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
+ ((adjusted_mode->crtc_vtotal - 1) << 16));
+ REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
+ ((adjusted_mode->crtc_hblank_end - 1) << 16));
+ REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
+ ((adjusted_mode->crtc_hsync_end - 1) << 16));
+ REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
+ ((adjusted_mode->crtc_vblank_end - 1) << 16));
+ REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
+ ((adjusted_mode->crtc_vsync_end - 1) << 16));
+ }
+
+ /* Flush the plane changes */
+ {
+ struct drm_crtc_helper_funcs *crtc_funcs =
+ crtc->helper_private;
+ crtc_funcs->mode_set_base(crtc, x, y, old_fb);
+ }
+
+ /* setup pipeconf */
+ *pipeconf = PIPEACONF_ENABLE; /* FIXME_JLIU7 REG_READ(pipeconf_reg); */
+
+ /* Set up the display plane register */
+ *dspcntr = REG_READ(dspcntr_reg);
+ *dspcntr |= pipe << DISPPLANE_SEL_PIPE_POS;
+ *dspcntr |= DISPLAY_PLANE_ENABLE;
+
+ if (is_mipi2)
+ goto mrst_crtc_mode_set_exit;
+ clk = adjusted_mode->clock;
+
+ if (is_hdmi) {
+ if ((ksel == KSEL_CRYSTAL_19) || (ksel == KSEL_BYPASS_19)) {
+ refclk = 19200;
+
+ if (is_mipi || is_mipi2)
+ clk_n = 1, clk_p2 = 8;
+ else if (is_hdmi)
+ clk_n = 1, clk_p2 = 10;
+ } else if (ksel == KSEL_BYPASS_25) {
+ refclk = 25000;
+
+ if (is_mipi || is_mipi2)
+ clk_n = 1, clk_p2 = 8;
+ else if (is_hdmi)
+ clk_n = 1, clk_p2 = 10;
+ } else if ((ksel == KSEL_BYPASS_83_100) &&
+ dev_priv->core_freq == 166) {
+ refclk = 83000;
+
+ if (is_mipi || is_mipi2)
+ clk_n = 4, clk_p2 = 8;
+ else if (is_hdmi)
+ clk_n = 4, clk_p2 = 10;
+ } else if ((ksel == KSEL_BYPASS_83_100) &&
+ (dev_priv->core_freq == 100 ||
+ dev_priv->core_freq == 200)) {
+ refclk = 100000;
+ if (is_mipi || is_mipi2)
+ clk_n = 4, clk_p2 = 8;
+ else if (is_hdmi)
+ clk_n = 4, clk_p2 = 10;
+ }
+
+ if (is_mipi)
+ clk_byte = dev_priv->bpp / 8;
+ else if (is_mipi2)
+ clk_byte = dev_priv->bpp2 / 8;
+
+ clk_tmp = clk * clk_n * clk_p2 * clk_byte;
+
+ dev_dbg(dev->dev, "clk = %d, clk_n = %d, clk_p2 = %d.\n",
+ clk, clk_n, clk_p2);
+ dev_dbg(dev->dev, "adjusted_mode->clock = %d, clk_tmp = %d.\n",
+ adjusted_mode->clock, clk_tmp);
+
+ ok = mdfldFindBestPLL(crtc, clk_tmp, refclk, &clock);
+
+ if (!ok) {
+ DRM_ERROR
+ ("mdfldFindBestPLL fail in mdfld_crtc_mode_set.\n");
+ } else {
+ m_conv = mdfld_m_converts[(clock.m - MDFLD_M_MIN)];
+
+ dev_dbg(dev->dev, "dot clock = %d,"
+ "m = %d, p1 = %d, m_conv = %d.\n",
+ clock.dot, clock.m,
+ clock.p1, m_conv);
+ }
+
+ dpll = REG_READ(dpll_reg);
+
+ if (dpll & DPLL_VCO_ENABLE) {
+ dpll &= ~DPLL_VCO_ENABLE;
+ REG_WRITE(dpll_reg, dpll);
+ REG_READ(dpll_reg);
+
+ /* FIXME jliu7 check the DPLL lock bit PIPEACONF[29] */
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+
+ /* reset M1, N1 & P1 */
+ REG_WRITE(fp_reg, 0);
+ dpll &= ~MDFLD_P1_MASK;
+ REG_WRITE(dpll_reg, dpll);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+ }
+
+ /* When ungating power of DPLL, needs to wait 0.5us before
+ * enable the VCO */
+ if (dpll & MDFLD_PWR_GATE_EN) {
+ dpll &= ~MDFLD_PWR_GATE_EN;
+ REG_WRITE(dpll_reg, dpll);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+ }
+ dpll = 0;
+
+#if 0 /* FIXME revisit later */
+ if (ksel == KSEL_CRYSTAL_19 || ksel == KSEL_BYPASS_19 ||
+ ksel == KSEL_BYPASS_25)
+ dpll &= ~MDFLD_INPUT_REF_SEL;
+ else if (ksel == KSEL_BYPASS_83_100)
+ dpll |= MDFLD_INPUT_REF_SEL;
+#endif /* FIXME revisit later */
+
+ if (is_hdmi)
+ dpll |= MDFLD_VCO_SEL;
+
+ fp = (clk_n / 2) << 16;
+ fp |= m_conv;
+
+ /* compute bitmask from p1 value */
+ dpll |= (1 << (clock.p1 - 2)) << 17;
+
+#if 0 /* 1080p30 & 720p */
+ dpll = 0x00050000;
+ fp = 0x000001be;
+#endif
+#if 0 /* 480p */
+ dpll = 0x02010000;
+ fp = 0x000000d2;
+#endif
+ } else {
+#if 0 /*DBI_TPO_480x864*/
+ dpll = 0x00020000;
+ fp = 0x00000156;
+#endif /* DBI_TPO_480x864 */ /* get from spec. */
+
+ dpll = 0x00800000;
+ fp = 0x000000c1;
+ }
+
+ REG_WRITE(fp_reg, fp);
+ REG_WRITE(dpll_reg, dpll);
+ /* FIXME_MDFLD PO - change 500 to 1 after PO */
+ udelay(500);
+
+ dpll |= DPLL_VCO_ENABLE;
+ REG_WRITE(dpll_reg, dpll);
+ REG_READ(dpll_reg);
+
+ /* wait for DSI PLL to lock */
+ while (timeout < 20000 &&
+ !(REG_READ(pipeconf_reg) & PIPECONF_DSIPLL_LOCK)) {
+ udelay(150);
+ timeout++;
+ }
+
+ if (is_mipi)
+ goto mrst_crtc_mode_set_exit;
+
+ dev_dbg(dev->dev, "is_mipi = 0x%x\n", is_mipi);
+
+ REG_WRITE(pipeconf_reg, *pipeconf);
+ REG_READ(pipeconf_reg);
+
+ /* Wait for for the pipe enable to take effect. */
+ REG_WRITE(dspcntr_reg, *dspcntr);
+ psb_intel_wait_for_vblank(dev);
+
+mrst_crtc_mode_set_exit:
+
+ gma_power_end(dev);
+
+ return 0;
+}
+
+const struct drm_crtc_helper_funcs mdfld_helper_funcs = {
+ .dpms = mdfld_crtc_dpms,
+ .mode_fixup = psb_intel_crtc_mode_fixup,
+ .mode_set = mdfld_crtc_mode_set,
+ .mode_set_base = mdfld__intel_pipe_set_base,
+ .prepare = psb_intel_crtc_prepare,
+ .commit = psb_intel_crtc_commit,
+};
+
diff --git a/drivers/gpu/drm/gma500/mdfld_output.c b/drivers/gpu/drm/gma500/mdfld_output.c
new file mode 100644
index 000000000000..c95966bb0c96
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_output.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicensen
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Thomas Eaton <thomas.g.eaton@intel.com>
+ * Scott Rowe <scott.m.rowe@intel.com>
+*/
+
+#include "mdfld_output.h"
+#include "mdfld_dsi_dpi.h"
+#include "mdfld_dsi_output.h"
+
+#include "tc35876x-dsi-lvds.h"
+
+int mdfld_get_panel_type(struct drm_device *dev, int pipe)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ return dev_priv->mdfld_panel_id;
+}
+
+static void mdfld_init_panel(struct drm_device *dev, int mipi_pipe,
+ int p_type)
+{
+ switch (p_type) {
+ case TPO_VID:
+ mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tpo_vid_funcs);
+ break;
+ case TC35876X:
+ tc35876x_init(dev);
+ mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tc35876x_funcs);
+ break;
+ case TMD_VID:
+ mdfld_dsi_output_init(dev, mipi_pipe, &mdfld_tmd_vid_funcs);
+ break;
+ case HDMI:
+/* if (dev_priv->mdfld_hdmi_present)
+ mdfld_hdmi_init(dev, &dev_priv->mode_dev); */
+ break;
+ }
+}
+
+
+int mdfld_output_init(struct drm_device *dev)
+{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ /* FIXME: hardcoded for now */
+ dev_priv->mdfld_panel_id = TC35876X;
+ /* MIPI panel 1 */
+ mdfld_init_panel(dev, 0, dev_priv->mdfld_panel_id);
+ /* HDMI panel */
+ mdfld_init_panel(dev, 1, HDMI);
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/gma500/mdfld_output.h b/drivers/gpu/drm/gma500/mdfld_output.h
new file mode 100644
index 000000000000..ab2b27c0f037
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_output.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicensen
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Thomas Eaton <thomas.g.eaton@intel.com>
+ * Scott Rowe <scott.m.rowe@intel.com>
+*/
+
+#ifndef MDFLD_OUTPUT_H
+#define MDFLD_OUTPUT_H
+
+#include "psb_drv.h"
+
+#define TPO_PANEL_WIDTH 84
+#define TPO_PANEL_HEIGHT 46
+#define TMD_PANEL_WIDTH 39
+#define TMD_PANEL_HEIGHT 71
+
+struct mdfld_dsi_config;
+
+enum panel_type {
+ TPO_VID,
+ TMD_VID,
+ HDMI,
+ TC35876X,
+};
+
+struct panel_info {
+ u32 width_mm;
+ u32 height_mm;
+ /* Other info */
+};
+
+struct panel_funcs {
+ const struct drm_encoder_funcs *encoder_funcs;
+ const struct drm_encoder_helper_funcs *encoder_helper_funcs;
+ struct drm_display_mode * (*get_config_mode)(struct drm_device *);
+ int (*get_panel_info)(struct drm_device *, int, struct panel_info *);
+ int (*reset)(int pipe);
+ void (*drv_ic_init)(struct mdfld_dsi_config *dsi_config, int pipe);
+};
+
+int mdfld_output_init(struct drm_device *dev);
+
+struct backlight_device *mdfld_get_backlight_device(void);
+int mdfld_set_brightness(struct backlight_device *bd);
+
+int mdfld_get_panel_type(struct drm_device *dev, int pipe);
+
+extern const struct drm_crtc_helper_funcs mdfld_helper_funcs;
+
+extern const struct panel_funcs mdfld_tmd_vid_funcs;
+extern const struct panel_funcs mdfld_tpo_vid_funcs;
+
+extern void mdfld_disable_crtc(struct drm_device *dev, int pipe);
+extern void mdfldWaitForPipeEnable(struct drm_device *dev, int pipe);
+extern void mdfldWaitForPipeDisable(struct drm_device *dev, int pipe);
+#endif
diff --git a/drivers/gpu/drm/gma500/mdfld_tmd_vid.c b/drivers/gpu/drm/gma500/mdfld_tmd_vid.c
new file mode 100644
index 000000000000..dc0c6c3d3d29
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_tmd_vid.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Jim Liu <jim.liu@intel.com>
+ * Jackie Li<yaodong.li@intel.com>
+ * Gideon Eaton <eaton.
+ * Scott Rowe <scott.m.rowe@intel.com>
+ */
+
+#include "mdfld_dsi_dpi.h"
+#include "mdfld_dsi_pkg_sender.h"
+
+static struct drm_display_mode *tmd_vid_get_config_mode(struct drm_device *dev)
+{
+ struct drm_display_mode *mode;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
+ bool use_gct = false; /*Disable GCT for now*/
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ if (use_gct) {
+ mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
+ mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
+ mode->hsync_start = mode->hdisplay + \
+ ((ti->hsync_offset_hi << 8) | \
+ ti->hsync_offset_lo);
+ mode->hsync_end = mode->hsync_start + \
+ ((ti->hsync_pulse_width_hi << 8) | \
+ ti->hsync_pulse_width_lo);
+ mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \
+ ti->hblank_lo);
+ mode->vsync_start = \
+ mode->vdisplay + ((ti->vsync_offset_hi << 8) | \
+ ti->vsync_offset_lo);
+ mode->vsync_end = \
+ mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) | \
+ ti->vsync_pulse_width_lo);
+ mode->vtotal = mode->vdisplay + \
+ ((ti->vblank_hi << 8) | ti->vblank_lo);
+ mode->clock = ti->pixel_clock * 10;
+
+ dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
+ dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
+ dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
+ dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
+ dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
+ dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
+ dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
+ dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
+ dev_dbg(dev->dev, "clock is %d\n", mode->clock);
+ } else {
+ mode->hdisplay = 480;
+ mode->vdisplay = 854;
+ mode->hsync_start = 487;
+ mode->hsync_end = 490;
+ mode->htotal = 499;
+ mode->vsync_start = 861;
+ mode->vsync_end = 865;
+ mode->vtotal = 873;
+ mode->clock = 33264;
+ }
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ return mode;
+}
+
+static int tmd_vid_get_panel_info(struct drm_device *dev,
+ int pipe,
+ struct panel_info *pi)
+{
+ if (!dev || !pi)
+ return -EINVAL;
+
+ pi->width_mm = TMD_PANEL_WIDTH;
+ pi->height_mm = TMD_PANEL_HEIGHT;
+
+ return 0;
+}
+
+/* ************************************************************************* *\
+ * FUNCTION: mdfld_init_TMD_MIPI
+ *
+ * DESCRIPTION: This function is called only by mrst_dsi_mode_set and
+ * restore_display_registers. since this function does not
+ * acquire the mutex, it is important that the calling function
+ * does!
+\* ************************************************************************* */
+
+/* FIXME: make the below data u8 instead of u32; note byte order! */
+static u32 tmd_cmd_mcap_off[] = {0x000000b2};
+static u32 tmd_cmd_enable_lane_switch[] = {0x000101ef};
+static u32 tmd_cmd_set_lane_num[] = {0x006360ef};
+static u32 tmd_cmd_pushing_clock0[] = {0x00cc2fef};
+static u32 tmd_cmd_pushing_clock1[] = {0x00dd6eef};
+static u32 tmd_cmd_set_mode[] = {0x000000b3};
+static u32 tmd_cmd_set_sync_pulse_mode[] = {0x000961ef};
+static u32 tmd_cmd_set_column[] = {0x0100002a, 0x000000df};
+static u32 tmd_cmd_set_page[] = {0x0300002b, 0x00000055};
+static u32 tmd_cmd_set_video_mode[] = {0x00000153};
+/*no auto_bl,need add in furture*/
+static u32 tmd_cmd_enable_backlight[] = {0x00005ab4};
+static u32 tmd_cmd_set_backlight_dimming[] = {0x00000ebd};
+
+static void mdfld_dsi_tmd_drv_ic_init(struct mdfld_dsi_config *dsi_config,
+ int pipe)
+{
+ struct mdfld_dsi_pkg_sender *sender
+ = mdfld_dsi_get_pkg_sender(dsi_config);
+
+ DRM_INFO("Enter mdfld init TMD MIPI display.\n");
+
+ if (!sender) {
+ DRM_ERROR("Cannot get sender\n");
+ return;
+ }
+
+ if (dsi_config->dvr_ic_inited)
+ return;
+
+ msleep(3);
+
+ /* FIXME: make the below data u8 instead of u32; note byte order! */
+
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_mcap_off,
+ sizeof(tmd_cmd_mcap_off), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_enable_lane_switch,
+ sizeof(tmd_cmd_enable_lane_switch), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_lane_num,
+ sizeof(tmd_cmd_set_lane_num), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_pushing_clock0,
+ sizeof(tmd_cmd_pushing_clock0), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_pushing_clock1,
+ sizeof(tmd_cmd_pushing_clock1), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_mode,
+ sizeof(tmd_cmd_set_mode), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_sync_pulse_mode,
+ sizeof(tmd_cmd_set_sync_pulse_mode), false);
+ mdfld_dsi_send_mcs_long(sender, (u8 *) tmd_cmd_set_column,
+ sizeof(tmd_cmd_set_column), false);
+ mdfld_dsi_send_mcs_long(sender, (u8 *) tmd_cmd_set_page,
+ sizeof(tmd_cmd_set_page), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_video_mode,
+ sizeof(tmd_cmd_set_video_mode), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_enable_backlight,
+ sizeof(tmd_cmd_enable_backlight), false);
+ mdfld_dsi_send_gen_long(sender, (u8 *) tmd_cmd_set_backlight_dimming,
+ sizeof(tmd_cmd_set_backlight_dimming), false);
+
+ dsi_config->dvr_ic_inited = 1;
+}
+
+/*TPO DPI encoder helper funcs*/
+static const struct drm_encoder_helper_funcs
+ mdfld_tpo_dpi_encoder_helper_funcs = {
+ .dpms = mdfld_dsi_dpi_dpms,
+ .mode_fixup = mdfld_dsi_dpi_mode_fixup,
+ .prepare = mdfld_dsi_dpi_prepare,
+ .mode_set = mdfld_dsi_dpi_mode_set,
+ .commit = mdfld_dsi_dpi_commit,
+};
+
+/*TPO DPI encoder funcs*/
+static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+const struct panel_funcs mdfld_tmd_vid_funcs = {
+ .encoder_funcs = &mdfld_tpo_dpi_encoder_funcs,
+ .encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs,
+ .get_config_mode = &tmd_vid_get_config_mode,
+ .get_panel_info = tmd_vid_get_panel_info,
+ .reset = mdfld_dsi_panel_reset,
+ .drv_ic_init = mdfld_dsi_tmd_drv_ic_init,
+};
diff --git a/drivers/gpu/drm/gma500/mdfld_tpo_vid.c b/drivers/gpu/drm/gma500/mdfld_tpo_vid.c
new file mode 100644
index 000000000000..d8d4170725b2
--- /dev/null
+++ b/drivers/gpu/drm/gma500/mdfld_tpo_vid.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright © 2010 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * jim liu <jim.liu@intel.com>
+ * Jackie Li<yaodong.li@intel.com>
+ */
+
+#include "mdfld_dsi_dpi.h"
+
+static struct drm_display_mode *tpo_vid_get_config_mode(struct drm_device *dev)
+{
+ struct drm_display_mode *mode;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
+ bool use_gct = false;
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ if (use_gct) {
+ mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
+ mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
+ mode->hsync_start = mode->hdisplay +
+ ((ti->hsync_offset_hi << 8) |
+ ti->hsync_offset_lo);
+ mode->hsync_end = mode->hsync_start +
+ ((ti->hsync_pulse_width_hi << 8) |
+ ti->hsync_pulse_width_lo);
+ mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) |
+ ti->hblank_lo);
+ mode->vsync_start =
+ mode->vdisplay + ((ti->vsync_offset_hi << 8) |
+ ti->vsync_offset_lo);
+ mode->vsync_end =
+ mode->vsync_start + ((ti->vsync_pulse_width_hi << 8) |
+ ti->vsync_pulse_width_lo);
+ mode->vtotal = mode->vdisplay +
+ ((ti->vblank_hi << 8) | ti->vblank_lo);
+ mode->clock = ti->pixel_clock * 10;
+
+ dev_dbg(dev->dev, "hdisplay is %d\n", mode->hdisplay);
+ dev_dbg(dev->dev, "vdisplay is %d\n", mode->vdisplay);
+ dev_dbg(dev->dev, "HSS is %d\n", mode->hsync_start);
+ dev_dbg(dev->dev, "HSE is %d\n", mode->hsync_end);
+ dev_dbg(dev->dev, "htotal is %d\n", mode->htotal);
+ dev_dbg(dev->dev, "VSS is %d\n", mode->vsync_start);
+ dev_dbg(dev->dev, "VSE is %d\n", mode->vsync_end);
+ dev_dbg(dev->dev, "vtotal is %d\n", mode->vtotal);
+ dev_dbg(dev->dev, "clock is %d\n", mode->clock);
+ } else {
+ mode->hdisplay = 864;
+ mode->vdisplay = 480;
+ mode->hsync_start = 873;
+ mode->hsync_end = 876;
+ mode->htotal = 887;
+ mode->vsync_start = 487;
+ mode->vsync_end = 490;
+ mode->vtotal = 499;
+ mode->clock = 33264;
+ }
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ return mode;
+}
+
+static int tpo_vid_get_panel_info(struct drm_device *dev,
+ int pipe,
+ struct panel_info *pi)
+{
+ if (!dev || !pi)
+ return -EINVAL;
+
+ pi->width_mm = TPO_PANEL_WIDTH;
+ pi->height_mm = TPO_PANEL_HEIGHT;
+
+ return 0;
+}
+
+/*TPO DPI encoder helper funcs*/
+static const struct drm_encoder_helper_funcs
+ mdfld_tpo_dpi_encoder_helper_funcs = {
+ .dpms = mdfld_dsi_dpi_dpms,
+ .mode_fixup = mdfld_dsi_dpi_mode_fixup,
+ .prepare = mdfld_dsi_dpi_prepare,
+ .mode_set = mdfld_dsi_dpi_mode_set,
+ .commit = mdfld_dsi_dpi_commit,
+};
+
+/*TPO DPI encoder funcs*/
+static const struct drm_encoder_funcs mdfld_tpo_dpi_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+const struct panel_funcs mdfld_tpo_vid_funcs = {
+ .encoder_funcs = &mdfld_tpo_dpi_encoder_funcs,
+ .encoder_helper_funcs = &mdfld_tpo_dpi_encoder_helper_funcs,
+ .get_config_mode = &tpo_vid_get_config_mode,
+ .get_panel_info = tpo_vid_get_panel_info,
+};
diff --git a/drivers/gpu/drm/gma500/mmu.c b/drivers/gpu/drm/gma500/mmu.c
index e80ee82f6caf..49bac41beefb 100644
--- a/drivers/gpu/drm/gma500/mmu.c
+++ b/drivers/gpu/drm/gma500/mmu.c
@@ -270,7 +270,7 @@ out_err1:
return NULL;
}
-void psb_mmu_free_pt(struct psb_mmu_pt *pt)
+static void psb_mmu_free_pt(struct psb_mmu_pt *pt)
{
__free_page(pt->p);
kfree(pt);
@@ -351,7 +351,7 @@ static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
return pt;
}
-struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
+static struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
unsigned long addr)
{
uint32_t index = psb_mmu_pd_index(addr);
@@ -488,15 +488,6 @@ struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver)
return pd;
}
-/* Returns the physical address of the PD shared by sgx/msvdx */
-uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver)
-{
- struct psb_mmu_pd *pd;
-
- pd = psb_mmu_get_default_pd(driver);
- return page_to_pfn(pd->p) << PAGE_SHIFT;
-}
-
void psb_mmu_driver_takedown(struct psb_mmu_driver *driver)
{
psb_mmu_free_pagedir(driver->default_pd);
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c
index 9d12a3ee1600..a39b0d0d680f 100644
--- a/drivers/gpu/drm/gma500/oaktrail_crtc.c
+++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c
@@ -115,7 +115,7 @@ static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock)
clock->dot = (refclk * clock->m) / (14 * clock->p1);
}
-void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock)
+static void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock)
{
pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n",
prefix, clock->dot, clock->m, clock->p1);
@@ -169,7 +169,6 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE;
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
u32 temp;
- bool enabled;
if (!gma_power_begin(dev, true))
return;
@@ -253,8 +252,6 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
break;
}
- enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
-
/*Set FIFO Watermarks*/
REG_WRITE(DSPARB, 0x3FFF);
REG_WRITE(DSPFW1, 0x3F88080A);
@@ -310,7 +307,7 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
struct oaktrail_clock_t clock;
u32 dpll = 0, fp = 0, dspcntr, pipeconf;
bool ok, is_sdvo = false;
- bool is_crt = false, is_lvds = false, is_tv = false;
+ bool is_lvds = false;
bool is_mipi = false;
struct drm_mode_config *mode_config = &dev->mode_config;
struct psb_intel_encoder *psb_intel_encoder = NULL;
@@ -340,12 +337,6 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
case INTEL_OUTPUT_SDVO:
is_sdvo = true;
break;
- case INTEL_OUTPUT_TVOUT:
- is_tv = true;
- break;
- case INTEL_OUTPUT_ANALOG:
- is_crt = true;
- break;
case INTEL_OUTPUT_MIPI:
is_mipi = true;
break;
@@ -428,9 +419,6 @@ static int oaktrail_crtc_mode_set(struct drm_crtc *crtc,
else
dspcntr |= DISPPLANE_SEL_PIPE_B;
- dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE;
- dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE;
-
if (is_mipi)
goto oaktrail_crtc_mode_set_exit;
@@ -517,7 +505,7 @@ static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc,
return true;
}
-int oaktrail_pipe_set_base(struct drm_crtc *crtc,
+static int oaktrail_pipe_set_base(struct drm_crtc *crtc,
int x, int y, struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
diff --git a/drivers/gpu/drm/gma500/oaktrail_device.c b/drivers/gpu/drm/gma500/oaktrail_device.c
index 63aea2f010d9..41d1924ea31e 100644
--- a/drivers/gpu/drm/gma500/oaktrail_device.c
+++ b/drivers/gpu/drm/gma500/oaktrail_device.c
@@ -141,7 +141,7 @@ static const struct backlight_ops oaktrail_ops = {
.update_status = oaktrail_set_brightness,
};
-int oaktrail_backlight_init(struct drm_device *dev)
+static int oaktrail_backlight_init(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
int ret;
@@ -176,10 +176,6 @@ int oaktrail_backlight_init(struct drm_device *dev)
* for power management
*/
-static void oaktrail_init_pm(struct drm_device *dev)
-{
-}
-
/**
* oaktrail_save_display_registers - save registers lost on suspend
* @dev: our DRM device
@@ -190,81 +186,82 @@ static void oaktrail_init_pm(struct drm_device *dev)
static int oaktrail_save_display_registers(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_save_area *regs = &dev_priv->regs;
int i;
u32 pp_stat;
/* Display arbitration control + watermarks */
- dev_priv->saveDSPARB = PSB_RVDC32(DSPARB);
- dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1);
- dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2);
- dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3);
- dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4);
- dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5);
- dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6);
- dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
+ regs->psb.saveDSPARB = PSB_RVDC32(DSPARB);
+ regs->psb.saveDSPFW1 = PSB_RVDC32(DSPFW1);
+ regs->psb.saveDSPFW2 = PSB_RVDC32(DSPFW2);
+ regs->psb.saveDSPFW3 = PSB_RVDC32(DSPFW3);
+ regs->psb.saveDSPFW4 = PSB_RVDC32(DSPFW4);
+ regs->psb.saveDSPFW5 = PSB_RVDC32(DSPFW5);
+ regs->psb.saveDSPFW6 = PSB_RVDC32(DSPFW6);
+ regs->psb.saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
/* Pipe & plane A info */
- dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF);
- dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC);
- dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0);
- dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1);
- dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A);
- dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A);
- dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A);
- dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A);
- dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A);
- dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A);
- dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A);
- dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A);
- dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR);
- dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE);
- dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE);
- dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF);
- dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF);
- dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF);
+ regs->psb.savePIPEACONF = PSB_RVDC32(PIPEACONF);
+ regs->psb.savePIPEASRC = PSB_RVDC32(PIPEASRC);
+ regs->psb.saveFPA0 = PSB_RVDC32(MRST_FPA0);
+ regs->psb.saveFPA1 = PSB_RVDC32(MRST_FPA1);
+ regs->psb.saveDPLL_A = PSB_RVDC32(MRST_DPLL_A);
+ regs->psb.saveHTOTAL_A = PSB_RVDC32(HTOTAL_A);
+ regs->psb.saveHBLANK_A = PSB_RVDC32(HBLANK_A);
+ regs->psb.saveHSYNC_A = PSB_RVDC32(HSYNC_A);
+ regs->psb.saveVTOTAL_A = PSB_RVDC32(VTOTAL_A);
+ regs->psb.saveVBLANK_A = PSB_RVDC32(VBLANK_A);
+ regs->psb.saveVSYNC_A = PSB_RVDC32(VSYNC_A);
+ regs->psb.saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A);
+ regs->psb.saveDSPACNTR = PSB_RVDC32(DSPACNTR);
+ regs->psb.saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE);
+ regs->psb.saveDSPAADDR = PSB_RVDC32(DSPABASE);
+ regs->psb.saveDSPASURF = PSB_RVDC32(DSPASURF);
+ regs->psb.saveDSPALINOFF = PSB_RVDC32(DSPALINOFF);
+ regs->psb.saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF);
/* Save cursor regs */
- dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR);
- dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE);
- dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS);
+ regs->psb.saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR);
+ regs->psb.saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE);
+ regs->psb.saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS);
/* Save palette (gamma) */
for (i = 0; i < 256; i++)
- dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2));
+ regs->psb.save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2));
if (dev_priv->hdmi_priv)
oaktrail_hdmi_save(dev);
/* Save performance state */
- dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE);
+ regs->psb.savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE);
/* LVDS state */
- dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL);
- dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
- dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS);
- dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL);
- dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2);
- dev_priv->saveLVDS = PSB_RVDC32(LVDS);
- dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
- dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON);
- dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF);
- dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE);
+ regs->psb.savePP_CONTROL = PSB_RVDC32(PP_CONTROL);
+ regs->psb.savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
+ regs->psb.savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS);
+ regs->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL);
+ regs->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2);
+ regs->psb.saveLVDS = PSB_RVDC32(LVDS);
+ regs->psb.savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
+ regs->psb.savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON);
+ regs->psb.savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF);
+ regs->psb.savePP_DIVISOR = PSB_RVDC32(PP_CYCLE);
/* HW overlay */
- dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD);
- dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0);
- dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1);
- dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2);
- dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3);
- dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4);
- dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5);
+ regs->psb.saveOV_OVADD = PSB_RVDC32(OV_OVADD);
+ regs->psb.saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0);
+ regs->psb.saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1);
+ regs->psb.saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2);
+ regs->psb.saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3);
+ regs->psb.saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4);
+ regs->psb.saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5);
/* DPST registers */
- dev_priv->saveHISTOGRAM_INT_CONTROL_REG =
+ regs->psb.saveHISTOGRAM_INT_CONTROL_REG =
PSB_RVDC32(HISTOGRAM_INT_CONTROL);
- dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG =
+ regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG =
PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
- dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC);
+ regs->psb.savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC);
if (dev_priv->iLVDS_enable) {
/* Shut down the panel */
@@ -302,79 +299,80 @@ static int oaktrail_save_display_registers(struct drm_device *dev)
static int oaktrail_restore_display_registers(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_save_area *regs = &dev_priv->regs;
u32 pp_stat;
int i;
/* Display arbitration + watermarks */
- PSB_WVDC32(dev_priv->saveDSPARB, DSPARB);
- PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1);
- PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2);
- PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3);
- PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4);
- PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5);
- PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6);
- PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT);
+ PSB_WVDC32(regs->psb.saveDSPARB, DSPARB);
+ PSB_WVDC32(regs->psb.saveDSPFW1, DSPFW1);
+ PSB_WVDC32(regs->psb.saveDSPFW2, DSPFW2);
+ PSB_WVDC32(regs->psb.saveDSPFW3, DSPFW3);
+ PSB_WVDC32(regs->psb.saveDSPFW4, DSPFW4);
+ PSB_WVDC32(regs->psb.saveDSPFW5, DSPFW5);
+ PSB_WVDC32(regs->psb.saveDSPFW6, DSPFW6);
+ PSB_WVDC32(regs->psb.saveCHICKENBIT, DSPCHICKENBIT);
/* Make sure VGA plane is off. it initializes to on after reset!*/
PSB_WVDC32(0x80000000, VGACNTRL);
/* set the plls */
- PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0);
- PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1);
+ PSB_WVDC32(regs->psb.saveFPA0, MRST_FPA0);
+ PSB_WVDC32(regs->psb.saveFPA1, MRST_FPA1);
/* Actually enable it */
- PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A);
+ PSB_WVDC32(regs->psb.saveDPLL_A, MRST_DPLL_A);
DRM_UDELAY(150);
/* Restore mode */
- PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A);
- PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A);
- PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A);
- PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A);
- PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A);
- PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A);
- PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC);
- PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A);
+ PSB_WVDC32(regs->psb.saveHTOTAL_A, HTOTAL_A);
+ PSB_WVDC32(regs->psb.saveHBLANK_A, HBLANK_A);
+ PSB_WVDC32(regs->psb.saveHSYNC_A, HSYNC_A);
+ PSB_WVDC32(regs->psb.saveVTOTAL_A, VTOTAL_A);
+ PSB_WVDC32(regs->psb.saveVBLANK_A, VBLANK_A);
+ PSB_WVDC32(regs->psb.saveVSYNC_A, VSYNC_A);
+ PSB_WVDC32(regs->psb.savePIPEASRC, PIPEASRC);
+ PSB_WVDC32(regs->psb.saveBCLRPAT_A, BCLRPAT_A);
/* Restore performance mode*/
- PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE);
+ PSB_WVDC32(regs->psb.savePERF_MODE, MRST_PERF_MODE);
/* Enable the pipe*/
if (dev_priv->iLVDS_enable)
- PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF);
+ PSB_WVDC32(regs->psb.savePIPEACONF, PIPEACONF);
/* Set up the plane*/
- PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF);
- PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE);
- PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF);
+ PSB_WVDC32(regs->psb.saveDSPALINOFF, DSPALINOFF);
+ PSB_WVDC32(regs->psb.saveDSPASTRIDE, DSPASTRIDE);
+ PSB_WVDC32(regs->psb.saveDSPATILEOFF, DSPATILEOFF);
/* Enable the plane */
- PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR);
- PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF);
+ PSB_WVDC32(regs->psb.saveDSPACNTR, DSPACNTR);
+ PSB_WVDC32(regs->psb.saveDSPASURF, DSPASURF);
/* Enable Cursor A */
- PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR);
- PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS);
- PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE);
+ PSB_WVDC32(regs->psb.saveDSPACURSOR_CTRL, CURACNTR);
+ PSB_WVDC32(regs->psb.saveDSPACURSOR_POS, CURAPOS);
+ PSB_WVDC32(regs->psb.saveDSPACURSOR_BASE, CURABASE);
/* Restore palette (gamma) */
for (i = 0; i < 256; i++)
- PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2));
+ PSB_WVDC32(regs->psb.save_palette_a[i], PALETTE_A + (i << 2));
if (dev_priv->hdmi_priv)
oaktrail_hdmi_restore(dev);
if (dev_priv->iLVDS_enable) {
- PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2);
- PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/
- PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL);
- PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
- PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS);
- PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL);
- PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON);
- PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF);
- PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE);
- PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL);
+ PSB_WVDC32(regs->saveBLC_PWM_CTL2, BLC_PWM_CTL2);
+ PSB_WVDC32(regs->psb.saveLVDS, LVDS); /*port 61180h*/
+ PSB_WVDC32(regs->psb.savePFIT_CONTROL, PFIT_CONTROL);
+ PSB_WVDC32(regs->psb.savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
+ PSB_WVDC32(regs->psb.savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS);
+ PSB_WVDC32(regs->saveBLC_PWM_CTL, BLC_PWM_CTL);
+ PSB_WVDC32(regs->psb.savePP_ON_DELAYS, LVDSPP_ON);
+ PSB_WVDC32(regs->psb.savePP_OFF_DELAYS, LVDSPP_OFF);
+ PSB_WVDC32(regs->psb.savePP_DIVISOR, PP_CYCLE);
+ PSB_WVDC32(regs->psb.savePP_CONTROL, PP_CONTROL);
}
/* Wait for cycle delay */
@@ -388,20 +386,20 @@ static int oaktrail_restore_display_registers(struct drm_device *dev)
} while (pp_stat & 0x10000000);
/* Restore HW overlay */
- PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD);
- PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0);
- PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1);
- PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2);
- PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3);
- PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4);
- PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5);
+ PSB_WVDC32(regs->psb.saveOV_OVADD, OV_OVADD);
+ PSB_WVDC32(regs->psb.saveOV_OGAMC0, OV_OGAMC0);
+ PSB_WVDC32(regs->psb.saveOV_OGAMC1, OV_OGAMC1);
+ PSB_WVDC32(regs->psb.saveOV_OGAMC2, OV_OGAMC2);
+ PSB_WVDC32(regs->psb.saveOV_OGAMC3, OV_OGAMC3);
+ PSB_WVDC32(regs->psb.saveOV_OGAMC4, OV_OGAMC4);
+ PSB_WVDC32(regs->psb.saveOV_OGAMC5, OV_OGAMC5);
/* DPST registers */
- PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG,
+ PSB_WVDC32(regs->psb.saveHISTOGRAM_INT_CONTROL_REG,
HISTOGRAM_INT_CONTROL);
- PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG,
+ PSB_WVDC32(regs->psb.saveHISTOGRAM_LOGIC_CONTROL_REG,
HISTOGRAM_LOGIC_CONTROL);
- PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC);
+ PSB_WVDC32(regs->psb.savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC);
return 0;
}
@@ -502,7 +500,6 @@ const struct psb_ops oaktrail_chip_ops = {
.backlight_init = oaktrail_backlight_init,
#endif
- .init_pm = oaktrail_init_pm,
.save_regs = oaktrail_save_display_registers,
.restore_regs = oaktrail_restore_display_registers,
.power_down = oaktrail_power_down,
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi.c b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
index 025d30970cc0..f8b367b45f66 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi.c
@@ -125,59 +125,6 @@ static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = {
.nf = { .min = NF_MIN, .max = NF_MAX },
};
-static void wait_for_vblank(struct drm_device *dev)
-{
- /* FIXME: Can we do this as a sleep ? */
- /* Wait for 20ms, i.e. one cycle at 50hz. */
- mdelay(20);
-}
-
-static void scu_busy_loop(void *scu_base)
-{
- u32 status = 0;
- u32 loop_count = 0;
-
- status = readl(scu_base + 0x04);
- while (status & 1) {
- udelay(1); /* scu processing time is in few u secods */
- status = readl(scu_base + 0x04);
- loop_count++;
- /* break if scu doesn't reset busy bit after huge retry */
- if (loop_count > 1000) {
- DRM_DEBUG_KMS("SCU IPC timed out");
- return;
- }
- }
-}
-
-static void oaktrail_hdmi_reset(struct drm_device *dev)
-{
- void *base;
- /* FIXME: at least make these defines */
- unsigned int scu_ipc_mmio = 0xff11c000;
- int scu_len = 1024;
-
- base = ioremap((resource_size_t)scu_ipc_mmio, scu_len);
- if (base == NULL) {
- DRM_ERROR("failed to map SCU mmio\n");
- return;
- }
-
- /* scu ipc: assert hdmi controller reset */
- writel(0xff11d118, base + 0x0c);
- writel(0x7fffffdf, base + 0x80);
- writel(0x42005, base + 0x0);
- scu_busy_loop(base);
-
- /* scu ipc: de-assert hdmi controller reset */
- writel(0xff11d118, base + 0x0c);
- writel(0x7fffffff, base + 0x80);
- writel(0x42005, base + 0x0);
- scu_busy_loop(base);
-
- iounmap(base);
-}
-
static void oaktrail_hdmi_audio_enable(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
@@ -208,104 +155,6 @@ static void oaktrail_hdmi_audio_disable(struct drm_device *dev)
HDMI_READ(HDMI_HCR);
}
-void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode)
-{
- struct drm_device *dev = crtc->dev;
- u32 temp;
-
- switch (mode) {
- case DRM_MODE_DPMS_OFF:
- /* Disable VGACNTRL */
- REG_WRITE(VGACNTRL, 0x80000000);
-
- /* Disable plane */
- temp = REG_READ(DSPBCNTR);
- if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
- REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE);
- REG_READ(DSPBCNTR);
- /* Flush the plane changes */
- REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
- REG_READ(DSPBSURF);
- }
-
- /* Disable pipe B */
- temp = REG_READ(PIPEBCONF);
- if ((temp & PIPEACONF_ENABLE) != 0) {
- REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE);
- REG_READ(PIPEBCONF);
- }
-
- /* Disable LNW Pipes, etc */
- temp = REG_READ(PCH_PIPEBCONF);
- if ((temp & PIPEACONF_ENABLE) != 0) {
- REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE);
- REG_READ(PCH_PIPEBCONF);
- }
- /* wait for pipe off */
- udelay(150);
- /* Disable dpll */
- temp = REG_READ(DPLL_CTRL);
- if ((temp & DPLL_PWRDN) == 0) {
- REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET));
- REG_WRITE(DPLL_STATUS, 0x1);
- }
- /* wait for dpll off */
- udelay(150);
- break;
- case DRM_MODE_DPMS_ON:
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- /* Enable dpll */
- temp = REG_READ(DPLL_CTRL);
- if ((temp & DPLL_PWRDN) != 0) {
- REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET));
- temp = REG_READ(DPLL_CLK_ENABLE);
- REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI);
- REG_READ(DPLL_CLK_ENABLE);
- }
- /* wait for dpll warm up */
- udelay(150);
-
- /* Enable pipe B */
- temp = REG_READ(PIPEBCONF);
- if ((temp & PIPEACONF_ENABLE) == 0) {
- REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE);
- REG_READ(PIPEBCONF);
- }
-
- /* Enable LNW Pipe B */
- temp = REG_READ(PCH_PIPEBCONF);
- if ((temp & PIPEACONF_ENABLE) == 0) {
- REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE);
- REG_READ(PCH_PIPEBCONF);
- }
- wait_for_vblank(dev);
-
- /* Enable plane */
- temp = REG_READ(DSPBCNTR);
- if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
- REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE);
- /* Flush the plane changes */
- REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
- REG_READ(DSPBSURF);
- }
- psb_intel_crtc_load_lut(crtc);
- }
- /* DSPARB */
- REG_WRITE(DSPARB, 0x00003fbf);
- /* FW1 */
- REG_WRITE(0x70034, 0x3f880a0a);
- /* FW2 */
- REG_WRITE(0x70038, 0x0b060808);
- /* FW4 */
- REG_WRITE(0x70050, 0x08030404);
- /* FW5 */
- REG_WRITE(0x70054, 0x04040404);
- /* LNC Chicken Bits */
- REG_WRITE(0x70400, 0x4000);
-}
-
-
static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
{
static int dpms_mode = -1;
@@ -327,182 +176,6 @@ static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
HDMI_WRITE(HDMI_VIDEO_REG, temp);
}
-static unsigned int htotal_calculate(struct drm_display_mode *mode)
-{
- u32 htotal, new_crtc_htotal;
-
- htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16);
-
- /*
- * 1024 x 768 new_crtc_htotal = 0x1024;
- * 1280 x 1024 new_crtc_htotal = 0x0c34;
- */
- new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock;
-
- return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16);
-}
-
-static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target,
- int refclk, struct oaktrail_hdmi_clock *best_clock)
-{
- int np_min, np_max, nr_min, nr_max;
- int np, nr, nf;
-
- np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10);
- np_max = oaktrail_hdmi_limit.vco.max / (target * 10);
- if (np_min < oaktrail_hdmi_limit.np.min)
- np_min = oaktrail_hdmi_limit.np.min;
- if (np_max > oaktrail_hdmi_limit.np.max)
- np_max = oaktrail_hdmi_limit.np.max;
-
- nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max));
- nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min));
- if (nr_min < oaktrail_hdmi_limit.nr.min)
- nr_min = oaktrail_hdmi_limit.nr.min;
- if (nr_max > oaktrail_hdmi_limit.nr.max)
- nr_max = oaktrail_hdmi_limit.nr.max;
-
- np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max));
- nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np));
- nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk);
- DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf);
-
- /*
- * 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000;
- * 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000;
- */
- best_clock->np = np;
- best_clock->nr = nr - 1;
- best_clock->nf = (nf << 14);
-}
-
-int oaktrail_crtc_hdmi_mode_set(struct drm_crtc *crtc,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode,
- int x, int y,
- struct drm_framebuffer *old_fb)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_psb_private *dev_priv = dev->dev_private;
- struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
- int pipe = 1;
- int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
- int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
- int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
- int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
- int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
- int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
- int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
- int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
- int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
- int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
- int refclk;
- struct oaktrail_hdmi_clock clock;
- u32 dspcntr, pipeconf, dpll, temp;
- int dspcntr_reg = DSPBCNTR;
-
- /* Disable the VGA plane that we never use */
- REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
-
- /* XXX: Disable the panel fitter if it was on our pipe */
-
- /* Disable dpll if necessary */
- dpll = REG_READ(DPLL_CTRL);
- if ((dpll & DPLL_PWRDN) == 0) {
- REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET));
- REG_WRITE(DPLL_DIV_CTRL, 0x00000000);
- REG_WRITE(DPLL_STATUS, 0x1);
- }
- udelay(150);
-
- /* reset controller: FIXME - can we sort out the ioremap mess ? */
- iounmap(hdmi_dev->regs);
- oaktrail_hdmi_reset(dev);
-
- /* program and enable dpll */
- refclk = 25000;
- oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock);
-
- /* Setting DPLL */
- dpll = REG_READ(DPLL_CTRL);
- dpll &= ~DPLL_PDIV_MASK;
- dpll &= ~(DPLL_PWRDN | DPLL_RESET);
- REG_WRITE(DPLL_CTRL, 0x00000008);
- REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr));
- REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1));
- REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN));
- REG_WRITE(DPLL_UPDATE, 0x80000000);
- REG_WRITE(DPLL_CLK_ENABLE, 0x80050102);
- udelay(150);
-
- hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
- if (hdmi_dev->regs == NULL) {
- DRM_ERROR("failed to do hdmi mmio mapping\n");
- return -ENOMEM;
- }
-
- /* configure HDMI */
- HDMI_WRITE(0x1004, 0x1fd);
- HDMI_WRITE(0x2000, 0x1);
- HDMI_WRITE(0x2008, 0x0);
- HDMI_WRITE(0x3130, 0x8);
- HDMI_WRITE(0x101c, 0x1800810);
-
- temp = htotal_calculate(adjusted_mode);
- REG_WRITE(htot_reg, temp);
- REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
- REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
- REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
- REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
- REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
- REG_WRITE(pipesrc_reg,
- ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1));
-
- REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16));
- REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
- REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
- REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
- REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
- REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
- REG_WRITE(PCH_PIPEBSRC,
- ((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1));
-
- temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
- HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp);
-
- REG_WRITE(dspsize_reg,
- ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
- REG_WRITE(dsppos_reg, 0);
-
- /* Flush the plane changes */
- {
- struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
- crtc_funcs->mode_set_base(crtc, x, y, old_fb);
- }
-
- /* Set up the display plane register */
- dspcntr = REG_READ(dspcntr_reg);
- dspcntr |= DISPPLANE_GAMMA_ENABLE;
- dspcntr |= DISPPLANE_SEL_PIPE_B;
- dspcntr |= DISPLAY_PLANE_ENABLE;
-
- /* setup pipeconf */
- pipeconf = REG_READ(pipeconf_reg);
- pipeconf |= PIPEACONF_ENABLE;
-
- REG_WRITE(pipeconf_reg, pipeconf);
- REG_READ(pipeconf_reg);
-
- REG_WRITE(PCH_PIPEBCONF, pipeconf);
- REG_READ(PCH_PIPEBCONF);
- wait_for_vblank(dev);
-
- REG_WRITE(dspcntr_reg, dspcntr);
- wait_for_vblank(dev);
-
- return 0;
-}
-
static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
@@ -692,7 +365,7 @@ failed_connector:
static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) },
- {}
+ { 0 }
};
void oaktrail_hdmi_setup(struct drm_device *dev)
@@ -766,6 +439,7 @@ void oaktrail_hdmi_save(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+ struct psb_state *regs = &dev_priv->regs.psb;
int i;
/* dpll */
@@ -776,14 +450,14 @@ void oaktrail_hdmi_save(struct drm_device *dev)
hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE);
/* pipe B */
- dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF);
- dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC);
- dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B);
- dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B);
- dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B);
- dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B);
- dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B);
- dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B);
+ regs->savePIPEBCONF = PSB_RVDC32(PIPEBCONF);
+ regs->savePIPEBSRC = PSB_RVDC32(PIPEBSRC);
+ regs->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B);
+ regs->saveHBLANK_B = PSB_RVDC32(HBLANK_B);
+ regs->saveHSYNC_B = PSB_RVDC32(HSYNC_B);
+ regs->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B);
+ regs->saveVBLANK_B = PSB_RVDC32(VBLANK_B);
+ regs->saveVSYNC_B = PSB_RVDC32(VSYNC_B);
hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF);
hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC);
@@ -795,21 +469,21 @@ void oaktrail_hdmi_save(struct drm_device *dev)
hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B);
/* plane */
- dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR);
- dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE);
- dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE);
- dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF);
- dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF);
- dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF);
+ regs->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR);
+ regs->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE);
+ regs->saveDSPBADDR = PSB_RVDC32(DSPBBASE);
+ regs->saveDSPBSURF = PSB_RVDC32(DSPBSURF);
+ regs->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF);
+ regs->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF);
/* cursor B */
- dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
- dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
- dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
+ regs->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
+ regs->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
+ regs->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
/* save palette */
for (i = 0; i < 256; i++)
- dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2));
+ regs->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2));
}
/* restore HDMI register state */
@@ -817,6 +491,7 @@ void oaktrail_hdmi_restore(struct drm_device *dev)
{
struct drm_psb_private *dev_priv = dev->dev_private;
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
+ struct psb_state *regs = &dev_priv->regs.psb;
int i;
/* dpll */
@@ -828,13 +503,13 @@ void oaktrail_hdmi_restore(struct drm_device *dev)
DRM_UDELAY(150);
/* pipe */
- PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC);
- PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B);
- PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B);
- PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B);
- PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B);
- PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B);
- PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B);
+ PSB_WVDC32(regs->savePIPEBSRC, PIPEBSRC);
+ PSB_WVDC32(regs->saveHTOTAL_B, HTOTAL_B);
+ PSB_WVDC32(regs->saveHBLANK_B, HBLANK_B);
+ PSB_WVDC32(regs->saveHSYNC_B, HSYNC_B);
+ PSB_WVDC32(regs->saveVTOTAL_B, VTOTAL_B);
+ PSB_WVDC32(regs->saveVBLANK_B, VBLANK_B);
+ PSB_WVDC32(regs->saveVSYNC_B, VSYNC_B);
PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC);
PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B);
@@ -844,22 +519,22 @@ void oaktrail_hdmi_restore(struct drm_device *dev)
PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B);
PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B);
- PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF);
+ PSB_WVDC32(regs->savePIPEBCONF, PIPEBCONF);
PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF);
/* plane */
- PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF);
- PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE);
- PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF);
- PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR);
- PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF);
+ PSB_WVDC32(regs->saveDSPBLINOFF, DSPBLINOFF);
+ PSB_WVDC32(regs->saveDSPBSTRIDE, DSPBSTRIDE);
+ PSB_WVDC32(regs->saveDSPBTILEOFF, DSPBTILEOFF);
+ PSB_WVDC32(regs->saveDSPBCNTR, DSPBCNTR);
+ PSB_WVDC32(regs->saveDSPBSURF, DSPBSURF);
/* cursor B */
- PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR);
- PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS);
- PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE);
+ PSB_WVDC32(regs->saveDSPBCURSOR_CTRL, CURBCNTR);
+ PSB_WVDC32(regs->saveDSPBCURSOR_POS, CURBPOS);
+ PSB_WVDC32(regs->saveDSPBCURSOR_BASE, CURBBASE);
/* restore palette */
for (i = 0; i < 256; i++)
- PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2));
+ PSB_WVDC32(regs->save_palette_b[i], PALETTE_B + (i << 2));
}
diff --git a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
index 705440874ac0..5e84fbde749b 100644
--- a/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
+++ b/drivers/gpu/drm/gma500/oaktrail_hdmi_i2c.c
@@ -127,7 +127,7 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
{
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
- int i, err = 0;
+ int i;
mutex_lock(&i2c_dev->i2c_lock);
@@ -139,9 +139,9 @@ static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
for (i = 0; i < num; i++) {
if (pmsg->len && pmsg->buf) {
if (pmsg->flags & I2C_M_RD)
- err = xfer_read(adap, pmsg);
+ xfer_read(adap, pmsg);
else
- err = xfer_write(adap, pmsg);
+ xfer_write(adap, pmsg);
}
pmsg++; /* next message */
}
diff --git a/drivers/gpu/drm/gma500/oaktrail_lvds.c b/drivers/gpu/drm/gma500/oaktrail_lvds.c
index 238bbe105304..654f32b22b21 100644
--- a/drivers/gpu/drm/gma500/oaktrail_lvds.c
+++ b/drivers/gpu/drm/gma500/oaktrail_lvds.c
@@ -192,7 +192,7 @@ static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev)
gma_power_end(dev);
} else
- ret = ((dev_priv->saveBLC_PWM_CTL &
+ ret = ((dev_priv->regs.saveBLC_PWM_CTL &
BACKLIGHT_MODULATION_FREQ_MASK) >>
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
@@ -331,7 +331,6 @@ void oaktrail_lvds_init(struct drm_device *dev,
struct drm_encoder *encoder;
struct drm_psb_private *dev_priv = dev->dev_private;
struct edid *edid;
- int ret = 0;
struct i2c_adapter *i2c_adap;
struct drm_display_mode *scan; /* *modes, *bios_mode; */
@@ -400,7 +399,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
if (edid) {
drm_mode_connector_update_edid_property(connector,
edid);
- ret = drm_add_edid_modes(connector, edid);
+ drm_add_edid_modes(connector, edid);
kfree(edid);
}
diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c
index 94025693bae1..889b854751da 100644
--- a/drivers/gpu/drm/gma500/power.c
+++ b/drivers/gpu/drm/gma500/power.c
@@ -58,7 +58,8 @@ void gma_power_init(struct drm_device *dev)
spin_lock_init(&power_ctrl_lock);
mutex_init(&power_mutex);
- dev_priv->ops->init_pm(dev);
+ if (dev_priv->ops->init_pm)
+ dev_priv->ops->init_pm(dev);
}
/**
@@ -101,9 +102,6 @@ static void gma_resume_display(struct pci_dev *pdev)
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_psb_private *dev_priv = dev->dev_private;
- if (dev_priv->suspended == false)
- return;
-
/* turn on the display power island */
dev_priv->ops->power_up(dev);
dev_priv->suspended = false;
@@ -132,9 +130,9 @@ static void gma_suspend_pci(struct pci_dev *pdev)
pci_save_state(pdev);
pci_read_config_dword(pdev, 0x5C, &bsm);
- dev_priv->saveBSM = bsm;
+ dev_priv->regs.saveBSM = bsm;
pci_read_config_dword(pdev, 0xFC, &vbt);
- dev_priv->saveVBT = vbt;
+ dev_priv->regs.saveVBT = vbt;
pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr);
pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data);
@@ -162,8 +160,8 @@ static bool gma_resume_pci(struct pci_dev *pdev)
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM);
- pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT);
+ pci_write_config_dword(pdev, 0x5c, dev_priv->regs.saveBSM);
+ pci_write_config_dword(pdev, 0xFC, dev_priv->regs.saveVBT);
/* restoring MSI address and data in PCIx space */
pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr);
pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data);
@@ -195,6 +193,7 @@ int gma_power_suspend(struct device *_dev)
if (!dev_priv->suspended) {
if (dev_priv->display_count) {
mutex_unlock(&power_mutex);
+ dev_err(dev->dev, "GPU hardware busy, cannot suspend\n");
return -EBUSY;
}
psb_irq_uninstall(dev);
@@ -302,7 +301,7 @@ int psb_runtime_suspend(struct device *dev)
int psb_runtime_resume(struct device *dev)
{
- return gma_power_resume(dev);;
+ return gma_power_resume(dev);
}
int psb_runtime_idle(struct device *dev)
diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c
index e5f5906172b0..95d163e4f1f4 100644
--- a/drivers/gpu/drm/gma500/psb_device.c
+++ b/drivers/gpu/drm/gma500/psb_device.c
@@ -177,16 +177,17 @@ static int psb_save_display_registers(struct drm_device *dev)
struct drm_psb_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc;
struct drm_connector *connector;
+ struct psb_state *regs = &dev_priv->regs.psb;
/* Display arbitration control + watermarks */
- dev_priv->saveDSPARB = PSB_RVDC32(DSPARB);
- dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1);
- dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2);
- dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3);
- dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4);
- dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5);
- dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6);
- dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
+ regs->saveDSPARB = PSB_RVDC32(DSPARB);
+ regs->saveDSPFW1 = PSB_RVDC32(DSPFW1);
+ regs->saveDSPFW2 = PSB_RVDC32(DSPFW2);
+ regs->saveDSPFW3 = PSB_RVDC32(DSPFW3);
+ regs->saveDSPFW4 = PSB_RVDC32(DSPFW4);
+ regs->saveDSPFW5 = PSB_RVDC32(DSPFW5);
+ regs->saveDSPFW6 = PSB_RVDC32(DSPFW6);
+ regs->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
/* Save crtc and output state */
mutex_lock(&dev->mode_config.mutex);
@@ -213,16 +214,17 @@ static int psb_restore_display_registers(struct drm_device *dev)
struct drm_psb_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc;
struct drm_connector *connector;
+ struct psb_state *regs = &dev_priv->regs.psb;
/* Display arbitration + watermarks */
- PSB_WVDC32(dev_priv->saveDSPARB, DSPARB);
- PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1);
- PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2);
- PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3);
- PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4);
- PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5);
- PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6);
- PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT);
+ PSB_WVDC32(regs->saveDSPARB, DSPARB);
+ PSB_WVDC32(regs->saveDSPFW1, DSPFW1);
+ PSB_WVDC32(regs->saveDSPFW2, DSPFW2);
+ PSB_WVDC32(regs->saveDSPFW3, DSPFW3);
+ PSB_WVDC32(regs->saveDSPFW4, DSPFW4);
+ PSB_WVDC32(regs->saveDSPFW5, DSPFW5);
+ PSB_WVDC32(regs->saveDSPFW6, DSPFW6);
+ PSB_WVDC32(regs->saveCHICKENBIT, DSPCHICKENBIT);
/*make sure VGA plane is off. it initializes to on after reset!*/
PSB_WVDC32(0x80000000, VGACNTRL);
diff --git a/drivers/gpu/drm/gma500/psb_drv.c b/drivers/gpu/drm/gma500/psb_drv.c
index f14768f2b364..c34adf9d910a 100644
--- a/drivers/gpu/drm/gma500/psb_drv.c
+++ b/drivers/gpu/drm/gma500/psb_drv.c
@@ -60,6 +60,16 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
/* Atom E620 */
{ 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
#endif
+#if defined(CONFIG_DRM_MEDFIELD)
+ {0x8086, 0x0130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0133, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0134, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0135, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+ {0x8086, 0x0137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &mdfld_chip_ops},
+#endif
#if defined(CONFIG_DRM_GMA3600)
{ 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
@@ -70,7 +80,7 @@ static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
{ 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
{ 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
#endif
- { 0, 0, 0}
+ { 0, }
};
MODULE_DEVICE_TABLE(pci, pciidlist);
@@ -78,27 +88,27 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
* Standard IOCTLs.
*/
-#define DRM_IOCTL_PSB_ADB \
+#define DRM_IOCTL_GMA_ADB \
DRM_IOWR(DRM_GMA_ADB + DRM_COMMAND_BASE, uint32_t)
-#define DRM_IOCTL_PSB_MODE_OPERATION \
+#define DRM_IOCTL_GMA_MODE_OPERATION \
DRM_IOWR(DRM_GMA_MODE_OPERATION + DRM_COMMAND_BASE, \
struct drm_psb_mode_operation_arg)
-#define DRM_IOCTL_PSB_STOLEN_MEMORY \
+#define DRM_IOCTL_GMA_STOLEN_MEMORY \
DRM_IOWR(DRM_GMA_STOLEN_MEMORY + DRM_COMMAND_BASE, \
struct drm_psb_stolen_memory_arg)
-#define DRM_IOCTL_PSB_GAMMA \
+#define DRM_IOCTL_GMA_GAMMA \
DRM_IOWR(DRM_GMA_GAMMA + DRM_COMMAND_BASE, \
struct drm_psb_dpst_lut_arg)
-#define DRM_IOCTL_PSB_DPST_BL \
+#define DRM_IOCTL_GMA_DPST_BL \
DRM_IOWR(DRM_GMA_DPST_BL + DRM_COMMAND_BASE, \
uint32_t)
-#define DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID \
+#define DRM_IOCTL_GMA_GET_PIPE_FROM_CRTC_ID \
DRM_IOWR(DRM_GMA_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \
struct drm_psb_get_pipe_from_crtc_id_arg)
-#define DRM_IOCTL_PSB_GEM_CREATE \
+#define DRM_IOCTL_GMA_GEM_CREATE \
DRM_IOWR(DRM_GMA_GEM_CREATE + DRM_COMMAND_BASE, \
struct drm_psb_gem_create)
-#define DRM_IOCTL_PSB_GEM_MMAP \
+#define DRM_IOCTL_GMA_GEM_MMAP \
DRM_IOWR(DRM_GMA_GEM_MMAP + DRM_COMMAND_BASE, \
struct drm_psb_gem_mmap)
@@ -113,22 +123,19 @@ static int psb_gamma_ioctl(struct drm_device *dev, void *data,
static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-#define PSB_IOCTL_DEF(ioctl, func, flags) \
- [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func}
-
static struct drm_ioctl_desc psb_ioctls[] = {
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_ADB, psb_adb_ioctl, DRM_AUTH),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_MODE_OPERATION, psb_mode_operation_ioctl,
+ DRM_IOCTL_DEF_DRV(GMA_ADB, psb_adb_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(GMA_MODE_OPERATION, psb_mode_operation_ioctl,
DRM_AUTH),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_STOLEN_MEMORY, psb_stolen_memory_ioctl,
+ DRM_IOCTL_DEF_DRV(GMA_STOLEN_MEMORY, psb_stolen_memory_ioctl,
DRM_AUTH),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_GAMMA, psb_gamma_ioctl, DRM_AUTH),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID,
+ DRM_IOCTL_DEF_DRV(GMA_GAMMA, psb_gamma_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(GMA_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(GMA_GET_PIPE_FROM_CRTC_ID,
psb_intel_get_pipe_from_crtc_id, 0),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_CREATE, psb_gem_create_ioctl,
+ DRM_IOCTL_DEF_DRV(GMA_GEM_CREATE, psb_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
- PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_MMAP, psb_gem_mmap_ioctl,
+ DRM_IOCTL_DEF_DRV(GMA_GEM_MMAP, psb_gem_mmap_ioctl,
DRM_UNLOCKED | DRM_AUTH),
};
@@ -268,10 +275,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
{
struct drm_psb_private *dev_priv;
unsigned long resource_start;
- struct psb_gtt *pg;
unsigned long irqflags;
int ret = -ENOMEM;
- uint32_t tt_pages;
struct drm_connector *connector;
struct psb_intel_encoder *psb_intel_encoder;
@@ -283,6 +288,8 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->dev = dev;
dev->dev_private = (void *) dev_priv;
+ pci_set_master(dev->pdev);
+
if (!IS_PSB(dev)) {
if (pci_enable_msi(dev->pdev))
dev_warn(dev->dev, "Enabling MSI failed!\n");
@@ -327,12 +334,6 @@ static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
if (!dev_priv->mmu)
goto out_err;
- pg = &dev_priv->gtt;
-
- tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ?
- (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT;
-
-
dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0);
if (!dev_priv->pf_pd)
goto out_err;
@@ -409,7 +410,7 @@ out_err:
return ret;
}
-int psb_driver_device_is_agp(struct drm_device *dev)
+static int psb_driver_device_is_agp(struct drm_device *dev)
{
return 0;
}
@@ -600,7 +601,7 @@ static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd,
/* When a client dies:
* - Check for and clean up flipped page state
*/
-void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv)
+static void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv)
{
}
@@ -677,7 +678,9 @@ static struct pci_driver psb_pci_driver = {
.id_table = pciidlist,
.probe = psb_probe,
.remove = psb_remove,
- .driver.pm = &psb_pm_ops,
+ .driver = {
+ .pm = &psb_pm_ops,
+ }
};
static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
index eb1568a0da95..40ce2c9bc2e4 100644
--- a/drivers/gpu/drm/gma500/psb_drv.h
+++ b/drivers/gpu/drm/gma500/psb_drv.h
@@ -276,6 +276,217 @@ struct intel_gmbus {
u32 reg0;
};
+/*
+ * Register save state. This is used to hold the context when the
+ * device is powered off. In the case of Oaktrail this can (but does not
+ * yet) include screen blank. Operations occuring during the save
+ * update the register cache instead.
+ */
+struct psb_state {
+ uint32_t saveDSPACNTR;
+ uint32_t saveDSPBCNTR;
+ uint32_t savePIPEACONF;
+ uint32_t savePIPEBCONF;
+ uint32_t savePIPEASRC;
+ uint32_t savePIPEBSRC;
+ uint32_t saveFPA0;
+ uint32_t saveFPA1;
+ uint32_t saveDPLL_A;
+ uint32_t saveDPLL_A_MD;
+ uint32_t saveHTOTAL_A;
+ uint32_t saveHBLANK_A;
+ uint32_t saveHSYNC_A;
+ uint32_t saveVTOTAL_A;
+ uint32_t saveVBLANK_A;
+ uint32_t saveVSYNC_A;
+ uint32_t saveDSPASTRIDE;
+ uint32_t saveDSPASIZE;
+ uint32_t saveDSPAPOS;
+ uint32_t saveDSPABASE;
+ uint32_t saveDSPASURF;
+ uint32_t saveDSPASTATUS;
+ uint32_t saveFPB0;
+ uint32_t saveFPB1;
+ uint32_t saveDPLL_B;
+ uint32_t saveDPLL_B_MD;
+ uint32_t saveHTOTAL_B;
+ uint32_t saveHBLANK_B;
+ uint32_t saveHSYNC_B;
+ uint32_t saveVTOTAL_B;
+ uint32_t saveVBLANK_B;
+ uint32_t saveVSYNC_B;
+ uint32_t saveDSPBSTRIDE;
+ uint32_t saveDSPBSIZE;
+ uint32_t saveDSPBPOS;
+ uint32_t saveDSPBBASE;
+ uint32_t saveDSPBSURF;
+ uint32_t saveDSPBSTATUS;
+ uint32_t saveVCLK_DIVISOR_VGA0;
+ uint32_t saveVCLK_DIVISOR_VGA1;
+ uint32_t saveVCLK_POST_DIV;
+ uint32_t saveVGACNTRL;
+ uint32_t saveADPA;
+ uint32_t saveLVDS;
+ uint32_t saveDVOA;
+ uint32_t saveDVOB;
+ uint32_t saveDVOC;
+ uint32_t savePP_ON;
+ uint32_t savePP_OFF;
+ uint32_t savePP_CONTROL;
+ uint32_t savePP_CYCLE;
+ uint32_t savePFIT_CONTROL;
+ uint32_t savePaletteA[256];
+ uint32_t savePaletteB[256];
+ uint32_t saveCLOCKGATING;
+ uint32_t saveDSPARB;
+ uint32_t saveDSPATILEOFF;
+ uint32_t saveDSPBTILEOFF;
+ uint32_t saveDSPAADDR;
+ uint32_t saveDSPBADDR;
+ uint32_t savePFIT_AUTO_RATIOS;
+ uint32_t savePFIT_PGM_RATIOS;
+ uint32_t savePP_ON_DELAYS;
+ uint32_t savePP_OFF_DELAYS;
+ uint32_t savePP_DIVISOR;
+ uint32_t saveBCLRPAT_A;
+ uint32_t saveBCLRPAT_B;
+ uint32_t saveDSPALINOFF;
+ uint32_t saveDSPBLINOFF;
+ uint32_t savePERF_MODE;
+ uint32_t saveDSPFW1;
+ uint32_t saveDSPFW2;
+ uint32_t saveDSPFW3;
+ uint32_t saveDSPFW4;
+ uint32_t saveDSPFW5;
+ uint32_t saveDSPFW6;
+ uint32_t saveCHICKENBIT;
+ uint32_t saveDSPACURSOR_CTRL;
+ uint32_t saveDSPBCURSOR_CTRL;
+ uint32_t saveDSPACURSOR_BASE;
+ uint32_t saveDSPBCURSOR_BASE;
+ uint32_t saveDSPACURSOR_POS;
+ uint32_t saveDSPBCURSOR_POS;
+ uint32_t save_palette_a[256];
+ uint32_t save_palette_b[256];
+ uint32_t saveOV_OVADD;
+ uint32_t saveOV_OGAMC0;
+ uint32_t saveOV_OGAMC1;
+ uint32_t saveOV_OGAMC2;
+ uint32_t saveOV_OGAMC3;
+ uint32_t saveOV_OGAMC4;
+ uint32_t saveOV_OGAMC5;
+ uint32_t saveOVC_OVADD;
+ uint32_t saveOVC_OGAMC0;
+ uint32_t saveOVC_OGAMC1;
+ uint32_t saveOVC_OGAMC2;
+ uint32_t saveOVC_OGAMC3;
+ uint32_t saveOVC_OGAMC4;
+ uint32_t saveOVC_OGAMC5;
+
+ /* DPST register save */
+ uint32_t saveHISTOGRAM_INT_CONTROL_REG;
+ uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG;
+ uint32_t savePWM_CONTROL_LOGIC;
+};
+
+struct medfield_state {
+ uint32_t saveDPLL_A;
+ uint32_t saveFPA0;
+ uint32_t savePIPEACONF;
+ uint32_t saveHTOTAL_A;
+ uint32_t saveHBLANK_A;
+ uint32_t saveHSYNC_A;
+ uint32_t saveVTOTAL_A;
+ uint32_t saveVBLANK_A;
+ uint32_t saveVSYNC_A;
+ uint32_t savePIPEASRC;
+ uint32_t saveDSPASTRIDE;
+ uint32_t saveDSPALINOFF;
+ uint32_t saveDSPATILEOFF;
+ uint32_t saveDSPASIZE;
+ uint32_t saveDSPAPOS;
+ uint32_t saveDSPASURF;
+ uint32_t saveDSPACNTR;
+ uint32_t saveDSPASTATUS;
+ uint32_t save_palette_a[256];
+ uint32_t saveMIPI;
+
+ uint32_t saveDPLL_B;
+ uint32_t saveFPB0;
+ uint32_t savePIPEBCONF;
+ uint32_t saveHTOTAL_B;
+ uint32_t saveHBLANK_B;
+ uint32_t saveHSYNC_B;
+ uint32_t saveVTOTAL_B;
+ uint32_t saveVBLANK_B;
+ uint32_t saveVSYNC_B;
+ uint32_t savePIPEBSRC;
+ uint32_t saveDSPBSTRIDE;
+ uint32_t saveDSPBLINOFF;
+ uint32_t saveDSPBTILEOFF;
+ uint32_t saveDSPBSIZE;
+ uint32_t saveDSPBPOS;
+ uint32_t saveDSPBSURF;
+ uint32_t saveDSPBCNTR;
+ uint32_t saveDSPBSTATUS;
+ uint32_t save_palette_b[256];
+
+ uint32_t savePIPECCONF;
+ uint32_t saveHTOTAL_C;
+ uint32_t saveHBLANK_C;
+ uint32_t saveHSYNC_C;
+ uint32_t saveVTOTAL_C;
+ uint32_t saveVBLANK_C;
+ uint32_t saveVSYNC_C;
+ uint32_t savePIPECSRC;
+ uint32_t saveDSPCSTRIDE;
+ uint32_t saveDSPCLINOFF;
+ uint32_t saveDSPCTILEOFF;
+ uint32_t saveDSPCSIZE;
+ uint32_t saveDSPCPOS;
+ uint32_t saveDSPCSURF;
+ uint32_t saveDSPCCNTR;
+ uint32_t saveDSPCSTATUS;
+ uint32_t save_palette_c[256];
+ uint32_t saveMIPI_C;
+
+ uint32_t savePFIT_CONTROL;
+ uint32_t savePFIT_PGM_RATIOS;
+ uint32_t saveHDMIPHYMISCCTL;
+ uint32_t saveHDMIB_CONTROL;
+};
+
+struct cdv_state {
+ uint32_t saveDSPCLK_GATE_D;
+ uint32_t saveRAMCLK_GATE_D;
+ uint32_t saveDSPARB;
+ uint32_t saveDSPFW[6];
+ uint32_t saveADPA;
+ uint32_t savePP_CONTROL;
+ uint32_t savePFIT_PGM_RATIOS;
+ uint32_t saveLVDS;
+ uint32_t savePFIT_CONTROL;
+ uint32_t savePP_ON_DELAYS;
+ uint32_t savePP_OFF_DELAYS;
+ uint32_t savePP_CYCLE;
+ uint32_t saveVGACNTRL;
+ uint32_t saveIER;
+ uint32_t saveIMR;
+ u8 saveLBB;
+};
+
+struct psb_save_area {
+ uint32_t saveBSM;
+ uint32_t saveVBT;
+ union {
+ struct psb_state psb;
+ struct medfield_state mdfld;
+ struct cdv_state cdv;
+ };
+ uint32_t saveBLC_PWM_CTL2;
+ uint32_t saveBLC_PWM_CTL;
+};
+
struct psb_ops;
#define PSB_NUM_PIPE 3
@@ -397,216 +608,21 @@ struct drm_psb_private {
struct oaktrail_vbt vbt_data;
struct oaktrail_gct_data gct_data;
- /* MIPI Panel type etc */
- int panel_id;
- bool dual_mipi; /* dual display - DPI & DBI */
- bool dpi_panel_on; /* The DPI panel power is on */
- bool dpi_panel_on2; /* The DPI panel power is on */
- bool dbi_panel_on; /* The DBI panel power is on */
- bool dbi_panel_on2; /* The DBI panel power is on */
- u32 dsr_fb_update; /* DSR FB update counter */
-
- /* Moorestown HDMI state */
+ /* Oaktrail HDMI state */
struct oaktrail_hdmi_dev *hdmi_priv;
-
- /* Moorestown pipe config register value cache */
- uint32_t pipeconf;
- uint32_t pipeconf1;
- uint32_t pipeconf2;
-
- /* Moorestown plane control register value cache */
- uint32_t dspcntr;
- uint32_t dspcntr1;
- uint32_t dspcntr2;
-
- /* Moorestown MM backlight cache */
- uint8_t saveBKLTCNT;
- uint8_t saveBKLTREQ;
- uint8_t saveBKLTBRTL;
-
+
/*
* Register state
*/
- uint32_t saveDSPACNTR;
- uint32_t saveDSPBCNTR;
- uint32_t savePIPEACONF;
- uint32_t savePIPEBCONF;
- uint32_t savePIPEASRC;
- uint32_t savePIPEBSRC;
- uint32_t saveFPA0;
- uint32_t saveFPA1;
- uint32_t saveDPLL_A;
- uint32_t saveDPLL_A_MD;
- uint32_t saveHTOTAL_A;
- uint32_t saveHBLANK_A;
- uint32_t saveHSYNC_A;
- uint32_t saveVTOTAL_A;
- uint32_t saveVBLANK_A;
- uint32_t saveVSYNC_A;
- uint32_t saveDSPASTRIDE;
- uint32_t saveDSPASIZE;
- uint32_t saveDSPAPOS;
- uint32_t saveDSPABASE;
- uint32_t saveDSPASURF;
- uint32_t saveDSPASTATUS;
- uint32_t saveFPB0;
- uint32_t saveFPB1;
- uint32_t saveDPLL_B;
- uint32_t saveDPLL_B_MD;
- uint32_t saveHTOTAL_B;
- uint32_t saveHBLANK_B;
- uint32_t saveHSYNC_B;
- uint32_t saveVTOTAL_B;
- uint32_t saveVBLANK_B;
- uint32_t saveVSYNC_B;
- uint32_t saveDSPBSTRIDE;
- uint32_t saveDSPBSIZE;
- uint32_t saveDSPBPOS;
- uint32_t saveDSPBBASE;
- uint32_t saveDSPBSURF;
- uint32_t saveDSPBSTATUS;
- uint32_t saveVCLK_DIVISOR_VGA0;
- uint32_t saveVCLK_DIVISOR_VGA1;
- uint32_t saveVCLK_POST_DIV;
- uint32_t saveVGACNTRL;
- uint32_t saveADPA;
- uint32_t saveLVDS;
- uint32_t saveDVOA;
- uint32_t saveDVOB;
- uint32_t saveDVOC;
- uint32_t savePP_ON;
- uint32_t savePP_OFF;
- uint32_t savePP_CONTROL;
- uint32_t savePP_CYCLE;
- uint32_t savePFIT_CONTROL;
- uint32_t savePaletteA[256];
- uint32_t savePaletteB[256];
- uint32_t saveBLC_PWM_CTL2;
- uint32_t saveBLC_PWM_CTL;
- uint32_t saveCLOCKGATING;
- uint32_t saveDSPARB;
- uint32_t saveDSPATILEOFF;
- uint32_t saveDSPBTILEOFF;
- uint32_t saveDSPAADDR;
- uint32_t saveDSPBADDR;
- uint32_t savePFIT_AUTO_RATIOS;
- uint32_t savePFIT_PGM_RATIOS;
- uint32_t savePP_ON_DELAYS;
- uint32_t savePP_OFF_DELAYS;
- uint32_t savePP_DIVISOR;
- uint32_t saveBSM;
- uint32_t saveVBT;
- uint32_t saveBCLRPAT_A;
- uint32_t saveBCLRPAT_B;
- uint32_t saveDSPALINOFF;
- uint32_t saveDSPBLINOFF;
- uint32_t savePERF_MODE;
- uint32_t saveDSPFW1;
- uint32_t saveDSPFW2;
- uint32_t saveDSPFW3;
- uint32_t saveDSPFW4;
- uint32_t saveDSPFW5;
- uint32_t saveDSPFW6;
- uint32_t saveCHICKENBIT;
- uint32_t saveDSPACURSOR_CTRL;
- uint32_t saveDSPBCURSOR_CTRL;
- uint32_t saveDSPACURSOR_BASE;
- uint32_t saveDSPBCURSOR_BASE;
- uint32_t saveDSPACURSOR_POS;
- uint32_t saveDSPBCURSOR_POS;
- uint32_t save_palette_a[256];
- uint32_t save_palette_b[256];
- uint32_t saveOV_OVADD;
- uint32_t saveOV_OGAMC0;
- uint32_t saveOV_OGAMC1;
- uint32_t saveOV_OGAMC2;
- uint32_t saveOV_OGAMC3;
- uint32_t saveOV_OGAMC4;
- uint32_t saveOV_OGAMC5;
- uint32_t saveOVC_OVADD;
- uint32_t saveOVC_OGAMC0;
- uint32_t saveOVC_OGAMC1;
- uint32_t saveOVC_OGAMC2;
- uint32_t saveOVC_OGAMC3;
- uint32_t saveOVC_OGAMC4;
- uint32_t saveOVC_OGAMC5;
+
+ struct psb_save_area regs;
/* MSI reg save */
uint32_t msi_addr;
uint32_t msi_data;
- /* Medfield specific register save state */
- uint32_t saveHDMIPHYMISCCTL;
- uint32_t saveHDMIB_CONTROL;
- uint32_t saveDSPCCNTR;
- uint32_t savePIPECCONF;
- uint32_t savePIPECSRC;
- uint32_t saveHTOTAL_C;
- uint32_t saveHBLANK_C;
- uint32_t saveHSYNC_C;
- uint32_t saveVTOTAL_C;
- uint32_t saveVBLANK_C;
- uint32_t saveVSYNC_C;
- uint32_t saveDSPCSTRIDE;
- uint32_t saveDSPCSIZE;
- uint32_t saveDSPCPOS;
- uint32_t saveDSPCSURF;
- uint32_t saveDSPCSTATUS;
- uint32_t saveDSPCLINOFF;
- uint32_t saveDSPCTILEOFF;
- uint32_t saveDSPCCURSOR_CTRL;
- uint32_t saveDSPCCURSOR_BASE;
- uint32_t saveDSPCCURSOR_POS;
- uint32_t save_palette_c[256];
- uint32_t saveOV_OVADD_C;
- uint32_t saveOV_OGAMC0_C;
- uint32_t saveOV_OGAMC1_C;
- uint32_t saveOV_OGAMC2_C;
- uint32_t saveOV_OGAMC3_C;
- uint32_t saveOV_OGAMC4_C;
- uint32_t saveOV_OGAMC5_C;
-
- /* DSI register save */
- uint32_t saveDEVICE_READY_REG;
- uint32_t saveINTR_EN_REG;
- uint32_t saveDSI_FUNC_PRG_REG;
- uint32_t saveHS_TX_TIMEOUT_REG;
- uint32_t saveLP_RX_TIMEOUT_REG;
- uint32_t saveTURN_AROUND_TIMEOUT_REG;
- uint32_t saveDEVICE_RESET_REG;
- uint32_t saveDPI_RESOLUTION_REG;
- uint32_t saveHORIZ_SYNC_PAD_COUNT_REG;
- uint32_t saveHORIZ_BACK_PORCH_COUNT_REG;
- uint32_t saveHORIZ_FRONT_PORCH_COUNT_REG;
- uint32_t saveHORIZ_ACTIVE_AREA_COUNT_REG;
- uint32_t saveVERT_SYNC_PAD_COUNT_REG;
- uint32_t saveVERT_BACK_PORCH_COUNT_REG;
- uint32_t saveVERT_FRONT_PORCH_COUNT_REG;
- uint32_t saveHIGH_LOW_SWITCH_COUNT_REG;
- uint32_t saveINIT_COUNT_REG;
- uint32_t saveMAX_RET_PAK_REG;
- uint32_t saveVIDEO_FMT_REG;
- uint32_t saveEOT_DISABLE_REG;
- uint32_t saveLP_BYTECLK_REG;
- uint32_t saveHS_LS_DBI_ENABLE_REG;
- uint32_t saveTXCLKESC_REG;
- uint32_t saveDPHY_PARAM_REG;
- uint32_t saveMIPI_CONTROL_REG;
- uint32_t saveMIPI;
- uint32_t saveMIPI_C;
-
- /* DPST register save */
- uint32_t saveHISTOGRAM_INT_CONTROL_REG;
- uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG;
- uint32_t savePWM_CONTROL_LOGIC;
/*
- * DSI info.
- */
- void * dbi_dsr_info;
- void * dbi_dpu_info;
- void * dsi_configs[2];
- /*
* LID-Switch
*/
spinlock_t lid_lock;
@@ -635,6 +651,24 @@ struct drm_psb_private {
/* 2D acceleration */
spinlock_t lock_2d;
+
+ /*
+ * Panel brightness
+ */
+ int brightness;
+ int brightness_adjusted;
+
+ bool dsr_enable;
+ u32 dsr_fb_update;
+ bool dpi_panel_on[3];
+ void *dsi_configs[2];
+ u32 bpp;
+ u32 bpp2;
+
+ u32 pipeconf[3];
+ u32 dspcntr[3];
+
+ int mdfld_panel_id;
};
@@ -830,6 +864,9 @@ extern const struct psb_ops psb_chip_ops;
/* oaktrail_device.c */
extern const struct psb_ops oaktrail_chip_ops;
+/* mdlfd_device.c */
+extern const struct psb_ops mdfld_chip_ops;
+
/* cdv_device.c */
extern const struct psb_ops cdv_chip_ops;
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index 49e983508d5c..2616558457c8 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -333,7 +333,7 @@ void psb_intel_wait_for_vblank(struct drm_device *dev)
mdelay(20);
}
-int psb_intel_pipe_set_base(struct drm_crtc *crtc,
+static int psb_intel_pipe_set_base(struct drm_crtc *crtc,
int x, int y, struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
@@ -433,7 +433,6 @@ static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
int dspbase_reg = (pipe == 0) ? DSPABASE : DSPBBASE;
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
u32 temp;
- bool enabled;
/* XXX: When our outputs are all unaware of DPMS modes other than off
* and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -518,8 +517,6 @@ static void psb_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
break;
}
- enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
-
/*Set FIFO Watermarks*/
REG_WRITE(DSPARB, 0x3F3E);
}
@@ -611,8 +608,8 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc,
int refclk;
struct psb_intel_clock_t clock;
u32 dpll = 0, fp = 0, dspcntr, pipeconf;
- bool ok, is_sdvo = false, is_dvo = false;
- bool is_crt = false, is_lvds = false, is_tv = false;
+ bool ok, is_sdvo = false;
+ bool is_lvds = false, is_tv = false;
struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_connector *connector;
@@ -637,15 +634,9 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc,
case INTEL_OUTPUT_SDVO:
is_sdvo = true;
break;
- case INTEL_OUTPUT_DVO:
- is_dvo = true;
- break;
case INTEL_OUTPUT_TVOUT:
is_tv = true;
break;
- case INTEL_OUTPUT_ANALOG:
- is_crt = true;
- break;
}
}
@@ -845,7 +836,7 @@ void psb_intel_crtc_load_lut(struct drm_crtc *crtc)
gma_power_end(dev);
} else {
for (i = 0; i < 256; i++) {
- dev_priv->save_palette_a[i] =
+ dev_priv->regs.psb.save_palette_a[i] =
((psb_intel_crtc->lut_r[i] +
psb_intel_crtc->lut_adj[i]) << 16) |
((psb_intel_crtc->lut_g[i] +
@@ -1141,18 +1132,20 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev,
gma_power_end(dev);
} else {
dpll = (pipe == 0) ?
- dev_priv->saveDPLL_A : dev_priv->saveDPLL_B;
+ dev_priv->regs.psb.saveDPLL_A :
+ dev_priv->regs.psb.saveDPLL_B;
if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0)
fp = (pipe == 0) ?
- dev_priv->saveFPA0 :
- dev_priv->saveFPB0;
+ dev_priv->regs.psb.saveFPA0 :
+ dev_priv->regs.psb.saveFPB0;
else
fp = (pipe == 0) ?
- dev_priv->saveFPA1 :
- dev_priv->saveFPB1;
+ dev_priv->regs.psb.saveFPA1 :
+ dev_priv->regs.psb.saveFPB1;
- is_lvds = (pipe == 1) && (dev_priv->saveLVDS & LVDS_PORT_EN);
+ is_lvds = (pipe == 1) && (dev_priv->regs.psb.saveLVDS &
+ LVDS_PORT_EN);
}
clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
@@ -1218,13 +1211,17 @@ struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev,
gma_power_end(dev);
} else {
htot = (pipe == 0) ?
- dev_priv->saveHTOTAL_A : dev_priv->saveHTOTAL_B;
+ dev_priv->regs.psb.saveHTOTAL_A :
+ dev_priv->regs.psb.saveHTOTAL_B;
hsync = (pipe == 0) ?
- dev_priv->saveHSYNC_A : dev_priv->saveHSYNC_B;
+ dev_priv->regs.psb.saveHSYNC_A :
+ dev_priv->regs.psb.saveHSYNC_B;
vtot = (pipe == 0) ?
- dev_priv->saveVTOTAL_A : dev_priv->saveVTOTAL_B;
+ dev_priv->regs.psb.saveVTOTAL_A :
+ dev_priv->regs.psb.saveVTOTAL_B;
vsync = (pipe == 0) ?
- dev_priv->saveVSYNC_A : dev_priv->saveVSYNC_B;
+ dev_priv->regs.psb.saveVSYNC_A :
+ dev_priv->regs.psb.saveVSYNC_B;
}
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
@@ -1419,13 +1416,6 @@ int psb_intel_connector_clones(struct drm_device *dev, int type_mask)
return index_mask;
}
-
-void psb_intel_modeset_cleanup(struct drm_device *dev)
-{
- drm_mode_config_cleanup(dev);
-}
-
-
/* current intel driver doesn't take advantage of encoders
always give back the encoder for the connector
*/
diff --git a/drivers/gpu/drm/gma500/psb_intel_lvds.c b/drivers/gpu/drm/gma500/psb_intel_lvds.c
index 0a437586d8cc..c83f5b5d1057 100644
--- a/drivers/gpu/drm/gma500/psb_intel_lvds.c
+++ b/drivers/gpu/drm/gma500/psb_intel_lvds.c
@@ -77,7 +77,7 @@ static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev)
ret = REG_READ(BLC_PWM_CTL);
gma_power_end(dev);
} else /* Powered off, use the saved value */
- ret = dev_priv->saveBLC_PWM_CTL;
+ ret = dev_priv->regs.saveBLC_PWM_CTL;
/* Top 15bits hold the frequency mask */
ret = (ret & BACKLIGHT_MODULATION_FREQ_MASK) >>
@@ -86,7 +86,7 @@ static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev)
ret *= 2; /* Return a 16bit range as needed for setting */
if (ret == 0)
dev_err(dev->dev, "BL bug: Reg %08x save %08X\n",
- REG_READ(BLC_PWM_CTL), dev_priv->saveBLC_PWM_CTL);
+ REG_READ(BLC_PWM_CTL), dev_priv->regs.saveBLC_PWM_CTL);
return ret;
}
@@ -203,13 +203,13 @@ static void psb_intel_lvds_set_backlight(struct drm_device *dev, int level)
REG_WRITE(BLC_PWM_CTL,
(blc_pwm_ctl |
(level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
- dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
+ dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl |
(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
gma_power_end(dev);
} else {
- blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL &
+ blc_pwm_ctl = dev_priv->regs.saveBLC_PWM_CTL &
~BACKLIGHT_DUTY_CYCLE_MASK;
- dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
+ dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl |
(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
}
}
@@ -283,7 +283,7 @@ static void psb_intel_lvds_save(struct drm_connector *connector)
lvds_priv->savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS);
/*TODO: move backlight_duty_cycle to psb_intel_lvds_priv*/
- dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
+ dev_priv->backlight_duty_cycle = (dev_priv->regs.saveBLC_PWM_CTL &
BACKLIGHT_DUTY_CYCLE_MASK);
/*
diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h
index fcc0af03d685..e89d3a2e8fdc 100644
--- a/drivers/gpu/drm/gma500/psb_intel_reg.h
+++ b/drivers/gpu/drm/gma500/psb_intel_reg.h
@@ -177,6 +177,9 @@
#define LVDSPP_OFF 0x6120c
#define PP_CYCLE 0x61210
+#define PP_ON_DELAYS 0x61208 /* Cedartrail */
+#define PP_OFF_DELAYS 0x6120c /* Cedartrail */
+
#define PFIT_CONTROL 0x61230
#define PFIT_ENABLE (1 << 31)
#define PFIT_PIPE_MASK (3 << 29)
@@ -1252,6 +1255,12 @@ No status bits are changed.
# define SB_BYTE_ENABLE_SHIFT 4
# define SB_BUSY (1 << 0)
+#define DSPCLK_GATE_D 0x6200
+# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* Fixed value on CDV */
+# define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11)
+# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6)
+
+#define RAMCLK_GATE_D 0x6210
/* 32-bit value read/written from the DPIO reg. */
#define SB_DATA 0x02104 /* cedarview */
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index 88b42971c0fd..36330cabcea2 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -1301,7 +1301,7 @@ psb_intel_sdvo_get_analog_edid(struct drm_connector *connector)
return NULL;
}
-enum drm_connector_status
+static enum drm_connector_status
psb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
{
struct psb_intel_sdvo *psb_intel_sdvo = intel_attached_sdvo(connector);
@@ -2312,10 +2312,8 @@ static bool psb_intel_sdvo_tv_create_property(struct psb_intel_sdvo *psb_intel_s
psb_intel_sdvo_connector->max_##name = data_value[0]; \
psb_intel_sdvo_connector->cur_##name = response; \
psb_intel_sdvo_connector->name = \
- drm_property_create(dev, DRM_MODE_PROP_RANGE, #name, 2); \
+ drm_property_create_range(dev, 0, #name, 0, data_value[0]); \
if (!psb_intel_sdvo_connector->name) return false; \
- psb_intel_sdvo_connector->name->values[0] = 0; \
- psb_intel_sdvo_connector->name->values[1] = data_value[0]; \
drm_connector_attach_property(connector, \
psb_intel_sdvo_connector->name, \
psb_intel_sdvo_connector->cur_##name); \
@@ -2349,25 +2347,19 @@ psb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo,
psb_intel_sdvo_connector->left_margin = data_value[0] - response;
psb_intel_sdvo_connector->right_margin = psb_intel_sdvo_connector->left_margin;
psb_intel_sdvo_connector->left =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "left_margin", 2);
+ drm_property_create_range(dev, 0, "left_margin", 0, data_value[0]);
if (!psb_intel_sdvo_connector->left)
return false;
- psb_intel_sdvo_connector->left->values[0] = 0;
- psb_intel_sdvo_connector->left->values[1] = data_value[0];
drm_connector_attach_property(connector,
psb_intel_sdvo_connector->left,
psb_intel_sdvo_connector->left_margin);
psb_intel_sdvo_connector->right =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "right_margin", 2);
+ drm_property_create_range(dev, 0, "right_margin", 0, data_value[0]);
if (!psb_intel_sdvo_connector->right)
return false;
- psb_intel_sdvo_connector->right->values[0] = 0;
- psb_intel_sdvo_connector->right->values[1] = data_value[0];
drm_connector_attach_property(connector,
psb_intel_sdvo_connector->right,
psb_intel_sdvo_connector->right_margin);
@@ -2391,25 +2383,19 @@ psb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo,
psb_intel_sdvo_connector->top_margin = data_value[0] - response;
psb_intel_sdvo_connector->bottom_margin = psb_intel_sdvo_connector->top_margin;
psb_intel_sdvo_connector->top =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "top_margin", 2);
+ drm_property_create_range(dev, 0, "top_margin", 0, data_value[0]);
if (!psb_intel_sdvo_connector->top)
return false;
- psb_intel_sdvo_connector->top->values[0] = 0;
- psb_intel_sdvo_connector->top->values[1] = data_value[0];
drm_connector_attach_property(connector,
psb_intel_sdvo_connector->top,
psb_intel_sdvo_connector->top_margin);
psb_intel_sdvo_connector->bottom =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "bottom_margin", 2);
+ drm_property_create_range(dev, 0, "bottom_margin", 0, data_value[0]);
if (!psb_intel_sdvo_connector->bottom)
return false;
- psb_intel_sdvo_connector->bottom->values[0] = 0;
- psb_intel_sdvo_connector->bottom->values[1] = data_value[0];
drm_connector_attach_property(connector,
psb_intel_sdvo_connector->bottom,
psb_intel_sdvo_connector->bottom_margin);
@@ -2438,12 +2424,10 @@ psb_intel_sdvo_create_enhance_property_tv(struct psb_intel_sdvo *psb_intel_sdvo,
psb_intel_sdvo_connector->max_dot_crawl = 1;
psb_intel_sdvo_connector->cur_dot_crawl = response & 0x1;
psb_intel_sdvo_connector->dot_crawl =
- drm_property_create(dev, DRM_MODE_PROP_RANGE, "dot_crawl", 2);
+ drm_property_create_range(dev, 0, "dot_crawl", 0, 1);
if (!psb_intel_sdvo_connector->dot_crawl)
return false;
- psb_intel_sdvo_connector->dot_crawl->values[0] = 0;
- psb_intel_sdvo_connector->dot_crawl->values[1] = 1;
drm_connector_attach_property(connector,
psb_intel_sdvo_connector->dot_crawl,
psb_intel_sdvo_connector->cur_dot_crawl);
diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c
index 7be802baceb5..1869586457b1 100644
--- a/drivers/gpu/drm/gma500/psb_irq.c
+++ b/drivers/gpu/drm/gma500/psb_irq.c
@@ -27,6 +27,8 @@
#include "psb_reg.h"
#include "psb_intel_reg.h"
#include "power.h"
+#include "psb_irq.h"
+#include "mdfld_output.h"
/*
* inline functions
@@ -113,7 +115,7 @@ psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
}
}
-void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
+static void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
{
if (gma_power_begin(dev_priv->dev, false)) {
u32 pipe_event = mid_pipe_event(pipe);
@@ -124,7 +126,7 @@ void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
}
}
-void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
+static void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
{
if (dev_priv->pipestat[pipe] == 0) {
if (gma_power_begin(dev_priv->dev, false)) {
@@ -453,6 +455,11 @@ int psb_enable_vblank(struct drm_device *dev, int pipe)
uint32_t reg_val = 0;
uint32_t pipeconf_reg = mid_pipeconf(pipe);
+ /* Medfield is different - we should perhaps extract out vblank
+ and blacklight etc ops */
+ if (IS_MFLD(dev))
+ return mdfld_enable_te(dev, pipe);
+
if (gma_power_begin(dev, false)) {
reg_val = REG_READ(pipeconf_reg);
gma_power_end(dev);
@@ -485,6 +492,8 @@ void psb_disable_vblank(struct drm_device *dev, int pipe)
struct drm_psb_private *dev_priv = dev->dev_private;
unsigned long irqflags;
+ if (IS_MFLD(dev))
+ mdfld_disable_te(dev, pipe);
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
if (pipe == 0)
@@ -499,6 +508,55 @@ void psb_disable_vblank(struct drm_device *dev, int pipe)
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
}
+/*
+ * It is used to enable TE interrupt
+ */
+int mdfld_enable_te(struct drm_device *dev, int pipe)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+ uint32_t reg_val = 0;
+ uint32_t pipeconf_reg = mid_pipeconf(pipe);
+
+ if (gma_power_begin(dev, false)) {
+ reg_val = REG_READ(pipeconf_reg);
+ gma_power_end(dev);
+ }
+
+ if (!(reg_val & PIPEACONF_ENABLE))
+ return -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ mid_enable_pipe_event(dev_priv, pipe);
+ psb_enable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+
+ return 0;
+}
+
+/*
+ * It is used to disable TE interrupt
+ */
+void mdfld_disable_te(struct drm_device *dev, int pipe)
+{
+ struct drm_psb_private *dev_priv =
+ (struct drm_psb_private *) dev->dev_private;
+ unsigned long irqflags;
+
+ if (!dev_priv->dsr_enable)
+ return;
+
+ spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
+
+ mid_disable_pipe_event(dev_priv, pipe);
+ psb_disable_pipestat(dev_priv, pipe, PIPE_TE_ENABLE);
+
+ spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
+}
+
/* Called from drm generic code, passed a 'crtc', which
* we use as a pipe index
*/
diff --git a/drivers/gpu/drm/gma500/psb_irq.h b/drivers/gpu/drm/gma500/psb_irq.h
index 216fda38b57d..603045bee58a 100644
--- a/drivers/gpu/drm/gma500/psb_irq.h
+++ b/drivers/gpu/drm/gma500/psb_irq.h
@@ -42,4 +42,6 @@ int psb_enable_vblank(struct drm_device *dev, int pipe);
void psb_disable_vblank(struct drm_device *dev, int pipe);
u32 psb_get_vblank_counter(struct drm_device *dev, int pipe);
+int mdfld_enable_te(struct drm_device *dev, int pipe);
+void mdfld_disable_te(struct drm_device *dev, int pipe);
#endif /* _SYSIRQ_H_ */
diff --git a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c
new file mode 100644
index 000000000000..4a07ab596174
--- /dev/null
+++ b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.c
@@ -0,0 +1,829 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#include "mdfld_dsi_dpi.h"
+#include "mdfld_output.h"
+#include "mdfld_dsi_pkg_sender.h"
+#include "tc35876x-dsi-lvds.h"
+#include <linux/i2c/tc35876x.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/intel_scu_ipc.h>
+
+static struct i2c_client *tc35876x_client;
+static struct i2c_client *cmi_lcd_i2c_client;
+
+#define FLD_MASK(start, end) (((1 << ((start) - (end) + 1)) - 1) << (end))
+#define FLD_VAL(val, start, end) (((val) << (end)) & FLD_MASK(start, end))
+
+/* DSI D-PHY Layer Registers */
+#define D0W_DPHYCONTTX 0x0004
+#define CLW_DPHYCONTRX 0x0020
+#define D0W_DPHYCONTRX 0x0024
+#define D1W_DPHYCONTRX 0x0028
+#define D2W_DPHYCONTRX 0x002C
+#define D3W_DPHYCONTRX 0x0030
+#define COM_DPHYCONTRX 0x0038
+#define CLW_CNTRL 0x0040
+#define D0W_CNTRL 0x0044
+#define D1W_CNTRL 0x0048
+#define D2W_CNTRL 0x004C
+#define D3W_CNTRL 0x0050
+#define DFTMODE_CNTRL 0x0054
+
+/* DSI PPI Layer Registers */
+#define PPI_STARTPPI 0x0104
+#define PPI_BUSYPPI 0x0108
+#define PPI_LINEINITCNT 0x0110
+#define PPI_LPTXTIMECNT 0x0114
+#define PPI_LANEENABLE 0x0134
+#define PPI_TX_RX_TA 0x013C
+#define PPI_CLS_ATMR 0x0140
+#define PPI_D0S_ATMR 0x0144
+#define PPI_D1S_ATMR 0x0148
+#define PPI_D2S_ATMR 0x014C
+#define PPI_D3S_ATMR 0x0150
+#define PPI_D0S_CLRSIPOCOUNT 0x0164
+#define PPI_D1S_CLRSIPOCOUNT 0x0168
+#define PPI_D2S_CLRSIPOCOUNT 0x016C
+#define PPI_D3S_CLRSIPOCOUNT 0x0170
+#define CLS_PRE 0x0180
+#define D0S_PRE 0x0184
+#define D1S_PRE 0x0188
+#define D2S_PRE 0x018C
+#define D3S_PRE 0x0190
+#define CLS_PREP 0x01A0
+#define D0S_PREP 0x01A4
+#define D1S_PREP 0x01A8
+#define D2S_PREP 0x01AC
+#define D3S_PREP 0x01B0
+#define CLS_ZERO 0x01C0
+#define D0S_ZERO 0x01C4
+#define D1S_ZERO 0x01C8
+#define D2S_ZERO 0x01CC
+#define D3S_ZERO 0x01D0
+#define PPI_CLRFLG 0x01E0
+#define PPI_CLRSIPO 0x01E4
+#define HSTIMEOUT 0x01F0
+#define HSTIMEOUTENABLE 0x01F4
+
+/* DSI Protocol Layer Registers */
+#define DSI_STARTDSI 0x0204
+#define DSI_BUSYDSI 0x0208
+#define DSI_LANEENABLE 0x0210
+#define DSI_LANESTATUS0 0x0214
+#define DSI_LANESTATUS1 0x0218
+#define DSI_INTSTATUS 0x0220
+#define DSI_INTMASK 0x0224
+#define DSI_INTCLR 0x0228
+#define DSI_LPTXTO 0x0230
+
+/* DSI General Registers */
+#define DSIERRCNT 0x0300
+
+/* DSI Application Layer Registers */
+#define APLCTRL 0x0400
+#define RDPKTLN 0x0404
+
+/* Video Path Registers */
+#define VPCTRL 0x0450
+#define HTIM1 0x0454
+#define HTIM2 0x0458
+#define VTIM1 0x045C
+#define VTIM2 0x0460
+#define VFUEN 0x0464
+
+/* LVDS Registers */
+#define LVMX0003 0x0480
+#define LVMX0407 0x0484
+#define LVMX0811 0x0488
+#define LVMX1215 0x048C
+#define LVMX1619 0x0490
+#define LVMX2023 0x0494
+#define LVMX2427 0x0498
+#define LVCFG 0x049C
+#define LVPHY0 0x04A0
+#define LVPHY1 0x04A4
+
+/* System Registers */
+#define SYSSTAT 0x0500
+#define SYSRST 0x0504
+
+/* GPIO Registers */
+/*#define GPIOC 0x0520*/
+#define GPIOO 0x0524
+#define GPIOI 0x0528
+
+/* I2C Registers */
+#define I2CTIMCTRL 0x0540
+#define I2CMADDR 0x0544
+#define WDATAQ 0x0548
+#define RDATAQ 0x054C
+
+/* Chip/Rev Registers */
+#define IDREG 0x0580
+
+/* Debug Registers */
+#define DEBUG00 0x05A0
+#define DEBUG01 0x05A4
+
+/* Panel CABC registers */
+#define PANEL_PWM_CONTROL 0x90
+#define PANEL_FREQ_DIVIDER_HI 0x91
+#define PANEL_FREQ_DIVIDER_LO 0x92
+#define PANEL_DUTY_CONTROL 0x93
+#define PANEL_MODIFY_RGB 0x94
+#define PANEL_FRAMERATE_CONTROL 0x96
+#define PANEL_PWM_MIN 0x97
+#define PANEL_PWM_REF 0x98
+#define PANEL_PWM_MAX 0x99
+#define PANEL_ALLOW_DISTORT 0x9A
+#define PANEL_BYPASS_PWMI 0x9B
+
+/* Panel color management registers */
+#define PANEL_CM_ENABLE 0x700
+#define PANEL_CM_HUE 0x701
+#define PANEL_CM_SATURATION 0x702
+#define PANEL_CM_INTENSITY 0x703
+#define PANEL_CM_BRIGHTNESS 0x704
+#define PANEL_CM_CE_ENABLE 0x705
+#define PANEL_CM_PEAK_EN 0x710
+#define PANEL_CM_GAIN 0x711
+#define PANEL_CM_HUETABLE_START 0x730
+#define PANEL_CM_HUETABLE_END 0x747 /* inclusive */
+
+/* Input muxing for registers LVMX0003...LVMX2427 */
+enum {
+ INPUT_R0, /* 0 */
+ INPUT_R1,
+ INPUT_R2,
+ INPUT_R3,
+ INPUT_R4,
+ INPUT_R5,
+ INPUT_R6,
+ INPUT_R7,
+ INPUT_G0, /* 8 */
+ INPUT_G1,
+ INPUT_G2,
+ INPUT_G3,
+ INPUT_G4,
+ INPUT_G5,
+ INPUT_G6,
+ INPUT_G7,
+ INPUT_B0, /* 16 */
+ INPUT_B1,
+ INPUT_B2,
+ INPUT_B3,
+ INPUT_B4,
+ INPUT_B5,
+ INPUT_B6,
+ INPUT_B7,
+ INPUT_HSYNC, /* 24 */
+ INPUT_VSYNC,
+ INPUT_DE,
+ LOGIC_0,
+ /* 28...31 undefined */
+};
+
+#define INPUT_MUX(lvmx03, lvmx02, lvmx01, lvmx00) \
+ (FLD_VAL(lvmx03, 29, 24) | FLD_VAL(lvmx02, 20, 16) | \
+ FLD_VAL(lvmx01, 12, 8) | FLD_VAL(lvmx00, 4, 0))
+
+/**
+ * tc35876x_regw - Write DSI-LVDS bridge register using I2C
+ * @client: struct i2c_client to use
+ * @reg: register address
+ * @value: value to write
+ *
+ * Returns 0 on success, or a negative error value.
+ */
+static int tc35876x_regw(struct i2c_client *client, u16 reg, u32 value)
+{
+ int r;
+ u8 tx_data[] = {
+ /* NOTE: Register address big-endian, data little-endian. */
+ (reg >> 8) & 0xff,
+ reg & 0xff,
+ value & 0xff,
+ (value >> 8) & 0xff,
+ (value >> 16) & 0xff,
+ (value >> 24) & 0xff,
+ };
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = tx_data,
+ .len = ARRAY_SIZE(tx_data),
+ },
+ };
+
+ r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (r < 0) {
+ dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x error %d\n",
+ __func__, reg, value, r);
+ return r;
+ }
+
+ if (r < ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: reg 0x%04x val 0x%08x msgs %d\n",
+ __func__, reg, value, r);
+ return -EAGAIN;
+ }
+
+ dev_dbg(&client->dev, "%s: reg 0x%04x val 0x%08x\n",
+ __func__, reg, value);
+
+ return 0;
+}
+
+/**
+ * tc35876x_regr - Read DSI-LVDS bridge register using I2C
+ * @client: struct i2c_client to use
+ * @reg: register address
+ * @value: pointer for storing the value
+ *
+ * Returns 0 on success, or a negative error value.
+ */
+static int tc35876x_regr(struct i2c_client *client, u16 reg, u32 *value)
+{
+ int r;
+ u8 tx_data[] = {
+ (reg >> 8) & 0xff,
+ reg & 0xff,
+ };
+ u8 rx_data[4];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = tx_data,
+ .len = ARRAY_SIZE(tx_data),
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = rx_data,
+ .len = ARRAY_SIZE(rx_data),
+ },
+ };
+
+ r = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (r < 0) {
+ dev_err(&client->dev, "%s: reg 0x%04x error %d\n", __func__,
+ reg, r);
+ return r;
+ }
+
+ if (r < ARRAY_SIZE(msgs)) {
+ dev_err(&client->dev, "%s: reg 0x%04x msgs %d\n", __func__,
+ reg, r);
+ return -EAGAIN;
+ }
+
+ *value = rx_data[0] << 24 | rx_data[1] << 16 |
+ rx_data[2] << 8 | rx_data[3];
+
+ dev_dbg(&client->dev, "%s: reg 0x%04x value 0x%08x\n", __func__,
+ reg, *value);
+
+ return 0;
+}
+
+void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state)
+{
+ struct tc35876x_platform_data *pdata;
+
+ if (WARN(!tc35876x_client, "%s called before probe", __func__))
+ return;
+
+ dev_dbg(&tc35876x_client->dev, "%s: state %d\n", __func__, state);
+
+ pdata = dev_get_platdata(&tc35876x_client->dev);
+
+ if (pdata->gpio_bridge_reset == -1)
+ return;
+
+ if (state) {
+ gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0);
+ mdelay(10);
+ } else {
+ /* Pull MIPI Bridge reset pin to Low */
+ gpio_set_value_cansleep(pdata->gpio_bridge_reset, 0);
+ mdelay(20);
+ /* Pull MIPI Bridge reset pin to High */
+ gpio_set_value_cansleep(pdata->gpio_bridge_reset, 1);
+ mdelay(40);
+ }
+}
+
+void tc35876x_configure_lvds_bridge(struct drm_device *dev)
+{
+ struct i2c_client *i2c = tc35876x_client;
+ u32 ppi_lptxtimecnt;
+ u32 txtagocnt;
+ u32 txtasurecnt;
+ u32 id;
+
+ if (WARN(!tc35876x_client, "%s called before probe", __func__))
+ return;
+
+ dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
+
+ if (!tc35876x_regr(i2c, IDREG, &id))
+ dev_info(&tc35876x_client->dev, "tc35876x ID 0x%08x\n", id);
+ else
+ dev_err(&tc35876x_client->dev, "Cannot read ID\n");
+
+ ppi_lptxtimecnt = 4;
+ txtagocnt = (5 * ppi_lptxtimecnt - 3) / 4;
+ txtasurecnt = 3 * ppi_lptxtimecnt / 2;
+ tc35876x_regw(i2c, PPI_TX_RX_TA, FLD_VAL(txtagocnt, 26, 16) |
+ FLD_VAL(txtasurecnt, 10, 0));
+ tc35876x_regw(i2c, PPI_LPTXTIMECNT, FLD_VAL(ppi_lptxtimecnt, 10, 0));
+
+ tc35876x_regw(i2c, PPI_D0S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
+ tc35876x_regw(i2c, PPI_D1S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
+ tc35876x_regw(i2c, PPI_D2S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
+ tc35876x_regw(i2c, PPI_D3S_CLRSIPOCOUNT, FLD_VAL(1, 5, 0));
+
+ /* Enabling MIPI & PPI lanes, Enable 4 lanes */
+ tc35876x_regw(i2c, PPI_LANEENABLE,
+ BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0));
+ tc35876x_regw(i2c, DSI_LANEENABLE,
+ BIT(4) | BIT(3) | BIT(2) | BIT(1) | BIT(0));
+ tc35876x_regw(i2c, PPI_STARTPPI, BIT(0));
+ tc35876x_regw(i2c, DSI_STARTDSI, BIT(0));
+
+ /* Setting LVDS output frequency */
+ tc35876x_regw(i2c, LVPHY0, FLD_VAL(1, 20, 16) |
+ FLD_VAL(2, 15, 14) | FLD_VAL(6, 4, 0)); /* 0x00048006 */
+
+ /* Setting video panel control register,0x00000120 VTGen=ON ?!?!? */
+ tc35876x_regw(i2c, VPCTRL, BIT(8) | BIT(5));
+
+ /* Horizontal back porch and horizontal pulse width. 0x00280028 */
+ tc35876x_regw(i2c, HTIM1, FLD_VAL(40, 24, 16) | FLD_VAL(40, 8, 0));
+
+ /* Horizontal front porch and horizontal active video size. 0x00500500*/
+ tc35876x_regw(i2c, HTIM2, FLD_VAL(80, 24, 16) | FLD_VAL(1280, 10, 0));
+
+ /* Vertical back porch and vertical sync pulse width. 0x000e000a */
+ tc35876x_regw(i2c, VTIM1, FLD_VAL(14, 23, 16) | FLD_VAL(10, 7, 0));
+
+ /* Vertical front porch and vertical display size. 0x000e0320 */
+ tc35876x_regw(i2c, VTIM2, FLD_VAL(14, 23, 16) | FLD_VAL(800, 10, 0));
+
+ /* Set above HTIM1, HTIM2, VTIM1, and VTIM2 at next VSYNC. */
+ tc35876x_regw(i2c, VFUEN, BIT(0));
+
+ /* Soft reset LCD controller. */
+ tc35876x_regw(i2c, SYSRST, BIT(2));
+
+ /* LVDS-TX input muxing */
+ tc35876x_regw(i2c, LVMX0003,
+ INPUT_MUX(INPUT_R5, INPUT_R4, INPUT_R3, INPUT_R2));
+ tc35876x_regw(i2c, LVMX0407,
+ INPUT_MUX(INPUT_G2, INPUT_R7, INPUT_R1, INPUT_R6));
+ tc35876x_regw(i2c, LVMX0811,
+ INPUT_MUX(INPUT_G1, INPUT_G0, INPUT_G4, INPUT_G3));
+ tc35876x_regw(i2c, LVMX1215,
+ INPUT_MUX(INPUT_B2, INPUT_G7, INPUT_G6, INPUT_G5));
+ tc35876x_regw(i2c, LVMX1619,
+ INPUT_MUX(INPUT_B4, INPUT_B3, INPUT_B1, INPUT_B0));
+ tc35876x_regw(i2c, LVMX2023,
+ INPUT_MUX(LOGIC_0, INPUT_B7, INPUT_B6, INPUT_B5));
+ tc35876x_regw(i2c, LVMX2427,
+ INPUT_MUX(INPUT_R0, INPUT_DE, INPUT_VSYNC, INPUT_HSYNC));
+
+ /* Enable LVDS transmitter. */
+ tc35876x_regw(i2c, LVCFG, BIT(0));
+
+ /* Clear notifications. Don't write reserved bits. Was write 0xffffffff
+ * to 0x0288, must be in error?! */
+ tc35876x_regw(i2c, DSI_INTCLR, FLD_MASK(31, 30) | FLD_MASK(22, 0));
+}
+
+#define GPIOPWMCTRL 0x38F
+#define PWM0CLKDIV0 0x62 /* low byte */
+#define PWM0CLKDIV1 0x61 /* high byte */
+
+#define SYSTEMCLK 19200000UL /* 19.2 MHz */
+#define PWM_FREQUENCY 9600 /* Hz */
+
+/* f = baseclk / (clkdiv + 1) => clkdiv = (baseclk - f) / f */
+static inline u16 calc_clkdiv(unsigned long baseclk, unsigned int f)
+{
+ return (baseclk - f) / f;
+}
+
+static void tc35876x_brightness_init(struct drm_device *dev)
+{
+ int ret;
+ u8 pwmctrl;
+ u16 clkdiv;
+
+ /* Make sure the PWM reference is the 19.2 MHz system clock. Read first
+ * instead of setting directly to catch potential conflicts between PWM
+ * users. */
+ ret = intel_scu_ipc_ioread8(GPIOPWMCTRL, &pwmctrl);
+ if (ret || pwmctrl != 0x01) {
+ if (ret)
+ dev_err(&dev->pdev->dev, "GPIOPWMCTRL read failed\n");
+ else
+ dev_warn(&dev->pdev->dev, "GPIOPWMCTRL was not set to system clock (pwmctrl = 0x%02x)\n", pwmctrl);
+
+ ret = intel_scu_ipc_iowrite8(GPIOPWMCTRL, 0x01);
+ if (ret)
+ dev_err(&dev->pdev->dev, "GPIOPWMCTRL set failed\n");
+ }
+
+ clkdiv = calc_clkdiv(SYSTEMCLK, PWM_FREQUENCY);
+
+ ret = intel_scu_ipc_iowrite8(PWM0CLKDIV1, (clkdiv >> 8) & 0xff);
+ if (!ret)
+ ret = intel_scu_ipc_iowrite8(PWM0CLKDIV0, clkdiv & 0xff);
+
+ if (ret)
+ dev_err(&dev->pdev->dev, "PWM0CLKDIV set failed\n");
+ else
+ dev_dbg(&dev->pdev->dev, "PWM0CLKDIV set to 0x%04x (%d Hz)\n",
+ clkdiv, PWM_FREQUENCY);
+}
+
+#define PWM0DUTYCYCLE 0x67
+
+void tc35876x_brightness_control(struct drm_device *dev, int level)
+{
+ int ret;
+ u8 duty_val;
+ u8 panel_duty_val;
+
+ level = clamp(level, 0, MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
+
+ /* PWM duty cycle 0x00...0x63 corresponds to 0...99% */
+ duty_val = level * 0x63 / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL;
+
+ /* I won't pretend to understand this formula. The panel spec is quite
+ * bad engrish.
+ */
+ panel_duty_val = (2 * level - 100) * 0xA9 /
+ MDFLD_DSI_BRIGHTNESS_MAX_LEVEL + 0x56;
+
+ ret = intel_scu_ipc_iowrite8(PWM0DUTYCYCLE, duty_val);
+ if (ret)
+ dev_err(&tc35876x_client->dev, "%s: ipc write fail\n",
+ __func__);
+
+ if (cmi_lcd_i2c_client) {
+ ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
+ PANEL_PWM_MAX, panel_duty_val);
+ if (ret < 0)
+ dev_err(&cmi_lcd_i2c_client->dev, "%s: i2c write failed\n",
+ __func__);
+ }
+}
+
+void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev)
+{
+ struct tc35876x_platform_data *pdata;
+
+ if (WARN(!tc35876x_client, "%s called before probe", __func__))
+ return;
+
+ dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
+
+ pdata = dev_get_platdata(&tc35876x_client->dev);
+
+ if (pdata->gpio_panel_bl_en != -1)
+ gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 0);
+
+ if (pdata->gpio_panel_vadd != -1)
+ gpio_set_value_cansleep(pdata->gpio_panel_vadd, 0);
+}
+
+void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev)
+{
+ struct tc35876x_platform_data *pdata;
+ struct drm_psb_private *dev_priv = dev->dev_private;
+
+ if (WARN(!tc35876x_client, "%s called before probe", __func__))
+ return;
+
+ dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
+
+ pdata = dev_get_platdata(&tc35876x_client->dev);
+
+ if (pdata->gpio_panel_vadd != -1) {
+ gpio_set_value_cansleep(pdata->gpio_panel_vadd, 1);
+ msleep(260);
+ }
+
+ if (cmi_lcd_i2c_client) {
+ int ret;
+ dev_dbg(&cmi_lcd_i2c_client->dev, "setting TCON\n");
+ /* Bit 4 is average_saving. Setting it to 1, the brightness is
+ * referenced to the average of the frame content. 0 means
+ * reference to the maximum of frame contents. Bits 3:0 are
+ * allow_distort. When set to a nonzero value, all color values
+ * between 255-allow_distort*2 and 255 are mapped to the
+ * 255-allow_distort*2 value.
+ */
+ ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
+ PANEL_ALLOW_DISTORT, 0x10);
+ if (ret < 0)
+ dev_err(&cmi_lcd_i2c_client->dev,
+ "i2c write failed (%d)\n", ret);
+ ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
+ PANEL_BYPASS_PWMI, 0);
+ if (ret < 0)
+ dev_err(&cmi_lcd_i2c_client->dev,
+ "i2c write failed (%d)\n", ret);
+ /* Set minimum brightness value - this is tunable */
+ ret = i2c_smbus_write_byte_data(cmi_lcd_i2c_client,
+ PANEL_PWM_MIN, 0x35);
+ if (ret < 0)
+ dev_err(&cmi_lcd_i2c_client->dev,
+ "i2c write failed (%d)\n", ret);
+ }
+
+ if (pdata->gpio_panel_bl_en != -1)
+ gpio_set_value_cansleep(pdata->gpio_panel_bl_en, 1);
+
+ tc35876x_brightness_control(dev, dev_priv->brightness_adjusted);
+}
+
+static struct drm_display_mode *tc35876x_get_config_mode(struct drm_device *dev)
+{
+ struct drm_display_mode *mode;
+
+ dev_dbg(&dev->pdev->dev, "%s\n", __func__);
+
+ mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+ if (!mode)
+ return NULL;
+
+ /* FIXME: do this properly. */
+ mode->hdisplay = 1280;
+ mode->vdisplay = 800;
+ mode->hsync_start = 1360;
+ mode->hsync_end = 1400;
+ mode->htotal = 1440;
+ mode->vsync_start = 814;
+ mode->vsync_end = 824;
+ mode->vtotal = 838;
+ mode->clock = 33324 << 1;
+
+ dev_info(&dev->pdev->dev, "hdisplay(w) = %d\n", mode->hdisplay);
+ dev_info(&dev->pdev->dev, "vdisplay(h) = %d\n", mode->vdisplay);
+ dev_info(&dev->pdev->dev, "HSS = %d\n", mode->hsync_start);
+ dev_info(&dev->pdev->dev, "HSE = %d\n", mode->hsync_end);
+ dev_info(&dev->pdev->dev, "htotal = %d\n", mode->htotal);
+ dev_info(&dev->pdev->dev, "VSS = %d\n", mode->vsync_start);
+ dev_info(&dev->pdev->dev, "VSE = %d\n", mode->vsync_end);
+ dev_info(&dev->pdev->dev, "vtotal = %d\n", mode->vtotal);
+ dev_info(&dev->pdev->dev, "clock = %d\n", mode->clock);
+
+ drm_mode_set_name(mode);
+ drm_mode_set_crtcinfo(mode, 0);
+
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ return mode;
+}
+
+/* DV1 Active area 216.96 x 135.6 mm */
+#define DV1_PANEL_WIDTH 217
+#define DV1_PANEL_HEIGHT 136
+
+static int tc35876x_get_panel_info(struct drm_device *dev, int pipe,
+ struct panel_info *pi)
+{
+ if (!dev || !pi)
+ return -EINVAL;
+
+ pi->width_mm = DV1_PANEL_WIDTH;
+ pi->height_mm = DV1_PANEL_HEIGHT;
+
+ return 0;
+}
+
+static int tc35876x_bridge_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tc35876x_platform_data *pdata;
+
+ dev_info(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "%s: i2c_check_functionality() failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ dev_err(&client->dev, "%s: no platform data\n", __func__);
+ return -ENODEV;
+ }
+
+ if (pdata->gpio_bridge_reset != -1) {
+ gpio_request(pdata->gpio_bridge_reset, "tc35876x bridge reset");
+ gpio_direction_output(pdata->gpio_bridge_reset, 0);
+ }
+
+ if (pdata->gpio_panel_bl_en != -1) {
+ gpio_request(pdata->gpio_panel_bl_en, "tc35876x panel bl en");
+ gpio_direction_output(pdata->gpio_panel_bl_en, 0);
+ }
+
+ if (pdata->gpio_panel_vadd != -1) {
+ gpio_request(pdata->gpio_panel_vadd, "tc35876x panel vadd");
+ gpio_direction_output(pdata->gpio_panel_vadd, 0);
+ }
+
+ tc35876x_client = client;
+
+ return 0;
+}
+
+static int tc35876x_bridge_remove(struct i2c_client *client)
+{
+ struct tc35876x_platform_data *pdata = dev_get_platdata(&client->dev);
+
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ if (pdata->gpio_bridge_reset != -1)
+ gpio_free(pdata->gpio_bridge_reset);
+
+ if (pdata->gpio_panel_bl_en != -1)
+ gpio_free(pdata->gpio_panel_bl_en);
+
+ if (pdata->gpio_panel_vadd != -1)
+ gpio_free(pdata->gpio_panel_vadd);
+
+ tc35876x_client = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id tc35876x_bridge_id[] = {
+ { "i2c_disp_brig", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tc35876x_bridge_id);
+
+static struct i2c_driver tc35876x_bridge_i2c_driver = {
+ .driver = {
+ .name = "i2c_disp_brig",
+ },
+ .id_table = tc35876x_bridge_id,
+ .probe = tc35876x_bridge_probe,
+ .remove = __devexit_p(tc35876x_bridge_remove),
+};
+
+/* LCD panel I2C */
+static int cmi_lcd_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ dev_info(&client->dev, "%s\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "%s: i2c_check_functionality() failed\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ cmi_lcd_i2c_client = client;
+
+ return 0;
+}
+
+static int cmi_lcd_i2c_remove(struct i2c_client *client)
+{
+ dev_dbg(&client->dev, "%s\n", __func__);
+
+ cmi_lcd_i2c_client = NULL;
+
+ return 0;
+}
+
+static const struct i2c_device_id cmi_lcd_i2c_id[] = {
+ { "cmi-lcd", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cmi_lcd_i2c_id);
+
+static struct i2c_driver cmi_lcd_i2c_driver = {
+ .driver = {
+ .name = "cmi-lcd",
+ },
+ .id_table = cmi_lcd_i2c_id,
+ .probe = cmi_lcd_i2c_probe,
+ .remove = __devexit_p(cmi_lcd_i2c_remove),
+};
+
+/* HACK to create I2C device while it's not created by platform code */
+#define CMI_LCD_I2C_ADAPTER 2
+#define CMI_LCD_I2C_ADDR 0x60
+
+static int cmi_lcd_hack_create_device(void)
+{
+ struct i2c_adapter *adapter;
+ struct i2c_client *client;
+ struct i2c_board_info info = {
+ .type = "cmi-lcd",
+ .addr = CMI_LCD_I2C_ADDR,
+ };
+
+ pr_debug("%s\n", __func__);
+
+ adapter = i2c_get_adapter(CMI_LCD_I2C_ADAPTER);
+ if (!adapter) {
+ pr_err("%s: i2c_get_adapter(%d) failed\n", __func__,
+ CMI_LCD_I2C_ADAPTER);
+ return -EINVAL;
+ }
+
+ client = i2c_new_device(adapter, &info);
+ if (!client) {
+ pr_err("%s: i2c_new_device() failed\n", __func__);
+ i2c_put_adapter(adapter);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs tc35876x_encoder_helper_funcs = {
+ .dpms = mdfld_dsi_dpi_dpms,
+ .mode_fixup = mdfld_dsi_dpi_mode_fixup,
+ .prepare = mdfld_dsi_dpi_prepare,
+ .mode_set = mdfld_dsi_dpi_mode_set,
+ .commit = mdfld_dsi_dpi_commit,
+};
+
+static const struct drm_encoder_funcs tc35876x_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
+};
+
+const struct panel_funcs mdfld_tc35876x_funcs = {
+ .encoder_funcs = &tc35876x_encoder_funcs,
+ .encoder_helper_funcs = &tc35876x_encoder_helper_funcs,
+ .get_config_mode = tc35876x_get_config_mode,
+ .get_panel_info = tc35876x_get_panel_info,
+};
+
+void tc35876x_init(struct drm_device *dev)
+{
+ int r;
+
+ dev_dbg(&dev->pdev->dev, "%s\n", __func__);
+
+ cmi_lcd_hack_create_device();
+
+ r = i2c_add_driver(&cmi_lcd_i2c_driver);
+ if (r < 0)
+ dev_err(&dev->pdev->dev,
+ "%s: i2c_add_driver() for %s failed (%d)\n",
+ __func__, cmi_lcd_i2c_driver.driver.name, r);
+
+ r = i2c_add_driver(&tc35876x_bridge_i2c_driver);
+ if (r < 0)
+ dev_err(&dev->pdev->dev,
+ "%s: i2c_add_driver() for %s failed (%d)\n",
+ __func__, tc35876x_bridge_i2c_driver.driver.name, r);
+
+ tc35876x_brightness_init(dev);
+}
+
+void tc35876x_exit(void)
+{
+ pr_debug("%s\n", __func__);
+
+ i2c_del_driver(&tc35876x_bridge_i2c_driver);
+
+ if (cmi_lcd_i2c_client)
+ i2c_del_driver(&cmi_lcd_i2c_driver);
+}
diff --git a/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h
new file mode 100644
index 000000000000..b14b7f9e7d1e
--- /dev/null
+++ b/drivers/gpu/drm/gma500/tc35876x-dsi-lvds.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2011 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __MDFLD_DSI_LVDS_BRIDGE_H__
+#define __MDFLD_DSI_LVDS_BRIDGE_H__
+
+void tc35876x_set_bridge_reset_state(struct drm_device *dev, int state);
+void tc35876x_configure_lvds_bridge(struct drm_device *dev);
+void tc35876x_brightness_control(struct drm_device *dev, int level);
+void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev);
+void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev);
+void tc35876x_init(struct drm_device *dev);
+void tc35876x_exit(void);
+
+extern const struct panel_funcs mdfld_tc35876x_funcs;
+
+#endif /*__MDFLD_DSI_LVDS_BRIDGE_H__*/
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
index 07d55df6623e..d3f2e8785010 100644
--- a/drivers/gpu/drm/i2c/ch7006_drv.c
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -252,10 +252,7 @@ static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
- priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "scale", 2);
- priv->scale_property->values[0] = 0;
- priv->scale_property->values[1] = 2;
+ priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2);
drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
priv->select_subconnector);
diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
index 7f4b4e10246e..2c8a60c3b98e 100644
--- a/drivers/gpu/drm/i810/i810_dma.c
+++ b/drivers/gpu/drm/i810/i810_dma.c
@@ -99,7 +99,6 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
buf_priv = buf->dev_private;
vma->vm_flags |= (VM_IO | VM_DONTCOPY);
- vma->vm_file = filp;
buf_priv->currently_mapped = I810_BUF_MAPPED;
@@ -1208,6 +1207,8 @@ int i810_driver_load(struct drm_device *dev, unsigned long flags)
dev->types[8] = _DRM_STAT_SECONDARY;
dev->types[9] = _DRM_STAT_DMA;
+ pci_set_master(dev->pdev);
+
return 0;
}
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 808b255d7fc6..ce7fc77678b4 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -3,7 +3,7 @@
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm
-i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
+i915-y := i915_drv.o i915_dma.o i915_irq.o \
i915_debugfs.o \
i915_suspend.o \
i915_gem.o \
diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index deaa657292b4..fdb7ccefffbd 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -83,6 +83,7 @@ static int i915_capabilities(struct seq_file *m, void *data)
B(supports_tv);
B(has_bsd_ring);
B(has_blt_ring);
+ B(has_llc);
#undef B
return 0;
@@ -563,45 +564,6 @@ static int i915_hws_info(struct seq_file *m, void *data)
return 0;
}
-static void i915_dump_object(struct seq_file *m,
- struct io_mapping *mapping,
- struct drm_i915_gem_object *obj)
-{
- int page, page_count, i;
-
- page_count = obj->base.size / PAGE_SIZE;
- for (page = 0; page < page_count; page++) {
- u32 *mem = io_mapping_map_wc(mapping,
- obj->gtt_offset + page * PAGE_SIZE);
- for (i = 0; i < PAGE_SIZE; i += 4)
- seq_printf(m, "%08x : %08x\n", i, mem[i / 4]);
- io_mapping_unmap(mem);
- }
-}
-
-static int i915_batchbuffer_info(struct seq_file *m, void *data)
-{
- struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct drm_device *dev = node->minor->dev;
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_gem_object *obj;
- int ret;
-
- ret = mutex_lock_interruptible(&dev->struct_mutex);
- if (ret)
- return ret;
-
- list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
- if (obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) {
- seq_printf(m, "--- gtt_offset = 0x%08x\n", obj->gtt_offset);
- i915_dump_object(m, dev_priv->mm.gtt_mapping, obj);
- }
- }
-
- mutex_unlock(&dev->struct_mutex);
- return 0;
-}
-
static int i915_ringbuffer_data(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -668,9 +630,9 @@ static int i915_ringbuffer_info(struct seq_file *m, void *data)
static const char *ring_str(int ring)
{
switch (ring) {
- case RING_RENDER: return " render";
- case RING_BSD: return " bsd";
- case RING_BLT: return " blt";
+ case RCS: return "render";
+ case VCS: return "bsd";
+ case BCS: return "blt";
default: return "";
}
}
@@ -713,7 +675,7 @@ static void print_error_buffers(struct seq_file *m,
seq_printf(m, "%s [%d]:\n", name, count);
while (count--) {
- seq_printf(m, " %08x %8u %04x %04x %08x%s%s%s%s%s%s",
+ seq_printf(m, " %08x %8u %04x %04x %08x%s%s%s%s%s%s%s",
err->gtt_offset,
err->size,
err->read_domains,
@@ -723,6 +685,7 @@ static void print_error_buffers(struct seq_file *m,
tiling_flag(err->tiling),
dirty_flag(err->dirty),
purgeable_flag(err->purgeable),
+ err->ring != -1 ? " " : "",
ring_str(err->ring),
cache_level_str(err->cache_level));
@@ -736,6 +699,38 @@ static void print_error_buffers(struct seq_file *m,
}
}
+static void i915_ring_error_state(struct seq_file *m,
+ struct drm_device *dev,
+ struct drm_i915_error_state *error,
+ unsigned ring)
+{
+ seq_printf(m, "%s command stream:\n", ring_str(ring));
+ seq_printf(m, " HEAD: 0x%08x\n", error->head[ring]);
+ seq_printf(m, " TAIL: 0x%08x\n", error->tail[ring]);
+ seq_printf(m, " ACTHD: 0x%08x\n", error->acthd[ring]);
+ seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]);
+ seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]);
+ seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]);
+ if (ring == RCS && INTEL_INFO(dev)->gen >= 4) {
+ seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1);
+ seq_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr);
+ }
+ if (INTEL_INFO(dev)->gen >= 4)
+ seq_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]);
+ seq_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]);
+ if (INTEL_INFO(dev)->gen >= 6) {
+ seq_printf(m, " FADDR: 0x%08x\n", error->faddr[ring]);
+ seq_printf(m, " FAULT_REG: 0x%08x\n", error->fault_reg[ring]);
+ seq_printf(m, " SYNC_0: 0x%08x\n",
+ error->semaphore_mboxes[ring][0]);
+ seq_printf(m, " SYNC_1: 0x%08x\n",
+ error->semaphore_mboxes[ring][1]);
+ }
+ seq_printf(m, " seqno: 0x%08x\n", error->seqno[ring]);
+ seq_printf(m, " ring->head: 0x%08x\n", error->cpu_ring_head[ring]);
+ seq_printf(m, " ring->tail: 0x%08x\n", error->cpu_ring_tail[ring]);
+}
+
static int i915_error_state(struct seq_file *m, void *unused)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
@@ -743,7 +738,7 @@ static int i915_error_state(struct seq_file *m, void *unused)
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_i915_error_state *error;
unsigned long flags;
- int i, page, offset, elt;
+ int i, j, page, offset, elt;
spin_lock_irqsave(&dev_priv->error_lock, flags);
if (!dev_priv->first_error) {
@@ -758,35 +753,20 @@ static int i915_error_state(struct seq_file *m, void *unused)
seq_printf(m, "PCI ID: 0x%04x\n", dev->pci_device);
seq_printf(m, "EIR: 0x%08x\n", error->eir);
seq_printf(m, "PGTBL_ER: 0x%08x\n", error->pgtbl_er);
+
+ for (i = 0; i < dev_priv->num_fence_regs; i++)
+ seq_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]);
+
if (INTEL_INFO(dev)->gen >= 6) {
seq_printf(m, "ERROR: 0x%08x\n", error->error);
- seq_printf(m, "Blitter command stream:\n");
- seq_printf(m, " ACTHD: 0x%08x\n", error->bcs_acthd);
- seq_printf(m, " IPEIR: 0x%08x\n", error->bcs_ipeir);
- seq_printf(m, " IPEHR: 0x%08x\n", error->bcs_ipehr);
- seq_printf(m, " INSTDONE: 0x%08x\n", error->bcs_instdone);
- seq_printf(m, " seqno: 0x%08x\n", error->bcs_seqno);
- seq_printf(m, "Video (BSD) command stream:\n");
- seq_printf(m, " ACTHD: 0x%08x\n", error->vcs_acthd);
- seq_printf(m, " IPEIR: 0x%08x\n", error->vcs_ipeir);
- seq_printf(m, " IPEHR: 0x%08x\n", error->vcs_ipehr);
- seq_printf(m, " INSTDONE: 0x%08x\n", error->vcs_instdone);
- seq_printf(m, " seqno: 0x%08x\n", error->vcs_seqno);
- }
- seq_printf(m, "Render command stream:\n");
- seq_printf(m, " ACTHD: 0x%08x\n", error->acthd);
- seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir);
- seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr);
- seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone);
- if (INTEL_INFO(dev)->gen >= 4) {
- seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1);
- seq_printf(m, " INSTPS: 0x%08x\n", error->instps);
+ seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg);
}
- seq_printf(m, " INSTPM: 0x%08x\n", error->instpm);
- seq_printf(m, " seqno: 0x%08x\n", error->seqno);
- for (i = 0; i < dev_priv->num_fence_regs; i++)
- seq_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]);
+ i915_ring_error_state(m, dev, error, RCS);
+ if (HAS_BLT(dev))
+ i915_ring_error_state(m, dev, error, BCS);
+ if (HAS_BSD(dev))
+ i915_ring_error_state(m, dev, error, VCS);
if (error->active_bo)
print_error_buffers(m, "Active",
@@ -798,10 +778,10 @@ static int i915_error_state(struct seq_file *m, void *unused)
error->pinned_bo,
error->pinned_bo_count);
- for (i = 0; i < ARRAY_SIZE(error->batchbuffer); i++) {
- if (error->batchbuffer[i]) {
- struct drm_i915_error_object *obj = error->batchbuffer[i];
+ for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
+ struct drm_i915_error_object *obj;
+ if ((obj = error->ring[i].batchbuffer)) {
seq_printf(m, "%s --- gtt_offset = 0x%08x\n",
dev_priv->ring[i].name,
obj->gtt_offset);
@@ -813,11 +793,20 @@ static int i915_error_state(struct seq_file *m, void *unused)
}
}
}
- }
- for (i = 0; i < ARRAY_SIZE(error->ringbuffer); i++) {
- if (error->ringbuffer[i]) {
- struct drm_i915_error_object *obj = error->ringbuffer[i];
+ if (error->ring[i].num_requests) {
+ seq_printf(m, "%s --- %d requests\n",
+ dev_priv->ring[i].name,
+ error->ring[i].num_requests);
+ for (j = 0; j < error->ring[i].num_requests; j++) {
+ seq_printf(m, " seqno 0x%08x, emitted %ld, tail 0x%08x\n",
+ error->ring[i].requests[j].seqno,
+ error->ring[i].requests[j].jiffies,
+ error->ring[i].requests[j].tail);
+ }
+ }
+
+ if ((obj = error->ring[i].ringbuffer)) {
seq_printf(m, "%s --- ringbuffer = 0x%08x\n",
dev_priv->ring[i].name,
obj->gtt_offset);
@@ -1414,9 +1403,108 @@ static int i915_gen6_forcewake_count_info(struct seq_file *m, void *data)
return 0;
}
+static const char *swizzle_string(unsigned swizzle)
+{
+ switch(swizzle) {
+ case I915_BIT_6_SWIZZLE_NONE:
+ return "none";
+ case I915_BIT_6_SWIZZLE_9:
+ return "bit9";
+ case I915_BIT_6_SWIZZLE_9_10:
+ return "bit9/bit10";
+ case I915_BIT_6_SWIZZLE_9_11:
+ return "bit9/bit11";
+ case I915_BIT_6_SWIZZLE_9_10_11:
+ return "bit9/bit10/bit11";
+ case I915_BIT_6_SWIZZLE_9_17:
+ return "bit9/bit17";
+ case I915_BIT_6_SWIZZLE_9_10_17:
+ return "bit9/bit10/bit17";
+ case I915_BIT_6_SWIZZLE_UNKNOWN:
+ return "unkown";
+ }
+
+ return "bug";
+}
+
+static int i915_swizzle_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ mutex_lock(&dev->struct_mutex);
+ seq_printf(m, "bit6 swizzle for X-tiling = %s\n",
+ swizzle_string(dev_priv->mm.bit_6_swizzle_x));
+ seq_printf(m, "bit6 swizzle for Y-tiling = %s\n",
+ swizzle_string(dev_priv->mm.bit_6_swizzle_y));
+
+ if (IS_GEN3(dev) || IS_GEN4(dev)) {
+ seq_printf(m, "DDC = 0x%08x\n",
+ I915_READ(DCC));
+ seq_printf(m, "C0DRB3 = 0x%04x\n",
+ I915_READ16(C0DRB3));
+ seq_printf(m, "C1DRB3 = 0x%04x\n",
+ I915_READ16(C1DRB3));
+ } else if (IS_GEN6(dev) || IS_GEN7(dev)) {
+ seq_printf(m, "MAD_DIMM_C0 = 0x%08x\n",
+ I915_READ(MAD_DIMM_C0));
+ seq_printf(m, "MAD_DIMM_C1 = 0x%08x\n",
+ I915_READ(MAD_DIMM_C1));
+ seq_printf(m, "MAD_DIMM_C2 = 0x%08x\n",
+ I915_READ(MAD_DIMM_C2));
+ seq_printf(m, "TILECTL = 0x%08x\n",
+ I915_READ(TILECTL));
+ seq_printf(m, "ARB_MODE = 0x%08x\n",
+ I915_READ(ARB_MODE));
+ seq_printf(m, "DISP_ARB_CTL = 0x%08x\n",
+ I915_READ(DISP_ARB_CTL));
+ }
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
+static int i915_ppgtt_info(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_ring_buffer *ring;
+ int i, ret;
+
+
+ ret = mutex_lock_interruptible(&dev->struct_mutex);
+ if (ret)
+ return ret;
+ if (INTEL_INFO(dev)->gen == 6)
+ seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(GFX_MODE));
+
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ ring = &dev_priv->ring[i];
+
+ seq_printf(m, "%s\n", ring->name);
+ if (INTEL_INFO(dev)->gen == 7)
+ seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(RING_MODE_GEN7(ring)));
+ seq_printf(m, "PP_DIR_BASE: 0x%08x\n", I915_READ(RING_PP_DIR_BASE(ring)));
+ seq_printf(m, "PP_DIR_BASE_READ: 0x%08x\n", I915_READ(RING_PP_DIR_BASE_READ(ring)));
+ seq_printf(m, "PP_DIR_DCLV: 0x%08x\n", I915_READ(RING_PP_DIR_DCLV(ring)));
+ }
+ if (dev_priv->mm.aliasing_ppgtt) {
+ struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+
+ seq_printf(m, "aliasing PPGTT:\n");
+ seq_printf(m, "pd gtt offset: 0x%08x\n", ppgtt->pd_offset);
+ }
+ seq_printf(m, "ECOCHK: 0x%08x\n", I915_READ(GAM_ECOCHK));
+ mutex_unlock(&dev->struct_mutex);
+
+ return 0;
+}
+
static int
-i915_wedged_open(struct inode *inode,
- struct file *filp)
+i915_debugfs_common_open(struct inode *inode,
+ struct file *filp)
{
filp->private_data = inode->i_private;
return 0;
@@ -1472,20 +1560,12 @@ i915_wedged_write(struct file *filp,
static const struct file_operations i915_wedged_fops = {
.owner = THIS_MODULE,
- .open = i915_wedged_open,
+ .open = i915_debugfs_common_open,
.read = i915_wedged_read,
.write = i915_wedged_write,
.llseek = default_llseek,
};
-static int
-i915_max_freq_open(struct inode *inode,
- struct file *filp)
-{
- filp->private_data = inode->i_private;
- return 0;
-}
-
static ssize_t
i915_max_freq_read(struct file *filp,
char __user *ubuf,
@@ -1542,20 +1622,12 @@ i915_max_freq_write(struct file *filp,
static const struct file_operations i915_max_freq_fops = {
.owner = THIS_MODULE,
- .open = i915_max_freq_open,
+ .open = i915_debugfs_common_open,
.read = i915_max_freq_read,
.write = i915_max_freq_write,
.llseek = default_llseek,
};
-static int
-i915_cache_sharing_open(struct inode *inode,
- struct file *filp)
-{
- filp->private_data = inode->i_private;
- return 0;
-}
-
static ssize_t
i915_cache_sharing_read(struct file *filp,
char __user *ubuf,
@@ -1621,7 +1693,7 @@ i915_cache_sharing_write(struct file *filp,
static const struct file_operations i915_cache_sharing_fops = {
.owner = THIS_MODULE,
- .open = i915_cache_sharing_open,
+ .open = i915_debugfs_common_open,
.read = i915_cache_sharing_read,
.write = i915_cache_sharing_write,
.llseek = default_llseek,
@@ -1653,21 +1725,6 @@ drm_add_fake_info_node(struct drm_minor *minor,
return 0;
}
-static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
-{
- struct drm_device *dev = minor->dev;
- struct dentry *ent;
-
- ent = debugfs_create_file("i915_wedged",
- S_IRUGO | S_IWUSR,
- root, dev,
- &i915_wedged_fops);
- if (IS_ERR(ent))
- return PTR_ERR(ent);
-
- return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
-}
-
static int i915_forcewake_open(struct inode *inode, struct file *file)
{
struct drm_device *dev = inode->i_private;
@@ -1729,34 +1786,22 @@ static int i915_forcewake_create(struct dentry *root, struct drm_minor *minor)
return drm_add_fake_info_node(minor, ent, &i915_forcewake_fops);
}
-static int i915_max_freq_create(struct dentry *root, struct drm_minor *minor)
-{
- struct drm_device *dev = minor->dev;
- struct dentry *ent;
-
- ent = debugfs_create_file("i915_max_freq",
- S_IRUGO | S_IWUSR,
- root, dev,
- &i915_max_freq_fops);
- if (IS_ERR(ent))
- return PTR_ERR(ent);
-
- return drm_add_fake_info_node(minor, ent, &i915_max_freq_fops);
-}
-
-static int i915_cache_sharing_create(struct dentry *root, struct drm_minor *minor)
+static int i915_debugfs_create(struct dentry *root,
+ struct drm_minor *minor,
+ const char *name,
+ const struct file_operations *fops)
{
struct drm_device *dev = minor->dev;
struct dentry *ent;
- ent = debugfs_create_file("i915_cache_sharing",
+ ent = debugfs_create_file(name,
S_IRUGO | S_IWUSR,
root, dev,
- &i915_cache_sharing_fops);
+ fops);
if (IS_ERR(ent))
return PTR_ERR(ent);
- return drm_add_fake_info_node(minor, ent, &i915_cache_sharing_fops);
+ return drm_add_fake_info_node(minor, ent, fops);
}
static struct drm_info_list i915_debugfs_list[] = {
@@ -1782,7 +1827,6 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_bsd_ringbuffer_info", i915_ringbuffer_info, 0, (void *)VCS},
{"i915_blt_ringbuffer_data", i915_ringbuffer_data, 0, (void *)BCS},
{"i915_blt_ringbuffer_info", i915_ringbuffer_info, 0, (void *)BCS},
- {"i915_batchbuffers", i915_batchbuffer_info, 0},
{"i915_error_state", i915_error_state, 0},
{"i915_rstdby_delays", i915_rstdby_delays, 0},
{"i915_cur_delayinfo", i915_cur_delayinfo, 0},
@@ -1798,6 +1842,8 @@ static struct drm_info_list i915_debugfs_list[] = {
{"i915_gem_framebuffer", i915_gem_framebuffer_info, 0},
{"i915_context_status", i915_context_status, 0},
{"i915_gen6_forcewake_count", i915_gen6_forcewake_count_info, 0},
+ {"i915_swizzle_info", i915_swizzle_info, 0},
+ {"i915_ppgtt_info", i915_ppgtt_info, 0},
};
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
@@ -1805,17 +1851,25 @@ int i915_debugfs_init(struct drm_minor *minor)
{
int ret;
- ret = i915_wedged_create(minor->debugfs_root, minor);
+ ret = i915_debugfs_create(minor->debugfs_root, minor,
+ "i915_wedged",
+ &i915_wedged_fops);
if (ret)
return ret;
ret = i915_forcewake_create(minor->debugfs_root, minor);
if (ret)
return ret;
- ret = i915_max_freq_create(minor->debugfs_root, minor);
+
+ ret = i915_debugfs_create(minor->debugfs_root, minor,
+ "i915_max_freq",
+ &i915_max_freq_fops);
if (ret)
return ret;
- ret = i915_cache_sharing_create(minor->debugfs_root, minor);
+
+ ret = i915_debugfs_create(minor->debugfs_root, minor,
+ "i915_cache_sharing",
+ &i915_cache_sharing_fops);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index ddfe3d902b2a..9341eb8ce93b 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -784,6 +784,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
case I915_PARAM_HAS_GEN7_SOL_RESET:
value = 1;
break;
+ case I915_PARAM_HAS_LLC:
+ value = HAS_LLC(dev);
+ break;
default:
DRM_DEBUG_DRIVER("Unknown parameter %d\n",
param->param);
@@ -1193,22 +1196,39 @@ static int i915_load_gem_init(struct drm_device *dev)
/* Basic memrange allocator for stolen space */
drm_mm_init(&dev_priv->mm.stolen, 0, prealloc_size);
- /* Let GEM Manage all of the aperture.
- *
- * However, leave one page at the end still bound to the scratch page.
- * There are a number of places where the hardware apparently
- * prefetches past the end of the object, and we've seen multiple
- * hangs with the GPU head pointer stuck in a batchbuffer bound
- * at the last page of the aperture. One page should be enough to
- * keep any prefetching inside of the aperture.
- */
- i915_gem_do_init(dev, 0, mappable_size, gtt_size - PAGE_SIZE);
-
mutex_lock(&dev->struct_mutex);
- ret = i915_gem_init_ringbuffer(dev);
+ if (i915_enable_ppgtt && HAS_ALIASING_PPGTT(dev)) {
+ /* PPGTT pdes are stolen from global gtt ptes, so shrink the
+ * aperture accordingly when using aliasing ppgtt. */
+ gtt_size -= I915_PPGTT_PD_ENTRIES*PAGE_SIZE;
+ /* For paranoia keep the guard page in between. */
+ gtt_size -= PAGE_SIZE;
+
+ i915_gem_do_init(dev, 0, mappable_size, gtt_size);
+
+ ret = i915_gem_init_aliasing_ppgtt(dev);
+ if (ret)
+ return ret;
+ } else {
+ /* Let GEM Manage all of the aperture.
+ *
+ * However, leave one page at the end still bound to the scratch
+ * page. There are a number of places where the hardware
+ * apparently prefetches past the end of the object, and we've
+ * seen multiple hangs with the GPU head pointer stuck in a
+ * batchbuffer bound at the last page of the aperture. One page
+ * should be enough to keep any prefetching inside of the
+ * aperture.
+ */
+ i915_gem_do_init(dev, 0, mappable_size, gtt_size - PAGE_SIZE);
+ }
+
+ ret = i915_gem_init_hw(dev);
mutex_unlock(&dev->struct_mutex);
- if (ret)
+ if (ret) {
+ i915_gem_cleanup_aliasing_ppgtt(dev);
return ret;
+ }
/* Try to set up FBC with a reasonable compressed buffer size */
if (I915_HAS_FBC(dev) && i915_powersave) {
@@ -1295,6 +1315,7 @@ cleanup_gem:
mutex_lock(&dev->struct_mutex);
i915_gem_cleanup_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
+ i915_gem_cleanup_aliasing_ppgtt(dev);
cleanup_vga_switcheroo:
vga_switcheroo_unregister_client(dev->pdev);
cleanup_vga_client:
@@ -1930,6 +1951,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
goto free_priv;
}
+ pci_set_master(dev->pdev);
+
/* overlay on gen2 is broken and can't address above 1G */
if (IS_GEN2(dev))
dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(30));
@@ -2129,7 +2152,7 @@ int i915_driver_unload(struct drm_device *dev)
unregister_shrinker(&dev_priv->mm.inactive_shrinker);
mutex_lock(&dev->struct_mutex);
- ret = i915_gpu_idle(dev);
+ ret = i915_gpu_idle(dev, true);
if (ret)
DRM_ERROR("failed to idle hardware: %d\n", ret);
mutex_unlock(&dev->struct_mutex);
@@ -2182,6 +2205,7 @@ int i915_driver_unload(struct drm_device *dev)
i915_gem_free_all_phys_object(dev);
i915_gem_cleanup_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
+ i915_gem_cleanup_aliasing_ppgtt(dev);
if (I915_HAS_FBC(dev) && i915_powersave)
i915_cleanup_compression(dev);
drm_mm_takedown(&dev_priv->mm.stolen);
@@ -2247,18 +2271,12 @@ void i915_driver_lastclose(struct drm_device * dev)
i915_gem_lastclose(dev);
- if (dev_priv->agp_heap)
- i915_mem_takedown(&(dev_priv->agp_heap));
-
i915_dma_cleanup(dev);
}
void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv)
{
- drm_i915_private_t *dev_priv = dev->dev_private;
i915_gem_release(dev, file_priv);
- if (!drm_core_check_feature(dev, DRIVER_MODESET))
- i915_mem_release(dev, file_priv, dev_priv->agp_heap);
}
void i915_driver_postclose(struct drm_device *dev, struct drm_file *file)
@@ -2277,11 +2295,11 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF_DRV(I915_IRQ_WAIT, i915_irq_wait, DRM_AUTH),
DRM_IOCTL_DEF_DRV(I915_GETPARAM, i915_getparam, DRM_AUTH),
DRM_IOCTL_DEF_DRV(I915_SETPARAM, i915_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
- DRM_IOCTL_DEF_DRV(I915_ALLOC, i915_mem_alloc, DRM_AUTH),
- DRM_IOCTL_DEF_DRV(I915_FREE, i915_mem_free, DRM_AUTH),
- DRM_IOCTL_DEF_DRV(I915_INIT_HEAP, i915_mem_init_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(I915_ALLOC, drm_noop, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(I915_FREE, drm_noop, DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(I915_INIT_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF_DRV(I915_CMDBUFFER, i915_cmdbuffer, DRM_AUTH),
- DRM_IOCTL_DEF_DRV(I915_DESTROY_HEAP, i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF_DRV(I915_DESTROY_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF_DRV(I915_SET_VBLANK_PIPE, i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF_DRV(I915_GET_VBLANK_PIPE, i915_vblank_pipe_get, DRM_AUTH),
DRM_IOCTL_DEF_DRV(I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH),
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 308f81913562..1a7559b59997 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -103,6 +103,11 @@ MODULE_PARM_DESC(enable_hangcheck,
"WARNING: Disabling this can cause system wide hangs. "
"(default: true)");
+bool i915_enable_ppgtt __read_mostly = 1;
+module_param_named(i915_enable_ppgtt, i915_enable_ppgtt, bool, 0600);
+MODULE_PARM_DESC(i915_enable_ppgtt,
+ "Enable PPGTT (default: true)");
+
static struct drm_driver driver;
extern int intel_agp_enabled;
@@ -198,7 +203,7 @@ static const struct intel_device_info intel_pineview_info = {
static const struct intel_device_info intel_ironlake_d_info = {
.gen = 5,
- .need_gfx_hws = 1, .has_pipe_cxsr = 1, .has_hotplug = 1,
+ .need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
};
@@ -214,6 +219,7 @@ static const struct intel_device_info intel_sandybridge_d_info = {
.need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
+ .has_llc = 1,
};
static const struct intel_device_info intel_sandybridge_m_info = {
@@ -222,6 +228,7 @@ static const struct intel_device_info intel_sandybridge_m_info = {
.has_fbc = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
+ .has_llc = 1,
};
static const struct intel_device_info intel_ivybridge_d_info = {
@@ -229,6 +236,7 @@ static const struct intel_device_info intel_ivybridge_d_info = {
.need_gfx_hws = 1, .has_hotplug = 1,
.has_bsd_ring = 1,
.has_blt_ring = 1,
+ .has_llc = 1,
};
static const struct intel_device_info intel_ivybridge_m_info = {
@@ -237,6 +245,7 @@ static const struct intel_device_info intel_ivybridge_m_info = {
.has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */
.has_bsd_ring = 1,
.has_blt_ring = 1,
+ .has_llc = 1,
};
static const struct pci_device_id pciidlist[] = { /* aka */
@@ -376,16 +385,27 @@ void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags);
}
+static void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
+{
+ u32 gtfifodbg;
+ gtfifodbg = I915_READ_NOTRACE(GTFIFODBG);
+ if (WARN(gtfifodbg & GT_FIFO_CPU_ERROR_MASK,
+ "MMIO read or write has been dropped %x\n", gtfifodbg))
+ I915_WRITE_NOTRACE(GTFIFODBG, GT_FIFO_CPU_ERROR_MASK);
+}
+
void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE, 0);
- POSTING_READ(FORCEWAKE);
+ /* The below doubles as a POSTING_READ */
+ gen6_gt_check_fifodbg(dev_priv);
}
void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE_MT, (1<<16) | 0);
- POSTING_READ(FORCEWAKE_MT);
+ /* The below doubles as a POSTING_READ */
+ gen6_gt_check_fifodbg(dev_priv);
}
/*
@@ -401,8 +421,10 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
spin_unlock_irqrestore(&dev_priv->gt_lock, irqflags);
}
-void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
+int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
{
+ int ret = 0;
+
if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) {
int loop = 500;
u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
@@ -410,10 +432,13 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
udelay(10);
fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
}
- WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES);
+ if (WARN_ON(loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES))
+ ++ret;
dev_priv->gt_fifo_count = fifo;
}
dev_priv->gt_fifo_count--;
+
+ return ret;
}
static int i915_drm_freeze(struct drm_device *dev)
@@ -442,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;
}
@@ -494,7 +523,7 @@ static int i915_drm_thaw(struct drm_device *dev)
mutex_lock(&dev->struct_mutex);
dev_priv->mm.suspended = 0;
- error = i915_gem_init_ringbuffer(dev);
+ error = i915_gem_init_hw(dev);
mutex_unlock(&dev->struct_mutex);
if (HAS_PCH_SPLIT(dev))
@@ -514,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;
}
@@ -633,7 +665,7 @@ static int gen6_do_reset(struct drm_device *dev, u8 flags)
}
/**
- * i965_reset - reset chip after a hang
+ * i915_reset - reset chip after a hang
* @dev: drm device to reset
* @flags: reset domains
*
@@ -709,12 +741,16 @@ int i915_reset(struct drm_device *dev, u8 flags)
!dev_priv->mm.suspended) {
dev_priv->mm.suspended = 0;
+ i915_gem_init_swizzling(dev);
+
dev_priv->ring[RCS].init(&dev_priv->ring[RCS]);
if (HAS_BSD(dev))
dev_priv->ring[VCS].init(&dev_priv->ring[VCS]);
if (HAS_BLT(dev))
dev_priv->ring[BCS].init(&dev_priv->ring[BCS]);
+ i915_gem_init_ppgtt(dev);
+
mutex_unlock(&dev->struct_mutex);
drm_irq_uninstall(dev);
drm_mode_config_reset(dev);
@@ -977,11 +1013,15 @@ __i915_read(64, q)
#define __i915_write(x, y) \
void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
+ u32 __fifo_ret = 0; \
trace_i915_reg_rw(true, reg, val, sizeof(val)); \
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
- __gen6_gt_wait_for_fifo(dev_priv); \
+ __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \
} \
write##y(val, dev_priv->regs + reg); \
+ if (unlikely(__fifo_ret)) { \
+ gen6_gt_check_fifodbg(dev_priv); \
+ } \
}
__i915_write(8, b)
__i915_write(16, w)
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 9689ca38b2b3..c0f19f572004 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -35,6 +35,7 @@
#include "intel_ringbuffer.h"
#include <linux/io-mapping.h>
#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
#include <drm/intel-gtt.h>
#include <linux/backlight.h>
@@ -135,6 +136,7 @@ struct drm_i915_fence_reg {
struct list_head lru_list;
struct drm_i915_gem_object *obj;
uint32_t setup_seqno;
+ int pin_count;
};
struct sdvo_device_mapping {
@@ -152,33 +154,40 @@ struct drm_i915_error_state {
u32 eir;
u32 pgtbl_er;
u32 pipestat[I915_MAX_PIPES];
- u32 ipeir;
- u32 ipehr;
- u32 instdone;
- u32 acthd;
+ u32 tail[I915_NUM_RINGS];
+ u32 head[I915_NUM_RINGS];
+ u32 ipeir[I915_NUM_RINGS];
+ u32 ipehr[I915_NUM_RINGS];
+ u32 instdone[I915_NUM_RINGS];
+ u32 acthd[I915_NUM_RINGS];
+ u32 semaphore_mboxes[I915_NUM_RINGS][I915_NUM_RINGS - 1];
+ /* our own tracking of ring head and tail */
+ u32 cpu_ring_head[I915_NUM_RINGS];
+ u32 cpu_ring_tail[I915_NUM_RINGS];
u32 error; /* gen6+ */
- u32 bcs_acthd; /* gen6+ blt engine */
- u32 bcs_ipehr;
- u32 bcs_ipeir;
- u32 bcs_instdone;
- u32 bcs_seqno;
- u32 vcs_acthd; /* gen6+ bsd engine */
- u32 vcs_ipehr;
- u32 vcs_ipeir;
- u32 vcs_instdone;
- u32 vcs_seqno;
- u32 instpm;
- u32 instps;
+ u32 instpm[I915_NUM_RINGS];
+ u32 instps[I915_NUM_RINGS];
u32 instdone1;
- u32 seqno;
+ u32 seqno[I915_NUM_RINGS];
u64 bbaddr;
+ u32 fault_reg[I915_NUM_RINGS];
+ u32 done_reg;
+ u32 faddr[I915_NUM_RINGS];
u64 fence[I915_MAX_NUM_FENCES];
struct timeval time;
- struct drm_i915_error_object {
- int page_count;
- u32 gtt_offset;
- u32 *pages[0];
- } *ringbuffer[I915_NUM_RINGS], *batchbuffer[I915_NUM_RINGS];
+ struct drm_i915_error_ring {
+ struct drm_i915_error_object {
+ int page_count;
+ u32 gtt_offset;
+ u32 *pages[0];
+ } *ringbuffer, *batchbuffer;
+ struct drm_i915_error_request {
+ long jiffies;
+ u32 seqno;
+ u32 tail;
+ } *requests;
+ int num_requests;
+ } ring[I915_NUM_RINGS];
struct drm_i915_error_buffer {
u32 size;
u32 name;
@@ -191,7 +200,7 @@ struct drm_i915_error_state {
u32 tiling:2;
u32 dirty:1;
u32 purgeable:1;
- u32 ring:4;
+ s32 ring:4;
u32 cache_level:2;
} *active_bo, *pinned_bo;
u32 active_bo_count, pinned_bo_count;
@@ -255,6 +264,17 @@ struct intel_device_info {
u8 supports_tv:1;
u8 has_bsd_ring:1;
u8 has_blt_ring:1;
+ u8 has_llc:1;
+};
+
+#define I915_PPGTT_PD_ENTRIES 512
+#define I915_PPGTT_PT_ENTRIES 1024
+struct i915_hw_ppgtt {
+ unsigned num_pd_entries;
+ struct page **pt_pages;
+ uint32_t pd_offset;
+ dma_addr_t *pt_dma_addr;
+ dma_addr_t scratch_page_dma_addr;
};
enum no_fbc_reason {
@@ -279,6 +299,16 @@ enum intel_pch {
struct intel_fbdev;
struct intel_fbc_work;
+struct intel_gmbus {
+ struct i2c_adapter adapter;
+ bool force_bit;
+ bool has_gpio;
+ u32 reg0;
+ u32 gpio_reg;
+ struct i2c_algo_bit_data bit_algo;
+ struct drm_i915_private *dev_priv;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
@@ -296,11 +326,11 @@ typedef struct drm_i915_private {
/** gt_lock is also taken in irq contexts. */
struct spinlock gt_lock;
- struct intel_gmbus {
- struct i2c_adapter adapter;
- struct i2c_adapter *force_bit;
- u32 reg0;
- } *gmbus;
+ struct intel_gmbus *gmbus;
+
+ /** gmbus_mutex protects against concurrent usage of the single hw gmbus
+ * controller on different i2c buses. */
+ struct mutex gmbus_mutex;
struct pci_dev *bridge_dev;
struct intel_ring_buffer ring[I915_NUM_RINGS];
@@ -335,7 +365,6 @@ typedef struct drm_i915_private {
int tex_lru_log_granularity;
int allow_batchbuffer;
- struct mem_block *agp_heap;
unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
int vblank_pipe;
int num_pipe;
@@ -584,6 +613,9 @@ typedef struct drm_i915_private {
struct io_mapping *gtt_mapping;
int gtt_mtrr;
+ /** PPGTT used for aliasing the PPGTT with the GTT */
+ struct i915_hw_ppgtt *aliasing_ppgtt;
+
struct shrinker inactive_shrinker;
/**
@@ -749,6 +781,13 @@ typedef struct drm_i915_private {
struct drm_property *force_audio_property;
} drm_i915_private_t;
+enum hdmi_force_audio {
+ HDMI_AUDIO_OFF_DVI = -2, /* no aux data for HDMI-DVI converter */
+ HDMI_AUDIO_OFF, /* force turn off HDMI audio */
+ HDMI_AUDIO_AUTO, /* trust EDID */
+ HDMI_AUDIO_ON, /* force turn on HDMI audio */
+};
+
enum i915_cache_level {
I915_CACHE_NONE,
I915_CACHE_LLC,
@@ -841,6 +880,8 @@ struct drm_i915_gem_object {
unsigned int cache_level:2;
+ unsigned int has_aliasing_ppgtt_mapping:1;
+
struct page **pages;
/**
@@ -918,6 +959,9 @@ struct drm_i915_gem_request {
/** GEM sequence number associated with this request. */
uint32_t seqno;
+ /** Postion in the ringbuffer of the end of the request */
+ u32 tail;
+
/** Time at which this request was emitted, in jiffies. */
unsigned long emitted_jiffies;
@@ -974,8 +1018,11 @@ struct drm_i915_file_private {
#define HAS_BSD(dev) (INTEL_INFO(dev)->has_bsd_ring)
#define HAS_BLT(dev) (INTEL_INFO(dev)->has_blt_ring)
+#define HAS_LLC(dev) (INTEL_INFO(dev)->has_llc)
#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
+#define HAS_ALIASING_PPGTT(dev) (INTEL_INFO(dev)->gen >=6)
+
#define HAS_OVERLAY(dev) (INTEL_INFO(dev)->has_overlay)
#define OVERLAY_NEEDS_PHYSICAL(dev) (INTEL_INFO(dev)->overlay_needs_physical)
@@ -1018,6 +1065,7 @@ extern int i915_vbt_sdvo_panel_type __read_mostly;
extern int i915_enable_rc6 __read_mostly;
extern int i915_enable_fbc __read_mostly;
extern bool i915_enable_hangcheck __read_mostly;
+extern bool i915_enable_ppgtt __read_mostly;
extern int i915_suspend(struct drm_device *dev, pm_message_t state);
extern int i915_resume(struct drm_device *dev);
@@ -1079,18 +1127,6 @@ extern void i915_destroy_error_state(struct drm_device *dev);
#endif
-/* i915_mem.c */
-extern int i915_mem_alloc(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-extern int i915_mem_free(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-extern int i915_mem_init_heap(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-extern int i915_mem_destroy_heap(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
-extern void i915_mem_takedown(struct mem_block **heap);
-extern void i915_mem_release(struct drm_device * dev,
- struct drm_file *file_priv, struct mem_block *heap);
/* i915_gem.c */
int i915_gem_init_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
@@ -1170,37 +1206,55 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2)
return (int32_t)(seq1 - seq2) >= 0;
}
-static inline u32
-i915_gem_next_request_seqno(struct intel_ring_buffer *ring)
-{
- drm_i915_private_t *dev_priv = ring->dev->dev_private;
- return ring->outstanding_lazy_request = dev_priv->next_seqno;
-}
+u32 i915_gem_next_request_seqno(struct intel_ring_buffer *ring);
int __must_check i915_gem_object_get_fence(struct drm_i915_gem_object *obj,
struct intel_ring_buffer *pipelined);
int __must_check i915_gem_object_put_fence(struct drm_i915_gem_object *obj);
+static inline void
+i915_gem_object_pin_fence(struct drm_i915_gem_object *obj)
+{
+ if (obj->fence_reg != I915_FENCE_REG_NONE) {
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+ dev_priv->fence_regs[obj->fence_reg].pin_count++;
+ }
+}
+
+static inline void
+i915_gem_object_unpin_fence(struct drm_i915_gem_object *obj)
+{
+ if (obj->fence_reg != I915_FENCE_REG_NONE) {
+ struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+ dev_priv->fence_regs[obj->fence_reg].pin_count--;
+ }
+}
+
void i915_gem_retire_requests(struct drm_device *dev);
+void i915_gem_retire_requests_ring(struct intel_ring_buffer *ring);
+
void i915_gem_reset(struct drm_device *dev);
void i915_gem_clflush_object(struct drm_i915_gem_object *obj);
int __must_check i915_gem_object_set_domain(struct drm_i915_gem_object *obj,
uint32_t read_domains,
uint32_t write_domain);
int __must_check i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj);
-int __must_check i915_gem_init_ringbuffer(struct drm_device *dev);
+int __must_check i915_gem_init_hw(struct drm_device *dev);
+void i915_gem_init_swizzling(struct drm_device *dev);
+void i915_gem_init_ppgtt(struct drm_device *dev);
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
void i915_gem_do_init(struct drm_device *dev,
unsigned long start,
unsigned long mappable_end,
unsigned long end);
-int __must_check i915_gpu_idle(struct drm_device *dev);
+int __must_check i915_gpu_idle(struct drm_device *dev, bool do_retire);
int __must_check i915_gem_idle(struct drm_device *dev);
int __must_check i915_add_request(struct intel_ring_buffer *ring,
struct drm_file *file,
struct drm_i915_gem_request *request);
int __must_check i915_wait_request(struct intel_ring_buffer *ring,
- uint32_t seqno);
+ uint32_t seqno,
+ bool do_retire);
int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
int __must_check
i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj,
@@ -1227,6 +1281,14 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
enum i915_cache_level cache_level);
/* i915_gem_gtt.c */
+int __must_check i915_gem_init_aliasing_ppgtt(struct drm_device *dev);
+void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev);
+void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
+ struct drm_i915_gem_object *obj,
+ enum i915_cache_level cache_level);
+void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
+ struct drm_i915_gem_object *obj);
+
void i915_gem_restore_gtt_mappings(struct drm_device *dev);
int __must_check i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj);
void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
@@ -1365,7 +1427,7 @@ extern void intel_display_print_error_state(struct seq_file *m,
*/
void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv);
void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv);
-void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
+int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
/* We give fast paths for the really cool registers */
#define NEEDS_FORCE_WAKE(dev_priv, reg) \
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index e55badb2d86d..1f441f5c2405 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -58,6 +58,7 @@ static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj);
static int i915_gem_inactive_shrink(struct shrinker *shrinker,
struct shrink_control *sc);
+static void i915_gem_object_truncate(struct drm_i915_gem_object *obj);
/* some bookkeeping */
static void i915_gem_info_add_obj(struct drm_i915_private *dev_priv,
@@ -258,73 +259,6 @@ static int i915_gem_object_needs_bit17_swizzle(struct drm_i915_gem_object *obj)
obj->tiling_mode != I915_TILING_NONE;
}
-static inline void
-slow_shmem_copy(struct page *dst_page,
- int dst_offset,
- struct page *src_page,
- int src_offset,
- int length)
-{
- char *dst_vaddr, *src_vaddr;
-
- dst_vaddr = kmap(dst_page);
- src_vaddr = kmap(src_page);
-
- memcpy(dst_vaddr + dst_offset, src_vaddr + src_offset, length);
-
- kunmap(src_page);
- kunmap(dst_page);
-}
-
-static inline void
-slow_shmem_bit17_copy(struct page *gpu_page,
- int gpu_offset,
- struct page *cpu_page,
- int cpu_offset,
- int length,
- int is_read)
-{
- char *gpu_vaddr, *cpu_vaddr;
-
- /* Use the unswizzled path if this page isn't affected. */
- if ((page_to_phys(gpu_page) & (1 << 17)) == 0) {
- if (is_read)
- return slow_shmem_copy(cpu_page, cpu_offset,
- gpu_page, gpu_offset, length);
- else
- return slow_shmem_copy(gpu_page, gpu_offset,
- cpu_page, cpu_offset, length);
- }
-
- gpu_vaddr = kmap(gpu_page);
- cpu_vaddr = kmap(cpu_page);
-
- /* Copy the data, XORing A6 with A17 (1). The user already knows he's
- * XORing with the other bits (A9 for Y, A9 and A10 for X)
- */
- while (length > 0) {
- int cacheline_end = ALIGN(gpu_offset + 1, 64);
- int this_length = min(cacheline_end - gpu_offset, length);
- int swizzled_gpu_offset = gpu_offset ^ 64;
-
- if (is_read) {
- memcpy(cpu_vaddr + cpu_offset,
- gpu_vaddr + swizzled_gpu_offset,
- this_length);
- } else {
- memcpy(gpu_vaddr + swizzled_gpu_offset,
- cpu_vaddr + cpu_offset,
- this_length);
- }
- cpu_offset += this_length;
- gpu_offset += this_length;
- length -= this_length;
- }
-
- kunmap(cpu_page);
- kunmap(gpu_page);
-}
-
/**
* This is the fast shmem pread path, which attempts to copy_from_user directly
* from the backing pages of the object to the user's address space. On a
@@ -385,6 +319,58 @@ i915_gem_shmem_pread_fast(struct drm_device *dev,
return 0;
}
+static inline int
+__copy_to_user_swizzled(char __user *cpu_vaddr,
+ const char *gpu_vaddr, int gpu_offset,
+ int length)
+{
+ int ret, cpu_offset = 0;
+
+ while (length > 0) {
+ int cacheline_end = ALIGN(gpu_offset + 1, 64);
+ int this_length = min(cacheline_end - gpu_offset, length);
+ int swizzled_gpu_offset = gpu_offset ^ 64;
+
+ ret = __copy_to_user(cpu_vaddr + cpu_offset,
+ gpu_vaddr + swizzled_gpu_offset,
+ this_length);
+ if (ret)
+ return ret + length;
+
+ cpu_offset += this_length;
+ gpu_offset += this_length;
+ length -= this_length;
+ }
+
+ return 0;
+}
+
+static inline int
+__copy_from_user_swizzled(char __user *gpu_vaddr, int gpu_offset,
+ const char *cpu_vaddr,
+ int length)
+{
+ int ret, cpu_offset = 0;
+
+ while (length > 0) {
+ int cacheline_end = ALIGN(gpu_offset + 1, 64);
+ int this_length = min(cacheline_end - gpu_offset, length);
+ int swizzled_gpu_offset = gpu_offset ^ 64;
+
+ ret = __copy_from_user(gpu_vaddr + swizzled_gpu_offset,
+ cpu_vaddr + cpu_offset,
+ this_length);
+ if (ret)
+ return ret + length;
+
+ cpu_offset += this_length;
+ gpu_offset += this_length;
+ length -= this_length;
+ }
+
+ return 0;
+}
+
/**
* This is the fallback shmem pread path, which allocates temporary storage
* in kernel space to copy_to_user into outside of the struct_mutex, so we
@@ -398,72 +384,34 @@ i915_gem_shmem_pread_slow(struct drm_device *dev,
struct drm_file *file)
{
struct address_space *mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
- struct mm_struct *mm = current->mm;
- struct page **user_pages;
+ char __user *user_data;
ssize_t remain;
- loff_t offset, pinned_pages, i;
- loff_t first_data_page, last_data_page, num_pages;
- int shmem_page_offset;
- int data_page_index, data_page_offset;
- int page_length;
- int ret;
- uint64_t data_ptr = args->data_ptr;
- int do_bit17_swizzling;
+ loff_t offset;
+ int shmem_page_offset, page_length, ret;
+ int obj_do_bit17_swizzling, page_do_bit17_swizzling;
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
remain = args->size;
- /* Pin the user pages containing the data. We can't fault while
- * holding the struct mutex, yet we want to hold it while
- * dereferencing the user data.
- */
- first_data_page = data_ptr / PAGE_SIZE;
- last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
- num_pages = last_data_page - first_data_page + 1;
+ obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
- user_pages = drm_malloc_ab(num_pages, sizeof(struct page *));
- if (user_pages == NULL)
- return -ENOMEM;
+ offset = args->offset;
mutex_unlock(&dev->struct_mutex);
- down_read(&mm->mmap_sem);
- pinned_pages = get_user_pages(current, mm, (uintptr_t)args->data_ptr,
- num_pages, 1, 0, user_pages, NULL);
- up_read(&mm->mmap_sem);
- mutex_lock(&dev->struct_mutex);
- if (pinned_pages < num_pages) {
- ret = -EFAULT;
- goto out;
- }
-
- ret = i915_gem_object_set_cpu_read_domain_range(obj,
- args->offset,
- args->size);
- if (ret)
- goto out;
-
- do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
-
- offset = args->offset;
while (remain > 0) {
struct page *page;
+ char *vaddr;
/* Operation in this page
*
* shmem_page_offset = offset within page in shmem file
- * data_page_index = page number in get_user_pages return
- * data_page_offset = offset with data_page_index page.
* page_length = bytes to copy for this page
*/
shmem_page_offset = offset_in_page(offset);
- data_page_index = data_ptr / PAGE_SIZE - first_data_page;
- data_page_offset = offset_in_page(data_ptr);
-
page_length = remain;
if ((shmem_page_offset + page_length) > PAGE_SIZE)
page_length = PAGE_SIZE - shmem_page_offset;
- if ((data_page_offset + page_length) > PAGE_SIZE)
- page_length = PAGE_SIZE - data_page_offset;
page = shmem_read_mapping_page(mapping, offset >> PAGE_SHIFT);
if (IS_ERR(page)) {
@@ -471,36 +419,38 @@ i915_gem_shmem_pread_slow(struct drm_device *dev,
goto out;
}
- if (do_bit17_swizzling) {
- slow_shmem_bit17_copy(page,
- shmem_page_offset,
- user_pages[data_page_index],
- data_page_offset,
- page_length,
- 1);
- } else {
- slow_shmem_copy(user_pages[data_page_index],
- data_page_offset,
- page,
- shmem_page_offset,
- page_length);
- }
+ page_do_bit17_swizzling = obj_do_bit17_swizzling &&
+ (page_to_phys(page) & (1 << 17)) != 0;
+
+ vaddr = kmap(page);
+ if (page_do_bit17_swizzling)
+ ret = __copy_to_user_swizzled(user_data,
+ vaddr, shmem_page_offset,
+ page_length);
+ else
+ ret = __copy_to_user(user_data,
+ vaddr + shmem_page_offset,
+ page_length);
+ kunmap(page);
mark_page_accessed(page);
page_cache_release(page);
+ if (ret) {
+ ret = -EFAULT;
+ goto out;
+ }
+
remain -= page_length;
- data_ptr += page_length;
+ user_data += page_length;
offset += page_length;
}
out:
- for (i = 0; i < pinned_pages; i++) {
- SetPageDirty(user_pages[i]);
- mark_page_accessed(user_pages[i]);
- page_cache_release(user_pages[i]);
- }
- drm_free_large(user_pages);
+ mutex_lock(&dev->struct_mutex);
+ /* Fixup: Kill any reinstated backing storage pages */
+ if (obj->madv == __I915_MADV_PURGED)
+ i915_gem_object_truncate(obj);
return ret;
}
@@ -841,71 +791,36 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev,
struct drm_file *file)
{
struct address_space *mapping = obj->base.filp->f_path.dentry->d_inode->i_mapping;
- struct mm_struct *mm = current->mm;
- struct page **user_pages;
ssize_t remain;
- loff_t offset, pinned_pages, i;
- loff_t first_data_page, last_data_page, num_pages;
- int shmem_page_offset;
- int data_page_index, data_page_offset;
- int page_length;
- int ret;
- uint64_t data_ptr = args->data_ptr;
- int do_bit17_swizzling;
+ loff_t offset;
+ char __user *user_data;
+ int shmem_page_offset, page_length, ret;
+ int obj_do_bit17_swizzling, page_do_bit17_swizzling;
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
remain = args->size;
- /* Pin the user pages containing the data. We can't fault while
- * holding the struct mutex, and all of the pwrite implementations
- * want to hold it while dereferencing the user data.
- */
- first_data_page = data_ptr / PAGE_SIZE;
- last_data_page = (data_ptr + args->size - 1) / PAGE_SIZE;
- num_pages = last_data_page - first_data_page + 1;
-
- user_pages = drm_malloc_ab(num_pages, sizeof(struct page *));
- if (user_pages == NULL)
- return -ENOMEM;
-
- mutex_unlock(&dev->struct_mutex);
- down_read(&mm->mmap_sem);
- pinned_pages = get_user_pages(current, mm, (uintptr_t)args->data_ptr,
- num_pages, 0, 0, user_pages, NULL);
- up_read(&mm->mmap_sem);
- mutex_lock(&dev->struct_mutex);
- if (pinned_pages < num_pages) {
- ret = -EFAULT;
- goto out;
- }
-
- ret = i915_gem_object_set_to_cpu_domain(obj, 1);
- if (ret)
- goto out;
-
- do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
+ obj_do_bit17_swizzling = i915_gem_object_needs_bit17_swizzle(obj);
offset = args->offset;
obj->dirty = 1;
+ mutex_unlock(&dev->struct_mutex);
+
while (remain > 0) {
struct page *page;
+ char *vaddr;
/* Operation in this page
*
* shmem_page_offset = offset within page in shmem file
- * data_page_index = page number in get_user_pages return
- * data_page_offset = offset with data_page_index page.
* page_length = bytes to copy for this page
*/
shmem_page_offset = offset_in_page(offset);
- data_page_index = data_ptr / PAGE_SIZE - first_data_page;
- data_page_offset = offset_in_page(data_ptr);
page_length = remain;
if ((shmem_page_offset + page_length) > PAGE_SIZE)
page_length = PAGE_SIZE - shmem_page_offset;
- if ((data_page_offset + page_length) > PAGE_SIZE)
- page_length = PAGE_SIZE - data_page_offset;
page = shmem_read_mapping_page(mapping, offset >> PAGE_SHIFT);
if (IS_ERR(page)) {
@@ -913,34 +828,45 @@ i915_gem_shmem_pwrite_slow(struct drm_device *dev,
goto out;
}
- if (do_bit17_swizzling) {
- slow_shmem_bit17_copy(page,
- shmem_page_offset,
- user_pages[data_page_index],
- data_page_offset,
- page_length,
- 0);
- } else {
- slow_shmem_copy(page,
- shmem_page_offset,
- user_pages[data_page_index],
- data_page_offset,
- page_length);
- }
+ page_do_bit17_swizzling = obj_do_bit17_swizzling &&
+ (page_to_phys(page) & (1 << 17)) != 0;
+
+ vaddr = kmap(page);
+ if (page_do_bit17_swizzling)
+ ret = __copy_from_user_swizzled(vaddr, shmem_page_offset,
+ user_data,
+ page_length);
+ else
+ ret = __copy_from_user(vaddr + shmem_page_offset,
+ user_data,
+ page_length);
+ kunmap(page);
set_page_dirty(page);
mark_page_accessed(page);
page_cache_release(page);
+ if (ret) {
+ ret = -EFAULT;
+ goto out;
+ }
+
remain -= page_length;
- data_ptr += page_length;
+ user_data += page_length;
offset += page_length;
}
out:
- for (i = 0; i < pinned_pages; i++)
- page_cache_release(user_pages[i]);
- drm_free_large(user_pages);
+ mutex_lock(&dev->struct_mutex);
+ /* Fixup: Kill any reinstated backing storage pages */
+ if (obj->madv == __I915_MADV_PURGED)
+ i915_gem_object_truncate(obj);
+ /* and flush dirty cachelines in case the object isn't in the cpu write
+ * domain anymore. */
+ if (obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
+ i915_gem_clflush_object(obj);
+ intel_gtt_chipset_flush();
+ }
return ret;
}
@@ -996,10 +922,13 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
* pread/pwrite currently are reading and writing from the CPU
* perspective, requiring manual detiling by the client.
*/
- if (obj->phys_obj)
+ if (obj->phys_obj) {
ret = i915_gem_phys_pwrite(dev, obj, args, file);
- else if (obj->gtt_space &&
- obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
+ goto out;
+ }
+
+ if (obj->gtt_space &&
+ obj->base.write_domain != I915_GEM_DOMAIN_CPU) {
ret = i915_gem_object_pin(obj, 0, true);
if (ret)
goto out;
@@ -1018,18 +947,24 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
out_unpin:
i915_gem_object_unpin(obj);
- } else {
- ret = i915_gem_object_set_to_cpu_domain(obj, 1);
- if (ret)
- goto out;
- ret = -EFAULT;
- if (!i915_gem_object_needs_bit17_swizzle(obj))
- ret = i915_gem_shmem_pwrite_fast(dev, obj, args, file);
- if (ret == -EFAULT)
- ret = i915_gem_shmem_pwrite_slow(dev, obj, args, file);
+ if (ret != -EFAULT)
+ goto out;
+ /* Fall through to the shmfs paths because the gtt paths might
+ * fail with non-page-backed user pointers (e.g. gtt mappings
+ * when moving data between textures). */
}
+ ret = i915_gem_object_set_to_cpu_domain(obj, 1);
+ if (ret)
+ goto out;
+
+ ret = -EFAULT;
+ if (!i915_gem_object_needs_bit17_swizzle(obj))
+ ret = i915_gem_shmem_pwrite_fast(dev, obj, args, file);
+ if (ret == -EFAULT)
+ ret = i915_gem_shmem_pwrite_slow(dev, obj, args, file);
+
out:
drm_gem_object_unreference(&obj->base);
unlock:
@@ -1141,7 +1076,6 @@ int
i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
- struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_mmap *args = data;
struct drm_gem_object *obj;
unsigned long addr;
@@ -1153,11 +1087,6 @@ i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
if (obj == NULL)
return -ENOENT;
- if (obj->size > dev_priv->mm.gtt_mappable_end) {
- drm_gem_object_unreference_unlocked(obj);
- return -E2BIG;
- }
-
down_write(&current->mm->mmap_sem);
addr = do_mmap(obj->filp, 0, args->size,
PROT_READ | PROT_WRITE, MAP_SHARED,
@@ -1647,6 +1576,28 @@ i915_gem_process_flushing_list(struct intel_ring_buffer *ring,
}
}
+static u32
+i915_gem_get_seqno(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 seqno = dev_priv->next_seqno;
+
+ /* reserve 0 for non-seqno */
+ if (++dev_priv->next_seqno == 0)
+ dev_priv->next_seqno = 1;
+
+ return seqno;
+}
+
+u32
+i915_gem_next_request_seqno(struct intel_ring_buffer *ring)
+{
+ if (ring->outstanding_lazy_request == 0)
+ ring->outstanding_lazy_request = i915_gem_get_seqno(ring->dev);
+
+ return ring->outstanding_lazy_request;
+}
+
int
i915_add_request(struct intel_ring_buffer *ring,
struct drm_file *file,
@@ -1654,10 +1605,19 @@ i915_add_request(struct intel_ring_buffer *ring,
{
drm_i915_private_t *dev_priv = ring->dev->dev_private;
uint32_t seqno;
+ u32 request_ring_position;
int was_empty;
int ret;
BUG_ON(request == NULL);
+ seqno = i915_gem_next_request_seqno(ring);
+
+ /* Record the position of the start of the request so that
+ * should we detect the updated seqno part-way through the
+ * GPU processing the request, we never over-estimate the
+ * position of the head.
+ */
+ request_ring_position = intel_ring_get_tail(ring);
ret = ring->add_request(ring, &seqno);
if (ret)
@@ -1667,6 +1627,7 @@ i915_add_request(struct intel_ring_buffer *ring,
request->seqno = seqno;
request->ring = ring;
+ request->tail = request_ring_position;
request->emitted_jiffies = jiffies;
was_empty = list_empty(&ring->request_list);
list_add_tail(&request->list, &ring->request_list);
@@ -1681,7 +1642,7 @@ i915_add_request(struct intel_ring_buffer *ring,
spin_unlock(&file_priv->mm.lock);
}
- ring->outstanding_lazy_request = false;
+ ring->outstanding_lazy_request = 0;
if (!dev_priv->mm.suspended) {
if (i915_enable_hangcheck) {
@@ -1803,7 +1764,7 @@ void i915_gem_reset(struct drm_device *dev)
/**
* This function clears the request list as sequence numbers are passed.
*/
-static void
+void
i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
{
uint32_t seqno;
@@ -1831,6 +1792,12 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
break;
trace_i915_gem_request_retire(ring, request->seqno);
+ /* We know the GPU must have read the request to have
+ * sent us the seqno + interrupt, so use the position
+ * of tail of the request to update the last known position
+ * of the GPU head.
+ */
+ ring->last_retired_head = request->tail;
list_del(&request->list);
i915_gem_request_remove_from_client(request);
@@ -1943,7 +1910,8 @@ i915_gem_retire_work_handler(struct work_struct *work)
*/
int
i915_wait_request(struct intel_ring_buffer *ring,
- uint32_t seqno)
+ uint32_t seqno,
+ bool do_retire)
{
drm_i915_private_t *dev_priv = ring->dev->dev_private;
u32 ier;
@@ -2017,17 +1985,12 @@ i915_wait_request(struct intel_ring_buffer *ring,
if (atomic_read(&dev_priv->mm.wedged))
ret = -EAGAIN;
- if (ret && ret != -ERESTARTSYS)
- DRM_ERROR("%s returns %d (awaiting %d at %d, next %d)\n",
- __func__, ret, seqno, ring->get_seqno(ring),
- dev_priv->next_seqno);
-
/* Directly dispatch request retiring. While we have the work queue
* to handle this, the waiter on a request often wants an associated
* buffer to have made it to the inactive list, and we would need
* a separate wait queue to handle that.
*/
- if (ret == 0)
+ if (ret == 0 && do_retire)
i915_gem_retire_requests_ring(ring);
return ret;
@@ -2051,7 +2014,8 @@ i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj)
* it.
*/
if (obj->active) {
- ret = i915_wait_request(obj->ring, obj->last_rendering_seqno);
+ ret = i915_wait_request(obj->ring, obj->last_rendering_seqno,
+ true);
if (ret)
return ret;
}
@@ -2089,6 +2053,7 @@ static void i915_gem_object_finish_gtt(struct drm_i915_gem_object *obj)
int
i915_gem_object_unbind(struct drm_i915_gem_object *obj)
{
+ drm_i915_private_t *dev_priv = obj->base.dev->dev_private;
int ret = 0;
if (obj->gtt_space == NULL)
@@ -2133,6 +2098,11 @@ i915_gem_object_unbind(struct drm_i915_gem_object *obj)
trace_i915_gem_object_unbind(obj);
i915_gem_gtt_unbind_object(obj);
+ if (obj->has_aliasing_ppgtt_mapping) {
+ i915_ppgtt_unbind_object(dev_priv->mm.aliasing_ppgtt, obj);
+ obj->has_aliasing_ppgtt_mapping = 0;
+ }
+
i915_gem_object_put_pages_gtt(obj);
list_del_init(&obj->gtt_list);
@@ -2172,7 +2142,7 @@ i915_gem_flush_ring(struct intel_ring_buffer *ring,
return 0;
}
-static int i915_ring_idle(struct intel_ring_buffer *ring)
+static int i915_ring_idle(struct intel_ring_buffer *ring, bool do_retire)
{
int ret;
@@ -2186,18 +2156,18 @@ static int i915_ring_idle(struct intel_ring_buffer *ring)
return ret;
}
- return i915_wait_request(ring, i915_gem_next_request_seqno(ring));
+ return i915_wait_request(ring, i915_gem_next_request_seqno(ring),
+ do_retire);
}
-int
-i915_gpu_idle(struct drm_device *dev)
+int i915_gpu_idle(struct drm_device *dev, bool do_retire)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int ret, i;
/* Flush everything onto the inactive list. */
for (i = 0; i < I915_NUM_RINGS; i++) {
- ret = i915_ring_idle(&dev_priv->ring[i]);
+ ret = i915_ring_idle(&dev_priv->ring[i], do_retire);
if (ret)
return ret;
}
@@ -2400,7 +2370,8 @@ i915_gem_object_flush_fence(struct drm_i915_gem_object *obj,
if (!ring_passed_seqno(obj->last_fenced_ring,
obj->last_fenced_seqno)) {
ret = i915_wait_request(obj->last_fenced_ring,
- obj->last_fenced_seqno);
+ obj->last_fenced_seqno,
+ true);
if (ret)
return ret;
}
@@ -2432,6 +2403,8 @@ i915_gem_object_put_fence(struct drm_i915_gem_object *obj)
if (obj->fence_reg != I915_FENCE_REG_NONE) {
struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
+
+ WARN_ON(dev_priv->fence_regs[obj->fence_reg].pin_count);
i915_gem_clear_fence_reg(obj->base.dev,
&dev_priv->fence_regs[obj->fence_reg]);
@@ -2456,7 +2429,7 @@ i915_find_fence_reg(struct drm_device *dev,
if (!reg->obj)
return reg;
- if (!reg->obj->pin_count)
+ if (!reg->pin_count)
avail = reg;
}
@@ -2466,7 +2439,7 @@ i915_find_fence_reg(struct drm_device *dev,
/* None available, try to steal one or wait for a user to finish */
avail = first = NULL;
list_for_each_entry(reg, &dev_priv->mm.fence_list, lru_list) {
- if (reg->obj->pin_count)
+ if (reg->pin_count)
continue;
if (first == NULL)
@@ -2541,7 +2514,8 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj,
if (!ring_passed_seqno(obj->last_fenced_ring,
reg->setup_seqno)) {
ret = i915_wait_request(obj->last_fenced_ring,
- reg->setup_seqno);
+ reg->setup_seqno,
+ true);
if (ret)
return ret;
}
@@ -2560,7 +2534,7 @@ i915_gem_object_get_fence(struct drm_i915_gem_object *obj,
reg = i915_find_fence_reg(dev, pipelined);
if (reg == NULL)
- return -ENOSPC;
+ return -EDEADLK;
ret = i915_gem_object_flush_fence(obj, pipelined);
if (ret)
@@ -2660,6 +2634,7 @@ i915_gem_clear_fence_reg(struct drm_device *dev,
list_del_init(&reg->lru_list);
reg->obj = NULL;
reg->setup_seqno = 0;
+ reg->pin_count = 0;
}
/**
@@ -2946,6 +2921,8 @@ i915_gem_object_set_to_gtt_domain(struct drm_i915_gem_object *obj, bool write)
int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
enum i915_cache_level cache_level)
{
+ struct drm_device *dev = obj->base.dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
if (obj->cache_level == cache_level)
@@ -2974,6 +2951,9 @@ int i915_gem_object_set_cache_level(struct drm_i915_gem_object *obj,
}
i915_gem_gtt_rebind_object(obj, cache_level);
+ if (obj->has_aliasing_ppgtt_mapping)
+ i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
+ obj, cache_level);
}
if (cache_level == I915_CACHE_NONE) {
@@ -3084,10 +3064,13 @@ i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj)
return ret;
}
+ ret = i915_gem_object_wait_rendering(obj);
+ if (ret)
+ return ret;
+
/* Ensure that we invalidate the GPU's caches and TLBs. */
obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
-
- return i915_gem_object_wait_rendering(obj);
+ return 0;
}
/**
@@ -3619,8 +3602,8 @@ struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
obj->base.write_domain = I915_GEM_DOMAIN_CPU;
obj->base.read_domains = I915_GEM_DOMAIN_CPU;
- if (IS_GEN6(dev) || IS_GEN7(dev)) {
- /* On Gen6, we can have the GPU use the LLC (the CPU
+ if (HAS_LLC(dev)) {
+ /* On some devices, we can have the GPU use the LLC (the CPU
* cache) for about a 10% performance improvement
* compared to uncached. Graphics requests other than
* display scanout are coherent with the CPU in
@@ -3710,7 +3693,7 @@ i915_gem_idle(struct drm_device *dev)
return 0;
}
- ret = i915_gpu_idle(dev);
+ ret = i915_gpu_idle(dev, true);
if (ret) {
mutex_unlock(&dev->struct_mutex);
return ret;
@@ -3745,12 +3728,71 @@ i915_gem_idle(struct drm_device *dev)
return 0;
}
+void i915_gem_init_swizzling(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+ if (INTEL_INFO(dev)->gen < 5 ||
+ dev_priv->mm.bit_6_swizzle_x == I915_BIT_6_SWIZZLE_NONE)
+ return;
+
+ I915_WRITE(DISP_ARB_CTL, I915_READ(DISP_ARB_CTL) |
+ DISP_TILE_SURFACE_SWIZZLING);
+
+ if (IS_GEN5(dev))
+ return;
+
+ I915_WRITE(TILECTL, I915_READ(TILECTL) | TILECTL_SWZCTL);
+ if (IS_GEN6(dev))
+ I915_WRITE(ARB_MODE, ARB_MODE_ENABLE(ARB_MODE_SWIZZLE_SNB));
+ else
+ I915_WRITE(ARB_MODE, ARB_MODE_ENABLE(ARB_MODE_SWIZZLE_IVB));
+}
+
+void i915_gem_init_ppgtt(struct drm_device *dev)
+{
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ uint32_t pd_offset;
+ struct intel_ring_buffer *ring;
+ int i;
+
+ if (!dev_priv->mm.aliasing_ppgtt)
+ return;
+
+ pd_offset = dev_priv->mm.aliasing_ppgtt->pd_offset;
+ pd_offset /= 64; /* in cachelines, */
+ pd_offset <<= 16;
+
+ if (INTEL_INFO(dev)->gen == 6) {
+ uint32_t ecochk = I915_READ(GAM_ECOCHK);
+ I915_WRITE(GAM_ECOCHK, ecochk | ECOCHK_SNB_BIT |
+ ECOCHK_PPGTT_CACHE64B);
+ I915_WRITE(GFX_MODE, GFX_MODE_ENABLE(GFX_PPGTT_ENABLE));
+ } else if (INTEL_INFO(dev)->gen >= 7) {
+ I915_WRITE(GAM_ECOCHK, ECOCHK_PPGTT_CACHE64B);
+ /* GFX_MODE is per-ring on gen7+ */
+ }
+
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ ring = &dev_priv->ring[i];
+
+ if (INTEL_INFO(dev)->gen >= 7)
+ I915_WRITE(RING_MODE_GEN7(ring),
+ GFX_MODE_ENABLE(GFX_PPGTT_ENABLE));
+
+ I915_WRITE(RING_PP_DIR_DCLV(ring), PP_DIR_DCLV_2G);
+ I915_WRITE(RING_PP_DIR_BASE(ring), pd_offset);
+ }
+}
+
int
-i915_gem_init_ringbuffer(struct drm_device *dev)
+i915_gem_init_hw(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
int ret;
+ i915_gem_init_swizzling(dev);
+
ret = intel_init_render_ring_buffer(dev);
if (ret)
return ret;
@@ -3769,6 +3811,8 @@ i915_gem_init_ringbuffer(struct drm_device *dev)
dev_priv->next_seqno = 1;
+ i915_gem_init_ppgtt(dev);
+
return 0;
cleanup_bsd_ring:
@@ -3806,7 +3850,7 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
mutex_lock(&dev->struct_mutex);
dev_priv->mm.suspended = 0;
- ret = i915_gem_init_ringbuffer(dev);
+ ret = i915_gem_init_hw(dev);
if (ret != 0) {
mutex_unlock(&dev->struct_mutex);
return ret;
@@ -4201,7 +4245,7 @@ rescan:
* This has a dramatic impact to reduce the number of
* OOM-killer events whilst running the GPU aggressively.
*/
- if (i915_gpu_idle(dev) == 0)
+ if (i915_gpu_idle(dev, true) == 0)
goto rescan;
}
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index ead5d00f91b0..21a82710f4b2 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -36,7 +36,6 @@ static bool
mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
{
list_add(&obj->exec_list, unwind);
- drm_gem_object_reference(&obj->base);
return drm_mm_scan_add_block(obj->gtt_space);
}
@@ -49,21 +48,6 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
struct drm_i915_gem_object *obj;
int ret = 0;
- i915_gem_retire_requests(dev);
-
- /* Re-check for free space after retiring requests */
- if (mappable) {
- if (drm_mm_search_free_in_range(&dev_priv->mm.gtt_space,
- min_size, alignment, 0,
- dev_priv->mm.gtt_mappable_end,
- 0))
- return 0;
- } else {
- if (drm_mm_search_free(&dev_priv->mm.gtt_space,
- min_size, alignment, 0))
- return 0;
- }
-
trace_i915_gem_evict(dev, min_size, alignment, mappable);
/*
@@ -139,7 +123,6 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
BUG_ON(ret);
list_del_init(&obj->exec_list);
- drm_gem_object_unreference(&obj->base);
}
/* We expect the caller to unpin, evict all and try again, or give up.
@@ -158,10 +141,10 @@ found:
exec_list);
if (drm_mm_scan_remove_block(obj->gtt_space)) {
list_move(&obj->exec_list, &eviction_list);
+ drm_gem_object_reference(&obj->base);
continue;
}
list_del_init(&obj->exec_list);
- drm_gem_object_unreference(&obj->base);
}
/* Unbinding will emit any required flushes */
@@ -195,7 +178,7 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
trace_i915_gem_evict_everything(dev, purgeable_only);
/* Flush everything (on to the inactive lists) and evict */
- ret = i915_gpu_idle(dev);
+ ret = i915_gpu_idle(dev, true);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 65e1f0043f9d..81687af00893 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -203,9 +203,9 @@ i915_gem_object_set_to_gpu_domain(struct drm_i915_gem_object *obj,
cd->invalidate_domains |= invalidate_domains;
cd->flush_domains |= flush_domains;
if (flush_domains & I915_GEM_GPU_DOMAINS)
- cd->flush_rings |= obj->ring->id;
+ cd->flush_rings |= intel_ring_flag(obj->ring);
if (invalidate_domains & I915_GEM_GPU_DOMAINS)
- cd->flush_rings |= ring->id;
+ cd->flush_rings |= intel_ring_flag(ring);
}
struct eb_objects {
@@ -287,14 +287,14 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
* exec_object list, so it should have a GTT space bound by now.
*/
if (unlikely(target_offset == 0)) {
- DRM_ERROR("No GTT space found for object %d\n",
+ DRM_DEBUG("No GTT space found for object %d\n",
reloc->target_handle);
return ret;
}
/* Validate that the target is in a valid r/w GPU domain */
if (unlikely(reloc->write_domain & (reloc->write_domain - 1))) {
- DRM_ERROR("reloc with multiple write domains: "
+ DRM_DEBUG("reloc with multiple write domains: "
"obj %p target %d offset %d "
"read %08x write %08x",
obj, reloc->target_handle,
@@ -303,8 +303,9 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
reloc->write_domain);
return ret;
}
- if (unlikely((reloc->write_domain | reloc->read_domains) & I915_GEM_DOMAIN_CPU)) {
- DRM_ERROR("reloc with read/write CPU domains: "
+ if (unlikely((reloc->write_domain | reloc->read_domains)
+ & ~I915_GEM_GPU_DOMAINS)) {
+ DRM_DEBUG("reloc with read/write non-GPU domains: "
"obj %p target %d offset %d "
"read %08x write %08x",
obj, reloc->target_handle,
@@ -315,7 +316,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
}
if (unlikely(reloc->write_domain && target_obj->pending_write_domain &&
reloc->write_domain != target_obj->pending_write_domain)) {
- DRM_ERROR("Write domain conflict: "
+ DRM_DEBUG("Write domain conflict: "
"obj %p target %d offset %d "
"new %08x old %08x\n",
obj, reloc->target_handle,
@@ -336,7 +337,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
/* Check that the relocation address is valid... */
if (unlikely(reloc->offset > obj->base.size - 4)) {
- DRM_ERROR("Relocation beyond object bounds: "
+ DRM_DEBUG("Relocation beyond object bounds: "
"obj %p target %d offset %d size %d.\n",
obj, reloc->target_handle,
(int) reloc->offset,
@@ -344,7 +345,7 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
return ret;
}
if (unlikely(reloc->offset & 3)) {
- DRM_ERROR("Relocation not 4-byte aligned: "
+ DRM_DEBUG("Relocation not 4-byte aligned: "
"obj %p target %d offset %d.\n",
obj, reloc->target_handle,
(int) reloc->offset);
@@ -461,11 +462,60 @@ i915_gem_execbuffer_relocate(struct drm_device *dev,
return ret;
}
+#define __EXEC_OBJECT_HAS_FENCE (1<<31)
+
+static int
+pin_and_fence_object(struct drm_i915_gem_object *obj,
+ struct intel_ring_buffer *ring)
+{
+ struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
+ bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
+ bool need_fence, need_mappable;
+ int ret;
+
+ need_fence =
+ has_fenced_gpu_access &&
+ entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
+ obj->tiling_mode != I915_TILING_NONE;
+ need_mappable =
+ entry->relocation_count ? true : need_fence;
+
+ ret = i915_gem_object_pin(obj, entry->alignment, need_mappable);
+ if (ret)
+ return ret;
+
+ if (has_fenced_gpu_access) {
+ if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
+ if (obj->tiling_mode) {
+ ret = i915_gem_object_get_fence(obj, ring);
+ if (ret)
+ goto err_unpin;
+
+ entry->flags |= __EXEC_OBJECT_HAS_FENCE;
+ i915_gem_object_pin_fence(obj);
+ } else {
+ ret = i915_gem_object_put_fence(obj);
+ if (ret)
+ goto err_unpin;
+ }
+ }
+ obj->pending_fenced_gpu_access = need_fence;
+ }
+
+ entry->offset = obj->gtt_offset;
+ return 0;
+
+err_unpin:
+ i915_gem_object_unpin(obj);
+ return ret;
+}
+
static int
i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
struct drm_file *file,
struct list_head *objects)
{
+ drm_i915_private_t *dev_priv = ring->dev->dev_private;
struct drm_i915_gem_object *obj;
int ret, retry;
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
@@ -518,6 +568,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
list_for_each_entry(obj, objects, exec_list) {
struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
bool need_fence, need_mappable;
+
if (!obj->gtt_space)
continue;
@@ -532,58 +583,55 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
(need_mappable && !obj->map_and_fenceable))
ret = i915_gem_object_unbind(obj);
else
- ret = i915_gem_object_pin(obj,
- entry->alignment,
- need_mappable);
+ ret = pin_and_fence_object(obj, ring);
if (ret)
goto err;
-
- entry++;
}
/* Bind fresh objects */
list_for_each_entry(obj, objects, exec_list) {
- struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
- bool need_fence;
+ if (obj->gtt_space)
+ continue;
- need_fence =
- has_fenced_gpu_access &&
- entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
- obj->tiling_mode != I915_TILING_NONE;
+ ret = pin_and_fence_object(obj, ring);
+ if (ret) {
+ int ret_ignore;
+
+ /* This can potentially raise a harmless
+ * -EINVAL if we failed to bind in the above
+ * call. It cannot raise -EINTR since we know
+ * that the bo is freshly bound and so will
+ * not need to be flushed or waited upon.
+ */
+ ret_ignore = i915_gem_object_unbind(obj);
+ (void)ret_ignore;
+ WARN_ON(obj->gtt_space);
+ break;
+ }
+ }
- if (!obj->gtt_space) {
- bool need_mappable =
- entry->relocation_count ? true : need_fence;
+ /* Decrement pin count for bound objects */
+ list_for_each_entry(obj, objects, exec_list) {
+ struct drm_i915_gem_exec_object2 *entry;
- ret = i915_gem_object_pin(obj,
- entry->alignment,
- need_mappable);
- if (ret)
- break;
- }
+ if (!obj->gtt_space)
+ continue;
- if (has_fenced_gpu_access) {
- if (need_fence) {
- ret = i915_gem_object_get_fence(obj, ring);
- if (ret)
- break;
- } else if (entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
- obj->tiling_mode == I915_TILING_NONE) {
- /* XXX pipelined! */
- ret = i915_gem_object_put_fence(obj);
- if (ret)
- break;
- }
- obj->pending_fenced_gpu_access = need_fence;
+ entry = obj->exec_entry;
+ if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
+ i915_gem_object_unpin_fence(obj);
+ entry->flags &= ~__EXEC_OBJECT_HAS_FENCE;
}
- entry->offset = obj->gtt_offset;
- }
+ i915_gem_object_unpin(obj);
- /* Decrement pin count for bound objects */
- list_for_each_entry(obj, objects, exec_list) {
- if (obj->gtt_space)
- i915_gem_object_unpin(obj);
+ /* ... and ensure ppgtt mapping exist if needed. */
+ if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) {
+ i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
+ obj, obj->cache_level);
+
+ obj->has_aliasing_ppgtt_mapping = 1;
+ }
}
if (ret != -ENOSPC || retry > 1)
@@ -600,16 +648,19 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
} while (1);
err:
- obj = list_entry(obj->exec_list.prev,
- struct drm_i915_gem_object,
- exec_list);
- while (objects != &obj->exec_list) {
- if (obj->gtt_space)
- i915_gem_object_unpin(obj);
+ list_for_each_entry_continue_reverse(obj, objects, exec_list) {
+ struct drm_i915_gem_exec_object2 *entry;
+
+ if (!obj->gtt_space)
+ continue;
+
+ entry = obj->exec_entry;
+ if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
+ i915_gem_object_unpin_fence(obj);
+ entry->flags &= ~__EXEC_OBJECT_HAS_FENCE;
+ }
- obj = list_entry(obj->exec_list.prev,
- struct drm_i915_gem_object,
- exec_list);
+ i915_gem_object_unpin(obj);
}
return ret;
@@ -682,7 +733,7 @@ i915_gem_execbuffer_relocate_slow(struct drm_device *dev,
obj = to_intel_bo(drm_gem_object_lookup(dev, file,
exec[i].handle));
if (&obj->base == NULL) {
- DRM_ERROR("Invalid object handle %d at index %d\n",
+ DRM_DEBUG("Invalid object handle %d at index %d\n",
exec[i].handle, i);
ret = -ENOENT;
goto err;
@@ -1013,7 +1064,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
int ret, mode, i;
if (!i915_gem_check_execbuffer(args)) {
- DRM_ERROR("execbuf with invalid offset/length\n");
+ DRM_DEBUG("execbuf with invalid offset/length\n");
return -EINVAL;
}
@@ -1028,20 +1079,20 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
break;
case I915_EXEC_BSD:
if (!HAS_BSD(dev)) {
- DRM_ERROR("execbuf with invalid ring (BSD)\n");
+ DRM_DEBUG("execbuf with invalid ring (BSD)\n");
return -EINVAL;
}
ring = &dev_priv->ring[VCS];
break;
case I915_EXEC_BLT:
if (!HAS_BLT(dev)) {
- DRM_ERROR("execbuf with invalid ring (BLT)\n");
+ DRM_DEBUG("execbuf with invalid ring (BLT)\n");
return -EINVAL;
}
ring = &dev_priv->ring[BCS];
break;
default:
- DRM_ERROR("execbuf with unknown ring: %d\n",
+ DRM_DEBUG("execbuf with unknown ring: %d\n",
(int)(args->flags & I915_EXEC_RING_MASK));
return -EINVAL;
}
@@ -1067,18 +1118,18 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
}
break;
default:
- DRM_ERROR("execbuf with unknown constants: %d\n", mode);
+ DRM_DEBUG("execbuf with unknown constants: %d\n", mode);
return -EINVAL;
}
if (args->buffer_count < 1) {
- DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
+ DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count);
return -EINVAL;
}
if (args->num_cliprects != 0) {
if (ring != &dev_priv->ring[RCS]) {
- DRM_ERROR("clip rectangles are only valid with the render ring\n");
+ DRM_DEBUG("clip rectangles are only valid with the render ring\n");
return -EINVAL;
}
@@ -1123,7 +1174,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
obj = to_intel_bo(drm_gem_object_lookup(dev, file,
exec[i].handle));
if (&obj->base == NULL) {
- DRM_ERROR("Invalid object handle %d at index %d\n",
+ DRM_DEBUG("Invalid object handle %d at index %d\n",
exec[i].handle, i);
/* prevent error path from reading uninitialized data */
ret = -ENOENT;
@@ -1131,7 +1182,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
}
if (!list_empty(&obj->exec_list)) {
- DRM_ERROR("Object %p [handle %d, index %d] appears more than once in object list\n",
+ DRM_DEBUG("Object %p [handle %d, index %d] appears more than once in object list\n",
obj, exec[i].handle, i);
ret = -EINVAL;
goto err;
@@ -1169,7 +1220,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
/* Set the pending read domains for the batch buffer to COMMAND */
if (batch_obj->base.pending_write_domain) {
- DRM_ERROR("Attempting to use self-modifying batch buffer\n");
+ DRM_DEBUG("Attempting to use self-modifying batch buffer\n");
ret = -EINVAL;
goto err;
}
@@ -1186,7 +1237,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
* so every billion or so execbuffers, we need to stall
* the GPU in order to reset the counters.
*/
- ret = i915_gpu_idle(dev);
+ ret = i915_gpu_idle(dev, true);
if (ret)
goto err;
@@ -1274,7 +1325,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
int ret, i;
if (args->buffer_count < 1) {
- DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
+ DRM_DEBUG("execbuf with %d buffers\n", args->buffer_count);
return -EINVAL;
}
@@ -1282,7 +1333,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
exec_list = drm_malloc_ab(sizeof(*exec_list), args->buffer_count);
exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count);
if (exec_list == NULL || exec2_list == NULL) {
- DRM_ERROR("Failed to allocate exec list for %d buffers\n",
+ DRM_DEBUG("Failed to allocate exec list for %d buffers\n",
args->buffer_count);
drm_free_large(exec_list);
drm_free_large(exec2_list);
@@ -1293,7 +1344,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
(uintptr_t) args->buffers_ptr,
sizeof(*exec_list) * args->buffer_count);
if (ret != 0) {
- DRM_ERROR("copy %d exec entries failed %d\n",
+ DRM_DEBUG("copy %d exec entries failed %d\n",
args->buffer_count, ret);
drm_free_large(exec_list);
drm_free_large(exec2_list);
@@ -1334,7 +1385,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
sizeof(*exec_list) * args->buffer_count);
if (ret) {
ret = -EFAULT;
- DRM_ERROR("failed to copy %d exec entries "
+ DRM_DEBUG("failed to copy %d exec entries "
"back to user (%d)\n",
args->buffer_count, ret);
}
@@ -1354,7 +1405,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
int ret;
if (args->buffer_count < 1) {
- DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count);
+ DRM_DEBUG("execbuf2 with %d buffers\n", args->buffer_count);
return -EINVAL;
}
@@ -1364,7 +1415,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
exec2_list = drm_malloc_ab(sizeof(*exec2_list),
args->buffer_count);
if (exec2_list == NULL) {
- DRM_ERROR("Failed to allocate exec list for %d buffers\n",
+ DRM_DEBUG("Failed to allocate exec list for %d buffers\n",
args->buffer_count);
return -ENOMEM;
}
@@ -1373,7 +1424,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
(uintptr_t) args->buffers_ptr,
sizeof(*exec2_list) * args->buffer_count);
if (ret != 0) {
- DRM_ERROR("copy %d exec entries failed %d\n",
+ DRM_DEBUG("copy %d exec entries failed %d\n",
args->buffer_count, ret);
drm_free_large(exec2_list);
return -EFAULT;
@@ -1388,7 +1439,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
sizeof(*exec2_list) * args->buffer_count);
if (ret) {
ret = -EFAULT;
- DRM_ERROR("failed to copy %d exec entries "
+ DRM_DEBUG("failed to copy %d exec entries "
"back to user (%d)\n",
args->buffer_count, ret);
}
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 6042c5e6d278..2eacd78bb93b 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -29,6 +29,279 @@
#include "i915_trace.h"
#include "intel_drv.h"
+/* PPGTT support for Sandybdrige/Gen6 and later */
+static void i915_ppgtt_clear_range(struct i915_hw_ppgtt *ppgtt,
+ unsigned first_entry,
+ unsigned num_entries)
+{
+ uint32_t *pt_vaddr;
+ uint32_t scratch_pte;
+ unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
+ unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
+ unsigned last_pte, i;
+
+ scratch_pte = GEN6_PTE_ADDR_ENCODE(ppgtt->scratch_page_dma_addr);
+ scratch_pte |= GEN6_PTE_VALID | GEN6_PTE_CACHE_LLC;
+
+ while (num_entries) {
+ last_pte = first_pte + num_entries;
+ if (last_pte > I915_PPGTT_PT_ENTRIES)
+ last_pte = I915_PPGTT_PT_ENTRIES;
+
+ pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
+
+ for (i = first_pte; i < last_pte; i++)
+ pt_vaddr[i] = scratch_pte;
+
+ kunmap_atomic(pt_vaddr);
+
+ num_entries -= last_pte - first_pte;
+ first_pte = 0;
+ act_pd++;
+ }
+}
+
+int i915_gem_init_aliasing_ppgtt(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_hw_ppgtt *ppgtt;
+ uint32_t pd_entry;
+ unsigned first_pd_entry_in_global_pt;
+ uint32_t __iomem *pd_addr;
+ int i;
+ int ret = -ENOMEM;
+
+ /* ppgtt PDEs reside in the global gtt pagetable, which has 512*1024
+ * entries. For aliasing ppgtt support we just steal them at the end for
+ * now. */
+ first_pd_entry_in_global_pt = 512*1024 - I915_PPGTT_PD_ENTRIES;
+
+ ppgtt = kzalloc(sizeof(*ppgtt), GFP_KERNEL);
+ if (!ppgtt)
+ return ret;
+
+ ppgtt->num_pd_entries = I915_PPGTT_PD_ENTRIES;
+ ppgtt->pt_pages = kzalloc(sizeof(struct page *)*ppgtt->num_pd_entries,
+ GFP_KERNEL);
+ if (!ppgtt->pt_pages)
+ goto err_ppgtt;
+
+ for (i = 0; i < ppgtt->num_pd_entries; i++) {
+ ppgtt->pt_pages[i] = alloc_page(GFP_KERNEL);
+ if (!ppgtt->pt_pages[i])
+ goto err_pt_alloc;
+ }
+
+ if (dev_priv->mm.gtt->needs_dmar) {
+ ppgtt->pt_dma_addr = kzalloc(sizeof(dma_addr_t)
+ *ppgtt->num_pd_entries,
+ GFP_KERNEL);
+ if (!ppgtt->pt_dma_addr)
+ goto err_pt_alloc;
+ }
+
+ pd_addr = dev_priv->mm.gtt->gtt + first_pd_entry_in_global_pt;
+ for (i = 0; i < ppgtt->num_pd_entries; i++) {
+ dma_addr_t pt_addr;
+ if (dev_priv->mm.gtt->needs_dmar) {
+ pt_addr = pci_map_page(dev->pdev, ppgtt->pt_pages[i],
+ 0, 4096,
+ PCI_DMA_BIDIRECTIONAL);
+
+ if (pci_dma_mapping_error(dev->pdev,
+ pt_addr)) {
+ ret = -EIO;
+ goto err_pd_pin;
+
+ }
+ ppgtt->pt_dma_addr[i] = pt_addr;
+ } else
+ pt_addr = page_to_phys(ppgtt->pt_pages[i]);
+
+ pd_entry = GEN6_PDE_ADDR_ENCODE(pt_addr);
+ pd_entry |= GEN6_PDE_VALID;
+
+ writel(pd_entry, pd_addr + i);
+ }
+ readl(pd_addr);
+
+ ppgtt->scratch_page_dma_addr = dev_priv->mm.gtt->scratch_page_dma;
+
+ i915_ppgtt_clear_range(ppgtt, 0,
+ ppgtt->num_pd_entries*I915_PPGTT_PT_ENTRIES);
+
+ ppgtt->pd_offset = (first_pd_entry_in_global_pt)*sizeof(uint32_t);
+
+ dev_priv->mm.aliasing_ppgtt = ppgtt;
+
+ return 0;
+
+err_pd_pin:
+ if (ppgtt->pt_dma_addr) {
+ for (i--; i >= 0; i--)
+ pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
+ 4096, PCI_DMA_BIDIRECTIONAL);
+ }
+err_pt_alloc:
+ kfree(ppgtt->pt_dma_addr);
+ for (i = 0; i < ppgtt->num_pd_entries; i++) {
+ if (ppgtt->pt_pages[i])
+ __free_page(ppgtt->pt_pages[i]);
+ }
+ kfree(ppgtt->pt_pages);
+err_ppgtt:
+ kfree(ppgtt);
+
+ return ret;
+}
+
+void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct i915_hw_ppgtt *ppgtt = dev_priv->mm.aliasing_ppgtt;
+ int i;
+
+ if (!ppgtt)
+ return;
+
+ if (ppgtt->pt_dma_addr) {
+ for (i = 0; i < ppgtt->num_pd_entries; i++)
+ pci_unmap_page(dev->pdev, ppgtt->pt_dma_addr[i],
+ 4096, PCI_DMA_BIDIRECTIONAL);
+ }
+
+ kfree(ppgtt->pt_dma_addr);
+ for (i = 0; i < ppgtt->num_pd_entries; i++)
+ __free_page(ppgtt->pt_pages[i]);
+ kfree(ppgtt->pt_pages);
+ kfree(ppgtt);
+}
+
+static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
+ struct scatterlist *sg_list,
+ unsigned sg_len,
+ unsigned first_entry,
+ uint32_t pte_flags)
+{
+ uint32_t *pt_vaddr, pte;
+ unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
+ unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
+ unsigned i, j, m, segment_len;
+ dma_addr_t page_addr;
+ struct scatterlist *sg;
+
+ /* init sg walking */
+ sg = sg_list;
+ i = 0;
+ segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
+ m = 0;
+
+ while (i < sg_len) {
+ pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
+
+ for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) {
+ page_addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
+ pte = GEN6_PTE_ADDR_ENCODE(page_addr);
+ pt_vaddr[j] = pte | pte_flags;
+
+ /* grab the next page */
+ m++;
+ if (m == segment_len) {
+ sg = sg_next(sg);
+ i++;
+ if (i == sg_len)
+ break;
+
+ segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
+ m = 0;
+ }
+ }
+
+ kunmap_atomic(pt_vaddr);
+
+ first_pte = 0;
+ act_pd++;
+ }
+}
+
+static void i915_ppgtt_insert_pages(struct i915_hw_ppgtt *ppgtt,
+ unsigned first_entry, unsigned num_entries,
+ struct page **pages, uint32_t pte_flags)
+{
+ uint32_t *pt_vaddr, pte;
+ unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
+ unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
+ unsigned last_pte, i;
+ dma_addr_t page_addr;
+
+ while (num_entries) {
+ last_pte = first_pte + num_entries;
+ last_pte = min_t(unsigned, last_pte, I915_PPGTT_PT_ENTRIES);
+
+ pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
+
+ for (i = first_pte; i < last_pte; i++) {
+ page_addr = page_to_phys(*pages);
+ pte = GEN6_PTE_ADDR_ENCODE(page_addr);
+ pt_vaddr[i] = pte | pte_flags;
+
+ pages++;
+ }
+
+ kunmap_atomic(pt_vaddr);
+
+ num_entries -= last_pte - first_pte;
+ first_pte = 0;
+ act_pd++;
+ }
+}
+
+void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
+ struct drm_i915_gem_object *obj,
+ enum i915_cache_level cache_level)
+{
+ struct drm_device *dev = obj->base.dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ uint32_t pte_flags = GEN6_PTE_VALID;
+
+ switch (cache_level) {
+ case I915_CACHE_LLC_MLC:
+ pte_flags |= GEN6_PTE_CACHE_LLC_MLC;
+ break;
+ case I915_CACHE_LLC:
+ pte_flags |= GEN6_PTE_CACHE_LLC;
+ break;
+ case I915_CACHE_NONE:
+ pte_flags |= GEN6_PTE_UNCACHED;
+ break;
+ default:
+ BUG();
+ }
+
+ if (dev_priv->mm.gtt->needs_dmar) {
+ BUG_ON(!obj->sg_list);
+
+ i915_ppgtt_insert_sg_entries(ppgtt,
+ obj->sg_list,
+ obj->num_sg,
+ obj->gtt_space->start >> PAGE_SHIFT,
+ pte_flags);
+ } else
+ i915_ppgtt_insert_pages(ppgtt,
+ obj->gtt_space->start >> PAGE_SHIFT,
+ obj->base.size >> PAGE_SHIFT,
+ obj->pages,
+ pte_flags);
+}
+
+void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
+ struct drm_i915_gem_object *obj)
+{
+ i915_ppgtt_clear_range(ppgtt,
+ obj->gtt_space->start >> PAGE_SHIFT,
+ obj->base.size >> PAGE_SHIFT);
+}
+
/* XXX kill agp_type! */
static unsigned int cache_level_to_agp_type(struct drm_device *dev,
enum i915_cache_level cache_level)
@@ -55,7 +328,7 @@ static bool do_idling(struct drm_i915_private *dev_priv)
if (unlikely(dev_priv->mm.gtt->do_idle_maps)) {
dev_priv->mm.interruptible = false;
- if (i915_gpu_idle(dev_priv->dev)) {
+ if (i915_gpu_idle(dev_priv->dev, false)) {
DRM_ERROR("Couldn't idle GPU\n");
/* Wait a bit, in hopes it avoids the hang */
udelay(10);
diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
index 31d334d9d9da..1a9306665987 100644
--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
+++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
@@ -93,8 +93,23 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
if (INTEL_INFO(dev)->gen >= 6) {
- swizzle_x = I915_BIT_6_SWIZZLE_NONE;
- swizzle_y = I915_BIT_6_SWIZZLE_NONE;
+ uint32_t dimm_c0, dimm_c1;
+ dimm_c0 = I915_READ(MAD_DIMM_C0);
+ dimm_c1 = I915_READ(MAD_DIMM_C1);
+ dimm_c0 &= MAD_DIMM_A_SIZE_MASK | MAD_DIMM_B_SIZE_MASK;
+ dimm_c1 &= MAD_DIMM_A_SIZE_MASK | MAD_DIMM_B_SIZE_MASK;
+ /* Enable swizzling when the channels are populated with
+ * identically sized dimms. We don't need to check the 3rd
+ * channel because no cpu with gpu attached ships in that
+ * configuration. Also, swizzling only makes sense for 2
+ * channels anyway. */
+ if (dimm_c0 == dimm_c1) {
+ swizzle_x = I915_BIT_6_SWIZZLE_9_10;
+ swizzle_y = I915_BIT_6_SWIZZLE_9;
+ } else {
+ swizzle_x = I915_BIT_6_SWIZZLE_NONE;
+ swizzle_y = I915_BIT_6_SWIZZLE_NONE;
+ }
} else if (IS_GEN5(dev)) {
/* On Ironlake whatever DRAM config, GPU always do
* same swizzling setup.
@@ -107,10 +122,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
*/
swizzle_x = I915_BIT_6_SWIZZLE_NONE;
swizzle_y = I915_BIT_6_SWIZZLE_NONE;
- } else if (IS_MOBILE(dev)) {
+ } else if (IS_MOBILE(dev) || (IS_GEN3(dev) && !IS_G33(dev))) {
uint32_t dcc;
- /* On mobile 9xx chipsets, channel interleave by the CPU is
+ /* On 9xx chipsets, channel interleave by the CPU is
* determined by DCC. For single-channel, neither the CPU
* nor the GPU do swizzling. For dual channel interleaved,
* the GPU's interleave is bit 9 and 10 for X tiled, and bit
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 5bd4361ea84d..afd4e03e337e 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -720,7 +720,6 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
reloc_offset = src->gtt_offset;
for (page = 0; page < page_count; page++) {
unsigned long flags;
- void __iomem *s;
void *d;
d = kmalloc(PAGE_SIZE, GFP_ATOMIC);
@@ -728,10 +727,29 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
goto unwind;
local_irq_save(flags);
- s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
- reloc_offset);
- memcpy_fromio(d, s, PAGE_SIZE);
- io_mapping_unmap_atomic(s);
+ if (reloc_offset < dev_priv->mm.gtt_mappable_end) {
+ void __iomem *s;
+
+ /* Simply ignore tiling or any overlapping fence.
+ * It's part of the error state, and this hopefully
+ * captures what the GPU read.
+ */
+
+ s = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
+ reloc_offset);
+ memcpy_fromio(d, s, PAGE_SIZE);
+ io_mapping_unmap_atomic(s);
+ } else {
+ void *s;
+
+ drm_clflush_pages(&src->pages[page], 1);
+
+ s = kmap_atomic(src->pages[page]);
+ memcpy(d, s, PAGE_SIZE);
+ kunmap_atomic(s);
+
+ drm_clflush_pages(&src->pages[page], 1);
+ }
local_irq_restore(flags);
dst->pages[page] = d;
@@ -770,11 +788,11 @@ i915_error_state_free(struct drm_device *dev,
{
int i;
- for (i = 0; i < ARRAY_SIZE(error->batchbuffer); i++)
- i915_error_object_free(error->batchbuffer[i]);
-
- for (i = 0; i < ARRAY_SIZE(error->ringbuffer); i++)
- i915_error_object_free(error->ringbuffer[i]);
+ for (i = 0; i < ARRAY_SIZE(error->ring); i++) {
+ i915_error_object_free(error->ring[i].batchbuffer);
+ i915_error_object_free(error->ring[i].ringbuffer);
+ kfree(error->ring[i].requests);
+ }
kfree(error->active_bo);
kfree(error->overlay);
@@ -804,7 +822,7 @@ static u32 capture_bo_list(struct drm_i915_error_buffer *err,
err->tiling = obj->tiling_mode;
err->dirty = obj->dirty;
err->purgeable = obj->madv != I915_MADV_WILLNEED;
- err->ring = obj->ring ? obj->ring->id : 0;
+ err->ring = obj->ring ? obj->ring->id : -1;
err->cache_level = obj->cache_level;
if (++i == count)
@@ -876,6 +894,92 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
return NULL;
}
+static void i915_record_ring_state(struct drm_device *dev,
+ struct drm_i915_error_state *error,
+ struct intel_ring_buffer *ring)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ if (INTEL_INFO(dev)->gen >= 6) {
+ error->faddr[ring->id] = I915_READ(RING_DMA_FADD(ring->mmio_base));
+ error->fault_reg[ring->id] = I915_READ(RING_FAULT_REG(ring));
+ error->semaphore_mboxes[ring->id][0]
+ = I915_READ(RING_SYNC_0(ring->mmio_base));
+ error->semaphore_mboxes[ring->id][1]
+ = I915_READ(RING_SYNC_1(ring->mmio_base));
+ }
+
+ if (INTEL_INFO(dev)->gen >= 4) {
+ error->ipeir[ring->id] = I915_READ(RING_IPEIR(ring->mmio_base));
+ error->ipehr[ring->id] = I915_READ(RING_IPEHR(ring->mmio_base));
+ error->instdone[ring->id] = I915_READ(RING_INSTDONE(ring->mmio_base));
+ error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base));
+ if (ring->id == RCS) {
+ error->instdone1 = I915_READ(INSTDONE1);
+ error->bbaddr = I915_READ64(BB_ADDR);
+ }
+ } else {
+ error->ipeir[ring->id] = I915_READ(IPEIR);
+ error->ipehr[ring->id] = I915_READ(IPEHR);
+ error->instdone[ring->id] = I915_READ(INSTDONE);
+ }
+
+ error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base));
+ error->seqno[ring->id] = ring->get_seqno(ring);
+ error->acthd[ring->id] = intel_ring_get_active_head(ring);
+ error->head[ring->id] = I915_READ_HEAD(ring);
+ error->tail[ring->id] = I915_READ_TAIL(ring);
+
+ error->cpu_ring_head[ring->id] = ring->head;
+ error->cpu_ring_tail[ring->id] = ring->tail;
+}
+
+static void i915_gem_record_rings(struct drm_device *dev,
+ struct drm_i915_error_state *error)
+{
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct drm_i915_gem_request *request;
+ int i, count;
+
+ for (i = 0; i < I915_NUM_RINGS; i++) {
+ struct intel_ring_buffer *ring = &dev_priv->ring[i];
+
+ if (ring->obj == NULL)
+ continue;
+
+ i915_record_ring_state(dev, error, ring);
+
+ error->ring[i].batchbuffer =
+ i915_error_first_batchbuffer(dev_priv, ring);
+
+ error->ring[i].ringbuffer =
+ i915_error_object_create(dev_priv, ring->obj);
+
+ count = 0;
+ list_for_each_entry(request, &ring->request_list, list)
+ count++;
+
+ error->ring[i].num_requests = count;
+ error->ring[i].requests =
+ kmalloc(count*sizeof(struct drm_i915_error_request),
+ GFP_ATOMIC);
+ if (error->ring[i].requests == NULL) {
+ error->ring[i].num_requests = 0;
+ continue;
+ }
+
+ count = 0;
+ list_for_each_entry(request, &ring->request_list, list) {
+ struct drm_i915_error_request *erq;
+
+ erq = &error->ring[i].requests[count++];
+ erq->seqno = request->seqno;
+ erq->jiffies = request->emitted_jiffies;
+ erq->tail = request->tail;
+ }
+ }
+}
+
/**
* i915_capture_error_state - capture an error record for later analysis
* @dev: drm device
@@ -900,7 +1004,7 @@ static void i915_capture_error_state(struct drm_device *dev)
return;
/* Account for pipe specific data like PIPE*STAT */
- error = kmalloc(sizeof(*error), GFP_ATOMIC);
+ error = kzalloc(sizeof(*error), GFP_ATOMIC);
if (!error) {
DRM_DEBUG_DRIVER("out of memory, not capturing error state\n");
return;
@@ -909,59 +1013,18 @@ static void i915_capture_error_state(struct drm_device *dev)
DRM_INFO("capturing error event; look for more information in /debug/dri/%d/i915_error_state\n",
dev->primary->index);
- error->seqno = dev_priv->ring[RCS].get_seqno(&dev_priv->ring[RCS]);
error->eir = I915_READ(EIR);
error->pgtbl_er = I915_READ(PGTBL_ER);
for_each_pipe(pipe)
error->pipestat[pipe] = I915_READ(PIPESTAT(pipe));
- error->instpm = I915_READ(INSTPM);
- error->error = 0;
+
if (INTEL_INFO(dev)->gen >= 6) {
error->error = I915_READ(ERROR_GEN6);
-
- error->bcs_acthd = I915_READ(BCS_ACTHD);
- error->bcs_ipehr = I915_READ(BCS_IPEHR);
- error->bcs_ipeir = I915_READ(BCS_IPEIR);
- error->bcs_instdone = I915_READ(BCS_INSTDONE);
- error->bcs_seqno = 0;
- if (dev_priv->ring[BCS].get_seqno)
- error->bcs_seqno = dev_priv->ring[BCS].get_seqno(&dev_priv->ring[BCS]);
-
- error->vcs_acthd = I915_READ(VCS_ACTHD);
- error->vcs_ipehr = I915_READ(VCS_IPEHR);
- error->vcs_ipeir = I915_READ(VCS_IPEIR);
- error->vcs_instdone = I915_READ(VCS_INSTDONE);
- error->vcs_seqno = 0;
- if (dev_priv->ring[VCS].get_seqno)
- error->vcs_seqno = dev_priv->ring[VCS].get_seqno(&dev_priv->ring[VCS]);
+ error->done_reg = I915_READ(DONE_REG);
}
- if (INTEL_INFO(dev)->gen >= 4) {
- error->ipeir = I915_READ(IPEIR_I965);
- error->ipehr = I915_READ(IPEHR_I965);
- error->instdone = I915_READ(INSTDONE_I965);
- error->instps = I915_READ(INSTPS);
- error->instdone1 = I915_READ(INSTDONE1);
- error->acthd = I915_READ(ACTHD_I965);
- error->bbaddr = I915_READ64(BB_ADDR);
- } else {
- error->ipeir = I915_READ(IPEIR);
- error->ipehr = I915_READ(IPEHR);
- error->instdone = I915_READ(INSTDONE);
- error->acthd = I915_READ(ACTHD);
- error->bbaddr = 0;
- }
- i915_gem_record_fences(dev, error);
-
- /* Record the active batch and ring buffers */
- for (i = 0; i < I915_NUM_RINGS; i++) {
- error->batchbuffer[i] =
- i915_error_first_batchbuffer(dev_priv,
- &dev_priv->ring[i]);
- error->ringbuffer[i] =
- i915_error_object_create(dev_priv,
- dev_priv->ring[i].obj);
- }
+ i915_gem_record_fences(dev, error);
+ i915_gem_record_rings(dev, error);
/* Record buffers on the active and pinned lists. */
error->active_bo = NULL;
@@ -1017,11 +1080,12 @@ void i915_destroy_error_state(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_error_state *error;
+ unsigned long flags;
- spin_lock(&dev_priv->error_lock);
+ spin_lock_irqsave(&dev_priv->error_lock, flags);
error = dev_priv->first_error;
dev_priv->first_error = NULL;
- spin_unlock(&dev_priv->error_lock);
+ spin_unlock_irqrestore(&dev_priv->error_lock, flags);
if (error)
i915_error_state_free(dev, error);
@@ -1698,6 +1762,7 @@ void i915_hangcheck_elapsed(unsigned long data)
dev_priv->last_instdone1 == instdone1) {
if (dev_priv->hangcheck_count++ > 1) {
DRM_ERROR("Hangcheck timer elapsed... GPU hung\n");
+ i915_handle_error(dev, true);
if (!IS_GEN2(dev)) {
/* Is the chip hanging on a WAIT_FOR_EVENT?
@@ -1705,7 +1770,6 @@ void i915_hangcheck_elapsed(unsigned long data)
* and break the hang. This should work on
* all but the second generation chipsets.
*/
-
if (kick_ring(&dev_priv->ring[RCS]))
goto repeat;
@@ -1718,7 +1782,6 @@ void i915_hangcheck_elapsed(unsigned long data)
goto repeat;
}
- i915_handle_error(dev, true);
return;
}
} else {
@@ -1752,18 +1815,6 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
I915_WRITE(HWSTAM, 0xeffe);
- if (IS_GEN6(dev)) {
- /* Workaround stalls observed on Sandy Bridge GPUs by
- * making the blitter command streamer generate a
- * write to the Hardware Status Page for
- * MI_USER_INTERRUPT. This appears to serialize the
- * previous seqno write out before the interrupt
- * happens.
- */
- I915_WRITE(GEN6_BLITTER_HWSTAM, ~GEN6_BLITTER_USER_INTERRUPT);
- I915_WRITE(GEN6_BSD_HWSTAM, ~GEN6_BSD_USER_INTERRUPT);
- }
-
/* XXX hotplug from PCH */
I915_WRITE(DEIMR, 0xffffffff);
diff --git a/drivers/gpu/drm/i915/i915_mem.c b/drivers/gpu/drm/i915/i915_mem.c
deleted file mode 100644
index cc8f6d49cf20..000000000000
--- a/drivers/gpu/drm/i915/i915_mem.c
+++ /dev/null
@@ -1,387 +0,0 @@
-/* i915_mem.c -- Simple agp/fb memory manager for i915 -*- linux-c -*-
- */
-/*
- * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
- * All Rights Reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sub license, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
- * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
- * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
- * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
- * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#include "drmP.h"
-#include "drm.h"
-#include "i915_drm.h"
-#include "i915_drv.h"
-
-/* This memory manager is integrated into the global/local lru
- * mechanisms used by the clients. Specifically, it operates by
- * setting the 'in_use' fields of the global LRU to indicate whether
- * this region is privately allocated to a client.
- *
- * This does require the client to actually respect that field.
- *
- * Currently no effort is made to allocate 'private' memory in any
- * clever way - the LRU information isn't used to determine which
- * block to allocate, and the ring is drained prior to allocations --
- * in other words allocation is expensive.
- */
-static void mark_block(struct drm_device * dev, struct mem_block *p, int in_use)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
- drm_i915_sarea_t *sarea_priv = master_priv->sarea_priv;
- struct drm_tex_region *list;
- unsigned shift, nr;
- unsigned start;
- unsigned end;
- unsigned i;
- int age;
-
- shift = dev_priv->tex_lru_log_granularity;
- nr = I915_NR_TEX_REGIONS;
-
- start = p->start >> shift;
- end = (p->start + p->size - 1) >> shift;
-
- age = ++sarea_priv->texAge;
- list = sarea_priv->texList;
-
- /* Mark the regions with the new flag and update their age. Move
- * them to head of list to preserve LRU semantics.
- */
- for (i = start; i <= end; i++) {
- list[i].in_use = in_use;
- list[i].age = age;
-
- /* remove_from_list(i)
- */
- list[(unsigned)list[i].next].prev = list[i].prev;
- list[(unsigned)list[i].prev].next = list[i].next;
-
- /* insert_at_head(list, i)
- */
- list[i].prev = nr;
- list[i].next = list[nr].next;
- list[(unsigned)list[nr].next].prev = i;
- list[nr].next = i;
- }
-}
-
-/* Very simple allocator for agp memory, working on a static range
- * already mapped into each client's address space.
- */
-
-static struct mem_block *split_block(struct mem_block *p, int start, int size,
- struct drm_file *file_priv)
-{
- /* Maybe cut off the start of an existing block */
- if (start > p->start) {
- struct mem_block *newblock = kmalloc(sizeof(*newblock),
- GFP_KERNEL);
- if (!newblock)
- goto out;
- newblock->start = start;
- newblock->size = p->size - (start - p->start);
- newblock->file_priv = NULL;
- newblock->next = p->next;
- newblock->prev = p;
- p->next->prev = newblock;
- p->next = newblock;
- p->size -= newblock->size;
- p = newblock;
- }
-
- /* Maybe cut off the end of an existing block */
- if (size < p->size) {
- struct mem_block *newblock = kmalloc(sizeof(*newblock),
- GFP_KERNEL);
- if (!newblock)
- goto out;
- newblock->start = start + size;
- newblock->size = p->size - size;
- newblock->file_priv = NULL;
- newblock->next = p->next;
- newblock->prev = p;
- p->next->prev = newblock;
- p->next = newblock;
- p->size = size;
- }
-
- out:
- /* Our block is in the middle */
- p->file_priv = file_priv;
- return p;
-}
-
-static struct mem_block *alloc_block(struct mem_block *heap, int size,
- int align2, struct drm_file *file_priv)
-{
- struct mem_block *p;
- int mask = (1 << align2) - 1;
-
- for (p = heap->next; p != heap; p = p->next) {
- int start = (p->start + mask) & ~mask;
- if (p->file_priv == NULL && start + size <= p->start + p->size)
- return split_block(p, start, size, file_priv);
- }
-
- return NULL;
-}
-
-static struct mem_block *find_block(struct mem_block *heap, int start)
-{
- struct mem_block *p;
-
- for (p = heap->next; p != heap; p = p->next)
- if (p->start == start)
- return p;
-
- return NULL;
-}
-
-static void free_block(struct mem_block *p)
-{
- p->file_priv = NULL;
-
- /* Assumes a single contiguous range. Needs a special file_priv in
- * 'heap' to stop it being subsumed.
- */
- if (p->next->file_priv == NULL) {
- struct mem_block *q = p->next;
- p->size += q->size;
- p->next = q->next;
- p->next->prev = p;
- kfree(q);
- }
-
- if (p->prev->file_priv == NULL) {
- struct mem_block *q = p->prev;
- q->size += p->size;
- q->next = p->next;
- q->next->prev = q;
- kfree(p);
- }
-}
-
-/* Initialize. How to check for an uninitialized heap?
- */
-static int init_heap(struct mem_block **heap, int start, int size)
-{
- struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
-
- if (!blocks)
- return -ENOMEM;
-
- *heap = kmalloc(sizeof(**heap), GFP_KERNEL);
- if (!*heap) {
- kfree(blocks);
- return -ENOMEM;
- }
-
- blocks->start = start;
- blocks->size = size;
- blocks->file_priv = NULL;
- blocks->next = blocks->prev = *heap;
-
- memset(*heap, 0, sizeof(**heap));
- (*heap)->file_priv = (struct drm_file *) -1;
- (*heap)->next = (*heap)->prev = blocks;
- return 0;
-}
-
-/* Free all blocks associated with the releasing file.
- */
-void i915_mem_release(struct drm_device * dev, struct drm_file *file_priv,
- struct mem_block *heap)
-{
- struct mem_block *p;
-
- if (!heap || !heap->next)
- return;
-
- for (p = heap->next; p != heap; p = p->next) {
- if (p->file_priv == file_priv) {
- p->file_priv = NULL;
- mark_block(dev, p, 0);
- }
- }
-
- /* Assumes a single contiguous range. Needs a special file_priv in
- * 'heap' to stop it being subsumed.
- */
- for (p = heap->next; p != heap; p = p->next) {
- while (p->file_priv == NULL && p->next->file_priv == NULL) {
- struct mem_block *q = p->next;
- p->size += q->size;
- p->next = q->next;
- p->next->prev = p;
- kfree(q);
- }
- }
-}
-
-/* Shutdown.
- */
-void i915_mem_takedown(struct mem_block **heap)
-{
- struct mem_block *p;
-
- if (!*heap)
- return;
-
- for (p = (*heap)->next; p != *heap;) {
- struct mem_block *q = p;
- p = p->next;
- kfree(q);
- }
-
- kfree(*heap);
- *heap = NULL;
-}
-
-static struct mem_block **get_heap(drm_i915_private_t * dev_priv, int region)
-{
- switch (region) {
- case I915_MEM_REGION_AGP:
- return &dev_priv->agp_heap;
- default:
- return NULL;
- }
-}
-
-/* IOCTL HANDLERS */
-
-int i915_mem_alloc(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_mem_alloc_t *alloc = data;
- struct mem_block *block, **heap;
-
- if (!dev_priv) {
- DRM_ERROR("called with no initialization\n");
- return -EINVAL;
- }
-
- heap = get_heap(dev_priv, alloc->region);
- if (!heap || !*heap)
- return -EFAULT;
-
- /* Make things easier on ourselves: all allocations at least
- * 4k aligned.
- */
- if (alloc->alignment < 12)
- alloc->alignment = 12;
-
- block = alloc_block(*heap, alloc->size, alloc->alignment, file_priv);
-
- if (!block)
- return -ENOMEM;
-
- mark_block(dev, block, 1);
-
- if (DRM_COPY_TO_USER(alloc->region_offset, &block->start,
- sizeof(int))) {
- DRM_ERROR("copy_to_user\n");
- return -EFAULT;
- }
-
- return 0;
-}
-
-int i915_mem_free(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_mem_free_t *memfree = data;
- struct mem_block *block, **heap;
-
- if (!dev_priv) {
- DRM_ERROR("called with no initialization\n");
- return -EINVAL;
- }
-
- heap = get_heap(dev_priv, memfree->region);
- if (!heap || !*heap)
- return -EFAULT;
-
- block = find_block(*heap, memfree->region_offset);
- if (!block)
- return -EFAULT;
-
- if (block->file_priv != file_priv)
- return -EPERM;
-
- mark_block(dev, block, 0);
- free_block(block);
- return 0;
-}
-
-int i915_mem_init_heap(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_mem_init_heap_t *initheap = data;
- struct mem_block **heap;
-
- if (!dev_priv) {
- DRM_ERROR("called with no initialization\n");
- return -EINVAL;
- }
-
- heap = get_heap(dev_priv, initheap->region);
- if (!heap)
- return -EFAULT;
-
- if (*heap) {
- DRM_ERROR("heap already initialized?");
- return -EFAULT;
- }
-
- return init_heap(heap, initheap->start, initheap->size);
-}
-
-int i915_mem_destroy_heap(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- drm_i915_mem_destroy_heap_t *destroyheap = data;
- struct mem_block **heap;
-
- if (!dev_priv) {
- DRM_ERROR("called with no initialization\n");
- return -EINVAL;
- }
-
- heap = get_heap(dev_priv, destroyheap->region);
- if (!heap) {
- DRM_ERROR("get_heap failed");
- return -EFAULT;
- }
-
- if (!*heap) {
- DRM_ERROR("heap not initialized?");
- return -EFAULT;
- }
-
- i915_mem_takedown(heap);
- return 0;
-}
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 558ac716a328..3886cf051bac 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -86,12 +86,45 @@
#define GEN6_MBC_SNPCR_LOW (2<<21)
#define GEN6_MBC_SNPCR_MIN (3<<21) /* only 1/16th of the cache is shared */
+#define GEN6_MBCTL 0x0907c
+#define GEN6_MBCTL_ENABLE_BOOT_FETCH (1 << 4)
+#define GEN6_MBCTL_CTX_FETCH_NEEDED (1 << 3)
+#define GEN6_MBCTL_BME_UPDATE_ENABLE (1 << 2)
+#define GEN6_MBCTL_MAE_UPDATE_ENABLE (1 << 1)
+#define GEN6_MBCTL_BOOT_FETCH_MECH (1 << 0)
+
#define GEN6_GDRST 0x941c
#define GEN6_GRDOM_FULL (1 << 0)
#define GEN6_GRDOM_RENDER (1 << 1)
#define GEN6_GRDOM_MEDIA (1 << 2)
#define GEN6_GRDOM_BLT (1 << 3)
+/* PPGTT stuff */
+#define GEN6_GTT_ADDR_ENCODE(addr) ((addr) | (((addr) >> 28) & 0xff0))
+
+#define GEN6_PDE_VALID (1 << 0)
+#define GEN6_PDE_LARGE_PAGE (2 << 0) /* use 32kb pages */
+/* gen6+ has bit 11-4 for physical addr bit 39-32 */
+#define GEN6_PDE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
+
+#define GEN6_PTE_VALID (1 << 0)
+#define GEN6_PTE_UNCACHED (1 << 1)
+#define GEN6_PTE_CACHE_LLC (2 << 1)
+#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
+#define GEN6_PTE_CACHE_BITS (3 << 1)
+#define GEN6_PTE_GFDT (1 << 3)
+#define GEN6_PTE_ADDR_ENCODE(addr) GEN6_GTT_ADDR_ENCODE(addr)
+
+#define RING_PP_DIR_BASE(ring) ((ring)->mmio_base+0x228)
+#define RING_PP_DIR_BASE_READ(ring) ((ring)->mmio_base+0x518)
+#define RING_PP_DIR_DCLV(ring) ((ring)->mmio_base+0x220)
+#define PP_DIR_DCLV_2G 0xffffffff
+
+#define GAM_ECOCHK 0x4090
+#define ECOCHK_SNB_BIT (1<<10)
+#define ECOCHK_PPGTT_CACHE64B (0x3<<3)
+#define ECOCHK_PPGTT_CACHE4B (0x0<<3)
+
/* VGA stuff */
#define VGA_ST01_MDA 0x3ba
@@ -295,6 +328,12 @@
#define FENCE_REG_SANDYBRIDGE_0 0x100000
#define SANDYBRIDGE_FENCE_PITCH_SHIFT 32
+/* control register for cpu gtt access */
+#define TILECTL 0x101000
+#define TILECTL_SWZCTL (1 << 0)
+#define TILECTL_TLB_PREFETCH_DIS (1 << 2)
+#define TILECTL_BACKSNOOP_DIS (1 << 3)
+
/*
* Instruction and interrupt control regs
*/
@@ -318,7 +357,14 @@
#define RING_MAX_IDLE(base) ((base)+0x54)
#define RING_HWS_PGA(base) ((base)+0x80)
#define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
+#define ARB_MODE 0x04030
+#define ARB_MODE_SWIZZLE_SNB (1<<4)
+#define ARB_MODE_SWIZZLE_IVB (1<<5)
+#define ARB_MODE_ENABLE(x) GFX_MODE_ENABLE(x)
+#define ARB_MODE_DISABLE(x) GFX_MODE_DISABLE(x)
#define RENDER_HWS_PGA_GEN7 (0x04080)
+#define RING_FAULT_REG(ring) (0x4094 + 0x100*(ring)->id)
+#define DONE_REG 0x40b0
#define BSD_HWS_PGA_GEN7 (0x04180)
#define BLT_HWS_PGA_GEN7 (0x04280)
#define RING_ACTHD(base) ((base)+0x74)
@@ -352,6 +398,12 @@
#define IPEIR_I965 0x02064
#define IPEHR_I965 0x02068
#define INSTDONE_I965 0x0206c
+#define RING_IPEIR(base) ((base)+0x64)
+#define RING_IPEHR(base) ((base)+0x68)
+#define RING_INSTDONE(base) ((base)+0x6c)
+#define RING_INSTPS(base) ((base)+0x70)
+#define RING_DMA_FADD(base) ((base)+0x78)
+#define RING_INSTPM(base) ((base)+0xc0)
#define INSTPS 0x02070 /* 965+ only */
#define INSTDONE1 0x0207c /* 965+ only */
#define ACTHD_I965 0x02074
@@ -365,14 +417,6 @@
#define INSTDONE 0x02090
#define NOPID 0x02094
#define HWSTAM 0x02098
-#define VCS_INSTDONE 0x1206C
-#define VCS_IPEIR 0x12064
-#define VCS_IPEHR 0x12068
-#define VCS_ACTHD 0x12074
-#define BCS_INSTDONE 0x2206C
-#define BCS_IPEIR 0x22064
-#define BCS_IPEHR 0x22068
-#define BCS_ACTHD 0x22074
#define ERROR_GEN6 0x040a0
@@ -391,10 +435,11 @@
#define MI_MODE 0x0209c
# define VS_TIMER_DISPATCH (1 << 6)
-# define MI_FLUSH_ENABLE (1 << 11)
+# define MI_FLUSH_ENABLE (1 << 12)
#define GFX_MODE 0x02520
#define GFX_MODE_GEN7 0x0229c
+#define RING_MODE_GEN7(ring) ((ring)->mmio_base+0x29c)
#define GFX_RUN_LIST_ENABLE (1<<15)
#define GFX_TLB_INVALIDATE_ALWAYS (1<<13)
#define GFX_SURFACE_FAULT_ENABLE (1<<12)
@@ -1037,6 +1082,29 @@
#define C0DRB3 0x10206
#define C1DRB3 0x10606
+/** snb MCH registers for reading the DRAM channel configuration */
+#define MAD_DIMM_C0 (MCHBAR_MIRROR_BASE_SNB + 0x5004)
+#define MAD_DIMM_C1 (MCHBAR_MIRROR_BASE_SNB + 0x5008)
+#define MAD_DIMM_C2 (MCHBAR_MIRROR_BASE_SNB + 0x500C)
+#define MAD_DIMM_ECC_MASK (0x3 << 24)
+#define MAD_DIMM_ECC_OFF (0x0 << 24)
+#define MAD_DIMM_ECC_IO_ON_LOGIC_OFF (0x1 << 24)
+#define MAD_DIMM_ECC_IO_OFF_LOGIC_ON (0x2 << 24)
+#define MAD_DIMM_ECC_ON (0x3 << 24)
+#define MAD_DIMM_ENH_INTERLEAVE (0x1 << 22)
+#define MAD_DIMM_RANK_INTERLEAVE (0x1 << 21)
+#define MAD_DIMM_B_WIDTH_X16 (0x1 << 20) /* X8 chips if unset */
+#define MAD_DIMM_A_WIDTH_X16 (0x1 << 19) /* X8 chips if unset */
+#define MAD_DIMM_B_DUAL_RANK (0x1 << 18)
+#define MAD_DIMM_A_DUAL_RANK (0x1 << 17)
+#define MAD_DIMM_A_SELECT (0x1 << 16)
+/* DIMM sizes are in multiples of 256mb. */
+#define MAD_DIMM_B_SIZE_SHIFT 8
+#define MAD_DIMM_B_SIZE_MASK (0xff << MAD_DIMM_B_SIZE_SHIFT)
+#define MAD_DIMM_A_SIZE_SHIFT 0
+#define MAD_DIMM_A_SIZE_MASK (0xff << MAD_DIMM_A_SIZE_SHIFT)
+
+
/* Clocking configuration register */
#define CLKCFG 0x10c00
#define CLKCFG_FSB_400 (5 << 0) /* hrawclk 100 */
@@ -1316,6 +1384,7 @@
#define _VSYNC_A 0x60014
#define _PIPEASRC 0x6001c
#define _BCLRPAT_A 0x60020
+#define _VSYNCSHIFT_A 0x60028
/* Pipe B timing regs */
#define _HTOTAL_B 0x61000
@@ -1326,6 +1395,8 @@
#define _VSYNC_B 0x61014
#define _PIPEBSRC 0x6101c
#define _BCLRPAT_B 0x61020
+#define _VSYNCSHIFT_B 0x61028
+
#define HTOTAL(pipe) _PIPE(pipe, _HTOTAL_A, _HTOTAL_B)
#define HBLANK(pipe) _PIPE(pipe, _HBLANK_A, _HBLANK_B)
@@ -1334,6 +1405,7 @@
#define VBLANK(pipe) _PIPE(pipe, _VBLANK_A, _VBLANK_B)
#define VSYNC(pipe) _PIPE(pipe, _VSYNC_A, _VSYNC_B)
#define BCLRPAT(pipe) _PIPE(pipe, _BCLRPAT_A, _BCLRPAT_B)
+#define VSYNCSHIFT(pipe) _PIPE(pipe, _VSYNCSHIFT_A, _VSYNCSHIFT_B)
/* VGA port control */
#define ADPA 0x61100
@@ -2319,10 +2391,21 @@
#define PIPECONF_PALETTE 0
#define PIPECONF_GAMMA (1<<24)
#define PIPECONF_FORCE_BORDER (1<<25)
-#define PIPECONF_PROGRESSIVE (0 << 21)
-#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21)
-#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21)
#define PIPECONF_INTERLACE_MASK (7 << 21)
+/* Note that pre-gen3 does not support interlaced display directly. Panel
+ * fitting must be disabled on pre-ilk for interlaced. */
+#define PIPECONF_PROGRESSIVE (0 << 21)
+#define PIPECONF_INTERLACE_W_SYNC_SHIFT_PANEL (4 << 21) /* gen4 only */
+#define PIPECONF_INTERLACE_W_SYNC_SHIFT (5 << 21) /* gen4 only */
+#define PIPECONF_INTERLACE_W_FIELD_INDICATION (6 << 21)
+#define PIPECONF_INTERLACE_FIELD_0_ONLY (7 << 21) /* gen3 only */
+/* Ironlake and later have a complete new set of values for interlaced. PFIT
+ * means panel fitter required, PF means progressive fetch, DBL means power
+ * saving pixel doubling. */
+#define PIPECONF_PFIT_PF_INTERLACED_ILK (1 << 21)
+#define PIPECONF_INTERLACED_ILK (3 << 21)
+#define PIPECONF_INTERLACED_DBL_ILK (4 << 21) /* ilk/snb only */
+#define PIPECONF_PFIT_PF_INTERLACED_DBL_ILK (5 << 21) /* ilk/snb only */
#define PIPECONF_CXSR_DOWNCLOCK (1<<16)
#define PIPECONF_BPP_MASK (0x000000e0)
#define PIPECONF_BPP_8 (0<<5)
@@ -3219,6 +3302,7 @@
#define _TRANS_VSYNC_A 0xe0014
#define TRANS_VSYNC_END_SHIFT 16
#define TRANS_VSYNC_START_SHIFT 0
+#define _TRANS_VSYNCSHIFT_A 0xe0028
#define _TRANSA_DATA_M1 0xe0030
#define _TRANSA_DATA_N1 0xe0034
@@ -3249,6 +3333,7 @@
#define _TRANS_VTOTAL_B 0xe100c
#define _TRANS_VBLANK_B 0xe1010
#define _TRANS_VSYNC_B 0xe1014
+#define _TRANS_VSYNCSHIFT_B 0xe1028
#define TRANS_HTOTAL(pipe) _PIPE(pipe, _TRANS_HTOTAL_A, _TRANS_HTOTAL_B)
#define TRANS_HBLANK(pipe) _PIPE(pipe, _TRANS_HBLANK_A, _TRANS_HBLANK_B)
@@ -3256,6 +3341,8 @@
#define TRANS_VTOTAL(pipe) _PIPE(pipe, _TRANS_VTOTAL_A, _TRANS_VTOTAL_B)
#define TRANS_VBLANK(pipe) _PIPE(pipe, _TRANS_VBLANK_A, _TRANS_VBLANK_B)
#define TRANS_VSYNC(pipe) _PIPE(pipe, _TRANS_VSYNC_A, _TRANS_VSYNC_B)
+#define TRANS_VSYNCSHIFT(pipe) _PIPE(pipe, _TRANS_VSYNCSHIFT_A, \
+ _TRANS_VSYNCSHIFT_B)
#define _TRANSB_DATA_M1 0xe1030
#define _TRANSB_DATA_N1 0xe1034
@@ -3289,7 +3376,10 @@
#define TRANS_FSYNC_DELAY_HB4 (3<<27)
#define TRANS_DP_AUDIO_ONLY (1<<26)
#define TRANS_DP_VIDEO_AUDIO (0<<26)
+#define TRANS_INTERLACE_MASK (7<<21)
#define TRANS_PROGRESSIVE (0<<21)
+#define TRANS_INTERLACED (3<<21)
+#define TRANS_LEGACY_INTERLACED_ILK (2<<21)
#define TRANS_8BPC (0<<5)
#define TRANS_10BPC (1<<5)
#define TRANS_6BPC (2<<5)
@@ -3628,6 +3718,12 @@
#define ECOBUS 0xa180
#define FORCEWAKE_MT_ENABLE (1<<5)
+#define GTFIFODBG 0x120000
+#define GT_FIFO_CPU_ERROR_MASK 7
+#define GT_FIFO_OVFERR (1<<2)
+#define GT_FIFO_IAWRERR (1<<1)
+#define GT_FIFO_IARDERR (1<<0)
+
#define GT_FIFO_FREE_ENTRIES 0x120008
#define GT_FIFO_NUM_RESERVED_ENTRIES 20
@@ -3757,4 +3853,16 @@
*/
#define GEN7_SO_WRITE_OFFSET(n) (0x5280 + (n) * 4)
+#define IBX_AUD_CONFIG_A 0xe2000
+#define CPT_AUD_CONFIG_A 0xe5000
+#define AUD_CONFIG_N_VALUE_INDEX (1 << 29)
+#define AUD_CONFIG_N_PROG_ENABLE (1 << 28)
+#define AUD_CONFIG_UPPER_N_SHIFT 20
+#define AUD_CONFIG_UPPER_N_VALUE (0xff << 20)
+#define AUD_CONFIG_LOWER_N_SHIFT 4
+#define AUD_CONFIG_LOWER_N_VALUE (0xfff << 4)
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI_SHIFT 16
+#define AUD_CONFIG_PIXEL_CLOCK_HDMI (0xf << 16)
+#define AUD_CONFIG_DISABLE_NCTS (1 << 3)
+
#endif /* _I915_REG_H_ */
diff --git a/drivers/gpu/drm/i915/intel_acpi.c b/drivers/gpu/drm/i915/intel_acpi.c
index cb912106d1a2..bae3edf956a4 100644
--- a/drivers/gpu/drm/i915/intel_acpi.c
+++ b/drivers/gpu/drm/i915/intel_acpi.c
@@ -208,7 +208,7 @@ static bool intel_dsm_pci_probe(struct pci_dev *pdev)
ret = intel_dsm(dhandle, INTEL_DSM_FN_SUPPORTED_FUNCTIONS, 0);
if (ret < 0) {
- DRM_ERROR("failed to get supported _DSM functions\n");
+ DRM_DEBUG_KMS("failed to get supported _DSM functions\n");
return false;
}
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 63880e2e5cfd..8168d8f8a634 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -572,7 +572,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
DRM_DEBUG_KMS("no child dev is parsed from VBT\n");
return;
}
- dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL);
+ dev_priv->child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL);
if (!dev_priv->child_dev) {
DRM_DEBUG_KMS("No memory space for child device\n");
return;
@@ -669,7 +669,7 @@ intel_parse_bios(struct drm_device *dev)
}
if (!vbt) {
- DRM_ERROR("VBT signature missing\n");
+ DRM_DEBUG_DRIVER("VBT signature missing\n");
pci_unmap_rom(pdev, bios);
return -1;
}
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index dd729d46a61f..4d3d736a4f56 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -594,7 +594,10 @@ void intel_crt_init(struct drm_device *dev)
1 << INTEL_ANALOG_CLONE_BIT |
1 << INTEL_SDVO_LVDS_CLONE_BIT);
crt->base.crtc_mask = (1 << 0) | (1 << 1);
- connector->interlace_allowed = 1;
+ if (IS_GEN2(dev))
+ connector->interlace_allowed = 0;
+ else
+ connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
drm_encoder_helper_add(&crt->base.base, &intel_crt_helper_funcs);
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 397087cf689e..d514719f65e2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -75,7 +75,7 @@ struct intel_limit {
intel_range_t dot, vco, n, m, m1, m2, p, p1;
intel_p2_t p2;
bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
- int, int, intel_clock_t *);
+ int, int, intel_clock_t *, intel_clock_t *);
};
/* FDI */
@@ -83,17 +83,21 @@ struct intel_limit {
static bool
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock);
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock);
static bool
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock);
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock);
static bool
intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock);
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock);
static bool
intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock);
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock);
static inline u32 /* units of 100MHz */
intel_fdi_link_freq(struct drm_device *dev)
@@ -515,7 +519,8 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
static bool
intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock)
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock)
{
struct drm_device *dev = crtc->dev;
@@ -562,6 +567,9 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
if (!intel_PLL_is_valid(dev, limit,
&clock))
continue;
+ if (match_clock &&
+ clock.p != match_clock->p)
+ continue;
this_err = abs(clock.dot - target);
if (this_err < err) {
@@ -578,7 +586,8 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
static bool
intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock)
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -625,6 +634,9 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
if (!intel_PLL_is_valid(dev, limit,
&clock))
continue;
+ if (match_clock &&
+ clock.p != match_clock->p)
+ continue;
this_err = abs(clock.dot - target);
if (this_err < err_most) {
@@ -642,7 +654,8 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
static bool
intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock)
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock)
{
struct drm_device *dev = crtc->dev;
intel_clock_t clock;
@@ -668,7 +681,8 @@ intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
/* DisplayPort has only two frequencies, 162MHz and 270MHz */
static bool
intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
- int target, int refclk, intel_clock_t *best_clock)
+ int target, int refclk, intel_clock_t *match_clock,
+ intel_clock_t *best_clock)
{
intel_clock_t clock;
if (target < 200000) {
@@ -922,6 +936,10 @@ void assert_pipe(struct drm_i915_private *dev_priv,
u32 val;
bool cur_state;
+ /* if we need the pipe A quirk it must be always on */
+ if (pipe == PIPE_A && dev_priv->quirks & QUIRK_PIPEA_FORCE)
+ state = true;
+
reg = PIPECONF(pipe);
val = I915_READ(reg);
cur_state = !!(val & PIPECONF_ENABLE);
@@ -930,19 +948,24 @@ void assert_pipe(struct drm_i915_private *dev_priv,
pipe_name(pipe), state_string(state), state_string(cur_state));
}
-static void assert_plane_enabled(struct drm_i915_private *dev_priv,
- enum plane plane)
+static void assert_plane(struct drm_i915_private *dev_priv,
+ enum plane plane, bool state)
{
int reg;
u32 val;
+ bool cur_state;
reg = DSPCNTR(plane);
val = I915_READ(reg);
- WARN(!(val & DISPLAY_PLANE_ENABLE),
- "plane %c assertion failure, should be active but is disabled\n",
- plane_name(plane));
+ cur_state = !!(val & DISPLAY_PLANE_ENABLE);
+ WARN(cur_state != state,
+ "plane %c assertion failure (expected %s, current %s)\n",
+ plane_name(plane), state_string(state), state_string(cur_state));
}
+#define assert_plane_enabled(d, p) assert_plane(d, p, true)
+#define assert_plane_disabled(d, p) assert_plane(d, p, false)
+
static void assert_planes_disabled(struct drm_i915_private *dev_priv,
enum pipe pipe)
{
@@ -951,8 +974,14 @@ static void assert_planes_disabled(struct drm_i915_private *dev_priv,
int cur_pipe;
/* Planes are fixed to pipes on ILK+ */
- if (HAS_PCH_SPLIT(dev_priv->dev))
+ if (HAS_PCH_SPLIT(dev_priv->dev)) {
+ reg = DSPCNTR(pipe);
+ val = I915_READ(reg);
+ WARN((val & DISPLAY_PLANE_ENABLE),
+ "plane %c assertion failure, should be disabled but not\n",
+ plane_name(pipe));
return;
+ }
/* Need to check both planes against the pipe */
for (i = 0; i < 2; i++) {
@@ -1071,7 +1100,7 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv,
{
u32 val = I915_READ(reg);
WARN(hdmi_pipe_enabled(dev_priv, val, pipe),
- "PCH DP (0x%08x) enabled on transcoder %c, should be disabled\n",
+ "PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n",
reg, pipe_name(pipe));
}
@@ -1237,7 +1266,8 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv,
enum pipe pipe)
{
int reg;
- u32 val;
+ u32 val, pipeconf_val;
+ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
/* PCH only available on ILK+ */
BUG_ON(dev_priv->info->gen < 5);
@@ -1251,6 +1281,7 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv,
reg = TRANSCONF(pipe);
val = I915_READ(reg);
+ pipeconf_val = I915_READ(PIPECONF(pipe));
if (HAS_PCH_IBX(dev_priv->dev)) {
/*
@@ -1258,8 +1289,19 @@ static void intel_enable_transcoder(struct drm_i915_private *dev_priv,
* that in pipeconf reg.
*/
val &= ~PIPE_BPC_MASK;
- val |= I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK;
+ val |= pipeconf_val & PIPE_BPC_MASK;
}
+
+ val &= ~TRANS_INTERLACE_MASK;
+ if ((pipeconf_val & PIPECONF_INTERLACE_MASK) == PIPECONF_INTERLACED_ILK)
+ if (HAS_PCH_IBX(dev_priv->dev) &&
+ intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO))
+ val |= TRANS_LEGACY_INTERLACED_ILK;
+ else
+ val |= TRANS_INTERLACED;
+ else
+ val |= TRANS_PROGRESSIVE;
+
I915_WRITE(reg, val | TRANS_ENABLE);
if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
DRM_ERROR("failed to enable transcoder %d\n", pipe);
@@ -2012,6 +2054,8 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
ret = i915_gem_object_get_fence(obj, pipelined);
if (ret)
goto err_unpin;
+
+ i915_gem_object_pin_fence(obj);
}
dev_priv->mm.interruptible = true;
@@ -2024,6 +2068,12 @@ err_interruptible:
return ret;
}
+void intel_unpin_fb_obj(struct drm_i915_gem_object *obj)
+{
+ i915_gem_object_unpin_fence(obj);
+ i915_gem_object_unpin(obj);
+}
+
static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
int x, int y)
{
@@ -2255,7 +2305,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y,
LEAVE_ATOMIC_MODE_SET);
if (ret) {
- i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
+ intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
DRM_ERROR("failed to update base address\n");
return ret;
@@ -2263,7 +2313,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
if (old_fb) {
intel_wait_for_vblank(dev, intel_crtc->pipe);
- i915_gem_object_unpin(to_intel_framebuffer(old_fb)->obj);
+ intel_unpin_fb_obj(to_intel_framebuffer(old_fb)->obj);
}
mutex_unlock(&dev->struct_mutex);
@@ -2936,6 +2986,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
I915_WRITE(TRANS_VTOTAL(pipe), I915_READ(VTOTAL(pipe)));
I915_WRITE(TRANS_VBLANK(pipe), I915_READ(VBLANK(pipe)));
I915_WRITE(TRANS_VSYNC(pipe), I915_READ(VSYNC(pipe)));
+ I915_WRITE(TRANS_VSYNCSHIFT(pipe), I915_READ(VSYNCSHIFT(pipe)));
intel_fdi_normal_train(crtc);
@@ -3321,10 +3372,12 @@ static void intel_crtc_disable(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+ assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane);
+ assert_pipe_disabled(dev->dev_private, to_intel_crtc(crtc)->pipe);
if (crtc->fb) {
mutex_lock(&dev->struct_mutex);
- i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
+ intel_unpin_fb_obj(to_intel_framebuffer(crtc->fb)->obj);
mutex_unlock(&dev->struct_mutex);
}
}
@@ -3398,11 +3451,8 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
return false;
}
- /* XXX some encoders set the crtcinfo, others don't.
- * Obviously we need some form of conflict resolution here...
- */
- if (adjusted_mode->crtc_htotal == 0)
- drm_mode_set_crtcinfo(adjusted_mode, 0);
+ /* All interlaced capable intel hw wants timings in frames. */
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
return true;
}
@@ -4521,6 +4571,7 @@ void sandybridge_update_wm(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */
+ u32 val;
int fbc_wm, plane_wm, cursor_wm;
unsigned int enabled;
@@ -4529,8 +4580,10 @@ void sandybridge_update_wm(struct drm_device *dev)
&sandybridge_display_wm_info, latency,
&sandybridge_cursor_wm_info, latency,
&plane_wm, &cursor_wm)) {
- I915_WRITE(WM0_PIPEA_ILK,
- (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
+ val = I915_READ(WM0_PIPEA_ILK);
+ val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
+ I915_WRITE(WM0_PIPEA_ILK, val |
+ ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
" plane %d, " "cursor: %d\n",
plane_wm, cursor_wm);
@@ -4541,8 +4594,10 @@ void sandybridge_update_wm(struct drm_device *dev)
&sandybridge_display_wm_info, latency,
&sandybridge_cursor_wm_info, latency,
&plane_wm, &cursor_wm)) {
- I915_WRITE(WM0_PIPEB_ILK,
- (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
+ val = I915_READ(WM0_PIPEB_ILK);
+ val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
+ I915_WRITE(WM0_PIPEB_ILK, val |
+ ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
" plane %d, cursor: %d\n",
plane_wm, cursor_wm);
@@ -4555,8 +4610,10 @@ void sandybridge_update_wm(struct drm_device *dev)
&sandybridge_display_wm_info, latency,
&sandybridge_cursor_wm_info, latency,
&plane_wm, &cursor_wm)) {
- I915_WRITE(WM0_PIPEC_IVB,
- (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
+ val = I915_READ(WM0_PIPEC_IVB);
+ val &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
+ I915_WRITE(WM0_PIPEC_IVB, val |
+ ((plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm));
DRM_DEBUG_KMS("FIFO watermarks For pipe C -"
" plane %d, cursor: %d\n",
plane_wm, cursor_wm);
@@ -4709,6 +4766,7 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
{
struct drm_i915_private *dev_priv = dev->dev_private;
int latency = SNB_READ_WM0_LATENCY() * 100; /* In unit 0.1us */
+ u32 val;
int sprite_wm, reg;
int ret;
@@ -4735,7 +4793,9 @@ static void sandybridge_update_sprite_wm(struct drm_device *dev, int pipe,
return;
}
- I915_WRITE(reg, I915_READ(reg) | (sprite_wm << WM0_PIPE_SPRITE_SHIFT));
+ val = I915_READ(reg);
+ val &= ~WM0_PIPE_SPRITE_MASK;
+ I915_WRITE(reg, val | (sprite_wm << WM0_PIPE_SPRITE_SHIFT));
DRM_DEBUG_KMS("sprite watermarks For pipe %d - %d\n", pipe, sprite_wm);
@@ -4977,6 +5037,82 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
return display_bpc != bpc;
}
+static int i9xx_get_refclk(struct drm_crtc *crtc, int num_connectors)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ int refclk;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+ intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
+ refclk = dev_priv->lvds_ssc_freq * 1000;
+ DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
+ refclk / 1000);
+ } else if (!IS_GEN2(dev)) {
+ refclk = 96000;
+ } else {
+ refclk = 48000;
+ }
+
+ return refclk;
+}
+
+static void i9xx_adjust_sdvo_tv_clock(struct drm_display_mode *adjusted_mode,
+ intel_clock_t *clock)
+{
+ /* SDVO TV has fixed PLL values depend on its clock range,
+ this mirrors vbios setting. */
+ if (adjusted_mode->clock >= 100000
+ && adjusted_mode->clock < 140500) {
+ clock->p1 = 2;
+ clock->p2 = 10;
+ clock->n = 3;
+ clock->m1 = 16;
+ clock->m2 = 8;
+ } else if (adjusted_mode->clock >= 140500
+ && adjusted_mode->clock <= 200000) {
+ clock->p1 = 1;
+ clock->p2 = 10;
+ clock->n = 6;
+ clock->m1 = 12;
+ clock->m2 = 8;
+ }
+}
+
+static void i9xx_update_pll_dividers(struct drm_crtc *crtc,
+ intel_clock_t *clock,
+ intel_clock_t *reduced_clock)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+ int pipe = intel_crtc->pipe;
+ u32 fp, fp2 = 0;
+
+ if (IS_PINEVIEW(dev)) {
+ fp = (1 << clock->n) << 16 | clock->m1 << 8 | clock->m2;
+ if (reduced_clock)
+ fp2 = (1 << reduced_clock->n) << 16 |
+ reduced_clock->m1 << 8 | reduced_clock->m2;
+ } else {
+ fp = clock->n << 16 | clock->m1 << 8 | clock->m2;
+ if (reduced_clock)
+ fp2 = reduced_clock->n << 16 | reduced_clock->m1 << 8 |
+ reduced_clock->m2;
+ }
+
+ I915_WRITE(FP0(pipe), fp);
+
+ intel_crtc->lowfreq_avail = false;
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+ reduced_clock && i915_powersave) {
+ I915_WRITE(FP1(pipe), fp2);
+ intel_crtc->lowfreq_avail = true;
+ } else {
+ I915_WRITE(FP1(pipe), fp);
+ }
+}
+
static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode,
@@ -4990,7 +5126,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
int plane = intel_crtc->plane;
int refclk, num_connectors = 0;
intel_clock_t clock, reduced_clock;
- u32 dpll, fp = 0, fp2 = 0, dspcntr, pipeconf;
+ u32 dpll, dspcntr, pipeconf, vsyncshift;
bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
struct drm_mode_config *mode_config = &dev->mode_config;
@@ -5031,15 +5167,7 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
num_connectors++;
}
- if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
- refclk = dev_priv->lvds_ssc_freq * 1000;
- DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
- refclk / 1000);
- } else if (!IS_GEN2(dev)) {
- refclk = 96000;
- } else {
- refclk = 48000;
- }
+ refclk = i9xx_get_refclk(crtc, num_connectors);
/*
* Returns a set of divisors for the desired target clock with the given
@@ -5047,7 +5175,8 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
*/
limit = intel_limit(crtc, refclk);
- ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
+ ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL,
+ &clock);
if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n");
return -EINVAL;
@@ -5057,53 +5186,24 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
intel_crtc_update_cursor(crtc, true);
if (is_lvds && dev_priv->lvds_downclock_avail) {
+ /*
+ * Ensure we match the reduced clock's P to the target clock.
+ * If the clocks don't match, we can't switch the display clock
+ * by using the FP0/FP1. In such case we will disable the LVDS
+ * downclock feature.
+ */
has_reduced_clock = limit->find_pll(limit, crtc,
dev_priv->lvds_downclock,
refclk,
+ &clock,
&reduced_clock);
- if (has_reduced_clock && (clock.p != reduced_clock.p)) {
- /*
- * If the different P is found, it means that we can't
- * switch the display clock by using the FP0/FP1.
- * In such case we will disable the LVDS downclock
- * feature.
- */
- DRM_DEBUG_KMS("Different P is found for "
- "LVDS clock/downclock\n");
- has_reduced_clock = 0;
- }
- }
- /* SDVO TV has fixed PLL values depend on its clock range,
- this mirrors vbios setting. */
- if (is_sdvo && is_tv) {
- if (adjusted_mode->clock >= 100000
- && adjusted_mode->clock < 140500) {
- clock.p1 = 2;
- clock.p2 = 10;
- clock.n = 3;
- clock.m1 = 16;
- clock.m2 = 8;
- } else if (adjusted_mode->clock >= 140500
- && adjusted_mode->clock <= 200000) {
- clock.p1 = 1;
- clock.p2 = 10;
- clock.n = 6;
- clock.m1 = 12;
- clock.m2 = 8;
- }
}
- if (IS_PINEVIEW(dev)) {
- fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
- if (has_reduced_clock)
- fp2 = (1 << reduced_clock.n) << 16 |
- reduced_clock.m1 << 8 | reduced_clock.m2;
- } else {
- fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
- if (has_reduced_clock)
- fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
- reduced_clock.m2;
- }
+ if (is_sdvo && is_tv)
+ i9xx_adjust_sdvo_tv_clock(adjusted_mode, &clock);
+
+ i9xx_update_pll_dividers(crtc, &clock, has_reduced_clock ?
+ &reduced_clock : NULL);
dpll = DPLL_VGA_MODE_DIS;
@@ -5177,8 +5277,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
/* Set up the display plane register */
dspcntr = DISPPLANE_GAMMA_ENABLE;
- /* Ironlake's plane is forced to pipe, bit 24 is to
- enable color space conversion */
if (pipe == 0)
dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
else
@@ -5213,7 +5311,6 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
drm_mode_debug_printmodeline(mode);
- I915_WRITE(FP0(pipe), fp);
I915_WRITE(DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
POSTING_READ(DPLL(pipe));
@@ -5300,34 +5397,32 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(DPLL(pipe), dpll);
}
- intel_crtc->lowfreq_avail = false;
- if (is_lvds && has_reduced_clock && i915_powersave) {
- I915_WRITE(FP1(pipe), fp2);
- intel_crtc->lowfreq_avail = true;
- if (HAS_PIPE_CXSR(dev)) {
+ if (HAS_PIPE_CXSR(dev)) {
+ if (intel_crtc->lowfreq_avail) {
DRM_DEBUG_KMS("enabling CxSR downclocking\n");
pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
- }
- } else {
- I915_WRITE(FP1(pipe), fp);
- if (HAS_PIPE_CXSR(dev)) {
+ } else {
DRM_DEBUG_KMS("disabling CxSR downclocking\n");
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
}
}
pipeconf &= ~PIPECONF_INTERLACE_MASK;
- if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
+ if (!IS_GEN2(dev) &&
+ adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
/* the chip adds 2 halflines automatically */
- adjusted_mode->crtc_vdisplay -= 1;
adjusted_mode->crtc_vtotal -= 1;
- adjusted_mode->crtc_vblank_start -= 1;
adjusted_mode->crtc_vblank_end -= 1;
- adjusted_mode->crtc_vsync_end -= 1;
- adjusted_mode->crtc_vsync_start -= 1;
- } else
+ vsyncshift = adjusted_mode->crtc_hsync_start
+ - adjusted_mode->crtc_htotal/2;
+ } else {
pipeconf |= PIPECONF_PROGRESSIVE;
+ vsyncshift = 0;
+ }
+
+ if (!IS_GEN3(dev))
+ I915_WRITE(VSYNCSHIFT(pipe), vsyncshift);
I915_WRITE(HTOTAL(pipe),
(adjusted_mode->crtc_hdisplay - 1) |
@@ -5593,7 +5688,8 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
* reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
*/
limit = intel_limit(crtc, refclk);
- ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
+ ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, NULL,
+ &clock);
if (!ok) {
DRM_ERROR("Couldn't find PLL settings for mode!\n");
return -EINVAL;
@@ -5603,21 +5699,17 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
intel_crtc_update_cursor(crtc, true);
if (is_lvds && dev_priv->lvds_downclock_avail) {
+ /*
+ * Ensure we match the reduced clock's P to the target clock.
+ * If the clocks don't match, we can't switch the display clock
+ * by using the FP0/FP1. In such case we will disable the LVDS
+ * downclock feature.
+ */
has_reduced_clock = limit->find_pll(limit, crtc,
dev_priv->lvds_downclock,
refclk,
+ &clock,
&reduced_clock);
- if (has_reduced_clock && (clock.p != reduced_clock.p)) {
- /*
- * If the different P is found, it means that we can't
- * switch the display clock by using the FP0/FP1.
- * In such case we will disable the LVDS downclock
- * feature.
- */
- DRM_DEBUG_KMS("Different P is found for "
- "LVDS clock/downclock\n");
- has_reduced_clock = 0;
- }
}
/* SDVO TV has fixed PLL values depend on its clock range,
this mirrors vbios setting. */
@@ -5914,16 +6006,17 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
pipeconf &= ~PIPECONF_INTERLACE_MASK;
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
- pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
+ pipeconf |= PIPECONF_INTERLACED_ILK;
/* the chip adds 2 halflines automatically */
- adjusted_mode->crtc_vdisplay -= 1;
adjusted_mode->crtc_vtotal -= 1;
- adjusted_mode->crtc_vblank_start -= 1;
adjusted_mode->crtc_vblank_end -= 1;
- adjusted_mode->crtc_vsync_end -= 1;
- adjusted_mode->crtc_vsync_start -= 1;
- } else
+ I915_WRITE(VSYNCSHIFT(pipe),
+ adjusted_mode->crtc_hsync_start
+ - adjusted_mode->crtc_htotal/2);
+ } else {
pipeconf |= PIPECONF_PROGRESSIVE;
+ I915_WRITE(VSYNCSHIFT(pipe), 0);
+ }
I915_WRITE(HTOTAL(pipe),
(adjusted_mode->crtc_hdisplay - 1) |
@@ -5966,12 +6059,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
intel_wait_for_vblank(dev, pipe);
- if (IS_GEN5(dev)) {
- /* enable address swizzle for tiling buffer */
- temp = I915_READ(DISP_ARB_CTL);
- I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING);
- }
-
I915_WRITE(DSPCNTR(plane), dspcntr);
POSTING_READ(DSPCNTR(plane));
@@ -6086,15 +6173,18 @@ static void ironlake_write_eld(struct drm_connector *connector,
uint32_t i;
int len;
int hdmiw_hdmiedid;
+ int aud_config;
int aud_cntl_st;
int aud_cntrl_st2;
if (HAS_PCH_IBX(connector->dev)) {
hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID_A;
+ aud_config = IBX_AUD_CONFIG_A;
aud_cntl_st = IBX_AUD_CNTL_ST_A;
aud_cntrl_st2 = IBX_AUD_CNTL_ST2;
} else {
hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID_A;
+ aud_config = CPT_AUD_CONFIG_A;
aud_cntl_st = CPT_AUD_CNTL_ST_A;
aud_cntrl_st2 = CPT_AUD_CNTRL_ST2;
}
@@ -6102,6 +6192,7 @@ static void ironlake_write_eld(struct drm_connector *connector,
i = to_intel_crtc(crtc)->pipe;
hdmiw_hdmiedid += i * 0x100;
aud_cntl_st += i * 0x100;
+ aud_config += i * 0x100;
DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
@@ -6121,7 +6212,9 @@ static void ironlake_write_eld(struct drm_connector *connector,
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
- }
+ I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
+ } else
+ I915_WRITE(aud_config, 0);
if (intel_eld_uptodate(connector,
aud_cntrl_st2, eldv,
@@ -6927,9 +7020,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
DRM_DEBUG_DRIVER("upclocking LVDS\n");
- /* Unlock panel regs */
- I915_WRITE(PP_CONTROL,
- I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
+ assert_panel_unlocked(dev_priv, pipe);
dpll &= ~DISPLAY_RATE_SELECT_FPA1;
I915_WRITE(dpll_reg, dpll);
@@ -6938,9 +7029,6 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
dpll = I915_READ(dpll_reg);
if (dpll & DISPLAY_RATE_SELECT_FPA1)
DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
-
- /* ...and lock them again */
- I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
}
/* Schedule downclock */
@@ -6970,9 +7058,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
DRM_DEBUG_DRIVER("downclocking LVDS\n");
- /* Unlock panel regs */
- I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
- PANEL_UNLOCK_REGS);
+ assert_panel_unlocked(dev_priv, pipe);
dpll |= DISPLAY_RATE_SELECT_FPA1;
I915_WRITE(dpll_reg, dpll);
@@ -6980,9 +7066,6 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
dpll = I915_READ(dpll_reg);
if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
DRM_DEBUG_DRIVER("failed to downclock LVDS!\n");
-
- /* ...and lock them again */
- I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
}
}
@@ -7097,7 +7180,7 @@ static void intel_unpin_work_fn(struct work_struct *__work)
container_of(__work, struct intel_unpin_work, work);
mutex_lock(&work->dev->struct_mutex);
- i915_gem_object_unpin(work->old_fb_obj);
+ intel_unpin_fb_obj(work->old_fb_obj);
drm_gem_object_unreference(&work->pending_flip_obj->base);
drm_gem_object_unreference(&work->old_fb_obj->base);
@@ -7247,7 +7330,7 @@ static int intel_gen2_queue_flip(struct drm_device *dev,
MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
OUT_RING(fb->pitches[0]);
OUT_RING(obj->gtt_offset + offset);
- OUT_RING(MI_NOOP);
+ OUT_RING(0); /* aux display base address, unused */
ADVANCE_LP_RING();
out:
return ret;
@@ -7681,10 +7764,9 @@ static void intel_setup_outputs(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *encoder;
bool dpd_is_edp = false;
- bool has_lvds = false;
+ bool has_lvds;
- if (IS_MOBILE(dev) && !IS_I830(dev))
- has_lvds = intel_lvds_init(dev);
+ has_lvds = intel_lvds_init(dev);
if (!has_lvds && !HAS_PCH_SPLIT(dev)) {
/* disable the panel fitter on everything but LVDS */
I915_WRITE(PFIT_CONTROL, 0);
@@ -7840,7 +7922,8 @@ int intel_framebuffer_init(struct drm_device *dev,
case DRM_FORMAT_VYUY:
break;
default:
- DRM_ERROR("unsupported pixel format\n");
+ DRM_DEBUG_KMS("unsupported pixel format %u\n",
+ mode_cmd->pixel_format);
return -EINVAL;
}
@@ -8162,6 +8245,7 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
u32 pcu_mbox, rc6_mask = 0;
+ u32 gtfifodbg;
int cur_freq, min_freq, max_freq;
int i;
@@ -8173,6 +8257,13 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
*/
I915_WRITE(GEN6_RC_STATE, 0);
mutex_lock(&dev_priv->dev->struct_mutex);
+
+ /* Clear the DBG now so we don't confuse earlier errors */
+ if ((gtfifodbg = I915_READ(GTFIFODBG))) {
+ DRM_ERROR("GT fifo had a previous error %x\n", gtfifodbg);
+ I915_WRITE(GTFIFODBG, gtfifodbg);
+ }
+
gen6_gt_force_wake_get(dev_priv);
/* disable the counters and set deterministic thresholds */
@@ -8959,8 +9050,6 @@ struct intel_quirk {
};
struct intel_quirk intel_quirks[] = {
- /* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */
- { 0x2a42, 0x103c, 0x30eb, quirk_pipea_force },
/* HP Mini needs pipe A force quirk (LP: #322104) */
{ 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
@@ -9037,6 +9126,9 @@ void intel_modeset_init(struct drm_device *dev)
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.prefer_shadow = 1;
+
dev->mode_config.funcs = (void *)&intel_mode_funcs;
intel_init_quirks(dev);
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 94f860cce3f7..110552ff302c 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -49,7 +49,7 @@ struct intel_dp {
uint32_t DP;
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
bool has_audio;
- int force_audio;
+ enum hdmi_force_audio force_audio;
uint32_t color_range;
int dpms_mode;
uint8_t link_bw;
@@ -352,7 +352,7 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
int recv_bytes;
uint32_t status;
uint32_t aux_clock_divider;
- int try, precharge;
+ int try, precharge = 5;
intel_dp_check_edp(intel_dp);
/* The clock divider is based off the hrawclk,
@@ -368,15 +368,10 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
else
aux_clock_divider = 225; /* eDP input clock at 450Mhz */
} else if (HAS_PCH_SPLIT(dev))
- aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */
+ aux_clock_divider = 63; /* IRL input clock fixed at 125Mhz */
else
aux_clock_divider = intel_hrawclk(dev) / 2;
- if (IS_GEN6(dev))
- precharge = 3;
- else
- precharge = 5;
-
/* Try to wait for any previous AUX channel activity */
for (try = 0; try < 3; try++) {
status = I915_READ(ch_ctl);
@@ -421,6 +416,10 @@ intel_dp_aux_ch(struct intel_dp *intel_dp,
DP_AUX_CH_CTL_DONE |
DP_AUX_CH_CTL_TIME_OUT_ERROR |
DP_AUX_CH_CTL_RECEIVE_ERROR);
+
+ if (status & (DP_AUX_CH_CTL_TIME_OUT_ERROR |
+ DP_AUX_CH_CTL_RECEIVE_ERROR))
+ continue;
if (status & DP_AUX_CH_CTL_DONE)
break;
}
@@ -2117,8 +2116,8 @@ intel_dp_detect(struct drm_connector *connector, bool force)
if (status != connector_status_connected)
return status;
- if (intel_dp->force_audio) {
- intel_dp->has_audio = intel_dp->force_audio > 0;
+ if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
+ intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
} else {
edid = intel_dp_get_edid(connector, &intel_dp->adapter);
if (edid) {
@@ -2218,10 +2217,10 @@ intel_dp_set_property(struct drm_connector *connector,
intel_dp->force_audio = i;
- if (i == 0)
+ if (i == HDMI_AUDIO_AUTO)
has_audio = intel_dp_detect_audio(connector);
else
- has_audio = i > 0;
+ has_audio = (i == HDMI_AUDIO_ON);
if (has_audio == intel_dp->has_audio)
return 0;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 1348705faf6b..5a14149b3794 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -374,6 +374,7 @@ extern void intel_init_emon(struct drm_device *dev);
extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,
struct drm_i915_gem_object *obj,
struct intel_ring_buffer *pipelined);
+extern void intel_unpin_fb_obj(struct drm_i915_gem_object *obj);
extern int intel_framebuffer_init(struct drm_device *dev,
struct intel_framebuffer *ifb,
@@ -381,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_dvo.c b/drivers/gpu/drm/i915/intel_dvo.c
index 6eda1b51c636..020a7d7f744d 100644
--- a/drivers/gpu/drm/i915/intel_dvo.c
+++ b/drivers/gpu/drm/i915/intel_dvo.c
@@ -157,7 +157,6 @@ static bool intel_dvo_mode_fixup(struct drm_encoder *encoder,
C(vsync_end);
C(vtotal);
C(clock);
- drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
#undef C
}
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 571375a3eef4..19ecd78b8a2c 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -152,11 +152,7 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
- info->pixmap.size = 64*1024;
- info->pixmap.buf_align = 8;
- info->pixmap.access_align = 32;
- info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->pixmap.scan_align = 1;
+ /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n",
fb->width, fb->height,
@@ -258,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/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 64541f7ef900..cae3e5f17a49 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -44,7 +44,7 @@ struct intel_hdmi {
uint32_t color_range;
bool has_hdmi_sink;
bool has_audio;
- int force_audio;
+ enum hdmi_force_audio force_audio;
void (*write_infoframe)(struct drm_encoder *encoder,
struct dip_infoframe *frame);
};
@@ -339,7 +339,9 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
if (edid) {
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
status = connector_status_connected;
- intel_hdmi->has_hdmi_sink = drm_detect_hdmi_monitor(edid);
+ if (intel_hdmi->force_audio != HDMI_AUDIO_OFF_DVI)
+ intel_hdmi->has_hdmi_sink =
+ drm_detect_hdmi_monitor(edid);
intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
}
connector->display_info.raw_edid = NULL;
@@ -347,8 +349,9 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
}
if (status == connector_status_connected) {
- if (intel_hdmi->force_audio)
- intel_hdmi->has_audio = intel_hdmi->force_audio > 0;
+ if (intel_hdmi->force_audio != HDMI_AUDIO_AUTO)
+ intel_hdmi->has_audio =
+ (intel_hdmi->force_audio == HDMI_AUDIO_ON);
}
return status;
@@ -402,7 +405,7 @@ intel_hdmi_set_property(struct drm_connector *connector,
return ret;
if (property == dev_priv->force_audio_property) {
- int i = val;
+ enum hdmi_force_audio i = val;
bool has_audio;
if (i == intel_hdmi->force_audio)
@@ -410,13 +413,13 @@ intel_hdmi_set_property(struct drm_connector *connector,
intel_hdmi->force_audio = i;
- if (i == 0)
+ if (i == HDMI_AUDIO_AUTO)
has_audio = intel_hdmi_detect_audio(connector);
else
- has_audio = i > 0;
+ has_audio = (i == HDMI_AUDIO_ON);
- if (has_audio == intel_hdmi->has_audio)
- return 0;
+ if (i == HDMI_AUDIO_OFF_DVI)
+ intel_hdmi->has_hdmi_sink = 0;
intel_hdmi->has_audio = has_audio;
goto done;
@@ -514,7 +517,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
intel_encoder->type = INTEL_OUTPUT_HDMI;
connector->polled = DRM_CONNECTOR_POLL_HPD;
- connector->interlace_allowed = 0;
+ connector->interlace_allowed = 1;
connector->doublescan_allowed = 0;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index d30ccccb9d73..601c86e664af 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -37,7 +37,7 @@
/* Intel GPIO access functions */
-#define I2C_RISEFALL_TIME 20
+#define I2C_RISEFALL_TIME 10
static inline struct intel_gmbus *
to_intel_gmbus(struct i2c_adapter *i2c)
@@ -45,13 +45,6 @@ to_intel_gmbus(struct i2c_adapter *i2c)
return container_of(i2c, struct intel_gmbus, adapter);
}
-struct intel_gpio {
- struct i2c_adapter adapter;
- struct i2c_algo_bit_data algo;
- struct drm_i915_private *dev_priv;
- u32 reg;
-};
-
void
intel_i2c_reset(struct drm_device *dev)
{
@@ -78,15 +71,15 @@ static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
I915_WRITE(DSPCLK_GATE_D, val);
}
-static u32 get_reserved(struct intel_gpio *gpio)
+static u32 get_reserved(struct intel_gmbus *bus)
{
- struct drm_i915_private *dev_priv = gpio->dev_priv;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
struct drm_device *dev = dev_priv->dev;
u32 reserved = 0;
/* On most chips, these bits must be preserved in software. */
if (!IS_I830(dev) && !IS_845G(dev))
- reserved = I915_READ_NOTRACE(gpio->reg) &
+ reserved = I915_READ_NOTRACE(bus->gpio_reg) &
(GPIO_DATA_PULLUP_DISABLE |
GPIO_CLOCK_PULLUP_DISABLE);
@@ -95,29 +88,29 @@ static u32 get_reserved(struct intel_gpio *gpio)
static int get_clock(void *data)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
- I915_WRITE_NOTRACE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK);
- I915_WRITE_NOTRACE(gpio->reg, reserved);
- return (I915_READ_NOTRACE(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0;
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_CLOCK_DIR_MASK);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+ return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_CLOCK_VAL_IN) != 0;
}
static int get_data(void *data)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
- I915_WRITE_NOTRACE(gpio->reg, reserved | GPIO_DATA_DIR_MASK);
- I915_WRITE_NOTRACE(gpio->reg, reserved);
- return (I915_READ_NOTRACE(gpio->reg) & GPIO_DATA_VAL_IN) != 0;
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_DATA_DIR_MASK);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+ return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_DATA_VAL_IN) != 0;
}
static void set_clock(void *data, int state_high)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
u32 clock_bits;
if (state_high)
@@ -126,15 +119,15 @@ static void set_clock(void *data, int state_high)
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
GPIO_CLOCK_VAL_MASK;
- I915_WRITE_NOTRACE(gpio->reg, reserved | clock_bits);
- POSTING_READ(gpio->reg);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | clock_bits);
+ POSTING_READ(bus->gpio_reg);
}
static void set_data(void *data, int state_high)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
u32 data_bits;
if (state_high)
@@ -143,13 +136,14 @@ static void set_data(void *data, int state_high)
data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
GPIO_DATA_VAL_MASK;
- I915_WRITE_NOTRACE(gpio->reg, reserved | data_bits);
- POSTING_READ(gpio->reg);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | data_bits);
+ POSTING_READ(bus->gpio_reg);
}
-static struct i2c_adapter *
-intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin)
+static bool
+intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
{
+ struct drm_i915_private *dev_priv = bus->dev_priv;
static const int map_pin_to_reg[] = {
0,
GPIOB,
@@ -160,65 +154,48 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin)
0,
GPIOF,
};
- struct intel_gpio *gpio;
+ struct i2c_algo_bit_data *algo;
if (pin >= ARRAY_SIZE(map_pin_to_reg) || !map_pin_to_reg[pin])
- return NULL;
+ return false;
- gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL);
- if (gpio == NULL)
- return NULL;
+ algo = &bus->bit_algo;
- gpio->reg = map_pin_to_reg[pin];
+ bus->gpio_reg = map_pin_to_reg[pin];
if (HAS_PCH_SPLIT(dev_priv->dev))
- gpio->reg += PCH_GPIOA - GPIOA;
- gpio->dev_priv = dev_priv;
-
- snprintf(gpio->adapter.name, sizeof(gpio->adapter.name),
- "i915 GPIO%c", "?BACDE?F"[pin]);
- gpio->adapter.owner = THIS_MODULE;
- gpio->adapter.algo_data = &gpio->algo;
- gpio->adapter.dev.parent = &dev_priv->dev->pdev->dev;
- gpio->algo.setsda = set_data;
- gpio->algo.setscl = set_clock;
- gpio->algo.getsda = get_data;
- gpio->algo.getscl = get_clock;
- gpio->algo.udelay = I2C_RISEFALL_TIME;
- gpio->algo.timeout = usecs_to_jiffies(2200);
- gpio->algo.data = gpio;
-
- if (i2c_bit_add_bus(&gpio->adapter))
- goto out_free;
-
- return &gpio->adapter;
-
-out_free:
- kfree(gpio);
- return NULL;
+ bus->gpio_reg += PCH_GPIOA - GPIOA;
+
+ bus->adapter.algo_data = algo;
+ algo->setsda = set_data;
+ algo->setscl = set_clock;
+ algo->getsda = get_data;
+ algo->getscl = get_clock;
+ algo->udelay = I2C_RISEFALL_TIME;
+ algo->timeout = usecs_to_jiffies(2200);
+ algo->data = bus;
+
+ return true;
}
static int
-intel_i2c_quirk_xfer(struct drm_i915_private *dev_priv,
- struct i2c_adapter *adapter,
+intel_i2c_quirk_xfer(struct intel_gmbus *bus,
struct i2c_msg *msgs,
int num)
{
- struct intel_gpio *gpio = container_of(adapter,
- struct intel_gpio,
- adapter);
+ struct drm_i915_private *dev_priv = bus->dev_priv;
int ret;
intel_i2c_reset(dev_priv->dev);
intel_i2c_quirk_set(dev_priv, true);
- set_data(gpio, 1);
- set_clock(gpio, 1);
+ set_data(bus, 1);
+ set_clock(bus, 1);
udelay(I2C_RISEFALL_TIME);
- ret = adapter->algo->master_xfer(adapter, msgs, num);
+ ret = i2c_bit_algo.master_xfer(&bus->adapter, msgs, num);
- set_data(gpio, 1);
- set_clock(gpio, 1);
+ set_data(bus, 1);
+ set_clock(bus, 1);
intel_i2c_quirk_set(dev_priv, false);
return ret;
@@ -232,12 +209,15 @@ gmbus_xfer(struct i2c_adapter *adapter,
struct intel_gmbus *bus = container_of(adapter,
struct intel_gmbus,
adapter);
- struct drm_i915_private *dev_priv = adapter->algo_data;
- int i, reg_offset;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ int i, reg_offset, ret;
- if (bus->force_bit)
- return intel_i2c_quirk_xfer(dev_priv,
- bus->force_bit, msgs, num);
+ mutex_lock(&dev_priv->gmbus_mutex);
+
+ if (bus->force_bit) {
+ ret = intel_i2c_quirk_xfer(bus, msgs, num);
+ goto out;
+ }
reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0;
@@ -249,7 +229,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
if (msgs[i].flags & I2C_M_RD) {
I915_WRITE(GMBUS1 + reg_offset,
- GMBUS_CYCLE_WAIT | (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
+ GMBUS_CYCLE_WAIT |
+ (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
(len << GMBUS_BYTE_COUNT_SHIFT) |
(msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
GMBUS_SLAVE_READ | GMBUS_SW_RDY);
@@ -278,7 +259,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
I915_WRITE(GMBUS3 + reg_offset, val);
I915_WRITE(GMBUS1 + reg_offset,
- (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) |
+ GMBUS_CYCLE_WAIT |
+ (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
(msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) |
(msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
@@ -317,11 +299,15 @@ clear_err:
I915_WRITE(GMBUS1 + reg_offset, 0);
done:
- /* Mark the GMBUS interface as disabled. We will re-enable it at the
- * start of the next xfer, till then let it sleep.
+ /* Mark the GMBUS interface as disabled after waiting for idle.
+ * We will re-enable it at the start of the next xfer,
+ * till then let it sleep.
*/
+ if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, 10))
+ DRM_INFO("GMBUS timed out waiting for idle\n");
I915_WRITE(GMBUS0 + reg_offset, 0);
- return i;
+ ret = i;
+ goto out;
timeout:
DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n",
@@ -329,23 +315,21 @@ timeout:
I915_WRITE(GMBUS0 + reg_offset, 0);
/* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
- bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff);
- if (!bus->force_bit)
- return -ENOMEM;
-
- return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num);
+ if (!bus->has_gpio) {
+ ret = -EIO;
+ } else {
+ bus->force_bit = true;
+ ret = intel_i2c_quirk_xfer(bus, msgs, num);
+ }
+out:
+ mutex_unlock(&dev_priv->gmbus_mutex);
+ return ret;
}
static u32 gmbus_func(struct i2c_adapter *adapter)
{
- struct intel_gmbus *bus = container_of(adapter,
- struct intel_gmbus,
- adapter);
-
- if (bus->force_bit)
- bus->force_bit->algo->functionality(bus->force_bit);
-
- return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ return i2c_bit_algo.functionality(adapter) &
+ (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
/* I2C_FUNC_10BIT_ADDR | */
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL);
@@ -375,11 +359,13 @@ int intel_setup_gmbus(struct drm_device *dev)
struct drm_i915_private *dev_priv = dev->dev_private;
int ret, i;
- dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS,
+ dev_priv->gmbus = kcalloc(GMBUS_NUM_PORTS, sizeof(struct intel_gmbus),
GFP_KERNEL);
if (dev_priv->gmbus == NULL)
return -ENOMEM;
+ mutex_init(&dev_priv->gmbus_mutex);
+
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
@@ -391,7 +377,7 @@ int intel_setup_gmbus(struct drm_device *dev)
names[i]);
bus->adapter.dev.parent = &dev->pdev->dev;
- bus->adapter.algo_data = dev_priv;
+ bus->dev_priv = dev_priv;
bus->adapter.algo = &gmbus_algorithm;
ret = i2c_add_adapter(&bus->adapter);
@@ -401,8 +387,11 @@ int intel_setup_gmbus(struct drm_device *dev)
/* By default use a conservative clock rate */
bus->reg0 = i | GMBUS_RATE_100KHZ;
+ bus->has_gpio = intel_gpio_setup(bus, i);
+
/* XXX force bit banging until GMBUS is fully debugged */
- bus->force_bit = intel_gpio_create(dev_priv, i);
+ if (bus->has_gpio && IS_GEN2(dev))
+ bus->force_bit = true;
}
intel_i2c_reset(dev_priv->dev);
@@ -430,19 +419,8 @@ void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
- if (force_bit) {
- if (bus->force_bit == NULL) {
- struct drm_i915_private *dev_priv = adapter->algo_data;
- bus->force_bit = intel_gpio_create(dev_priv,
- bus->reg0 & 0xff);
- }
- } else {
- if (bus->force_bit) {
- i2c_del_adapter(bus->force_bit);
- kfree(bus->force_bit);
- bus->force_bit = NULL;
- }
- }
+ if (bus->has_gpio)
+ bus->force_bit = force_bit;
}
void intel_teardown_gmbus(struct drm_device *dev)
@@ -455,10 +433,6 @@ void intel_teardown_gmbus(struct drm_device *dev)
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
- if (bus->force_bit) {
- i2c_del_adapter(bus->force_bit);
- kfree(bus->force_bit);
- }
i2c_del_adapter(&bus->adapter);
}
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index aa84832b0e1a..c5c0973af8a1 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -739,6 +739,22 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
},
},
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Hewlett-Packard t5745",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_BOARD_NAME, "hp t5745"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Hewlett-Packard st5747",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_BOARD_NAME, "hp st5747"),
+ },
+ },
{ } /* terminating entry */
};
@@ -844,6 +860,18 @@ static bool lvds_is_present_in_vbt(struct drm_device *dev,
return false;
}
+static bool intel_lvds_supported(struct drm_device *dev)
+{
+ /* With the introduction of the PCH we gained a dedicated
+ * LVDS presence pin, use it. */
+ if (HAS_PCH_SPLIT(dev))
+ return true;
+
+ /* Otherwise LVDS was only attached to mobile products,
+ * except for the inglorious 830gm */
+ return IS_MOBILE(dev) && !IS_I830(dev);
+}
+
/**
* intel_lvds_init - setup LVDS connectors on this device
* @dev: drm device
@@ -865,6 +893,9 @@ bool intel_lvds_init(struct drm_device *dev)
int pipe;
u8 pin;
+ if (!intel_lvds_supported(dev))
+ return false;
+
/* Skip init on machines we know falsely report LVDS */
if (dmi_check_system(intel_no_lvds))
return false;
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index be2c6fe07d12..d1928e79d9b6 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -28,6 +28,7 @@
#include <linux/fb.h>
#include <drm/drm_edid.h>
#include "drmP.h"
+#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drv.h"
@@ -42,13 +43,13 @@ bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus)
u8 buf[2];
struct i2c_msg msgs[] = {
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = 1,
.buf = buf,
@@ -83,10 +84,11 @@ int intel_ddc_get_modes(struct drm_connector *connector,
return ret;
}
-static const char *force_audio_names[] = {
- "off",
- "auto",
- "on",
+static const struct drm_prop_enum_list force_audio_names[] = {
+ { HDMI_AUDIO_OFF_DVI, "force-dvi" },
+ { HDMI_AUDIO_OFF, "off" },
+ { HDMI_AUDIO_AUTO, "auto" },
+ { HDMI_AUDIO_ON, "on" },
};
void
@@ -95,27 +97,24 @@ intel_attach_force_audio_property(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_property *prop;
- int i;
prop = dev_priv->force_audio_property;
if (prop == NULL) {
- prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ prop = drm_property_create_enum(dev, 0,
"audio",
+ force_audio_names,
ARRAY_SIZE(force_audio_names));
if (prop == NULL)
return;
- for (i = 0; i < ARRAY_SIZE(force_audio_names); i++)
- drm_property_add_enum(prop, i, i-1, force_audio_names[i]);
-
dev_priv->force_audio_property = prop;
}
drm_connector_attach_property(connector, prop, 0);
}
-static const char *broadcast_rgb_names[] = {
- "Full",
- "Limited 16:235",
+static const struct drm_prop_enum_list broadcast_rgb_names[] = {
+ { 0, "Full" },
+ { 1, "Limited 16:235" },
};
void
@@ -124,19 +123,16 @@ intel_attach_broadcast_rgb_property(struct drm_connector *connector)
struct drm_device *dev = connector->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_property *prop;
- int i;
prop = dev_priv->broadcast_rgb_property;
if (prop == NULL) {
- prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
+ prop = drm_property_create_enum(dev, DRM_MODE_PROP_ENUM,
"Broadcast RGB",
+ broadcast_rgb_names,
ARRAY_SIZE(broadcast_rgb_names));
if (prop == NULL)
return;
- for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++)
- drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]);
-
dev_priv->broadcast_rgb_property = prop;
}
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
index cdf17d4cc1f7..80b331c322fb 100644
--- a/drivers/gpu/drm/i915/intel_overlay.c
+++ b/drivers/gpu/drm/i915/intel_overlay.c
@@ -25,8 +25,6 @@
*
* Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
*/
-
-#include <linux/seq_file.h>
#include "drmP.h"
#include "drm.h"
#include "i915_drm.h"
@@ -227,7 +225,8 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
}
overlay->last_flip_req = request->seqno;
overlay->flip_tail = tail;
- ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req);
+ ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req,
+ true);
if (ret)
return ret;
@@ -263,7 +262,7 @@ i830_activate_pipe_a(struct drm_device *dev)
DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n");
mode = drm_mode_duplicate(dev, &vesa_640x480);
- drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_set_crtcinfo(mode, 0);
if (!drm_crtc_helper_set_mode(&crtc->base, mode,
crtc->base.x, crtc->base.y,
crtc->base.fb))
@@ -448,7 +447,8 @@ static int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay)
if (overlay->last_flip_req == 0)
return 0;
- ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req);
+ ret = i915_wait_request(LP_RING(dev_priv), overlay->last_flip_req,
+ true);
if (ret)
return ret;
@@ -935,10 +935,10 @@ static int check_overlay_dst(struct intel_overlay *overlay,
{
struct drm_display_mode *mode = &overlay->crtc->base.mode;
- if (rec->dst_x < mode->crtc_hdisplay &&
- rec->dst_x + rec->dst_width <= mode->crtc_hdisplay &&
- rec->dst_y < mode->crtc_vdisplay &&
- rec->dst_y + rec->dst_height <= mode->crtc_vdisplay)
+ if (rec->dst_x < mode->hdisplay &&
+ rec->dst_x + rec->dst_width <= mode->hdisplay &&
+ rec->dst_y < mode->vdisplay &&
+ rec->dst_y + rec->dst_height <= mode->vdisplay)
return 0;
else
return -EINVAL;
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 04d79fd1dc9d..230a141dbea3 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -48,7 +48,7 @@ intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
adjusted_mode->clock = fixed_mode->clock;
- drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
}
/* adjusted_mode has been preset to be the panel's fixed mode */
@@ -141,8 +141,8 @@ static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
dev_priv->saveBLC_PWM_CTL2 = val;
} else if (val == 0) {
I915_WRITE(BLC_PWM_PCH_CTL2,
- dev_priv->saveBLC_PWM_CTL);
- val = dev_priv->saveBLC_PWM_CTL;
+ dev_priv->saveBLC_PWM_CTL2);
+ val = dev_priv->saveBLC_PWM_CTL2;
}
} else {
val = I915_READ(BLC_PWM_CTL);
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index 536191540b03..fc66af6a9448 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -52,20 +52,6 @@ static inline int ring_space(struct intel_ring_buffer *ring)
return space;
}
-static u32 i915_gem_get_seqno(struct drm_device *dev)
-{
- drm_i915_private_t *dev_priv = dev->dev_private;
- u32 seqno;
-
- seqno = dev_priv->next_seqno;
-
- /* reserve 0 for non-seqno */
- if (++dev_priv->next_seqno == 0)
- dev_priv->next_seqno = 1;
-
- return seqno;
-}
-
static int
render_ring_flush(struct intel_ring_buffer *ring,
u32 invalidate_domains,
@@ -399,8 +385,6 @@ static int init_render_ring(struct intel_ring_buffer *ring)
if (INTEL_INFO(dev)->gen > 3) {
int mode = VS_TIMER_DISPATCH << 16 | VS_TIMER_DISPATCH;
- if (IS_GEN6(dev) || IS_GEN7(dev))
- mode |= MI_FLUSH_ENABLE << 16 | MI_FLUSH_ENABLE;
I915_WRITE(MI_MODE, mode);
if (IS_GEN7(dev))
I915_WRITE(GFX_MODE_GEN7,
@@ -467,7 +451,7 @@ gen6_add_request(struct intel_ring_buffer *ring,
mbox1_reg = ring->signal_mbox[0];
mbox2_reg = ring->signal_mbox[1];
- *seqno = i915_gem_get_seqno(ring->dev);
+ *seqno = i915_gem_next_request_seqno(ring);
update_mboxes(ring, *seqno, mbox1_reg);
update_mboxes(ring, *seqno, mbox2_reg);
@@ -565,8 +549,7 @@ static int
pc_render_add_request(struct intel_ring_buffer *ring,
u32 *result)
{
- struct drm_device *dev = ring->dev;
- u32 seqno = i915_gem_get_seqno(dev);
+ u32 seqno = i915_gem_next_request_seqno(ring);
struct pipe_control *pc = ring->private;
u32 scratch_addr = pc->gtt_offset + 128;
int ret;
@@ -600,6 +583,7 @@ pc_render_add_request(struct intel_ring_buffer *ring,
PIPE_CONTROL_FLUSH(ring, scratch_addr);
scratch_addr += 128;
PIPE_CONTROL_FLUSH(ring, scratch_addr);
+
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
PIPE_CONTROL_WRITE_FLUSH |
PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE |
@@ -617,8 +601,7 @@ static int
render_ring_add_request(struct intel_ring_buffer *ring,
u32 *result)
{
- struct drm_device *dev = ring->dev;
- u32 seqno = i915_gem_get_seqno(dev);
+ u32 seqno = i915_gem_next_request_seqno(ring);
int ret;
ret = intel_ring_begin(ring, 4);
@@ -744,13 +727,13 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
*/
if (IS_GEN7(dev)) {
switch (ring->id) {
- case RING_RENDER:
+ case RCS:
mmio = RENDER_HWS_PGA_GEN7;
break;
- case RING_BLT:
+ case BCS:
mmio = BLT_HWS_PGA_GEN7;
break;
- case RING_BSD:
+ case VCS:
mmio = BSD_HWS_PGA_GEN7;
break;
}
@@ -792,7 +775,7 @@ ring_add_request(struct intel_ring_buffer *ring,
if (ret)
return ret;
- seqno = i915_gem_get_seqno(ring->dev);
+ seqno = i915_gem_next_request_seqno(ring);
intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
@@ -816,8 +799,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
/* It looks like we need to prevent the gt from suspending while waiting
* for an notifiy irq, otherwise irqs seem to get lost on at least the
* blt/bsd rings on ivb. */
- if (IS_GEN7(dev))
- gen6_gt_force_wake_get(dev_priv);
+ gen6_gt_force_wake_get(dev_priv);
spin_lock(&ring->irq_lock);
if (ring->irq_refcount++ == 0) {
@@ -844,8 +826,7 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring, u32 gflag, u32 rflag)
}
spin_unlock(&ring->irq_lock);
- if (IS_GEN7(dev))
- gen6_gt_force_wake_put(dev_priv);
+ gen6_gt_force_wake_put(dev_priv);
}
static bool
@@ -1127,11 +1108,93 @@ static int intel_wrap_ring_buffer(struct intel_ring_buffer *ring)
return 0;
}
+static int intel_ring_wait_seqno(struct intel_ring_buffer *ring, u32 seqno)
+{
+ struct drm_i915_private *dev_priv = ring->dev->dev_private;
+ bool was_interruptible;
+ int ret;
+
+ /* XXX As we have not yet audited all the paths to check that
+ * they are ready for ERESTARTSYS from intel_ring_begin, do not
+ * allow us to be interruptible by a signal.
+ */
+ was_interruptible = dev_priv->mm.interruptible;
+ dev_priv->mm.interruptible = false;
+
+ ret = i915_wait_request(ring, seqno, true);
+
+ dev_priv->mm.interruptible = was_interruptible;
+
+ return ret;
+}
+
+static int intel_ring_wait_request(struct intel_ring_buffer *ring, int n)
+{
+ struct drm_i915_gem_request *request;
+ u32 seqno = 0;
+ int ret;
+
+ i915_gem_retire_requests_ring(ring);
+
+ if (ring->last_retired_head != -1) {
+ ring->head = ring->last_retired_head;
+ ring->last_retired_head = -1;
+ ring->space = ring_space(ring);
+ if (ring->space >= n)
+ return 0;
+ }
+
+ list_for_each_entry(request, &ring->request_list, list) {
+ int space;
+
+ if (request->tail == -1)
+ continue;
+
+ space = request->tail - (ring->tail + 8);
+ if (space < 0)
+ space += ring->size;
+ if (space >= n) {
+ seqno = request->seqno;
+ break;
+ }
+
+ /* Consume this request in case we need more space than
+ * is available and so need to prevent a race between
+ * updating last_retired_head and direct reads of
+ * I915_RING_HEAD. It also provides a nice sanity check.
+ */
+ request->tail = -1;
+ }
+
+ if (seqno == 0)
+ return -ENOSPC;
+
+ ret = intel_ring_wait_seqno(ring, seqno);
+ if (ret)
+ return ret;
+
+ if (WARN_ON(ring->last_retired_head == -1))
+ return -ENOSPC;
+
+ ring->head = ring->last_retired_head;
+ ring->last_retired_head = -1;
+ ring->space = ring_space(ring);
+ if (WARN_ON(ring->space < n))
+ return -ENOSPC;
+
+ return 0;
+}
+
int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n)
{
struct drm_device *dev = ring->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long end;
+ int ret;
+
+ ret = intel_ring_wait_request(ring, n);
+ if (ret != -ENOSPC)
+ return ret;
trace_i915_ring_wait_begin(ring);
if (drm_core_check_feature(dev, DRIVER_GEM))
@@ -1200,7 +1263,7 @@ void intel_ring_advance(struct intel_ring_buffer *ring)
static const struct intel_ring_buffer render_ring = {
.name = "render ring",
- .id = RING_RENDER,
+ .id = RCS,
.mmio_base = RENDER_RING_BASE,
.size = 32 * PAGE_SIZE,
.init = init_render_ring,
@@ -1223,7 +1286,7 @@ static const struct intel_ring_buffer render_ring = {
static const struct intel_ring_buffer bsd_ring = {
.name = "bsd ring",
- .id = RING_BSD,
+ .id = VCS,
.mmio_base = BSD_RING_BASE,
.size = 32 * PAGE_SIZE,
.init = init_ring_common,
@@ -1333,7 +1396,7 @@ gen6_bsd_ring_put_irq(struct intel_ring_buffer *ring)
/* ring buffer for Video Codec for Gen6+ */
static const struct intel_ring_buffer gen6_bsd_ring = {
.name = "gen6 bsd ring",
- .id = RING_BSD,
+ .id = VCS,
.mmio_base = GEN6_BSD_RING_BASE,
.size = 32 * PAGE_SIZE,
.init = init_ring_common,
@@ -1369,79 +1432,13 @@ blt_ring_put_irq(struct intel_ring_buffer *ring)
GEN6_BLITTER_USER_INTERRUPT);
}
-
-/* Workaround for some stepping of SNB,
- * each time when BLT engine ring tail moved,
- * the first command in the ring to be parsed
- * should be MI_BATCH_BUFFER_START
- */
-#define NEED_BLT_WORKAROUND(dev) \
- (IS_GEN6(dev) && (dev->pdev->revision < 8))
-
-static inline struct drm_i915_gem_object *
-to_blt_workaround(struct intel_ring_buffer *ring)
-{
- return ring->private;
-}
-
-static int blt_ring_init(struct intel_ring_buffer *ring)
-{
- if (NEED_BLT_WORKAROUND(ring->dev)) {
- struct drm_i915_gem_object *obj;
- u32 *ptr;
- int ret;
-
- obj = i915_gem_alloc_object(ring->dev, 4096);
- if (obj == NULL)
- return -ENOMEM;
-
- ret = i915_gem_object_pin(obj, 4096, true);
- if (ret) {
- drm_gem_object_unreference(&obj->base);
- return ret;
- }
-
- ptr = kmap(obj->pages[0]);
- *ptr++ = MI_BATCH_BUFFER_END;
- *ptr++ = MI_NOOP;
- kunmap(obj->pages[0]);
-
- ret = i915_gem_object_set_to_gtt_domain(obj, false);
- if (ret) {
- i915_gem_object_unpin(obj);
- drm_gem_object_unreference(&obj->base);
- return ret;
- }
-
- ring->private = obj;
- }
-
- return init_ring_common(ring);
-}
-
-static int blt_ring_begin(struct intel_ring_buffer *ring,
- int num_dwords)
-{
- if (ring->private) {
- int ret = intel_ring_begin(ring, num_dwords+2);
- if (ret)
- return ret;
-
- intel_ring_emit(ring, MI_BATCH_BUFFER_START);
- intel_ring_emit(ring, to_blt_workaround(ring)->gtt_offset);
-
- return 0;
- } else
- return intel_ring_begin(ring, 4);
-}
-
static int blt_ring_flush(struct intel_ring_buffer *ring,
u32 invalidate, u32 flush)
{
uint32_t cmd;
int ret;
- ret = blt_ring_begin(ring, 4);
+ ret = intel_ring_begin(ring, 4);
if (ret)
return ret;
@@ -1456,22 +1453,12 @@ static int blt_ring_flush(struct intel_ring_buffer *ring,
return 0;
}
-static void blt_ring_cleanup(struct intel_ring_buffer *ring)
-{
- if (!ring->private)
- return;
-
- i915_gem_object_unpin(ring->private);
- drm_gem_object_unreference(ring->private);
- ring->private = NULL;
-}
-
static const struct intel_ring_buffer gen6_blt_ring = {
.name = "blt ring",
- .id = RING_BLT,
+ .id = BCS,
.mmio_base = BLT_RING_BASE,
.size = 32 * PAGE_SIZE,
- .init = blt_ring_init,
+ .init = init_ring_common,
.write_tail = ring_write_tail,
.flush = blt_ring_flush,
.add_request = gen6_add_request,
@@ -1479,7 +1466,6 @@ static const struct intel_ring_buffer gen6_blt_ring = {
.irq_get = blt_ring_get_irq,
.irq_put = blt_ring_put_irq,
.dispatch_execbuffer = gen6_ring_dispatch_execbuffer,
- .cleanup = blt_ring_cleanup,
.sync_to = gen6_blt_ring_sync_to,
.semaphore_register = {MI_SEMAPHORE_SYNC_BR,
MI_SEMAPHORE_SYNC_BV,
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 68281c96c558..bc0365b8fa4d 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -1,13 +1,6 @@
#ifndef _INTEL_RINGBUFFER_H_
#define _INTEL_RINGBUFFER_H_
-enum {
- RCS = 0x0,
- VCS,
- BCS,
- I915_NUM_RINGS,
-};
-
struct intel_hw_status_page {
u32 __iomem *page_addr;
unsigned int gfx_addr;
@@ -36,10 +29,11 @@ struct intel_hw_status_page {
struct intel_ring_buffer {
const char *name;
enum intel_ring_id {
- RING_RENDER = 0x1,
- RING_BSD = 0x2,
- RING_BLT = 0x4,
+ RCS = 0x0,
+ VCS,
+ BCS,
} id;
+#define I915_NUM_RINGS 3
u32 mmio_base;
void __iomem *virtual_start;
struct drm_device *dev;
@@ -52,6 +46,16 @@ struct intel_ring_buffer {
int effective_size;
struct intel_hw_status_page status_page;
+ /** We track the position of the requests in the ring buffer, and
+ * when each is retired we increment last_retired_head as the GPU
+ * must have finished processing the request and so we know we
+ * can advance the ringbuffer up to that position.
+ *
+ * last_retired_head is set to -1 after the value is consumed so
+ * we can detect new retirements.
+ */
+ u32 last_retired_head;
+
spinlock_t irq_lock;
u32 irq_refcount;
u32 irq_mask;
@@ -119,6 +123,12 @@ struct intel_ring_buffer {
void *private;
};
+static inline unsigned
+intel_ring_flag(struct intel_ring_buffer *ring)
+{
+ return 1 << ring->id;
+}
+
static inline u32
intel_ring_sync_index(struct intel_ring_buffer *ring,
struct intel_ring_buffer *other)
@@ -193,6 +203,11 @@ int intel_init_blt_ring_buffer(struct drm_device *dev);
u32 intel_ring_get_active_head(struct intel_ring_buffer *ring);
void intel_ring_setup_status_page(struct intel_ring_buffer *ring);
+static inline u32 intel_ring_get_tail(struct intel_ring_buffer *ring)
+{
+ return ring->tail;
+}
+
static inline void i915_trace_irq_get(struct intel_ring_buffer *ring, u32 seqno)
{
if (ring->trace_irq_seqno == 0 && ring->irq_get(ring))
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index e334ec33a47d..e36b171c1e7d 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -148,7 +148,7 @@ struct intel_sdvo_connector {
/* Mark the type of connector */
uint16_t output_flag;
- int force_audio;
+ enum hdmi_force_audio force_audio;
/* This contains all current supported TV format */
u8 tv_format_supported[TV_FORMAT_NUM];
@@ -944,7 +944,6 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo,
intel_sdvo_get_mode_from_dtd(adjusted_mode, &intel_sdvo->input_dtd);
- drm_mode_set_crtcinfo(adjusted_mode, 0);
return true;
}
@@ -1310,8 +1309,8 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
if (status == connector_status_connected) {
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
- if (intel_sdvo_connector->force_audio)
- intel_sdvo->has_hdmi_audio = intel_sdvo_connector->force_audio > 0;
+ if (intel_sdvo_connector->force_audio != HDMI_AUDIO_AUTO)
+ intel_sdvo->has_hdmi_audio = (intel_sdvo_connector->force_audio == HDMI_AUDIO_ON);
}
return status;
@@ -1684,10 +1683,10 @@ intel_sdvo_set_property(struct drm_connector *connector,
intel_sdvo_connector->force_audio = i;
- if (i == 0)
+ if (i == HDMI_AUDIO_AUTO)
has_audio = intel_sdvo_detect_hdmi_audio(connector);
else
- has_audio = i > 0;
+ has_audio = (i == HDMI_AUDIO_ON);
if (has_audio == intel_sdvo->has_hdmi_audio)
return 0;
@@ -1985,7 +1984,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
drm_connector_helper_add(&connector->base.base,
&intel_sdvo_connector_helper_funcs);
- connector->base.base.interlace_allowed = 0;
+ connector->base.base.interlace_allowed = 1;
connector->base.base.doublescan_allowed = 0;
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
@@ -2277,10 +2276,8 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
intel_sdvo_connector->max_##name = data_value[0]; \
intel_sdvo_connector->cur_##name = response; \
intel_sdvo_connector->name = \
- drm_property_create(dev, DRM_MODE_PROP_RANGE, #name, 2); \
+ drm_property_create_range(dev, 0, #name, 0, data_value[0]); \
if (!intel_sdvo_connector->name) return false; \
- intel_sdvo_connector->name->values[0] = 0; \
- intel_sdvo_connector->name->values[1] = data_value[0]; \
drm_connector_attach_property(connector, \
intel_sdvo_connector->name, \
intel_sdvo_connector->cur_##name); \
@@ -2314,25 +2311,19 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
intel_sdvo_connector->left_margin = data_value[0] - response;
intel_sdvo_connector->right_margin = intel_sdvo_connector->left_margin;
intel_sdvo_connector->left =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "left_margin", 2);
+ drm_property_create_range(dev, 0, "left_margin", 0, data_value[0]);
if (!intel_sdvo_connector->left)
return false;
- intel_sdvo_connector->left->values[0] = 0;
- intel_sdvo_connector->left->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->left,
intel_sdvo_connector->left_margin);
intel_sdvo_connector->right =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "right_margin", 2);
+ drm_property_create_range(dev, 0, "right_margin", 0, data_value[0]);
if (!intel_sdvo_connector->right)
return false;
- intel_sdvo_connector->right->values[0] = 0;
- intel_sdvo_connector->right->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->right,
intel_sdvo_connector->right_margin);
@@ -2356,25 +2347,21 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
intel_sdvo_connector->top_margin = data_value[0] - response;
intel_sdvo_connector->bottom_margin = intel_sdvo_connector->top_margin;
intel_sdvo_connector->top =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "top_margin", 2);
+ drm_property_create_range(dev, 0,
+ "top_margin", 0, data_value[0]);
if (!intel_sdvo_connector->top)
return false;
- intel_sdvo_connector->top->values[0] = 0;
- intel_sdvo_connector->top->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->top,
intel_sdvo_connector->top_margin);
intel_sdvo_connector->bottom =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "bottom_margin", 2);
+ drm_property_create_range(dev, 0,
+ "bottom_margin", 0, data_value[0]);
if (!intel_sdvo_connector->bottom)
return false;
- intel_sdvo_connector->bottom->values[0] = 0;
- intel_sdvo_connector->bottom->values[1] = data_value[0];
drm_connector_attach_property(connector,
intel_sdvo_connector->bottom,
intel_sdvo_connector->bottom_margin);
@@ -2403,12 +2390,10 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
intel_sdvo_connector->max_dot_crawl = 1;
intel_sdvo_connector->cur_dot_crawl = response & 0x1;
intel_sdvo_connector->dot_crawl =
- drm_property_create(dev, DRM_MODE_PROP_RANGE, "dot_crawl", 2);
+ drm_property_create_range(dev, 0, "dot_crawl", 0, 1);
if (!intel_sdvo_connector->dot_crawl)
return false;
- intel_sdvo_connector->dot_crawl->values[0] = 0;
- intel_sdvo_connector->dot_crawl->values[1] = 1;
drm_connector_attach_property(connector,
intel_sdvo_connector->dot_crawl,
intel_sdvo_connector->cur_dot_crawl);
diff --git a/drivers/gpu/drm/i915/intel_sprite.c b/drivers/gpu/drm/i915/intel_sprite.c
index a0835040c86b..7aa0450399a1 100644
--- a/drivers/gpu/drm/i915/intel_sprite.c
+++ b/drivers/gpu/drm/i915/intel_sprite.c
@@ -501,7 +501,7 @@ intel_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
intel_wait_for_vblank(dev, to_intel_crtc(crtc)->pipe);
mutex_lock(&dev->struct_mutex);
}
- i915_gem_object_unpin(old_obj);
+ intel_unpin_fb_obj(old_obj);
}
out_unlock:
@@ -528,7 +528,7 @@ intel_disable_plane(struct drm_plane *plane)
goto out;
mutex_lock(&dev->struct_mutex);
- i915_gem_object_unpin(intel_plane->obj);
+ intel_unpin_fb_obj(intel_plane->obj);
intel_plane->obj = NULL;
mutex_unlock(&dev->struct_mutex);
out:
diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
index 1571be37ce3e..05f765ef5464 100644
--- a/drivers/gpu/drm/i915/intel_tv.c
+++ b/drivers/gpu/drm/i915/intel_tv.c
@@ -1240,7 +1240,7 @@ intel_tv_detect(struct drm_connector *connector, bool force)
int type;
mode = reported_modes[0];
- drm_mode_set_crtcinfo(&mode, CRTC_INTERLACE_HALVE_V);
+ drm_mode_set_crtcinfo(&mode, 0);
if (intel_tv->base.base.crtc && intel_tv->base.base.crtc->enabled) {
type = intel_tv_detect_type(intel_tv, connector);
diff --git a/drivers/gpu/drm/mga/mga_dma.c b/drivers/gpu/drm/mga/mga_dma.c
index 5ccb65deb83c..507aa3df0168 100644
--- a/drivers/gpu/drm/mga/mga_dma.c
+++ b/drivers/gpu/drm/mga/mga_dma.c
@@ -403,6 +403,8 @@ int mga_driver_load(struct drm_device *dev, unsigned long flags)
dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT;
dev_priv->chipset = flags;
+ pci_set_master(dev->pdev);
+
dev_priv->mmio_base = pci_resource_start(dev->pdev, 1);
dev_priv->mmio_size = pci_resource_len(dev->pdev, 1);
diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 9f27e3d9e69a..1a2ad7eb1734 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -14,7 +14,8 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \
nv04_timer.o \
nv04_mc.o nv40_mc.o nv50_mc.o \
- nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \
+ nv04_fb.o nv10_fb.o nv20_fb.o nv30_fb.o nv40_fb.o \
+ nv50_fb.o nvc0_fb.o \
nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o nvc0_fifo.o \
nv04_graph.o nv10_graph.o nv20_graph.o \
nv40_graph.o nv50_graph.o nvc0_graph.o \
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
index e5cbead85e50..637afe71de56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
@@ -65,195 +65,232 @@ static bool nv_cksum(const uint8_t *data, unsigned int length)
}
static int
-score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable)
+score_vbios(struct nvbios *bios, const bool writeable)
{
- if (!(data[0] == 0x55 && data[1] == 0xAA)) {
- NV_TRACEWARN(dev, "... BIOS signature not found\n");
+ if (!bios->data || bios->data[0] != 0x55 || bios->data[1] != 0xAA) {
+ NV_TRACEWARN(bios->dev, "... BIOS signature not found\n");
return 0;
}
- if (nv_cksum(data, data[2] * 512)) {
- NV_TRACEWARN(dev, "... BIOS checksum invalid\n");
+ if (nv_cksum(bios->data, bios->data[2] * 512)) {
+ NV_TRACEWARN(bios->dev, "... BIOS checksum invalid\n");
/* if a ro image is somewhat bad, it's probably all rubbish */
return writeable ? 2 : 1;
- } else
- NV_TRACE(dev, "... appears to be valid\n");
+ }
+ NV_TRACE(bios->dev, "... appears to be valid\n");
return 3;
}
-static void load_vbios_prom(struct drm_device *dev, uint8_t *data)
+static void
+bios_shadow_prom(struct nvbios *bios)
{
+ struct drm_device *dev = bios->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t pci_nv_20, save_pci_nv_20;
- int pcir_ptr;
+ u32 pcireg, access;
+ u16 pcir;
int i;
+ /* enable access to rom */
if (dev_priv->card_type >= NV_50)
- pci_nv_20 = 0x88050;
+ pcireg = 0x088050;
else
- pci_nv_20 = NV_PBUS_PCI_NV_20;
+ pcireg = NV_PBUS_PCI_NV_20;
+ access = nv_mask(dev, pcireg, 0x00000001, 0x00000000);
- /* enable ROM access */
- save_pci_nv_20 = nvReadMC(dev, pci_nv_20);
- nvWriteMC(dev, pci_nv_20,
- save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+ /* bail if no rom signature, with a workaround for a PROM reading
+ * issue on some chipsets. the first read after a period of
+ * inactivity returns the wrong result, so retry the first header
+ * byte a few times before giving up as a workaround
+ */
+ i = 16;
+ do {
+ if (nv_rd08(dev, NV_PROM_OFFSET + 0) == 0x55)
+ break;
+ } while (i--);
- /* bail if no rom signature */
- if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 ||
- nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
+ if (!i || nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
goto out;
/* additional check (see note below) - read PCI record header */
- pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
- nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
- if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' ||
- nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' ||
- nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' ||
- nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R')
+ pcir = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
+ nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
+ if (nv_rd08(dev, NV_PROM_OFFSET + pcir + 0) != 'P' ||
+ nv_rd08(dev, NV_PROM_OFFSET + pcir + 1) != 'C' ||
+ nv_rd08(dev, NV_PROM_OFFSET + pcir + 2) != 'I' ||
+ nv_rd08(dev, NV_PROM_OFFSET + pcir + 3) != 'R')
goto out;
- /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a
- * a good read may be obtained by waiting or re-reading (cargocult: 5x)
- * each byte. we'll hope pramin has something usable instead
- */
- for (i = 0; i < NV_PROM_SIZE; i++)
- data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
+ /* read entire bios image to system memory */
+ bios->length = nv_rd08(dev, NV_PROM_OFFSET + 2) * 512;
+ bios->data = kmalloc(bios->length, GFP_KERNEL);
+ if (bios->data) {
+ for (i = 0; i < bios->length; i++)
+ bios->data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
+ }
out:
- /* disable ROM access */
- nvWriteMC(dev, pci_nv_20,
- save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
+ /* disable access to rom */
+ nv_wr32(dev, pcireg, access);
}
-static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
+static void
+bios_shadow_pramin(struct nvbios *bios)
{
+ struct drm_device *dev = bios->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
- uint32_t old_bar0_pramin = 0;
+ u32 bar0 = 0;
int i;
if (dev_priv->card_type >= NV_50) {
u64 addr = (u64)(nv_rd32(dev, 0x619f04) & 0xffffff00) << 8;
if (!addr) {
- addr = (u64)nv_rd32(dev, 0x1700) << 16;
+ addr = (u64)nv_rd32(dev, 0x001700) << 16;
addr += 0xf0000;
}
- old_bar0_pramin = nv_rd32(dev, 0x1700);
- nv_wr32(dev, 0x1700, addr >> 16);
+ bar0 = nv_mask(dev, 0x001700, 0xffffffff, addr >> 16);
}
/* bail if no rom signature */
- if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 ||
+ if (nv_rd08(dev, NV_PRAMIN_OFFSET + 0) != 0x55 ||
nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa)
goto out;
- for (i = 0; i < NV_PROM_SIZE; i++)
- data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
+ bios->length = nv_rd08(dev, NV_PRAMIN_OFFSET + 2) * 512;
+ bios->data = kmalloc(bios->length, GFP_KERNEL);
+ if (bios->data) {
+ for (i = 0; i < bios->length; i++)
+ bios->data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
+ }
out:
if (dev_priv->card_type >= NV_50)
- nv_wr32(dev, 0x1700, old_bar0_pramin);
+ nv_wr32(dev, 0x001700, bar0);
}
-static void load_vbios_pci(struct drm_device *dev, uint8_t *data)
+static void
+bios_shadow_pci(struct nvbios *bios)
+{
+ struct pci_dev *pdev = bios->dev->pdev;
+ size_t length;
+
+ if (!pci_enable_rom(pdev)) {
+ void __iomem *rom = pci_map_rom(pdev, &length);
+ if (rom) {
+ bios->data = kmalloc(length, GFP_KERNEL);
+ if (bios->data) {
+ memcpy_fromio(bios->data, rom, length);
+ bios->length = length;
+ }
+ pci_unmap_rom(pdev, rom);
+ }
+
+ pci_disable_rom(pdev);
+ }
+}
+
+static void
+bios_shadow_acpi(struct nvbios *bios)
{
- void __iomem *rom = NULL;
- size_t rom_len;
- int ret;
+ struct pci_dev *pdev = bios->dev->pdev;
+ int ptr, len, ret;
+ u8 data[3];
- ret = pci_enable_rom(dev->pdev);
- if (ret)
+ if (!nouveau_acpi_rom_supported(pdev))
return;
- rom = pci_map_rom(dev->pdev, &rom_len);
- if (!rom)
- goto out;
- memcpy_fromio(data, rom, rom_len);
- pci_unmap_rom(dev->pdev, rom);
+ ret = nouveau_acpi_get_bios_chunk(data, 0, sizeof(data));
+ if (ret != sizeof(data))
+ return;
-out:
- pci_disable_rom(dev->pdev);
-}
+ bios->length = min(data[2] * 512, 65536);
+ bios->data = kmalloc(bios->length, GFP_KERNEL);
+ if (!bios->data)
+ return;
-static void load_vbios_acpi(struct drm_device *dev, uint8_t *data)
-{
- int i;
- int ret;
- int size = 64 * 1024;
+ len = bios->length;
+ ptr = 0;
+ while (len) {
+ int size = (len > ROM_BIOS_PAGE) ? ROM_BIOS_PAGE : len;
- if (!nouveau_acpi_rom_supported(dev->pdev))
- return;
+ ret = nouveau_acpi_get_bios_chunk(bios->data, ptr, size);
+ if (ret != size) {
+ kfree(bios->data);
+ bios->data = NULL;
+ return;
+ }
- for (i = 0; i < (size / ROM_BIOS_PAGE); i++) {
- ret = nouveau_acpi_get_bios_chunk(data,
- (i * ROM_BIOS_PAGE),
- ROM_BIOS_PAGE);
- if (ret <= 0)
- break;
+ len -= size;
+ ptr += size;
}
- return;
}
struct methods {
const char desc[8];
- void (*loadbios)(struct drm_device *, uint8_t *);
+ void (*shadow)(struct nvbios *);
const bool rw;
+ int score;
+ u32 size;
+ u8 *data;
};
-static struct methods shadow_methods[] = {
- { "PRAMIN", load_vbios_pramin, true },
- { "PROM", load_vbios_prom, false },
- { "PCIROM", load_vbios_pci, true },
- { "ACPI", load_vbios_acpi, true },
-};
-#define NUM_SHADOW_METHODS ARRAY_SIZE(shadow_methods)
-
-static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
-{
- struct methods *methods = shadow_methods;
- int testscore = 3;
- int scores[NUM_SHADOW_METHODS], i;
+static bool
+bios_shadow(struct drm_device *dev)
+{
+ struct methods shadow_methods[] = {
+ { "PRAMIN", bios_shadow_pramin, true, 0, 0, NULL },
+ { "PROM", bios_shadow_prom, false, 0, 0, NULL },
+ { "ACPI", bios_shadow_acpi, true, 0, 0, NULL },
+ { "PCIROM", bios_shadow_pci, true, 0, 0, NULL },
+ {}
+ };
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct methods *mthd, *best;
if (nouveau_vbios) {
- for (i = 0; i < NUM_SHADOW_METHODS; i++)
- if (!strcasecmp(nouveau_vbios, methods[i].desc))
- break;
-
- if (i < NUM_SHADOW_METHODS) {
- NV_INFO(dev, "Attempting to use BIOS image from %s\n",
- methods[i].desc);
+ mthd = shadow_methods;
+ do {
+ if (strcasecmp(nouveau_vbios, mthd->desc))
+ continue;
+ NV_INFO(dev, "VBIOS source: %s\n", mthd->desc);
- methods[i].loadbios(dev, data);
- if (score_vbios(dev, data, methods[i].rw))
+ mthd->shadow(bios);
+ mthd->score = score_vbios(bios, mthd->rw);
+ if (mthd->score)
return true;
- }
+ } while ((++mthd)->shadow);
NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
}
- for (i = 0; i < NUM_SHADOW_METHODS; i++) {
- NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
- methods[i].desc);
- data[0] = data[1] = 0; /* avoid reuse of previous image */
- methods[i].loadbios(dev, data);
- scores[i] = score_vbios(dev, data, methods[i].rw);
- if (scores[i] == testscore)
- return true;
- }
-
- while (--testscore > 0) {
- for (i = 0; i < NUM_SHADOW_METHODS; i++) {
- if (scores[i] == testscore) {
- NV_TRACE(dev, "Using BIOS image from %s\n",
- methods[i].desc);
- methods[i].loadbios(dev, data);
- return true;
- }
+ mthd = shadow_methods;
+ do {
+ NV_TRACE(dev, "Checking %s for VBIOS\n", mthd->desc);
+ mthd->shadow(bios);
+ mthd->score = score_vbios(bios, mthd->rw);
+ mthd->size = bios->length;
+ mthd->data = bios->data;
+ } while (mthd->score != 3 && (++mthd)->shadow);
+
+ mthd = shadow_methods;
+ best = mthd;
+ do {
+ if (mthd->score > best->score) {
+ kfree(best->data);
+ best = mthd;
}
+ } while ((++mthd)->shadow);
+
+ if (best->score) {
+ NV_TRACE(dev, "Using VBIOS from %s\n", best->desc);
+ bios->length = best->size;
+ bios->data = best->data;
+ return true;
}
- NV_ERROR(dev, "No valid BIOS image found\n");
+ NV_ERROR(dev, "No valid VBIOS image found\n");
return false;
}
@@ -1107,7 +1144,8 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
break;
case 1:
case 2:
- if (!(entry[5] & cond))
+ if ((table[0] < 0x40 && !(entry[5] & cond)) ||
+ (table[0] == 0x40 && !(entry[4] & cond)))
iexec->execute = false;
break;
case 5:
@@ -6334,11 +6372,7 @@ static bool NVInitVBIOS(struct drm_device *dev)
spin_lock_init(&bios->lock);
bios->dev = dev;
- if (!NVShadowVBIOS(dev, bios->data))
- return false;
-
- bios->length = NV_PROM_SIZE;
- return true;
+ return bios_shadow(dev);
}
static int nouveau_parse_vbios_struct(struct drm_device *dev)
@@ -6498,6 +6532,10 @@ nouveau_bios_init(struct drm_device *dev)
void
nouveau_bios_takedown(struct drm_device *dev)
{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
nouveau_mxm_fini(dev);
nouveau_i2c_fini(dev);
+
+ kfree(dev_priv->vbios.data);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
index a37c31e358aa..298a3af48d14 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
+++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
@@ -69,12 +69,16 @@ enum dcb_connector_type {
DCB_CONNECTOR_TV_3 = 0x13,
DCB_CONNECTOR_DVI_I = 0x30,
DCB_CONNECTOR_DVI_D = 0x31,
+ DCB_CONNECTOR_DMS59_0 = 0x38,
+ DCB_CONNECTOR_DMS59_1 = 0x39,
DCB_CONNECTOR_LVDS = 0x40,
DCB_CONNECTOR_LVDS_SPWG = 0x41,
DCB_CONNECTOR_DP = 0x46,
DCB_CONNECTOR_eDP = 0x47,
DCB_CONNECTOR_HDMI_0 = 0x60,
DCB_CONNECTOR_HDMI_1 = 0x61,
+ DCB_CONNECTOR_DMS59_DP0 = 0x64,
+ DCB_CONNECTOR_DMS59_DP1 = 0x65,
DCB_CONNECTOR_NONE = 0xff
};
@@ -209,6 +213,8 @@ struct nvbios {
NVBIOS_BIT
} type;
uint16_t offset;
+ uint32_t length;
+ uint8_t *data;
uint8_t chip_version;
@@ -219,8 +225,6 @@ struct nvbios {
spinlock_t lock;
- uint8_t data[NV_PROM_SIZE];
- unsigned int length;
bool execute;
uint8_t major_version;
diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index ec54364ac828..7d15a774f9c9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -693,16 +693,12 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
struct ttm_mem_reg *new_mem)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+ struct nouveau_channel *chan = chan = dev_priv->channel;
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct ttm_mem_reg *old_mem = &bo->mem;
- struct nouveau_channel *chan;
int ret;
- chan = nvbo->channel;
- if (!chan) {
- chan = dev_priv->channel;
- mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX);
- }
+ mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX);
/* create temporary vmas for the transfer and attach them to the
* old nouveau_mem node, these will get cleaned up after ttm has
@@ -734,8 +730,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
}
out:
- if (chan == dev_priv->channel)
- mutex_unlock(&chan->mutex);
+ mutex_unlock(&chan->mutex);
return ret;
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index a018defb7621..44e6416d4a33 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -122,7 +122,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
struct nouveau_channel *chan;
unsigned long flags;
- int ret;
+ int ret, i;
/* allocate and lock channel structure */
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
@@ -184,7 +184,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
return ret;
}
- nouveau_dma_pre_init(chan);
+ nouveau_dma_init(chan);
chan->user_put = 0x40;
chan->user_get = 0x44;
if (dev_priv->card_type >= NV_50)
@@ -202,9 +202,18 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
pfifo->reassign(dev, true);
- ret = nouveau_dma_init(chan);
- if (!ret)
- ret = nouveau_fence_channel_init(chan);
+ /* Insert NOPs for NOUVEAU_DMA_SKIPS */
+ ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
+ if (ret) {
+ nouveau_channel_put(&chan);
+ return ret;
+ }
+
+ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
+ OUT_RING (chan, 0x00000000);
+ FIRE_RING(chan);
+
+ ret = nouveau_fence_channel_init(chan);
if (ret) {
nouveau_channel_put(&chan);
return ret;
@@ -427,18 +436,11 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
}
if (dev_priv->card_type < NV_C0) {
- init->subchan[0].handle = NvM2MF;
- if (dev_priv->card_type < NV_50)
- init->subchan[0].grclass = 0x0039;
- else
- init->subchan[0].grclass = 0x5039;
- init->subchan[1].handle = NvSw;
- init->subchan[1].grclass = NV_SW;
- init->nr_subchan = 2;
- } else {
- init->subchan[0].handle = 0x9039;
- init->subchan[0].grclass = 0x9039;
+ init->subchan[0].handle = NvSw;
+ init->subchan[0].grclass = NV_SW;
init->nr_subchan = 1;
+ } else {
+ init->nr_subchan = 0;
}
/* Named memory object area */
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index f3ce34be082a..fa860358add1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -519,6 +519,19 @@ nouveau_connector_set_property(struct drm_connector *connector,
return nv_crtc->set_dither(nv_crtc, true);
}
+ if (nv_crtc && nv_crtc->set_color_vibrance) {
+ /* Hue */
+ if (property == disp->vibrant_hue_property) {
+ nv_crtc->vibrant_hue = value - 90;
+ return nv_crtc->set_color_vibrance(nv_crtc, true);
+ }
+ /* Saturation */
+ if (property == disp->color_vibrance_property) {
+ nv_crtc->color_vibrance = value - 100;
+ return nv_crtc->set_color_vibrance(nv_crtc, true);
+ }
+ }
+
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
return get_slave_funcs(encoder)->set_property(
encoder, connector, property, value);
@@ -641,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) {
@@ -854,10 +870,14 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb)
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV;
+ case DCB_CONNECTOR_DMS59_0 :
+ case DCB_CONNECTOR_DMS59_1 :
case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII;
case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID;
case DCB_CONNECTOR_LVDS :
case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS;
+ case DCB_CONNECTOR_DMS59_DP0:
+ case DCB_CONNECTOR_DMS59_DP1:
case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
case DCB_CONNECTOR_HDMI_0 :
@@ -998,11 +1018,10 @@ nouveau_connector_create(struct drm_device *dev, int index)
/* Add overscan compensation options to digital outputs */
if (disp->underscan_property &&
- (nv_connector->type == DCB_CONNECTOR_DVI_D ||
- nv_connector->type == DCB_CONNECTOR_DVI_I ||
- nv_connector->type == DCB_CONNECTOR_HDMI_0 ||
- nv_connector->type == DCB_CONNECTOR_HDMI_1 ||
- nv_connector->type == DCB_CONNECTOR_DP)) {
+ (type == DRM_MODE_CONNECTOR_DVID ||
+ type == DRM_MODE_CONNECTOR_DVII ||
+ type == DRM_MODE_CONNECTOR_HDMIA ||
+ type == DRM_MODE_CONNECTOR_DisplayPort)) {
drm_connector_attach_property(connector,
disp->underscan_property,
UNDERSCAN_OFF);
@@ -1014,6 +1033,16 @@ nouveau_connector_create(struct drm_device *dev, int index)
0);
}
+ /* Add hue and saturation options */
+ if (disp->vibrant_hue_property)
+ drm_connector_attach_property(connector,
+ disp->vibrant_hue_property,
+ 90);
+ if (disp->color_vibrance_property)
+ drm_connector_attach_property(connector,
+ disp->color_vibrance_property,
+ 150);
+
switch (nv_connector->type) {
case DCB_CONNECTOR_VGA:
if (dev_priv->card_type >= NV_50) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
index 686f6b4a1da3..e6d0d1eb0133 100644
--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -35,6 +35,8 @@ struct nouveau_crtc {
uint32_t dpms_saved_fp_control;
uint32_t fp_users;
int saturation;
+ int color_vibrance;
+ int vibrant_hue;
int sharpness;
int last_dpms;
@@ -67,6 +69,7 @@ struct nouveau_crtc {
int (*set_dither)(struct nouveau_crtc *crtc, bool update);
int (*set_scale)(struct nouveau_crtc *crtc, bool update);
+ int (*set_color_vibrance)(struct nouveau_crtc *crtc, bool update);
};
static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 795a9e3c990a..a85e112863d1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -155,20 +155,20 @@ static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
};
-struct drm_prop_enum_list {
+struct nouveau_drm_prop_enum_list {
u8 gen_mask;
int type;
char *name;
};
-static struct drm_prop_enum_list underscan[] = {
+static struct nouveau_drm_prop_enum_list underscan[] = {
{ 6, UNDERSCAN_AUTO, "auto" },
{ 6, UNDERSCAN_OFF, "off" },
{ 6, UNDERSCAN_ON, "on" },
{}
};
-static struct drm_prop_enum_list dither_mode[] = {
+static struct nouveau_drm_prop_enum_list dither_mode[] = {
{ 7, DITHERING_MODE_AUTO, "auto" },
{ 7, DITHERING_MODE_OFF, "off" },
{ 1, DITHERING_MODE_ON, "on" },
@@ -178,7 +178,7 @@ static struct drm_prop_enum_list dither_mode[] = {
{}
};
-static struct drm_prop_enum_list dither_depth[] = {
+static struct nouveau_drm_prop_enum_list dither_depth[] = {
{ 6, DITHERING_DEPTH_AUTO, "auto" },
{ 6, DITHERING_DEPTH_6BPC, "6 bpc" },
{ 6, DITHERING_DEPTH_8BPC, "8 bpc" },
@@ -186,7 +186,7 @@ static struct drm_prop_enum_list dither_depth[] = {
};
#define PROP_ENUM(p,gen,n,list) do { \
- struct drm_prop_enum_list *l = (list); \
+ struct nouveau_drm_prop_enum_list *l = (list); \
int c = 0; \
while (l->gen_mask) { \
if (l->gen_mask & (1 << (gen))) \
@@ -281,16 +281,24 @@ nouveau_display_create(struct drm_device *dev)
PROP_ENUM(disp->underscan_property, gen, "underscan", underscan);
disp->underscan_hborder_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "underscan hborder", 2);
- disp->underscan_hborder_property->values[0] = 0;
- disp->underscan_hborder_property->values[1] = 128;
+ drm_property_create_range(dev, 0, "underscan hborder", 0, 128);
disp->underscan_vborder_property =
- drm_property_create(dev, DRM_MODE_PROP_RANGE,
- "underscan vborder", 2);
- disp->underscan_vborder_property->values[0] = 0;
- disp->underscan_vborder_property->values[1] = 128;
+ drm_property_create_range(dev, 0, "underscan vborder", 0, 128);
+
+ if (gen == 1) {
+ disp->vibrant_hue_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "vibrant hue", 2);
+ disp->vibrant_hue_property->values[0] = 0;
+ disp->vibrant_hue_property->values[1] = 180; /* -90..+90 */
+
+ disp->color_vibrance_property =
+ drm_property_create(dev, DRM_MODE_PROP_RANGE,
+ "color vibrance", 2);
+ disp->color_vibrance_property->values[0] = 0;
+ disp->color_vibrance_property->values[1] = 200; /* -100..+100 */
+ }
dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1);
@@ -309,6 +317,9 @@ nouveau_display_create(struct drm_device *dev)
dev->mode_config.max_height = 8192;
}
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.prefer_shadow = 1;
+
drm_kms_helper_poll_init(dev);
drm_kms_helper_poll_disable(dev);
@@ -430,15 +441,19 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
goto fail;
/* Emit the pageflip */
- ret = RING_SPACE(chan, 2);
+ ret = RING_SPACE(chan, 3);
if (ret)
goto fail;
- if (dev_priv->card_type < NV_C0)
+ if (dev_priv->card_type < NV_C0) {
BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
- else
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0500, 1);
- OUT_RING (chan, 0);
+ OUT_RING (chan, 0x00000000);
+ OUT_RING (chan, 0x00000000);
+ } else {
+ BEGIN_NVC0(chan, 2, 0, NV10_SUBCHAN_REF_CNT, 1);
+ OUT_RING (chan, ++chan->fence.sequence);
+ BEGIN_NVC0(chan, 8, 0, NVSW_SUBCHAN_PAGE_FLIP, 0x0000);
+ }
FIRE_RING (chan);
ret = nouveau_fence_new(chan, pfence, true);
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 4c2e4e5925fe..295932e66ac5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -31,7 +31,7 @@
#include "nouveau_ramht.h"
void
-nouveau_dma_pre_init(struct nouveau_channel *chan)
+nouveau_dma_init(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nouveau_bo *pushbuf = chan->pushbuf_bo;
@@ -54,65 +54,6 @@ nouveau_dma_pre_init(struct nouveau_channel *chan)
chan->dma.free = chan->dma.max - chan->dma.cur;
}
-int
-nouveau_dma_init(struct nouveau_channel *chan)
-{
- struct drm_device *dev = chan->dev;
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- int ret, i;
-
- if (dev_priv->card_type >= NV_C0) {
- ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039);
- if (ret)
- return ret;
-
- ret = RING_SPACE(chan, 2);
- if (ret)
- return ret;
-
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0000, 1);
- OUT_RING (chan, 0x00009039);
- FIRE_RING (chan);
- return 0;
- }
-
- /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
- ret = nouveau_gpuobj_gr_new(chan, NvM2MF, dev_priv->card_type < NV_50 ?
- 0x0039 : 0x5039);
- if (ret)
- return ret;
-
- /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
- ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,
- &chan->m2mf_ntfy);
- if (ret)
- return ret;
-
- /* Insert NOPS for NOUVEAU_DMA_SKIPS */
- ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
- if (ret)
- return ret;
-
- for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
- OUT_RING(chan, 0);
-
- /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
- ret = RING_SPACE(chan, 6);
- if (ret)
- return ret;
- BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
- OUT_RING (chan, NvM2MF);
- BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 3);
- OUT_RING (chan, NvNotify0);
- OUT_RING (chan, chan->vram_handle);
- OUT_RING (chan, chan->gart_handle);
-
- /* Sit back and pray the channel works.. */
- FIRE_RING(chan);
-
- return 0;
-}
-
void
OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
{
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h
index 23d4edf992b7..bcf0fd9e313e 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.h
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.h
@@ -48,8 +48,8 @@ void nv50_dma_push(struct nouveau_channel *, struct nouveau_bo *,
/* Hardcoded object assignments to subchannels (subchannel id). */
enum {
- NvSubM2MF = 0,
- NvSubSw = 1,
+ NvSubSw = 0,
+ NvSubM2MF = 1,
NvSub2D = 2,
NvSubCtxSurf2D = 2,
NvSubGdiRect = 3,
diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
index 9b93b703ceab..d996134b1b28 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dp.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
@@ -161,116 +161,6 @@ out:
return ret;
}
-static u32
-dp_link_bw_get(struct drm_device *dev, int or, int link)
-{
- u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800));
- if (!(ctrl & 0x000c0000))
- return 162000;
- return 270000;
-}
-
-static int
-dp_lane_count_get(struct drm_device *dev, int or, int link)
-{
- u32 ctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
- switch (ctrl & 0x000f0000) {
- case 0x00010000: return 1;
- case 0x00030000: return 2;
- default:
- return 4;
- }
-}
-
-void
-nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
-{
- const u32 symbol = 100000;
- int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
- int TU, VTUi, VTUf, VTUa;
- u64 link_data_rate, link_ratio, unk;
- u32 best_diff = 64 * symbol;
- u32 link_nr, link_bw, r;
-
- /* calculate packed data rate for each lane */
- link_nr = dp_lane_count_get(dev, or, link);
- link_data_rate = (clk * bpp / 8) / link_nr;
-
- /* calculate ratio of packed data rate to link symbol rate */
- link_bw = dp_link_bw_get(dev, or, link);
- link_ratio = link_data_rate * symbol;
- r = do_div(link_ratio, link_bw);
-
- for (TU = 64; TU >= 32; TU--) {
- /* calculate average number of valid symbols in each TU */
- u32 tu_valid = link_ratio * TU;
- u32 calc, diff;
-
- /* find a hw representation for the fraction.. */
- VTUi = tu_valid / symbol;
- calc = VTUi * symbol;
- diff = tu_valid - calc;
- if (diff) {
- if (diff >= (symbol / 2)) {
- VTUf = symbol / (symbol - diff);
- if (symbol - (VTUf * diff))
- VTUf++;
-
- if (VTUf <= 15) {
- VTUa = 1;
- calc += symbol - (symbol / VTUf);
- } else {
- VTUa = 0;
- VTUf = 1;
- calc += symbol;
- }
- } else {
- VTUa = 0;
- VTUf = min((int)(symbol / diff), 15);
- calc += symbol / VTUf;
- }
-
- diff = calc - tu_valid;
- } else {
- /* no remainder, but the hw doesn't like the fractional
- * part to be zero. decrement the integer part and
- * have the fraction add a whole symbol back
- */
- VTUa = 0;
- VTUf = 1;
- VTUi--;
- }
-
- if (diff < best_diff) {
- best_diff = diff;
- bestTU = TU;
- bestVTUa = VTUa;
- bestVTUf = VTUf;
- bestVTUi = VTUi;
- if (diff == 0)
- break;
- }
- }
-
- if (!bestTU) {
- NV_ERROR(dev, "DP: unable to find suitable config\n");
- return;
- }
-
- /* XXX close to vbios numbers, but not right */
- unk = (symbol - link_ratio) * bestTU;
- unk *= link_ratio;
- r = do_div(unk, symbol);
- r = do_div(unk, symbol);
- unk += 6;
-
- nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
- nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
- bestVTUf << 16 |
- bestVTUi << 8 |
- unk);
-}
-
u8 *
nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
{
@@ -298,6 +188,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
case 0x20:
case 0x21:
case 0x30:
+ case 0x40:
break;
default:
NV_ERROR(dev, "displayport table 0x%02x unknown\n", table[0]);
@@ -318,13 +209,10 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
* link training
*****************************************************************************/
struct dp_state {
+ struct dp_train_func *func;
struct dcb_entry *dcb;
- u8 *table;
- u8 *entry;
int auxch;
int crtc;
- int or;
- int link;
u8 *dpcd;
int link_nr;
u32 link_bw;
@@ -335,142 +223,58 @@ struct dp_state {
static void
dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
{
- int or = dp->or, link = dp->link;
- u8 *entry, sink[2];
- u32 dp_ctrl;
- u16 script;
+ u8 sink[2];
NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
- /* set selected link rate on source */
- switch (dp->link_bw) {
- case 270000:
- nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00040000);
- sink[0] = DP_LINK_BW_2_7;
- break;
- default:
- nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000);
- sink[0] = DP_LINK_BW_1_62;
- break;
- }
-
- /* offset +0x0a of each dp encoder table entry is a pointer to another
- * table, that has (among other things) pointers to more scripts that
- * need to be executed, this time depending on link speed.
- */
- entry = ROMPTR(dev, dp->entry[10]);
- if (entry) {
- if (dp->table[0] < 0x30) {
- while (dp->link_bw < (ROM16(entry[0]) * 10))
- entry += 4;
- script = ROM16(entry[2]);
- } else {
- while (dp->link_bw < (entry[0] * 27000))
- entry += 3;
- script = ROM16(entry[1]);
- }
-
- nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
- }
+ /* set desired link configuration on the source */
+ dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw,
+ dp->dpcd[2] & DP_ENHANCED_FRAME_CAP);
- /* configure lane count on the source */
- dp_ctrl = ((1 << dp->link_nr) - 1) << 16;
+ /* inform the sink of the new configuration */
+ sink[0] = dp->link_bw / 27000;
sink[1] = dp->link_nr;
- if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) {
- dp_ctrl |= 0x00004000;
+ if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP)
sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
- }
-
- nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl);
- /* inform the sink of the new configuration */
auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2);
}
static void
-dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp)
+dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern)
{
u8 sink_tp;
- NV_DEBUG_KMS(dev, "training pattern %d\n", tp);
+ NV_DEBUG_KMS(dev, "training pattern %d\n", pattern);
- nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24);
+ dp->func->train_set(dev, dp->dcb, pattern);
auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
sink_tp &= ~DP_TRAINING_PATTERN_MASK;
- sink_tp |= tp;
+ sink_tp |= pattern;
auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1);
}
-static const u8 nv50_lane_map[] = { 16, 8, 0, 24 };
-static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 };
-
static int
dp_link_train_commit(struct drm_device *dev, struct dp_state *dp)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- u32 mask = 0, drv = 0, pre = 0, unk = 0;
- const u8 *shifts;
- int link = dp->link;
- int or = dp->or;
int i;
- if (dev_priv->chipset != 0xaf)
- shifts = nv50_lane_map;
- else
- shifts = nvaf_lane_map;
-
for (i = 0; i < dp->link_nr; i++) {
- u8 *conf = dp->entry + dp->table[4];
u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf;
u8 lpre = (lane & 0x0c) >> 2;
u8 lvsw = (lane & 0x03) >> 0;
- mask |= 0xff << shifts[i];
- unk |= 1 << (shifts[i] >> 3);
-
dp->conf[i] = (lpre << 3) | lvsw;
if (lvsw == DP_TRAIN_VOLTAGE_SWING_1200)
dp->conf[i] |= DP_TRAIN_MAX_SWING_REACHED;
- if (lpre == DP_TRAIN_PRE_EMPHASIS_9_5)
+ if ((lpre << 3) == DP_TRAIN_PRE_EMPHASIS_9_5)
dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
NV_DEBUG_KMS(dev, "config lane %d %02x\n", i, dp->conf[i]);
-
- if (dp->table[0] < 0x30) {
- u8 *last = conf + (dp->entry[4] * dp->table[5]);
- while (lvsw != conf[0] || lpre != conf[1]) {
- conf += dp->table[5];
- if (conf >= last)
- return -EINVAL;
- }
-
- conf += 2;
- } else {
- /* no lookup table anymore, set entries for each
- * combination of voltage swing and pre-emphasis
- * level allowed by the DP spec.
- */
- switch (lvsw) {
- case 0: lpre += 0; break;
- case 1: lpre += 4; break;
- case 2: lpre += 7; break;
- case 3: lpre += 9; break;
- }
-
- conf = conf + (lpre * dp->table[5]);
- conf++;
- }
-
- drv |= conf[0] << shifts[i];
- pre |= conf[1] << shifts[i];
- unk = (unk & ~0x0000ff00) | (conf[2] << 8);
+ dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre);
}
- nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv);
- nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre);
- nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk);
-
return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4);
}
@@ -554,8 +358,60 @@ dp_link_train_eq(struct drm_device *dev, struct dp_state *dp)
return eq_done ? 0 : -1;
}
+static void
+dp_set_downspread(struct drm_device *dev, struct dp_state *dp, bool enable)
+{
+ u16 script = 0x0000;
+ u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry);
+ if (table) {
+ if (table[0] >= 0x20 && table[0] <= 0x30) {
+ if (enable) script = ROM16(entry[12]);
+ else script = ROM16(entry[14]);
+ } else
+ if (table[0] == 0x40) {
+ if (enable) script = ROM16(entry[11]);
+ else script = ROM16(entry[13]);
+ }
+ }
+
+ nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
+}
+
+static void
+dp_link_train_init(struct drm_device *dev, struct dp_state *dp)
+{
+ u16 script = 0x0000;
+ u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry);
+ if (table) {
+ if (table[0] >= 0x20 && table[0] <= 0x30)
+ script = ROM16(entry[6]);
+ else
+ if (table[0] == 0x40)
+ script = ROM16(entry[5]);
+ }
+
+ nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
+}
+
+static void
+dp_link_train_fini(struct drm_device *dev, struct dp_state *dp)
+{
+ u16 script = 0x0000;
+ u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry);
+ if (table) {
+ if (table[0] >= 0x20 && table[0] <= 0x30)
+ script = ROM16(entry[8]);
+ else
+ if (table[0] == 0x40)
+ script = ROM16(entry[7]);
+ }
+
+ nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc);
+}
+
bool
-nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
+nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate,
+ struct dp_train_func *func)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
@@ -571,17 +427,15 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
if (!auxch)
return false;
- dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry);
- if (!dp.table)
- return -EINVAL;
-
+ dp.func = func;
dp.dcb = nv_encoder->dcb;
dp.crtc = nv_crtc->index;
dp.auxch = auxch->drive;
- dp.or = nv_encoder->or;
- dp.link = !(nv_encoder->dcb->sorconf.link & 1);
dp.dpcd = nv_encoder->dp.dpcd;
+ /* adjust required bandwidth for 8B/10B coding overhead */
+ datarate = (datarate / 8) * 10;
+
/* some sinks toggle hotplug in response to some of the actions
* we take during link training (DP_SET_POWER is one), we need
* to ignore them for the moment to avoid races.
@@ -589,16 +443,10 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, false);
/* enable down-spreading, if possible */
- if (dp.table[1] >= 16) {
- u16 script = ROM16(dp.entry[14]);
- if (nv_encoder->dp.dpcd[3] & 1)
- script = ROM16(dp.entry[12]);
-
- nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc);
- }
+ dp_set_downspread(dev, &dp, nv_encoder->dp.dpcd[3] & 1);
/* execute pre-train script from vbios */
- nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc);
+ dp_link_train_init(dev, &dp);
/* start off at highest link rate supported by encoder and display */
while (*link_bw > nv_encoder->dp.link_bw)
@@ -632,13 +480,36 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE);
/* execute post-train script from vbios */
- nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc);
+ dp_link_train_fini(dev, &dp);
/* re-enable hotplug detect */
nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, true);
return true;
}
+void
+nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate,
+ struct dp_train_func *func)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct nouveau_i2c_chan *auxch;
+ u8 status;
+
+ auxch = nouveau_i2c_find(encoder->dev, nv_encoder->dcb->i2c_index);
+ if (!auxch)
+ return;
+
+ if (mode == DRM_MODE_DPMS_ON)
+ status = DP_SET_POWER_D0;
+ else
+ status = DP_SET_POWER_D3;
+
+ nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
+
+ if (mode == DRM_MODE_DPMS_ON)
+ nouveau_dp_link_train(encoder, datarate, func);
+}
+
bool
nouveau_dp_detect(struct drm_encoder *encoder)
{
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index 81d7962e7252..4f2030bd5676 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -57,6 +57,10 @@ MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM");
int nouveau_vram_notify = 0;
module_param_named(vram_notify, nouveau_vram_notify, int, 0400);
+MODULE_PARM_DESC(vram_type, "Override detected VRAM type");
+char *nouveau_vram_type;
+module_param_named(vram_type, nouveau_vram_type, charp, 0400);
+
MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)");
int nouveau_duallink = 1;
module_param_named(duallink, nouveau_duallink, int, 0400);
@@ -89,7 +93,7 @@ MODULE_PARM_DESC(override_conntype, "Ignore DCB connector type");
int nouveau_override_conntype = 0;
module_param_named(override_conntype, nouveau_override_conntype, int, 0400);
-MODULE_PARM_DESC(tv_disable, "Disable TV-out detection\n");
+MODULE_PARM_DESC(tv_disable, "Disable TV-out detection");
int nouveau_tv_disable = 0;
module_param_named(tv_disable, nouveau_tv_disable, int, 0400);
@@ -104,27 +108,27 @@ module_param_named(tv_norm, nouveau_tv_norm, charp, 0400);
MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n"
"\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n"
"\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n"
- "\t\t0x100 vgaattr, 0x200 EVO (G80+). ");
+ "\t\t0x100 vgaattr, 0x200 EVO (G80+)");
int nouveau_reg_debug;
module_param_named(reg_debug, nouveau_reg_debug, int, 0600);
-MODULE_PARM_DESC(perflvl, "Performance level (default: boot)\n");
+MODULE_PARM_DESC(perflvl, "Performance level (default: boot)");
char *nouveau_perflvl;
module_param_named(perflvl, nouveau_perflvl, charp, 0400);
-MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)\n");
+MODULE_PARM_DESC(perflvl_wr, "Allow perflvl changes (warning: dangerous!)");
int nouveau_perflvl_wr;
module_param_named(perflvl_wr, nouveau_perflvl_wr, int, 0400);
-MODULE_PARM_DESC(msi, "Enable MSI (default: off)\n");
+MODULE_PARM_DESC(msi, "Enable MSI (default: off)");
int nouveau_msi;
module_param_named(msi, nouveau_msi, int, 0400);
-MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n");
+MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)");
int nouveau_ctxfw;
module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
-MODULE_PARM_DESC(mxmdcb, "Santise DCB table according to MXM-SIS\n");
+MODULE_PARM_DESC(mxmdcb, "Santise DCB table according to MXM-SIS");
int nouveau_mxmdcb = 1;
module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index b82709828931..3aef353a926c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -26,15 +26,15 @@
#define __NOUVEAU_DRV_H__
#define DRIVER_AUTHOR "Stephane Marchesin"
-#define DRIVER_EMAIL "dri-devel@lists.sourceforge.net"
+#define DRIVER_EMAIL "nouveau@lists.freedesktop.org"
#define DRIVER_NAME "nouveau"
#define DRIVER_DESC "nVidia Riva/TNT/GeForce"
-#define DRIVER_DATE "20090420"
+#define DRIVER_DATE "20120316"
-#define DRIVER_MAJOR 0
+#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
-#define DRIVER_PATCHLEVEL 16
+#define DRIVER_PATCHLEVEL 0
#define NOUVEAU_FAMILY 0x0000FFFF
#define NOUVEAU_FLAGS 0xFFFF0000
@@ -113,8 +113,6 @@ struct nouveau_bo {
int pbbo_index;
bool validate_mapped;
- struct nouveau_channel *channel;
-
struct list_head vma_list;
unsigned page_shift;
@@ -296,7 +294,7 @@ struct nouveau_channel {
uint32_t sw_subchannel[8];
- struct nouveau_vma dispc_vma[2];
+ struct nouveau_vma dispc_vma[4];
struct {
struct nouveau_gpuobj *vblsem;
uint32_t vblsem_head;
@@ -406,6 +404,9 @@ struct nouveau_display_engine {
struct drm_property *underscan_property;
struct drm_property *underscan_hborder_property;
struct drm_property *underscan_vborder_property;
+ /* not really hue and saturation: */
+ struct drm_property *vibrant_hue_property;
+ struct drm_property *color_vibrance_property;
};
struct nouveau_gpio_engine {
@@ -432,58 +433,85 @@ struct nouveau_pm_voltage {
int nr_level;
};
+/* Exclusive upper limits */
+#define NV_MEM_CL_DDR2_MAX 8
+#define NV_MEM_WR_DDR2_MAX 9
+#define NV_MEM_CL_DDR3_MAX 17
+#define NV_MEM_WR_DDR3_MAX 17
+#define NV_MEM_CL_GDDR3_MAX 16
+#define NV_MEM_WR_GDDR3_MAX 18
+#define NV_MEM_CL_GDDR5_MAX 21
+#define NV_MEM_WR_GDDR5_MAX 20
+
struct nouveau_pm_memtiming {
int id;
- u32 reg_0; /* 0x10f290 on Fermi, 0x100220 for older */
- u32 reg_1;
- u32 reg_2;
- u32 reg_3;
- u32 reg_4;
- u32 reg_5;
- u32 reg_6;
- u32 reg_7;
- u32 reg_8;
- /* To be written to 0x1002c0 */
- u8 CL;
- u8 WR;
+
+ u32 reg[9];
+ u32 mr[4];
+
+ u8 tCWL;
+
+ u8 odt;
+ u8 drive_strength;
};
-struct nouveau_pm_tbl_header{
+struct nouveau_pm_tbl_header {
u8 version;
u8 header_len;
u8 entry_cnt;
u8 entry_len;
};
-struct nouveau_pm_tbl_entry{
+struct nouveau_pm_tbl_entry {
u8 tWR;
- u8 tUNK_1;
+ u8 tWTR;
u8 tCL;
- u8 tRP; /* Byte 3 */
+ u8 tRC;
u8 empty_4;
- u8 tRAS; /* Byte 5 */
+ u8 tRFC; /* Byte 5 */
u8 empty_6;
- u8 tRFC; /* Byte 7 */
+ u8 tRAS; /* Byte 7 */
u8 empty_8;
- u8 tRC; /* Byte 9 */
- u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
- u8 empty_15,empty_16,empty_17;
- u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
+ u8 tRP; /* Byte 9 */
+ u8 tRCDRD;
+ u8 tRCDWR;
+ u8 tRRD;
+ u8 tUNK_13;
+ u8 RAM_FT1; /* 14, a bitmask of random RAM features */
+ u8 empty_15;
+ u8 tUNK_16;
+ u8 empty_17;
+ u8 tUNK_18;
+ u8 tCWL;
+ u8 tUNK_20, tUNK_21;
};
-/* nouveau_mem.c */
-void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
- struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
- struct nouveau_pm_memtiming *timing);
+struct nouveau_pm_profile;
+struct nouveau_pm_profile_func {
+ void (*destroy)(struct nouveau_pm_profile *);
+ void (*init)(struct nouveau_pm_profile *);
+ void (*fini)(struct nouveau_pm_profile *);
+ struct nouveau_pm_level *(*select)(struct nouveau_pm_profile *);
+};
+
+struct nouveau_pm_profile {
+ const struct nouveau_pm_profile_func *func;
+ struct list_head head;
+ char name[8];
+};
#define NOUVEAU_PM_MAX_LEVEL 8
struct nouveau_pm_level {
+ struct nouveau_pm_profile profile;
struct device_attribute dev_attr;
char name[32];
int id;
- u32 core;
+ struct nouveau_pm_memtiming timing;
u32 memory;
+ u16 memscript;
+
+ u32 core;
u32 shader;
u32 rop;
u32 copy;
@@ -498,9 +526,6 @@ struct nouveau_pm_level {
u32 volt_min; /* microvolts */
u32 volt_max;
u8 fanspeed;
-
- u16 memscript;
- struct nouveau_pm_memtiming *timing;
};
struct nouveau_pm_temp_sensor_constants {
@@ -517,27 +542,26 @@ struct nouveau_pm_threshold_temp {
s16 fan_boost;
};
-struct nouveau_pm_memtimings {
- bool supported;
- struct nouveau_pm_memtiming *timing;
- int nr_timing;
-};
-
struct nouveau_pm_fan {
+ u32 percent;
u32 min_duty;
u32 max_duty;
u32 pwm_freq;
+ u32 pwm_divisor;
};
struct nouveau_pm_engine {
struct nouveau_pm_voltage voltage;
struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];
int nr_perflvl;
- struct nouveau_pm_memtimings memtimings;
struct nouveau_pm_temp_sensor_constants sensor_constants;
struct nouveau_pm_threshold_temp threshold_temp;
struct nouveau_pm_fan fan;
- u32 pwm_divisor;
+
+ struct nouveau_pm_profile *profile_ac;
+ struct nouveau_pm_profile *profile_dc;
+ struct nouveau_pm_profile *profile;
+ struct list_head profiles;
struct nouveau_pm_level boot;
struct nouveau_pm_level *cur;
@@ -669,14 +693,15 @@ struct nv04_mode_state {
};
enum nouveau_card_type {
- NV_04 = 0x00,
+ NV_04 = 0x04,
NV_10 = 0x10,
NV_20 = 0x20,
NV_30 = 0x30,
NV_40 = 0x40,
NV_50 = 0x50,
NV_C0 = 0xc0,
- NV_D0 = 0xd0
+ NV_D0 = 0xd0,
+ NV_E0 = 0xe0,
};
struct drm_nouveau_private {
@@ -772,8 +797,22 @@ struct drm_nouveau_private {
} tile;
/* VRAM/fb configuration */
+ enum {
+ NV_MEM_TYPE_UNKNOWN = 0,
+ NV_MEM_TYPE_STOLEN,
+ NV_MEM_TYPE_SGRAM,
+ NV_MEM_TYPE_SDRAM,
+ NV_MEM_TYPE_DDR1,
+ NV_MEM_TYPE_DDR2,
+ NV_MEM_TYPE_DDR3,
+ NV_MEM_TYPE_GDDR2,
+ NV_MEM_TYPE_GDDR3,
+ NV_MEM_TYPE_GDDR4,
+ NV_MEM_TYPE_GDDR5
+ } vram_type;
uint64_t vram_size;
uint64_t vram_sys_base;
+ bool vram_rank_B;
uint64_t fb_available_size;
uint64_t fb_mappable_pages;
@@ -846,6 +885,7 @@ extern int nouveau_uscript_lvds;
extern int nouveau_uscript_tmds;
extern int nouveau_vram_pushbuf;
extern int nouveau_vram_notify;
+extern char *nouveau_vram_type;
extern int nouveau_fbpercrtc;
extern int nouveau_tv_disable;
extern char *nouveau_tv_norm;
@@ -894,8 +934,12 @@ extern void nouveau_mem_gart_fini(struct drm_device *);
extern int nouveau_mem_init_agp(struct drm_device *);
extern int nouveau_mem_reset_agp(struct drm_device *);
extern void nouveau_mem_close(struct drm_device *);
-extern int nouveau_mem_detect(struct drm_device *);
extern bool nouveau_mem_flags_valid(struct drm_device *, u32 tile_flags);
+extern int nouveau_mem_timing_calc(struct drm_device *, u32 freq,
+ struct nouveau_pm_memtiming *);
+extern void nouveau_mem_timing_read(struct drm_device *,
+ struct nouveau_pm_memtiming *);
+extern int nouveau_mem_vbios_type(struct drm_device *);
extern struct nouveau_tile_reg *nv10_mem_set_tiling(
struct drm_device *dev, uint32_t addr, uint32_t size,
uint32_t pitch, uint32_t flags);
@@ -1046,8 +1090,7 @@ nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
#endif
/* nouveau_dma.c */
-extern void nouveau_dma_pre_init(struct nouveau_channel *);
-extern int nouveau_dma_init(struct nouveau_channel *);
+extern void nouveau_dma_init(struct nouveau_channel *);
extern int nouveau_dma_wait(struct nouveau_channel *, int slots, int size);
/* nouveau_acpi.c */
@@ -1117,19 +1160,14 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
/* nouveau_hdmi.c */
void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
-/* nouveau_dp.c */
-int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
- uint8_t *data, int data_nr);
-bool nouveau_dp_detect(struct drm_encoder *);
-bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate);
-void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
-u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
-
/* nv04_fb.c */
+extern int nv04_fb_vram_init(struct drm_device *);
extern int nv04_fb_init(struct drm_device *);
extern void nv04_fb_takedown(struct drm_device *);
/* nv10_fb.c */
+extern int nv10_fb_vram_init(struct drm_device *dev);
+extern int nv1a_fb_vram_init(struct drm_device *dev);
extern int nv10_fb_init(struct drm_device *);
extern void nv10_fb_takedown(struct drm_device *);
extern void nv10_fb_init_tile_region(struct drm_device *dev, int i,
@@ -1138,6 +1176,16 @@ extern void nv10_fb_init_tile_region(struct drm_device *dev, int i,
extern void nv10_fb_set_tile_region(struct drm_device *dev, int i);
extern void nv10_fb_free_tile_region(struct drm_device *dev, int i);
+/* nv20_fb.c */
+extern int nv20_fb_vram_init(struct drm_device *dev);
+extern int nv20_fb_init(struct drm_device *);
+extern void nv20_fb_takedown(struct drm_device *);
+extern void nv20_fb_init_tile_region(struct drm_device *dev, int i,
+ uint32_t addr, uint32_t size,
+ uint32_t pitch, uint32_t flags);
+extern void nv20_fb_set_tile_region(struct drm_device *dev, int i);
+extern void nv20_fb_free_tile_region(struct drm_device *dev, int i);
+
/* nv30_fb.c */
extern int nv30_fb_init(struct drm_device *);
extern void nv30_fb_takedown(struct drm_device *);
@@ -1147,6 +1195,7 @@ extern void nv30_fb_init_tile_region(struct drm_device *dev, int i,
extern void nv30_fb_free_tile_region(struct drm_device *dev, int i);
/* nv40_fb.c */
+extern int nv40_fb_vram_init(struct drm_device *dev);
extern int nv40_fb_init(struct drm_device *);
extern void nv40_fb_takedown(struct drm_device *);
extern void nv40_fb_set_tile_region(struct drm_device *dev, int i);
@@ -1703,6 +1752,7 @@ nv44_graph_class(struct drm_device *dev)
#define NV_MEM_ACCESS_RW (NV_MEM_ACCESS_RO | NV_MEM_ACCESS_WO)
#define NV_MEM_ACCESS_SYS 4
#define NV_MEM_ACCESS_VM 8
+#define NV_MEM_ACCESS_NOSNOOP 16
#define NV_MEM_TARGET_VRAM 0
#define NV_MEM_TARGET_PCI 1
@@ -1713,13 +1763,27 @@ nv44_graph_class(struct drm_device *dev)
#define NV_MEM_TYPE_VM 0x7f
#define NV_MEM_COMP_VM 0x03
+/* FIFO methods */
+#define NV01_SUBCHAN_OBJECT 0x00000000
+#define NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH 0x00000010
+#define NV84_SUBCHAN_SEMAPHORE_ADDRESS_LOW 0x00000014
+#define NV84_SUBCHAN_SEMAPHORE_SEQUENCE 0x00000018
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER 0x0000001c
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL 0x00000001
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG 0x00000002
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL 0x00000004
+#define NV84_SUBCHAN_NOTIFY_INTR 0x00000020
+#define NV84_SUBCHAN_WRCACHE_FLUSH 0x00000024
+#define NV10_SUBCHAN_REF_CNT 0x00000050
+#define NVSW_SUBCHAN_PAGE_FLIP 0x00000054
+#define NV11_SUBCHAN_DMA_SEMAPHORE 0x00000060
+#define NV11_SUBCHAN_SEMAPHORE_OFFSET 0x00000064
+#define NV11_SUBCHAN_SEMAPHORE_ACQUIRE 0x00000068
+#define NV11_SUBCHAN_SEMAPHORE_RELEASE 0x0000006c
+#define NV40_SUBCHAN_YIELD 0x00000080
+
/* NV_SW object class */
#define NV_SW 0x0000506e
-#define NV_SW_DMA_SEMAPHORE 0x00000060
-#define NV_SW_SEMAPHORE_OFFSET 0x00000064
-#define NV_SW_SEMAPHORE_ACQUIRE 0x00000068
-#define NV_SW_SEMAPHORE_RELEASE 0x0000006c
-#define NV_SW_YIELD 0x00000080
#define NV_SW_DMA_VBLSEM 0x0000018c
#define NV_SW_VBLSEM_OFFSET 0x00000400
#define NV_SW_VBLSEM_RELEASE_VALUE 0x00000404
diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
index e5d6e3faff3d..3dc14a3dcc4c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_encoder.h
+++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
@@ -32,6 +32,14 @@
#define NV_DPMS_CLEARED 0x80
+struct dp_train_func {
+ void (*link_set)(struct drm_device *, struct dcb_entry *, int crtc,
+ int nr, u32 bw, bool enhframe);
+ void (*train_set)(struct drm_device *, struct dcb_entry *, u8 pattern);
+ void (*train_adj)(struct drm_device *, struct dcb_entry *,
+ u8 lane, u8 swing, u8 preem);
+};
+
struct nouveau_encoder {
struct drm_encoder_slave base;
@@ -78,9 +86,19 @@ get_slave_funcs(struct drm_encoder *enc)
return to_encoder_slave(enc)->slave_funcs;
}
+/* nouveau_dp.c */
+int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
+ uint8_t *data, int data_nr);
+bool nouveau_dp_detect(struct drm_encoder *);
+void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate,
+ struct dp_train_func *);
+u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
+
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
+void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32);
int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
+
#endif /* __NOUVEAU_ENCODER_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 9892218d7452..8113e9201ed9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -381,11 +381,7 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
goto out_unref;
}
- info->pixmap.size = 64*1024;
- info->pixmap.buf_align = 8;
- info->pixmap.access_align = 32;
- info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->pixmap.scan_align = 1;
+ /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 2f6daae68b9d..c1dc20f6cb85 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -93,18 +93,17 @@ nouveau_fence_update(struct nouveau_channel *chan)
}
list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
- sequence = fence->sequence;
+ if (fence->sequence > chan->fence.sequence_ack)
+ break;
+
fence->signalled = true;
list_del(&fence->entry);
-
- if (unlikely(fence->work))
+ if (fence->work)
fence->work(fence->priv, true);
kref_put(&fence->refcount, nouveau_fence_del);
-
- if (sequence == chan->fence.sequence_ack)
- break;
}
+
out:
spin_unlock(&chan->fence.lock);
}
@@ -165,9 +164,9 @@ nouveau_fence_emit(struct nouveau_fence *fence)
if (USE_REFCNT(dev)) {
if (dev_priv->card_type < NV_C0)
- BEGIN_RING(chan, NvSubSw, 0x0050, 1);
+ BEGIN_RING(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
else
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0050, 1);
+ BEGIN_NVC0(chan, 2, 0, NV10_SUBCHAN_REF_CNT, 1);
} else {
BEGIN_RING(chan, NvSubSw, 0x0150, 1);
}
@@ -344,7 +343,7 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
if (ret)
return ret;
- BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 3);
+ BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 3);
OUT_RING (chan, NvSema);
OUT_RING (chan, offset);
OUT_RING (chan, 1);
@@ -354,9 +353,9 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
if (ret)
return ret;
- BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
+ BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
OUT_RING (chan, chan->vram_handle);
- BEGIN_RING(chan, NvSubSw, 0x0010, 4);
+ BEGIN_RING(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
OUT_RING (chan, 1);
@@ -366,7 +365,7 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
if (ret)
return ret;
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+ BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
OUT_RING (chan, 1);
@@ -397,10 +396,10 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
if (ret)
return ret;
- BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 2);
+ BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
OUT_RING (chan, NvSema);
OUT_RING (chan, offset);
- BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1);
+ BEGIN_RING(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1);
OUT_RING (chan, 1);
} else
if (dev_priv->chipset < 0xc0) {
@@ -408,9 +407,9 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
if (ret)
return ret;
- BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
+ BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
OUT_RING (chan, chan->vram_handle);
- BEGIN_RING(chan, NvSubSw, 0x0010, 4);
+ BEGIN_RING(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
OUT_RING (chan, 1);
@@ -420,7 +419,7 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
if (ret)
return ret;
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+ BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
OUT_RING (chan, 1);
@@ -510,7 +509,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
if (ret)
return ret;
- BEGIN_RING(chan, NvSubSw, 0, 1);
+ BEGIN_RING(chan, NvSubSw, NV01_SUBCHAN_OBJECT, 1);
OUT_RING (chan, NvSw);
FIRE_RING (chan);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 7ce3fde40743..ed52a6f41613 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -426,9 +426,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
return ret;
}
- nvbo->channel = (b->read_domains & (1 << 31)) ? NULL : chan;
ret = nouveau_bo_validate(nvbo, true, false, false);
- nvbo->channel = NULL;
if (unlikely(ret)) {
if (ret != -ERESTARTSYS)
NV_ERROR(dev, "fail ttm_validate\n");
@@ -678,19 +676,13 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
return PTR_ERR(bo);
}
- /* Mark push buffers as being used on PFIFO, the validation code
- * will then make sure that if the pushbuf bo moves, that they
- * happen on the kernel channel, which will in turn cause a sync
- * to happen before we try and submit the push buffer.
- */
+ /* Ensure all push buffers are on validate list */
for (i = 0; i < req->nr_push; i++) {
if (push[i].bo_index >= req->nr_buffers) {
NV_ERROR(dev, "push %d buffer not in list\n", i);
ret = -EINVAL;
goto out_prevalid;
}
-
- bo[push[i].bo_index].read_domains |= (1 << 31);
}
/* Validate buffer list */
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index 820ae7f52044..e2be95af2e52 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -277,7 +277,7 @@ i2c_bit_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-const struct i2c_algorithm i2c_bit_algo = {
+const struct i2c_algorithm nouveau_i2c_bit_algo = {
.master_xfer = i2c_bit_xfer,
.functionality = i2c_bit_func
};
@@ -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;
@@ -384,12 +384,12 @@ nouveau_i2c_init(struct drm_device *dev)
case 0: /* NV04:NV50 */
port->drive = entry[0];
port->sense = entry[1];
- port->adapter.algo = &i2c_bit_algo;
+ port->adapter.algo = &nouveau_i2c_bit_algo;
break;
case 4: /* NV4E */
port->drive = 0x600800 + entry[1];
port->sense = port->drive;
- port->adapter.algo = &i2c_bit_algo;
+ port->adapter.algo = &nouveau_i2c_bit_algo;
break;
case 5: /* NV50- */
port->drive = entry[0] & 0x0f;
@@ -402,7 +402,7 @@ nouveau_i2c_init(struct drm_device *dev)
port->drive = 0x00d014 + (port->drive * 0x20);
port->sense = port->drive;
}
- port->adapter.algo = &i2c_bit_algo;
+ port->adapter.algo = &nouveau_i2c_bit_algo;
break;
case 6: /* NV50- DP AUX */
port->drive = entry[0];
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index c3a5745e9c79..b08065f981df 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -26,7 +26,8 @@
* DEALINGS IN THE SOFTWARE.
*
* Authors:
- * Keith Whitwell <keith@tungstengraphics.com>
+ * Ben Skeggs <bskeggs@redhat.com>
+ * Roy Spliet <r.spliet@student.tudelft.nl>
*/
@@ -192,75 +193,6 @@ nouveau_mem_gart_fini(struct drm_device *dev)
}
}
-static uint32_t
-nouveau_mem_detect_nv04(struct drm_device *dev)
-{
- uint32_t boot0 = nv_rd32(dev, NV04_PFB_BOOT_0);
-
- if (boot0 & 0x00000100)
- return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
-
- switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
- case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
- return 32 * 1024 * 1024;
- case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
- return 16 * 1024 * 1024;
- case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
- return 8 * 1024 * 1024;
- case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
- return 4 * 1024 * 1024;
- }
-
- return 0;
-}
-
-static uint32_t
-nouveau_mem_detect_nforce(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct pci_dev *bridge;
- uint32_t mem;
-
- bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
- if (!bridge) {
- NV_ERROR(dev, "no bridge device\n");
- return 0;
- }
-
- if (dev_priv->flags & NV_NFORCE) {
- pci_read_config_dword(bridge, 0x7C, &mem);
- return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
- } else
- if (dev_priv->flags & NV_NFORCE2) {
- pci_read_config_dword(bridge, 0x84, &mem);
- return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
- }
-
- NV_ERROR(dev, "impossible!\n");
- return 0;
-}
-
-int
-nouveau_mem_detect(struct drm_device *dev)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- if (dev_priv->card_type == NV_04) {
- dev_priv->vram_size = nouveau_mem_detect_nv04(dev);
- } else
- if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
- dev_priv->vram_size = nouveau_mem_detect_nforce(dev);
- } else
- if (dev_priv->card_type < NV_50) {
- dev_priv->vram_size = nv_rd32(dev, NV04_PFB_FIFO_DATA);
- dev_priv->vram_size &= NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
- }
-
- if (dev_priv->vram_size)
- return 0;
- return -ENOMEM;
-}
-
bool
nouveau_mem_flags_valid(struct drm_device *dev, u32 tile_flags)
{
@@ -385,11 +317,29 @@ nouveau_mem_init_agp(struct drm_device *dev)
return 0;
}
+static const struct vram_types {
+ int value;
+ const char *name;
+} vram_type_map[] = {
+ { NV_MEM_TYPE_STOLEN , "stolen system memory" },
+ { NV_MEM_TYPE_SGRAM , "SGRAM" },
+ { NV_MEM_TYPE_SDRAM , "SDRAM" },
+ { NV_MEM_TYPE_DDR1 , "DDR1" },
+ { NV_MEM_TYPE_DDR2 , "DDR2" },
+ { NV_MEM_TYPE_DDR3 , "DDR3" },
+ { NV_MEM_TYPE_GDDR2 , "GDDR2" },
+ { NV_MEM_TYPE_GDDR3 , "GDDR3" },
+ { NV_MEM_TYPE_GDDR4 , "GDDR4" },
+ { NV_MEM_TYPE_GDDR5 , "GDDR5" },
+ { NV_MEM_TYPE_UNKNOWN, "unknown type" }
+};
+
int
nouveau_mem_vram_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
+ const struct vram_types *vram_type;
int ret, dma_bits;
dma_bits = 32;
@@ -427,7 +377,21 @@ nouveau_mem_vram_init(struct drm_device *dev)
return ret;
}
- NV_INFO(dev, "Detected %dMiB VRAM\n", (int)(dev_priv->vram_size >> 20));
+ vram_type = vram_type_map;
+ while (vram_type->value != NV_MEM_TYPE_UNKNOWN) {
+ if (nouveau_vram_type) {
+ if (!strcasecmp(nouveau_vram_type, vram_type->name))
+ break;
+ dev_priv->vram_type = vram_type->value;
+ } else {
+ if (vram_type->value == dev_priv->vram_type)
+ break;
+ }
+ vram_type++;
+ }
+
+ NV_INFO(dev, "Detected %dMiB VRAM (%s)\n",
+ (int)(dev_priv->vram_size >> 20), vram_type->name);
if (dev_priv->vram_sys_base) {
NV_INFO(dev, "Stolen system memory at: 0x%010llx\n",
dev_priv->vram_sys_base);
@@ -508,216 +472,617 @@ nouveau_mem_gart_init(struct drm_device *dev)
return 0;
}
-/* XXX: For now a dummy. More samples required, possibly even a card
- * Called from nouveau_perf.c */
-void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
- struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
- struct nouveau_pm_memtiming *timing) {
-
- NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers");
-}
-
-void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
- struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
- struct nouveau_pm_memtiming *timing) {
-
- timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
+static int
+nv40_mem_timing_calc(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
+ t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC);
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
- timing->reg_1 = (e->tWR + 2 + magic_number) << 24 |
- 1 << 16 |
- (e->tUNK_1 + 2 + magic_number) << 8 |
- (e->tCL + 2 - magic_number);
- timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
- timing->reg_2 |= 0x20200000;
-
- NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id,
- timing->reg_0, timing->reg_1,timing->reg_2);
+ t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 |
+ 1 << 16 |
+ (e->tWTR + 2 + (t->tCWL - 1)) << 8 |
+ (e->tCL + 2 - (t->tCWL - 1));
+
+ t->reg[2] = 0x20200000 |
+ ((t->tCWL - 1) << 24 |
+ e->tRRD << 16 |
+ e->tRCDWR << 8 |
+ e->tRCDRD);
+
+ NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", t->id,
+ t->reg[0], t->reg[1], t->reg[2]);
+ return 0;
}
-void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr,
- struct nouveau_pm_tbl_entry *e, uint8_t magic_number,struct nouveau_pm_memtiming *timing) {
+static int
+nv50_mem_timing_calc(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct bit_entry P;
+ uint8_t unk18 = 1, unk20 = 0, unk21 = 0, tmp7_3;
- uint8_t unk18 = 1,
- unk19 = 1,
- unk20 = 0,
- unk21 = 0;
+ if (bit_table(dev, 'P', &P))
+ return -EINVAL;
- switch (min(hdr->entry_len, (u8) 22)) {
+ switch (min(len, (u8) 22)) {
case 22:
unk21 = e->tUNK_21;
case 21:
unk20 = e->tUNK_20;
case 20:
- unk19 = e->tUNK_19;
+ if (e->tCWL > 0)
+ t->tCWL = e->tCWL;
case 19:
unk18 = e->tUNK_18;
break;
}
- timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
+ t->reg[0] = (e->tRP << 24 | e->tRAS << 16 | e->tRFC << 8 | e->tRC);
- /* XXX: I don't trust the -1's and +1's... they must come
- * from somewhere! */
- timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 |
- max(unk18, (u8) 1) << 16 |
- (e->tUNK_1 + unk19 + 1 + magic_number) << 8;
- if (dev_priv->chipset == 0xa8) {
- timing->reg_1 |= (e->tCL - 1);
- } else {
- timing->reg_1 |= (e->tCL + 2 - magic_number);
- }
- timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
-
- timing->reg_5 = (e->tRAS << 24 | e->tRC);
- timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16;
-
- if (P->version == 1) {
- timing->reg_2 |= magic_number << 24;
- timing->reg_3 = (0x14 + e->tCL) << 24 |
- 0x16 << 16 |
- (e->tCL - 1) << 8 |
- (e->tCL - 1);
- timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13;
- timing->reg_5 |= (e->tCL + 2) << 8;
- timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16;
+ t->reg[1] = (e->tWR + 2 + (t->tCWL - 1)) << 24 |
+ max(unk18, (u8) 1) << 16 |
+ (e->tWTR + 2 + (t->tCWL - 1)) << 8;
+
+ t->reg[2] = ((t->tCWL - 1) << 24 |
+ e->tRRD << 16 |
+ e->tRCDWR << 8 |
+ e->tRCDRD);
+
+ t->reg[4] = e->tUNK_13 << 8 | e->tUNK_13;
+
+ t->reg[5] = (e->tRFC << 24 | max(e->tRCDRD, e->tRCDWR) << 16 | e->tRP);
+
+ t->reg[8] = boot->reg[8] & 0xffffff00;
+
+ if (P.version == 1) {
+ t->reg[1] |= (e->tCL + 2 - (t->tCWL - 1));
+
+ t->reg[3] = (0x14 + e->tCL) << 24 |
+ 0x16 << 16 |
+ (e->tCL - 1) << 8 |
+ (e->tCL - 1);
+
+ t->reg[4] |= boot->reg[4] & 0xffff0000;
+
+ t->reg[6] = (0x33 - t->tCWL) << 16 |
+ t->tCWL << 8 |
+ (0x2e + e->tCL - t->tCWL);
+
+ t->reg[7] = 0x4000202 | (e->tCL - 1) << 16;
+
+ /* XXX: P.version == 1 only has DDR2 and GDDR3? */
+ if (dev_priv->vram_type == NV_MEM_TYPE_DDR2) {
+ t->reg[5] |= (e->tCL + 3) << 8;
+ t->reg[6] |= (t->tCWL - 2) << 8;
+ t->reg[8] |= (e->tCL - 4);
+ } else {
+ t->reg[5] |= (e->tCL + 2) << 8;
+ t->reg[6] |= t->tCWL << 8;
+ t->reg[8] |= (e->tCL - 2);
+ }
} else {
- timing->reg_2 |= (unk19 - 1) << 24;
- /* XXX: reg_10022c for recentish cards pretty much unknown*/
- timing->reg_3 = e->tCL - 1;
- timing->reg_4 = (unk20 << 24 | unk21 << 16 |
- e->tUNK_13 << 8 | e->tUNK_13);
+ t->reg[1] |= (5 + e->tCL - (t->tCWL));
+
+ /* XXX: 0xb? 0x30? */
+ t->reg[3] = (0x30 + e->tCL) << 24 |
+ (boot->reg[3] & 0x00ff0000)|
+ (0xb + e->tCL) << 8 |
+ (e->tCL - 1);
+
+ t->reg[4] |= (unk20 << 24 | unk21 << 16);
+
/* XXX: +6? */
- timing->reg_5 |= (unk19 + 6) << 8;
+ t->reg[5] |= (t->tCWL + 6) << 8;
- /* XXX: reg_10023c currently unknown
- * 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
- timing->reg_7 = 0x202;
+ t->reg[6] = (0x5a + e->tCL) << 16 |
+ (6 - e->tCL + t->tCWL) << 8 |
+ (0x50 + e->tCL - t->tCWL);
+
+ tmp7_3 = (boot->reg[7] & 0xff000000) >> 24;
+ t->reg[7] = (tmp7_3 << 24) |
+ ((tmp7_3 - 6 + e->tCL) << 16) |
+ 0x202;
}
- NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id,
- timing->reg_0, timing->reg_1,
- timing->reg_2, timing->reg_3);
+ NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", t->id,
+ t->reg[0], t->reg[1], t->reg[2], t->reg[3]);
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
- timing->reg_4, timing->reg_5,
- timing->reg_6, timing->reg_7);
- NV_DEBUG(dev, " 240: %08x\n", timing->reg_8);
-}
-
-void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
- struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) {
- timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP);
- timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f);
- timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8;
- timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13;
- timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15;
- NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id,
- timing->reg_0, timing->reg_1,
- timing->reg_2, timing->reg_3);
- NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n",
- timing->reg_4, timing->reg_5,
- timing->reg_6, timing->reg_7);
+ t->reg[4], t->reg[5], t->reg[6], t->reg[7]);
+ NV_DEBUG(dev, " 240: %08x\n", t->reg[8]);
+ return 0;
+}
+
+static int
+nvc0_mem_timing_calc(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
+ if (e->tCWL > 0)
+ t->tCWL = e->tCWL;
+
+ t->reg[0] = (e->tRP << 24 | (e->tRAS & 0x7f) << 17 |
+ e->tRFC << 8 | e->tRC);
+
+ t->reg[1] = (boot->reg[1] & 0xff000000) |
+ (e->tRCDWR & 0x0f) << 20 |
+ (e->tRCDRD & 0x0f) << 14 |
+ (t->tCWL << 7) |
+ (e->tCL & 0x0f);
+
+ t->reg[2] = (boot->reg[2] & 0xff0000ff) |
+ e->tWR << 16 | e->tWTR << 8;
+
+ t->reg[3] = (e->tUNK_20 & 0x1f) << 9 |
+ (e->tUNK_21 & 0xf) << 5 |
+ (e->tUNK_13 & 0x1f);
+
+ t->reg[4] = (boot->reg[4] & 0xfff00fff) |
+ (e->tRRD&0x1f) << 15;
+
+ NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", t->id,
+ t->reg[0], t->reg[1], t->reg[2], t->reg[3]);
+ NV_DEBUG(dev, " 2a0: %08x\n", t->reg[4]);
+ return 0;
}
/**
- * Processes the Memory Timing BIOS table, stores generated
- * register values
- * @pre init scripts were run, memtiming regs are initialized
+ * MR generation methods
*/
-void
-nouveau_mem_timing_init(struct drm_device *dev)
+
+static int
+nouveau_mem_ddr2_mr(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
+ t->drive_strength = 0;
+ if (len < 15) {
+ t->odt = boot->odt;
+ } else {
+ t->odt = e->RAM_FT1 & 0x07;
+ }
+
+ if (e->tCL >= NV_MEM_CL_DDR2_MAX) {
+ NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL);
+ return -ERANGE;
+ }
+
+ if (e->tWR >= NV_MEM_WR_DDR2_MAX) {
+ NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR);
+ return -ERANGE;
+ }
+
+ if (t->odt > 3) {
+ NV_WARN(dev, "(%u) Invalid odt value, assuming disabled: %x",
+ t->id, t->odt);
+ t->odt = 0;
+ }
+
+ t->mr[0] = (boot->mr[0] & 0x100f) |
+ (e->tCL) << 4 |
+ (e->tWR - 1) << 9;
+ t->mr[1] = (boot->mr[1] & 0x101fbb) |
+ (t->odt & 0x1) << 2 |
+ (t->odt & 0x2) << 5;
+
+ NV_DEBUG(dev, "(%u) MR: %08x", t->id, t->mr[0]);
+ return 0;
+}
+
+uint8_t nv_mem_wr_lut_ddr3[NV_MEM_WR_DDR3_MAX] = {
+ 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0};
+
+static int
+nouveau_mem_ddr3_mr(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
+ u8 cl = e->tCL - 4;
+
+ t->drive_strength = 0;
+ if (len < 15) {
+ t->odt = boot->odt;
+ } else {
+ t->odt = e->RAM_FT1 & 0x07;
+ }
+
+ if (e->tCL >= NV_MEM_CL_DDR3_MAX || e->tCL < 4) {
+ NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL);
+ return -ERANGE;
+ }
+
+ if (e->tWR >= NV_MEM_WR_DDR3_MAX || e->tWR < 4) {
+ NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR);
+ return -ERANGE;
+ }
+
+ if (e->tCWL < 5) {
+ NV_WARN(dev, "(%u) Invalid tCWL: %u", t->id, e->tCWL);
+ return -ERANGE;
+ }
+
+ t->mr[0] = (boot->mr[0] & 0x180b) |
+ /* CAS */
+ (cl & 0x7) << 4 |
+ (cl & 0x8) >> 1 |
+ (nv_mem_wr_lut_ddr3[e->tWR]) << 9;
+ t->mr[1] = (boot->mr[1] & 0x101dbb) |
+ (t->odt & 0x1) << 2 |
+ (t->odt & 0x2) << 5 |
+ (t->odt & 0x4) << 7;
+ t->mr[2] = (boot->mr[2] & 0x20ffb7) | (e->tCWL - 5) << 3;
+
+ NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[2]);
+ return 0;
+}
+
+uint8_t nv_mem_cl_lut_gddr3[NV_MEM_CL_GDDR3_MAX] = {
+ 0, 0, 0, 0, 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11};
+uint8_t nv_mem_wr_lut_gddr3[NV_MEM_WR_GDDR3_MAX] = {
+ 0, 0, 0, 0, 0, 2, 3, 8, 9, 10, 11, 0, 0, 1, 1, 0, 3};
+
+static int
+nouveau_mem_gddr3_mr(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
+ if (len < 15) {
+ t->drive_strength = boot->drive_strength;
+ t->odt = boot->odt;
+ } else {
+ t->drive_strength = (e->RAM_FT1 & 0x30) >> 4;
+ t->odt = e->RAM_FT1 & 0x07;
+ }
+
+ if (e->tCL >= NV_MEM_CL_GDDR3_MAX) {
+ NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL);
+ return -ERANGE;
+ }
+
+ if (e->tWR >= NV_MEM_WR_GDDR3_MAX) {
+ NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR);
+ return -ERANGE;
+ }
+
+ if (t->odt > 3) {
+ NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x",
+ t->id, t->odt);
+ t->odt = 0;
+ }
+
+ t->mr[0] = (boot->mr[0] & 0xe0b) |
+ /* CAS */
+ ((nv_mem_cl_lut_gddr3[e->tCL] & 0x7) << 4) |
+ ((nv_mem_cl_lut_gddr3[e->tCL] & 0x8) >> 2);
+ t->mr[1] = (boot->mr[1] & 0x100f40) | t->drive_strength |
+ (t->odt << 2) |
+ (nv_mem_wr_lut_gddr3[e->tWR] & 0xf) << 4;
+ t->mr[2] = boot->mr[2];
+
+ NV_DEBUG(dev, "(%u) MR: %08x %08x %08x", t->id,
+ t->mr[0], t->mr[1], t->mr[2]);
+ return 0;
+}
+
+static int
+nouveau_mem_gddr5_mr(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_tbl_entry *e, u8 len,
+ struct nouveau_pm_memtiming *boot,
+ struct nouveau_pm_memtiming *t)
+{
+ if (len < 15) {
+ t->drive_strength = boot->drive_strength;
+ t->odt = boot->odt;
+ } else {
+ t->drive_strength = (e->RAM_FT1 & 0x30) >> 4;
+ t->odt = e->RAM_FT1 & 0x03;
+ }
+
+ if (e->tCL >= NV_MEM_CL_GDDR5_MAX) {
+ NV_WARN(dev, "(%u) Invalid tCL: %u", t->id, e->tCL);
+ return -ERANGE;
+ }
+
+ if (e->tWR >= NV_MEM_WR_GDDR5_MAX) {
+ NV_WARN(dev, "(%u) Invalid tWR: %u", t->id, e->tWR);
+ return -ERANGE;
+ }
+
+ if (t->odt > 3) {
+ NV_WARN(dev, "(%u) Invalid odt value, assuming autocal: %x",
+ t->id, t->odt);
+ t->odt = 0;
+ }
+
+ t->mr[0] = (boot->mr[0] & 0x007) |
+ ((e->tCL - 5) << 3) |
+ ((e->tWR - 4) << 8);
+ t->mr[1] = (boot->mr[1] & 0x1007f0) |
+ t->drive_strength |
+ (t->odt << 2);
+
+ NV_DEBUG(dev, "(%u) MR: %08x %08x", t->id, t->mr[0], t->mr[1]);
+ return 0;
+}
+
+int
+nouveau_mem_timing_calc(struct drm_device *dev, u32 freq,
+ struct nouveau_pm_memtiming *t)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
- struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
- struct nvbios *bios = &dev_priv->vbios;
- struct bit_entry P;
- struct nouveau_pm_tbl_header *hdr = NULL;
- uint8_t magic_number;
- u8 *entry;
- int i;
+ struct nouveau_pm_memtiming *boot = &pm->boot.timing;
+ struct nouveau_pm_tbl_entry *e;
+ u8 ver, len, *ptr, *ramcfg;
+ int ret;
+
+ ptr = nouveau_perf_timing(dev, freq, &ver, &len);
+ if (!ptr || ptr[0] == 0x00) {
+ *t = *boot;
+ return 0;
+ }
+ e = (struct nouveau_pm_tbl_entry *)ptr;
+
+ t->tCWL = boot->tCWL;
+
+ switch (dev_priv->card_type) {
+ case NV_40:
+ ret = nv40_mem_timing_calc(dev, freq, e, len, boot, t);
+ break;
+ case NV_50:
+ ret = nv50_mem_timing_calc(dev, freq, e, len, boot, t);
+ break;
+ case NV_C0:
+ ret = nvc0_mem_timing_calc(dev, freq, e, len, boot, t);
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
- if (bios->type == NVBIOS_BIT) {
- if (bit_table(dev, 'P', &P))
- return;
+ switch (dev_priv->vram_type * !ret) {
+ case NV_MEM_TYPE_GDDR3:
+ ret = nouveau_mem_gddr3_mr(dev, freq, e, len, boot, t);
+ break;
+ case NV_MEM_TYPE_GDDR5:
+ ret = nouveau_mem_gddr5_mr(dev, freq, e, len, boot, t);
+ break;
+ case NV_MEM_TYPE_DDR2:
+ ret = nouveau_mem_ddr2_mr(dev, freq, e, len, boot, t);
+ break;
+ case NV_MEM_TYPE_DDR3:
+ ret = nouveau_mem_ddr3_mr(dev, freq, e, len, boot, t);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ ramcfg = nouveau_perf_ramcfg(dev, freq, &ver, &len);
+ if (ramcfg) {
+ int dll_off;
- if (P.version == 1)
- hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[4]);
+ if (ver == 0x00)
+ dll_off = !!(ramcfg[3] & 0x04);
else
- if (P.version == 2)
- hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[8]);
- else {
- NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
+ dll_off = !!(ramcfg[2] & 0x40);
+
+ switch (dev_priv->vram_type) {
+ case NV_MEM_TYPE_GDDR3:
+ t->mr[1] &= ~0x00000040;
+ t->mr[1] |= 0x00000040 * dll_off;
+ break;
+ default:
+ t->mr[1] &= ~0x00000001;
+ t->mr[1] |= 0x00000001 * dll_off;
+ break;
}
+ }
+
+ return ret;
+}
+
+void
+nouveau_mem_timing_read(struct drm_device *dev, struct nouveau_pm_memtiming *t)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 timing_base, timing_regs, mr_base;
+ int i;
+
+ if (dev_priv->card_type >= 0xC0) {
+ timing_base = 0x10f290;
+ mr_base = 0x10f300;
} else {
- NV_DEBUG(dev, "BMP version too old for memory\n");
- return;
+ timing_base = 0x100220;
+ mr_base = 0x1002c0;
}
- if (!hdr) {
- NV_DEBUG(dev, "memory timing table pointer invalid\n");
+ t->id = -1;
+
+ switch (dev_priv->card_type) {
+ case NV_50:
+ timing_regs = 9;
+ break;
+ case NV_C0:
+ case NV_D0:
+ timing_regs = 5;
+ break;
+ case NV_30:
+ case NV_40:
+ timing_regs = 3;
+ break;
+ default:
+ timing_regs = 0;
return;
}
+ for(i = 0; i < timing_regs; i++)
+ t->reg[i] = nv_rd32(dev, timing_base + (0x04 * i));
+
+ t->tCWL = 0;
+ if (dev_priv->card_type < NV_C0) {
+ t->tCWL = ((nv_rd32(dev, 0x100228) & 0x0f000000) >> 24) + 1;
+ } else if (dev_priv->card_type <= NV_D0) {
+ t->tCWL = ((nv_rd32(dev, 0x10f294) & 0x00000f80) >> 7);
+ }
- if (hdr->version != 0x10) {
- NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version);
- return;
+ t->mr[0] = nv_rd32(dev, mr_base);
+ t->mr[1] = nv_rd32(dev, mr_base + 0x04);
+ t->mr[2] = nv_rd32(dev, mr_base + 0x20);
+ t->mr[3] = nv_rd32(dev, mr_base + 0x24);
+
+ t->odt = 0;
+ t->drive_strength = 0;
+
+ switch (dev_priv->vram_type) {
+ case NV_MEM_TYPE_DDR3:
+ t->odt |= (t->mr[1] & 0x200) >> 7;
+ case NV_MEM_TYPE_DDR2:
+ t->odt |= (t->mr[1] & 0x04) >> 2 |
+ (t->mr[1] & 0x40) >> 5;
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ case NV_MEM_TYPE_GDDR5:
+ t->drive_strength = t->mr[1] & 0x03;
+ t->odt = (t->mr[1] & 0x0c) >> 2;
+ break;
+ default:
+ break;
}
+}
- /* validate record length */
- if (hdr->entry_len < 15) {
- NV_ERROR(dev, "mem timing table length unknown: %d\n", hdr->entry_len);
- return;
+int
+nouveau_mem_exec(struct nouveau_mem_exec_func *exec,
+ struct nouveau_pm_level *perflvl)
+{
+ struct drm_nouveau_private *dev_priv = exec->dev->dev_private;
+ struct nouveau_pm_memtiming *info = &perflvl->timing;
+ u32 tMRD = 1000, tCKSRE = 0, tCKSRX = 0, tXS = 0, tDLLK = 0;
+ u32 mr[3] = { info->mr[0], info->mr[1], info->mr[2] };
+ u32 mr1_dlloff;
+
+ switch (dev_priv->vram_type) {
+ case NV_MEM_TYPE_DDR2:
+ tDLLK = 2000;
+ mr1_dlloff = 0x00000001;
+ break;
+ case NV_MEM_TYPE_DDR3:
+ tDLLK = 12000;
+ mr1_dlloff = 0x00000001;
+ break;
+ case NV_MEM_TYPE_GDDR3:
+ tDLLK = 40000;
+ mr1_dlloff = 0x00000040;
+ break;
+ default:
+ NV_ERROR(exec->dev, "cannot reclock unsupported memtype\n");
+ return -ENODEV;
}
- /* parse vbios entries into common format */
- memtimings->timing =
- kcalloc(hdr->entry_cnt, sizeof(*memtimings->timing), GFP_KERNEL);
- if (!memtimings->timing)
- return;
+ /* fetch current MRs */
+ switch (dev_priv->vram_type) {
+ case NV_MEM_TYPE_GDDR3:
+ case NV_MEM_TYPE_DDR3:
+ mr[2] = exec->mrg(exec, 2);
+ default:
+ mr[1] = exec->mrg(exec, 1);
+ mr[0] = exec->mrg(exec, 0);
+ break;
+ }
- /* Get "some number" from the timing reg for NV_40 and NV_50
- * Used in calculations later... source unknown */
- magic_number = 0;
- if (P.version == 1) {
- magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24;
+ /* DLL 'on' -> DLL 'off' mode, disable before entering self-refresh */
+ if (!(mr[1] & mr1_dlloff) && (info->mr[1] & mr1_dlloff)) {
+ exec->precharge(exec);
+ exec->mrs (exec, 1, mr[1] | mr1_dlloff);
+ exec->wait(exec, tMRD);
}
- entry = (u8*) hdr + hdr->header_len;
- for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) {
- struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
- if (entry[0] == 0)
- continue;
+ /* enter self-refresh mode */
+ exec->precharge(exec);
+ exec->refresh(exec);
+ exec->refresh(exec);
+ exec->refresh_auto(exec, false);
+ exec->refresh_self(exec, true);
+ exec->wait(exec, tCKSRE);
+
+ /* modify input clock frequency */
+ exec->clock_set(exec);
+
+ /* exit self-refresh mode */
+ exec->wait(exec, tCKSRX);
+ exec->precharge(exec);
+ exec->refresh_self(exec, false);
+ exec->refresh_auto(exec, true);
+ exec->wait(exec, tXS);
+
+ /* update MRs */
+ if (mr[2] != info->mr[2]) {
+ exec->mrs (exec, 2, info->mr[2]);
+ exec->wait(exec, tMRD);
+ }
+
+ if (mr[1] != info->mr[1]) {
+ /* need to keep DLL off until later, at least on GDDR3 */
+ exec->mrs (exec, 1, info->mr[1] | (mr[1] & mr1_dlloff));
+ exec->wait(exec, tMRD);
+ }
+
+ if (mr[0] != info->mr[0]) {
+ exec->mrs (exec, 0, info->mr[0]);
+ exec->wait(exec, tMRD);
+ }
- timing->id = i;
- timing->WR = entry[0];
- timing->CL = entry[2];
+ /* update PFB timing registers */
+ exec->timing_set(exec);
- if(dev_priv->card_type <= NV_40) {
- nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
- } else if(dev_priv->card_type == NV_50){
- nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
- } else if(dev_priv->card_type == NV_C0) {
- nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]);
+ /* DLL (enable + ) reset */
+ if (!(info->mr[1] & mr1_dlloff)) {
+ if (mr[1] & mr1_dlloff) {
+ exec->mrs (exec, 1, info->mr[1]);
+ exec->wait(exec, tMRD);
}
+ exec->mrs (exec, 0, info->mr[0] | 0x00000100);
+ exec->wait(exec, tMRD);
+ exec->mrs (exec, 0, info->mr[0] | 0x00000000);
+ exec->wait(exec, tMRD);
+ exec->wait(exec, tDLLK);
+ if (dev_priv->vram_type == NV_MEM_TYPE_GDDR3)
+ exec->precharge(exec);
}
- memtimings->nr_timing = hdr->entry_cnt;
- memtimings->supported = P.version == 1;
+ return 0;
}
-void
-nouveau_mem_timing_fini(struct drm_device *dev)
+int
+nouveau_mem_vbios_type(struct drm_device *dev)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings;
+ struct bit_entry M;
+ u8 ramcfg = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2;
+ if (!bit_table(dev, 'M', &M) || M.version != 2 || M.length < 5) {
+ u8 *table = ROMPTR(dev, M.data[3]);
+ if (table && table[0] == 0x10 && ramcfg < table[3]) {
+ u8 *entry = table + table[1] + (ramcfg * table[2]);
+ switch (entry[0] & 0x0f) {
+ case 0: return NV_MEM_TYPE_DDR2;
+ case 1: return NV_MEM_TYPE_DDR3;
+ case 2: return NV_MEM_TYPE_GDDR3;
+ case 3: return NV_MEM_TYPE_GDDR5;
+ default:
+ break;
+ }
- if(mem->timing) {
- kfree(mem->timing);
- mem->timing = NULL;
+ }
}
+ return NV_MEM_TYPE_UNKNOWN;
}
static int
diff --git a/drivers/gpu/drm/nouveau/nouveau_mxm.c b/drivers/gpu/drm/nouveau/nouveau_mxm.c
index e5a64f0f4cb7..07d0d1e03690 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mxm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mxm.c
@@ -582,6 +582,35 @@ mxm_shadow_dsm(struct drm_device *dev, u8 version)
#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
+static u8
+wmi_wmmx_mxmi(struct drm_device *dev, u8 version)
+{
+ u32 mxmi_args[] = { 0x494D584D /* MXMI */, version, 0 };
+ struct acpi_buffer args = { sizeof(mxmi_args), mxmi_args };
+ struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
+ union acpi_object *obj;
+ acpi_status status;
+
+ status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
+ if (ACPI_FAILURE(status)) {
+ MXM_DBG(dev, "WMMX MXMI returned %d\n", status);
+ return 0x00;
+ }
+
+ obj = retn.pointer;
+ if (obj->type == ACPI_TYPE_INTEGER) {
+ version = obj->integer.value;
+ MXM_DBG(dev, "WMMX MXMI version %d.%d\n",
+ (version >> 4), version & 0x0f);
+ } else {
+ version = 0;
+ MXM_DBG(dev, "WMMX MXMI returned non-integer\n");
+ }
+
+ kfree(obj);
+ return version;
+}
+
static bool
mxm_shadow_wmi(struct drm_device *dev, u8 version)
{
@@ -592,7 +621,15 @@ mxm_shadow_wmi(struct drm_device *dev, u8 version)
union acpi_object *obj;
acpi_status status;
- if (!wmi_has_guid(WMI_WMMX_GUID))
+ if (!wmi_has_guid(WMI_WMMX_GUID)) {
+ MXM_DBG(dev, "WMMX GUID not found\n");
+ return false;
+ }
+
+ mxms_args[1] = wmi_wmmx_mxmi(dev, 0x00);
+ if (!mxms_args[1])
+ mxms_args[1] = wmi_wmmx_mxmi(dev, version);
+ if (!mxms_args[1])
return false;
status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
diff --git a/drivers/gpu/drm/nouveau/nouveau_perf.c b/drivers/gpu/drm/nouveau/nouveau_perf.c
index 58f497343cec..69a528d106e6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_perf.c
+++ b/drivers/gpu/drm/nouveau/nouveau_perf.c
@@ -27,6 +27,178 @@
#include "nouveau_drv.h"
#include "nouveau_pm.h"
+static u8 *
+nouveau_perf_table(struct drm_device *dev, u8 *ver)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct bit_entry P;
+
+ if (!bit_table(dev, 'P', &P) && P.version && P.version <= 2) {
+ u8 *perf = ROMPTR(dev, P.data[0]);
+ if (perf) {
+ *ver = perf[0];
+ return perf;
+ }
+ }
+
+ if (bios->type == NVBIOS_BMP) {
+ if (bios->data[bios->offset + 6] >= 0x25) {
+ u8 *perf = ROMPTR(dev, bios->data[bios->offset + 0x94]);
+ if (perf) {
+ *ver = perf[1];
+ return perf;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static u8 *
+nouveau_perf_entry(struct drm_device *dev, int idx,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ u8 *perf = nouveau_perf_table(dev, ver);
+ if (perf) {
+ if (*ver >= 0x12 && *ver < 0x20 && idx < perf[2]) {
+ *hdr = perf[3];
+ *cnt = 0;
+ *len = 0;
+ return perf + perf[0] + idx * perf[3];
+ } else
+ if (*ver >= 0x20 && *ver < 0x40 && idx < perf[2]) {
+ *hdr = perf[3];
+ *cnt = perf[4];
+ *len = perf[5];
+ return perf + perf[1] + idx * (*hdr + (*cnt * *len));
+ } else
+ if (*ver >= 0x40 && *ver < 0x41 && idx < perf[5]) {
+ *hdr = perf[2];
+ *cnt = perf[4];
+ *len = perf[3];
+ return perf + perf[1] + idx * (*hdr + (*cnt * *len));
+ }
+ }
+ return NULL;
+}
+
+static u8 *
+nouveau_perf_rammap(struct drm_device *dev, u32 freq,
+ u8 *ver, u8 *hdr, u8 *cnt, u8 *len)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct bit_entry P;
+ u8 *perf, i = 0;
+
+ if (!bit_table(dev, 'P', &P) && P.version == 2) {
+ u8 *rammap = ROMPTR(dev, P.data[4]);
+ if (rammap) {
+ u8 *ramcfg = rammap + rammap[1];
+
+ *ver = rammap[0];
+ *hdr = rammap[2];
+ *cnt = rammap[4];
+ *len = rammap[3];
+
+ freq /= 1000;
+ for (i = 0; i < rammap[5]; i++) {
+ if (freq >= ROM16(ramcfg[0]) &&
+ freq <= ROM16(ramcfg[2]))
+ return ramcfg;
+
+ ramcfg += *hdr + (*cnt * *len);
+ }
+ }
+
+ return NULL;
+ }
+
+ if (dev_priv->chipset == 0x49 ||
+ dev_priv->chipset == 0x4b)
+ freq /= 2;
+
+ while ((perf = nouveau_perf_entry(dev, i++, ver, hdr, cnt, len))) {
+ if (*ver >= 0x20 && *ver < 0x25) {
+ if (perf[0] != 0xff && freq <= ROM16(perf[11]) * 1000)
+ break;
+ } else
+ if (*ver >= 0x25 && *ver < 0x40) {
+ if (perf[0] != 0xff && freq <= ROM16(perf[12]) * 1000)
+ break;
+ }
+ }
+
+ if (perf) {
+ u8 *ramcfg = perf + *hdr;
+ *ver = 0x00;
+ *hdr = 0;
+ return ramcfg;
+ }
+
+ return NULL;
+}
+
+u8 *
+nouveau_perf_ramcfg(struct drm_device *dev, u32 freq, u8 *ver, u8 *len)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ u8 strap, hdr, cnt;
+ u8 *rammap;
+
+ strap = (nv_rd32(dev, 0x101000) & 0x0000003c) >> 2;
+ if (bios->ram_restrict_tbl_ptr)
+ strap = bios->data[bios->ram_restrict_tbl_ptr + strap];
+
+ rammap = nouveau_perf_rammap(dev, freq, ver, &hdr, &cnt, len);
+ if (rammap && strap < cnt)
+ return rammap + hdr + (strap * *len);
+
+ return NULL;
+}
+
+u8 *
+nouveau_perf_timing(struct drm_device *dev, u32 freq, u8 *ver, u8 *len)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->vbios;
+ struct bit_entry P;
+ u8 *perf, *timing = NULL;
+ u8 i = 0, hdr, cnt;
+
+ if (bios->type == NVBIOS_BMP) {
+ while ((perf = nouveau_perf_entry(dev, i++, ver, &hdr, &cnt,
+ len)) && *ver == 0x15) {
+ if (freq <= ROM32(perf[5]) * 20) {
+ *ver = 0x00;
+ *len = 14;
+ return perf + 41;
+ }
+ }
+ return NULL;
+ }
+
+ if (!bit_table(dev, 'P', &P)) {
+ if (P.version == 1)
+ timing = ROMPTR(dev, P.data[4]);
+ else
+ if (P.version == 2)
+ timing = ROMPTR(dev, P.data[8]);
+ }
+
+ if (timing && timing[0] == 0x10) {
+ u8 *ramcfg = nouveau_perf_ramcfg(dev, freq, ver, len);
+ if (ramcfg && ramcfg[1] < timing[2]) {
+ *ver = timing[0];
+ *len = timing[3];
+ return timing + timing[1] + (ramcfg[1] * timing[3]);
+ }
+ }
+
+ return NULL;
+}
+
static void
legacy_perf_init(struct drm_device *dev)
{
@@ -72,74 +244,11 @@ legacy_perf_init(struct drm_device *dev)
pm->nr_perflvl = 1;
}
-static struct nouveau_pm_memtiming *
-nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
- u16 memclk, u8 *entry, u8 recordlen, u8 entries)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
- struct nvbios *bios = &dev_priv->vbios;
- u8 ramcfg;
- int i;
-
- /* perf v2 has a separate "timing map" table, we have to match
- * the target memory clock to a specific entry, *then* use
- * ramcfg to select the correct subentry
- */
- if (P->version == 2) {
- u8 *tmap = ROMPTR(dev, P->data[4]);
- if (!tmap) {
- NV_DEBUG(dev, "no timing map pointer\n");
- return NULL;
- }
-
- if (tmap[0] != 0x10) {
- NV_WARN(dev, "timing map 0x%02x unknown\n", tmap[0]);
- return NULL;
- }
-
- entry = tmap + tmap[1];
- recordlen = tmap[2] + (tmap[4] * tmap[3]);
- for (i = 0; i < tmap[5]; i++, entry += recordlen) {
- if (memclk >= ROM16(entry[0]) &&
- memclk <= ROM16(entry[2]))
- break;
- }
-
- if (i == tmap[5]) {
- NV_WARN(dev, "no match in timing map table\n");
- return NULL;
- }
-
- entry += tmap[2];
- recordlen = tmap[3];
- entries = tmap[4];
- }
-
- ramcfg = (nv_rd32(dev, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2;
- if (bios->ram_restrict_tbl_ptr)
- ramcfg = bios->data[bios->ram_restrict_tbl_ptr + ramcfg];
-
- if (ramcfg >= entries) {
- NV_WARN(dev, "ramcfg strap out of bounds!\n");
- return NULL;
- }
-
- entry += ramcfg * recordlen;
- if (entry[1] >= pm->memtimings.nr_timing) {
- if (entry[1] != 0xff)
- NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
- return NULL;
- }
-
- return &pm->memtimings.timing[entry[1]];
-}
-
static void
-nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
- struct nouveau_pm_level *perflvl)
+nouveau_perf_voltage(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct bit_entry P;
u8 *vmap;
int id;
@@ -158,13 +267,13 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
/* on newer ones, the perflvl stores an index into yet another
* vbios table containing a min/max voltage value for the perflvl
*/
- if (P->version != 2 || P->length < 34) {
+ if (bit_table(dev, 'P', &P) || P.version != 2 || P.length < 34) {
NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n",
- P->version, P->length);
+ P.version, P.length);
return;
}
- vmap = ROMPTR(dev, P->data[32]);
+ vmap = ROMPTR(dev, P.data[32]);
if (!vmap) {
NV_DEBUG(dev, "volt map table pointer invalid\n");
return;
@@ -183,130 +292,70 @@ nouveau_perf_init(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nvbios *bios = &dev_priv->vbios;
- struct bit_entry P;
- struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
- struct nouveau_pm_tbl_header mt_hdr;
- u8 version, headerlen, recordlen, entries;
- u8 *perf, *entry;
- int vid, i;
-
- if (bios->type == NVBIOS_BIT) {
- if (bit_table(dev, 'P', &P))
- return;
-
- if (P.version != 1 && P.version != 2) {
- NV_WARN(dev, "unknown perf for BIT P %d\n", P.version);
- return;
- }
-
- perf = ROMPTR(dev, P.data[0]);
- version = perf[0];
- headerlen = perf[1];
- if (version < 0x40) {
- recordlen = perf[3] + (perf[4] * perf[5]);
- entries = perf[2];
-
- pm->pwm_divisor = ROM16(perf[6]);
- } else {
- recordlen = perf[2] + (perf[3] * perf[4]);
- entries = perf[5];
- }
- } else {
- if (bios->data[bios->offset + 6] < 0x25) {
- legacy_perf_init(dev);
- return;
- }
+ u8 *perf, ver, hdr, cnt, len;
+ int ret, vid, i = -1;
- perf = ROMPTR(dev, bios->data[bios->offset + 0x94]);
- if (!perf) {
- NV_DEBUG(dev, "perf table pointer invalid\n");
- return;
- }
-
- version = perf[1];
- headerlen = perf[0];
- recordlen = perf[3];
- entries = perf[2];
- }
-
- if (entries > NOUVEAU_PM_MAX_LEVEL) {
- NV_DEBUG(dev, "perf table has too many entries - buggy vbios?\n");
- entries = NOUVEAU_PM_MAX_LEVEL;
+ if (bios->type == NVBIOS_BMP && bios->data[bios->offset + 6] < 0x25) {
+ legacy_perf_init(dev);
+ return;
}
- entry = perf + headerlen;
-
- /* For version 0x15, initialize memtiming table */
- if(version == 0x15) {
- memtimings->timing =
- kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
- if (!memtimings->timing) {
- NV_WARN(dev,"Could not allocate memtiming table\n");
- return;
- }
-
- mt_hdr.entry_cnt = entries;
- mt_hdr.entry_len = 14;
- mt_hdr.version = version;
- mt_hdr.header_len = 4;
- }
+ perf = nouveau_perf_table(dev, &ver);
+ if (ver >= 0x20 && ver < 0x40)
+ pm->fan.pwm_divisor = ROM16(perf[6]);
- for (i = 0; i < entries; i++) {
+ while ((perf = nouveau_perf_entry(dev, ++i, &ver, &hdr, &cnt, &len))) {
struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
- perflvl->timing = NULL;
-
- if (entry[0] == 0xff) {
- entry += recordlen;
+ if (perf[0] == 0xff)
continue;
- }
- switch (version) {
+ switch (ver) {
case 0x12:
case 0x13:
case 0x15:
- perflvl->fanspeed = entry[55];
- if (recordlen > 56)
- perflvl->volt_min = entry[56];
- perflvl->core = ROM32(entry[1]) * 10;
- perflvl->memory = ROM32(entry[5]) * 20;
+ perflvl->fanspeed = perf[55];
+ if (hdr > 56)
+ perflvl->volt_min = perf[56];
+ perflvl->core = ROM32(perf[1]) * 10;
+ perflvl->memory = ROM32(perf[5]) * 20;
break;
case 0x21:
case 0x23:
case 0x24:
- perflvl->fanspeed = entry[4];
- perflvl->volt_min = entry[5];
- perflvl->shader = ROM16(entry[6]) * 1000;
+ perflvl->fanspeed = perf[4];
+ perflvl->volt_min = perf[5];
+ perflvl->shader = ROM16(perf[6]) * 1000;
perflvl->core = perflvl->shader;
- perflvl->core += (signed char)entry[8] * 1000;
+ perflvl->core += (signed char)perf[8] * 1000;
if (dev_priv->chipset == 0x49 ||
dev_priv->chipset == 0x4b)
- perflvl->memory = ROM16(entry[11]) * 1000;
+ perflvl->memory = ROM16(perf[11]) * 1000;
else
- perflvl->memory = ROM16(entry[11]) * 2000;
+ perflvl->memory = ROM16(perf[11]) * 2000;
break;
case 0x25:
- perflvl->fanspeed = entry[4];
- perflvl->volt_min = entry[5];
- perflvl->core = ROM16(entry[6]) * 1000;
- perflvl->shader = ROM16(entry[10]) * 1000;
- perflvl->memory = ROM16(entry[12]) * 1000;
+ perflvl->fanspeed = perf[4];
+ perflvl->volt_min = perf[5];
+ perflvl->core = ROM16(perf[6]) * 1000;
+ perflvl->shader = ROM16(perf[10]) * 1000;
+ perflvl->memory = ROM16(perf[12]) * 1000;
break;
case 0x30:
- perflvl->memscript = ROM16(entry[2]);
+ perflvl->memscript = ROM16(perf[2]);
case 0x35:
- perflvl->fanspeed = entry[6];
- perflvl->volt_min = entry[7];
- perflvl->core = ROM16(entry[8]) * 1000;
- perflvl->shader = ROM16(entry[10]) * 1000;
- perflvl->memory = ROM16(entry[12]) * 1000;
- perflvl->vdec = ROM16(entry[16]) * 1000;
- perflvl->dom6 = ROM16(entry[20]) * 1000;
+ perflvl->fanspeed = perf[6];
+ perflvl->volt_min = perf[7];
+ perflvl->core = ROM16(perf[8]) * 1000;
+ perflvl->shader = ROM16(perf[10]) * 1000;
+ perflvl->memory = ROM16(perf[12]) * 1000;
+ perflvl->vdec = ROM16(perf[16]) * 1000;
+ perflvl->dom6 = ROM16(perf[20]) * 1000;
break;
case 0x40:
-#define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000
+#define subent(n) ((ROM16(perf[hdr + (n) * len]) & 0xfff) * 1000)
perflvl->fanspeed = 0; /*XXX*/
- perflvl->volt_min = entry[2];
+ perflvl->volt_min = perf[2];
if (dev_priv->card_type == NV_50) {
perflvl->core = subent(0);
perflvl->shader = subent(1);
@@ -329,36 +378,34 @@ nouveau_perf_init(struct drm_device *dev)
}
/* make sure vid is valid */
- nouveau_perf_voltage(dev, &P, perflvl);
+ nouveau_perf_voltage(dev, perflvl);
if (pm->voltage.supported && perflvl->volt_min) {
vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
if (vid < 0) {
- NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
- entry += recordlen;
+ NV_DEBUG(dev, "perflvl %d, bad vid\n", i);
continue;
}
}
/* get the corresponding memory timings */
- if (version == 0x15) {
- memtimings->timing[i].id = i;
- nv30_mem_timing_entry(dev,&mt_hdr,(struct nouveau_pm_tbl_entry*) &entry[41],0,&memtimings->timing[i]);
- perflvl->timing = &memtimings->timing[i];
- } else if (version > 0x15) {
- /* last 3 args are for < 0x40, ignored for >= 0x40 */
- perflvl->timing =
- nouveau_perf_timing(dev, &P,
- perflvl->memory / 1000,
- entry + perf[3],
- perf[5], perf[4]);
+ ret = nouveau_mem_timing_calc(dev, perflvl->memory,
+ &perflvl->timing);
+ if (ret) {
+ NV_DEBUG(dev, "perflvl %d, bad timing: %d\n", i, ret);
+ continue;
}
snprintf(perflvl->name, sizeof(perflvl->name),
"performance_level_%d", i);
perflvl->id = i;
- pm->nr_perflvl++;
- entry += recordlen;
+ snprintf(perflvl->profile.name, sizeof(perflvl->profile.name),
+ "%d", perflvl->id);
+ perflvl->profile.func = &nouveau_pm_static_profile_func;
+ list_add_tail(&perflvl->profile.head, &pm->profiles);
+
+
+ pm->nr_perflvl++;
}
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 9064d7f19794..34d591b7d4ef 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -50,7 +50,7 @@ nouveau_pwmfan_get(struct drm_device *dev)
ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
if (ret == 0) {
ret = pm->pwm_get(dev, gpio.line, &divs, &duty);
- if (ret == 0) {
+ if (ret == 0 && divs) {
divs = max(divs, duty);
if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
duty = divs - duty;
@@ -77,7 +77,7 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent)
ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
if (ret == 0) {
- divs = pm->pwm_divisor;
+ divs = pm->fan.pwm_divisor;
if (pm->fan.pwm_freq) {
/*XXX: PNVIO clock more than likely... */
divs = 135000 / pm->fan.pwm_freq;
@@ -89,7 +89,10 @@ nouveau_pwmfan_set(struct drm_device *dev, int percent)
if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
duty = divs - duty;
- return pm->pwm_set(dev, gpio.line, divs, duty);
+ ret = pm->pwm_set(dev, gpio.line, divs, duty);
+ if (!ret)
+ pm->fan.percent = percent;
+ return ret;
}
return -ENODEV;
@@ -144,9 +147,13 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
return ret;
state = pm->clocks_pre(dev, perflvl);
- if (IS_ERR(state))
- return PTR_ERR(state);
- pm->clocks_set(dev, state);
+ if (IS_ERR(state)) {
+ ret = PTR_ERR(state);
+ goto error;
+ }
+ ret = pm->clocks_set(dev, state);
+ if (ret)
+ goto error;
ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
if (ret)
@@ -154,6 +161,65 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
pm->cur = perflvl;
return 0;
+
+error:
+ /* restore the fan speed and voltage before leaving */
+ nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
+ return ret;
+}
+
+void
+nouveau_pm_trigger(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_profile *profile = NULL;
+ struct nouveau_pm_level *perflvl = NULL;
+ int ret;
+
+ /* select power profile based on current power source */
+ if (power_supply_is_system_supplied())
+ profile = pm->profile_ac;
+ else
+ profile = pm->profile_dc;
+
+ if (profile != pm->profile) {
+ pm->profile->func->fini(pm->profile);
+ pm->profile = profile;
+ pm->profile->func->init(pm->profile);
+ }
+
+ /* select performance level based on profile */
+ perflvl = profile->func->select(profile);
+
+ /* change perflvl, if necessary */
+ if (perflvl != pm->cur) {
+ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
+ u64 time0 = ptimer->read(dev);
+
+ NV_INFO(dev, "setting performance level: %d", perflvl->id);
+ ret = nouveau_pm_perflvl_set(dev, perflvl);
+ if (ret)
+ NV_INFO(dev, "> reclocking failed: %d\n\n", ret);
+
+ NV_INFO(dev, "> reclocking took %lluns\n\n",
+ ptimer->read(dev) - time0);
+ }
+}
+
+static struct nouveau_pm_profile *
+profile_find(struct drm_device *dev, const char *string)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_profile *profile;
+
+ list_for_each_entry(profile, &pm->profiles, head) {
+ if (!strncmp(profile->name, string, sizeof(profile->name)))
+ return profile;
+ }
+
+ return NULL;
}
static int
@@ -161,33 +227,54 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
- struct nouveau_pm_level *perflvl = NULL;
+ struct nouveau_pm_profile *ac = NULL, *dc = NULL;
+ char string[16], *cur = string, *ptr;
/* safety precaution, for now */
if (nouveau_perflvl_wr != 7777)
return -EPERM;
- if (!strncmp(profile, "boot", 4))
- perflvl = &pm->boot;
- else {
- int pl = simple_strtol(profile, NULL, 10);
- int i;
+ strncpy(string, profile, sizeof(string));
+ if ((ptr = strchr(string, '\n')))
+ *ptr = '\0';
- for (i = 0; i < pm->nr_perflvl; i++) {
- if (pm->perflvl[i].id == pl) {
- perflvl = &pm->perflvl[i];
- break;
- }
- }
+ ptr = strsep(&cur, ",");
+ if (ptr)
+ ac = profile_find(dev, ptr);
- if (!perflvl)
- return -EINVAL;
- }
+ ptr = strsep(&cur, ",");
+ if (ptr)
+ dc = profile_find(dev, ptr);
+ else
+ dc = ac;
+
+ if (ac == NULL || dc == NULL)
+ return -EINVAL;
+
+ pm->profile_ac = ac;
+ pm->profile_dc = dc;
+ nouveau_pm_trigger(dev);
+ return 0;
+}
+
+static void
+nouveau_pm_static_dummy(struct nouveau_pm_profile *profile)
+{
+}
- NV_INFO(dev, "setting performance level: %s\n", profile);
- return nouveau_pm_perflvl_set(dev, perflvl);
+static struct nouveau_pm_level *
+nouveau_pm_static_select(struct nouveau_pm_profile *profile)
+{
+ return container_of(profile, struct nouveau_pm_level, profile);
}
+const struct nouveau_pm_profile_func nouveau_pm_static_profile_func = {
+ .destroy = nouveau_pm_static_dummy,
+ .init = nouveau_pm_static_dummy,
+ .fini = nouveau_pm_static_dummy,
+ .select = nouveau_pm_static_select,
+};
+
static int
nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
@@ -197,9 +284,11 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
memset(perflvl, 0, sizeof(*perflvl));
- ret = pm->clocks_get(dev, perflvl);
- if (ret)
- return ret;
+ if (pm->clocks_get) {
+ ret = pm->clocks_get(dev, perflvl);
+ if (ret)
+ return ret;
+ }
if (pm->voltage.supported && pm->voltage_get) {
ret = pm->voltage_get(dev);
@@ -213,13 +302,14 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
if (ret > 0)
perflvl->fanspeed = ret;
+ nouveau_mem_timing_read(dev, &perflvl->timing);
return 0;
}
static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
{
- char c[16], s[16], v[32], f[16], t[16], m[16];
+ char c[16], s[16], v[32], f[16], m[16];
c[0] = '\0';
if (perflvl->core)
@@ -247,18 +337,15 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->fanspeed)
snprintf(f, sizeof(f), " fanspeed %d%%", perflvl->fanspeed);
- t[0] = '\0';
- if (perflvl->timing)
- snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
-
- snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
+ snprintf(ptr, len, "%s%s%s%s%s\n", c, s, m, v, f);
}
static ssize_t
nouveau_pm_get_perflvl_info(struct device *d,
struct device_attribute *a, char *buf)
{
- struct nouveau_pm_level *perflvl = (struct nouveau_pm_level *)a;
+ struct nouveau_pm_level *perflvl =
+ container_of(a, struct nouveau_pm_level, dev_attr);
char *ptr = buf;
int len = PAGE_SIZE;
@@ -280,12 +367,8 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
int len = PAGE_SIZE, ret;
char *ptr = buf;
- if (!pm->cur)
- snprintf(ptr, len, "setting: boot\n");
- else if (pm->cur == &pm->boot)
- snprintf(ptr, len, "setting: boot\nc:");
- else
- snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
+ snprintf(ptr, len, "profile: %s, %s\nc:",
+ pm->profile_ac->name, pm->profile_dc->name);
ptr += strlen(buf);
len -= strlen(buf);
@@ -397,7 +480,7 @@ nouveau_hwmon_set_max_temp(struct device *d, struct device_attribute *a,
struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
long value;
- if (strict_strtol(buf, 10, &value) == -EINVAL)
+ if (kstrtol(buf, 10, &value) == -EINVAL)
return count;
temp->down_clock = value/1000;
@@ -432,7 +515,7 @@ nouveau_hwmon_set_critical_temp(struct device *d, struct device_attribute *a,
struct nouveau_pm_threshold_temp *temp = &pm->threshold_temp;
long value;
- if (strict_strtol(buf, 10, &value) == -EINVAL)
+ if (kstrtol(buf, 10, &value) == -EINVAL)
return count;
temp->critical = value/1000;
@@ -529,7 +612,7 @@ nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a,
if (nouveau_perflvl_wr != 7777)
return -EPERM;
- if (strict_strtol(buf, 10, &value) == -EINVAL)
+ if (kstrtol(buf, 10, &value) == -EINVAL)
return -EINVAL;
if (value < pm->fan.min_duty)
@@ -568,7 +651,7 @@ nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a,
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
long value;
- if (strict_strtol(buf, 10, &value) == -EINVAL)
+ if (kstrtol(buf, 10, &value) == -EINVAL)
return -EINVAL;
if (value < 0)
@@ -609,7 +692,7 @@ nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a,
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
long value;
- if (strict_strtol(buf, 10, &value) == -EINVAL)
+ if (kstrtol(buf, 10, &value) == -EINVAL)
return -EINVAL;
if (value < 0)
@@ -731,8 +814,10 @@ nouveau_hwmon_fini(struct drm_device *dev)
if (pm->hwmon) {
sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
- sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup);
- sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup);
+ sysfs_remove_group(&dev->pdev->dev.kobj,
+ &hwmon_pwm_fan_attrgroup);
+ sysfs_remove_group(&dev->pdev->dev.kobj,
+ &hwmon_fan_rpm_attrgroup);
hwmon_device_unregister(pm->hwmon);
}
@@ -752,6 +837,7 @@ nouveau_pm_acpi_event(struct notifier_block *nb, unsigned long val, void *data)
bool ac = power_supply_is_system_supplied();
NV_DEBUG(dev, "power supply changed: %s\n", ac ? "AC" : "DC");
+ nouveau_pm_trigger(dev);
}
return NOTIFY_OK;
@@ -766,35 +852,48 @@ nouveau_pm_init(struct drm_device *dev)
char info[256];
int ret, i;
- nouveau_mem_timing_init(dev);
+ /* parse aux tables from vbios */
nouveau_volt_init(dev);
- nouveau_perf_init(dev);
nouveau_temp_init(dev);
+ /* determine current ("boot") performance level */
+ ret = nouveau_pm_perflvl_get(dev, &pm->boot);
+ if (ret) {
+ NV_ERROR(dev, "failed to determine boot perflvl\n");
+ return ret;
+ }
+
+ strncpy(pm->boot.name, "boot", 4);
+ strncpy(pm->boot.profile.name, "boot", 4);
+ pm->boot.profile.func = &nouveau_pm_static_profile_func;
+
+ INIT_LIST_HEAD(&pm->profiles);
+ list_add(&pm->boot.profile.head, &pm->profiles);
+
+ pm->profile_ac = &pm->boot.profile;
+ pm->profile_dc = &pm->boot.profile;
+ pm->profile = &pm->boot.profile;
+ pm->cur = &pm->boot;
+
+ /* add performance levels from vbios */
+ nouveau_perf_init(dev);
+
+ /* display available performance levels */
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
for (i = 0; i < pm->nr_perflvl; i++) {
nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
}
- /* determine current ("boot") performance level */
- ret = nouveau_pm_perflvl_get(dev, &pm->boot);
- if (ret == 0) {
- strncpy(pm->boot.name, "boot", 4);
- pm->cur = &pm->boot;
-
- nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
- NV_INFO(dev, "c:%s", info);
- }
+ nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
+ NV_INFO(dev, "c:%s", info);
/* switch performance levels now if requested */
- if (nouveau_perflvl != NULL) {
- ret = nouveau_pm_profile_set(dev, nouveau_perflvl);
- if (ret) {
- NV_ERROR(dev, "error setting perflvl \"%s\": %d\n",
- nouveau_perflvl, ret);
- }
- }
+ if (nouveau_perflvl != NULL)
+ nouveau_pm_profile_set(dev, nouveau_perflvl);
+
+ /* determine the current fan speed */
+ pm->fan.percent = nouveau_pwmfan_get(dev);
nouveau_sysfs_init(dev);
nouveau_hwmon_init(dev);
@@ -811,6 +910,12 @@ nouveau_pm_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
+ struct nouveau_pm_profile *profile, *tmp;
+
+ list_for_each_entry_safe(profile, tmp, &pm->profiles, head) {
+ list_del(&profile->head);
+ profile->func->destroy(profile);
+ }
if (pm->cur != &pm->boot)
nouveau_pm_perflvl_set(dev, &pm->boot);
@@ -818,7 +923,6 @@ nouveau_pm_fini(struct drm_device *dev)
nouveau_temp_fini(dev);
nouveau_perf_fini(dev);
nouveau_volt_fini(dev);
- nouveau_mem_timing_fini(dev);
#if defined(CONFIG_ACPI) && defined(CONFIG_POWER_SUPPLY)
unregister_acpi_notifier(&pm->acpi_nb);
@@ -840,4 +944,5 @@ nouveau_pm_resume(struct drm_device *dev)
perflvl = pm->cur;
pm->cur = &pm->boot;
nouveau_pm_perflvl_set(dev, perflvl);
+ nouveau_pwmfan_set(dev, pm->fan.percent);
}
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.h b/drivers/gpu/drm/nouveau/nouveau_pm.h
index 2f8e14fbcff8..3f82dfea61dd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.h
@@ -25,10 +25,30 @@
#ifndef __NOUVEAU_PM_H__
#define __NOUVEAU_PM_H__
+struct nouveau_mem_exec_func {
+ struct drm_device *dev;
+ void (*precharge)(struct nouveau_mem_exec_func *);
+ void (*refresh)(struct nouveau_mem_exec_func *);
+ void (*refresh_auto)(struct nouveau_mem_exec_func *, bool);
+ void (*refresh_self)(struct nouveau_mem_exec_func *, bool);
+ void (*wait)(struct nouveau_mem_exec_func *, u32 nsec);
+ u32 (*mrg)(struct nouveau_mem_exec_func *, int mr);
+ void (*mrs)(struct nouveau_mem_exec_func *, int mr, u32 data);
+ void (*clock_set)(struct nouveau_mem_exec_func *);
+ void (*timing_set)(struct nouveau_mem_exec_func *);
+ void *priv;
+};
+
+/* nouveau_mem.c */
+int nouveau_mem_exec(struct nouveau_mem_exec_func *,
+ struct nouveau_pm_level *);
+
/* nouveau_pm.c */
int nouveau_pm_init(struct drm_device *dev);
void nouveau_pm_fini(struct drm_device *dev);
void nouveau_pm_resume(struct drm_device *dev);
+extern const struct nouveau_pm_profile_func nouveau_pm_static_profile_func;
+void nouveau_pm_trigger(struct drm_device *dev);
/* nouveau_volt.c */
void nouveau_volt_init(struct drm_device *);
@@ -41,6 +61,8 @@ int nouveau_voltage_gpio_set(struct drm_device *, int voltage);
/* nouveau_perf.c */
void nouveau_perf_init(struct drm_device *);
void nouveau_perf_fini(struct drm_device *);
+u8 *nouveau_perf_timing(struct drm_device *, u32 freq, u8 *ver, u8 *len);
+u8 *nouveau_perf_ramcfg(struct drm_device *, u32 freq, u8 *ver, u8 *len);
/* nouveau_mem.c */
void nouveau_mem_timing_init(struct drm_device *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index f80c5e0762ff..a4886b36d0fa 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -87,7 +87,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
- engine->vram.init = nouveau_mem_detect;
+ engine->vram.init = nv04_fb_vram_init;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -134,7 +134,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
- engine->vram.init = nouveau_mem_detect;
+ if (dev_priv->chipset == 0x1a ||
+ dev_priv->chipset == 0x1f)
+ engine->vram.init = nv1a_fb_vram_init;
+ else
+ engine->vram.init = nv10_fb_vram_init;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -153,11 +157,11 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
- engine->fb.init = nv10_fb_init;
- engine->fb.takedown = nv10_fb_takedown;
- engine->fb.init_tile_region = nv10_fb_init_tile_region;
- engine->fb.set_tile_region = nv10_fb_set_tile_region;
- engine->fb.free_tile_region = nv10_fb_free_tile_region;
+ engine->fb.init = nv20_fb_init;
+ engine->fb.takedown = nv20_fb_takedown;
+ engine->fb.init_tile_region = nv20_fb_init_tile_region;
+ engine->fb.set_tile_region = nv20_fb_set_tile_region;
+ engine->fb.free_tile_region = nv20_fb_free_tile_region;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nv04_fifo_fini;
@@ -181,7 +185,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
- engine->vram.init = nouveau_mem_detect;
+ engine->vram.init = nv20_fb_vram_init;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -230,7 +234,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clocks_set = nv04_pm_clocks_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
- engine->vram.init = nouveau_mem_detect;
+ engine->vram.init = nv20_fb_vram_init;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -286,7 +290,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.temp_get = nv40_temp_get;
engine->pm.pwm_get = nv40_pm_pwm_get;
engine->pm.pwm_set = nv40_pm_pwm_set;
- engine->vram.init = nouveau_mem_detect;
+ engine->vram.init = nv40_fb_vram_init;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
break;
@@ -475,6 +479,47 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
break;
+ case 0xe0:
+ engine->instmem.init = nvc0_instmem_init;
+ engine->instmem.takedown = nvc0_instmem_takedown;
+ engine->instmem.suspend = nvc0_instmem_suspend;
+ engine->instmem.resume = nvc0_instmem_resume;
+ engine->instmem.get = nv50_instmem_get;
+ engine->instmem.put = nv50_instmem_put;
+ engine->instmem.map = nv50_instmem_map;
+ engine->instmem.unmap = nv50_instmem_unmap;
+ engine->instmem.flush = nv84_instmem_flush;
+ engine->mc.init = nv50_mc_init;
+ engine->mc.takedown = nv50_mc_takedown;
+ engine->timer.init = nv04_timer_init;
+ engine->timer.read = nv04_timer_read;
+ engine->timer.takedown = nv04_timer_takedown;
+ engine->fb.init = nvc0_fb_init;
+ engine->fb.takedown = nvc0_fb_takedown;
+ engine->fifo.channels = 0;
+ engine->fifo.init = nouveau_stub_init;
+ engine->fifo.takedown = nouveau_stub_takedown;
+ engine->fifo.disable = nvc0_fifo_disable;
+ engine->fifo.enable = nvc0_fifo_enable;
+ engine->fifo.reassign = nvc0_fifo_reassign;
+ engine->fifo.unload_context = nouveau_stub_init;
+ engine->display.early_init = nouveau_stub_init;
+ engine->display.late_takedown = nouveau_stub_takedown;
+ engine->display.create = nvd0_display_create;
+ engine->display.destroy = nvd0_display_destroy;
+ engine->display.init = nvd0_display_init;
+ engine->display.fini = nvd0_display_fini;
+ engine->gpio.init = nv50_gpio_init;
+ engine->gpio.fini = nv50_gpio_fini;
+ engine->gpio.drive = nvd0_gpio_drive;
+ engine->gpio.sense = nvd0_gpio_sense;
+ engine->gpio.irq_enable = nv50_gpio_irq_enable;
+ engine->vram.init = nvc0_vram_init;
+ engine->vram.takedown = nv50_vram_fini;
+ engine->vram.get = nvc0_vram_new;
+ engine->vram.put = nv50_vram_del;
+ engine->vram.flags_valid = nvc0_vram_flags_valid;
+ break;
default:
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
return 1;
@@ -548,6 +593,75 @@ static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev)
return can_switch;
}
+static void
+nouveau_card_channel_fini(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ if (dev_priv->channel)
+ nouveau_channel_put_unlocked(&dev_priv->channel);
+}
+
+static int
+nouveau_card_channel_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan;
+ int ret, oclass;
+
+ ret = nouveau_channel_alloc(dev, &chan, NULL, NvDmaFB, NvDmaTT);
+ dev_priv->channel = chan;
+ if (ret)
+ return ret;
+
+ mutex_unlock(&dev_priv->channel->mutex);
+
+ if (dev_priv->card_type <= NV_50) {
+ if (dev_priv->card_type < NV_50)
+ oclass = 0x0039;
+ else
+ oclass = 0x5039;
+
+ ret = nouveau_gpuobj_gr_new(chan, NvM2MF, oclass);
+ if (ret)
+ goto error;
+
+ ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,
+ &chan->m2mf_ntfy);
+ if (ret)
+ goto error;
+
+ ret = RING_SPACE(chan, 6);
+ if (ret)
+ goto error;
+
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
+ OUT_RING (chan, NvM2MF);
+ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 3);
+ OUT_RING (chan, NvNotify0);
+ OUT_RING (chan, chan->vram_handle);
+ OUT_RING (chan, chan->gart_handle);
+ } else
+ if (dev_priv->card_type <= NV_C0) {
+ ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039);
+ if (ret)
+ goto error;
+
+ ret = RING_SPACE(chan, 2);
+ if (ret)
+ goto error;
+
+ BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0000, 1);
+ OUT_RING (chan, 0x00009039);
+ }
+
+ FIRE_RING (chan);
+error:
+ if (ret)
+ nouveau_card_channel_fini(dev);
+ return ret;
+}
+
int
nouveau_card_init(struct drm_device *dev)
{
@@ -588,47 +702,45 @@ nouveau_card_init(struct drm_device *dev)
nv_mask(dev, 0x00088080, 0x00000800, 0x00000000);
}
- nouveau_pm_init(dev);
-
- ret = engine->vram.init(dev);
+ /* PMC */
+ ret = engine->mc.init(dev);
if (ret)
goto out_bios;
- ret = nouveau_gpuobj_init(dev);
+ /* PTIMER */
+ ret = engine->timer.init(dev);
if (ret)
- goto out_vram;
+ goto out_mc;
- ret = engine->instmem.init(dev);
+ /* PFB */
+ ret = engine->fb.init(dev);
if (ret)
- goto out_gpuobj;
+ goto out_timer;
- ret = nouveau_mem_vram_init(dev);
+ ret = engine->vram.init(dev);
if (ret)
- goto out_instmem;
+ goto out_fb;
- ret = nouveau_mem_gart_init(dev);
+ /* PGPIO */
+ ret = nouveau_gpio_create(dev);
if (ret)
- goto out_ttmvram;
+ goto out_vram;
- /* PMC */
- ret = engine->mc.init(dev);
+ ret = nouveau_gpuobj_init(dev);
if (ret)
- goto out_gart;
+ goto out_gpio;
- /* PGPIO */
- ret = nouveau_gpio_create(dev);
+ ret = engine->instmem.init(dev);
if (ret)
- goto out_mc;
+ goto out_gpuobj;
- /* PTIMER */
- ret = engine->timer.init(dev);
+ ret = nouveau_mem_vram_init(dev);
if (ret)
- goto out_gpio;
+ goto out_instmem;
- /* PFB */
- ret = engine->fb.init(dev);
+ ret = nouveau_mem_gart_init(dev);
if (ret)
- goto out_timer;
+ goto out_ttmvram;
if (!dev_priv->noaccel) {
switch (dev_priv->card_type) {
@@ -734,18 +846,16 @@ nouveau_card_init(struct drm_device *dev)
goto out_irq;
nouveau_backlight_init(dev);
+ nouveau_pm_init(dev);
- if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
- ret = nouveau_fence_init(dev);
- if (ret)
- goto out_disp;
+ ret = nouveau_fence_init(dev);
+ if (ret)
+ goto out_pm;
- ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
- NvDmaFB, NvDmaTT);
+ if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
+ ret = nouveau_card_channel_init(dev);
if (ret)
goto out_fence;
-
- mutex_unlock(&dev_priv->channel->mutex);
}
if (dev->mode_config.num_crtc) {
@@ -759,10 +869,11 @@ nouveau_card_init(struct drm_device *dev)
return 0;
out_chan:
- nouveau_channel_put_unlocked(&dev_priv->channel);
+ nouveau_card_channel_fini(dev);
out_fence:
nouveau_fence_fini(dev);
-out_disp:
+out_pm:
+ nouveau_pm_fini(dev);
nouveau_backlight_exit(dev);
nouveau_display_destroy(dev);
out_irq:
@@ -779,15 +890,6 @@ out_engine:
dev_priv->eng[e]->destroy(dev,e );
}
}
-
- engine->fb.takedown(dev);
-out_timer:
- engine->timer.takedown(dev);
-out_gpio:
- nouveau_gpio_destroy(dev);
-out_mc:
- engine->mc.takedown(dev);
-out_gart:
nouveau_mem_gart_fini(dev);
out_ttmvram:
nouveau_mem_vram_fini(dev);
@@ -795,10 +897,17 @@ out_instmem:
engine->instmem.takedown(dev);
out_gpuobj:
nouveau_gpuobj_takedown(dev);
+out_gpio:
+ nouveau_gpio_destroy(dev);
out_vram:
engine->vram.takedown(dev);
+out_fb:
+ engine->fb.takedown(dev);
+out_timer:
+ engine->timer.takedown(dev);
+out_mc:
+ engine->mc.takedown(dev);
out_bios:
- nouveau_pm_fini(dev);
nouveau_bios_takedown(dev);
out_display_early:
engine->display.late_takedown(dev);
@@ -818,11 +927,9 @@ static void nouveau_card_takedown(struct drm_device *dev)
nouveau_display_fini(dev);
}
- if (dev_priv->channel) {
- nouveau_channel_put_unlocked(&dev_priv->channel);
- nouveau_fence_fini(dev);
- }
-
+ nouveau_card_channel_fini(dev);
+ nouveau_fence_fini(dev);
+ nouveau_pm_fini(dev);
nouveau_backlight_exit(dev);
nouveau_display_destroy(dev);
@@ -835,11 +942,6 @@ static void nouveau_card_takedown(struct drm_device *dev)
}
}
}
- engine->fb.takedown(dev);
- engine->timer.takedown(dev);
- nouveau_gpio_destroy(dev);
- engine->mc.takedown(dev);
- engine->display.late_takedown(dev);
if (dev_priv->vga_ram) {
nouveau_bo_unpin(dev_priv->vga_ram);
@@ -855,12 +957,17 @@ static void nouveau_card_takedown(struct drm_device *dev)
engine->instmem.takedown(dev);
nouveau_gpuobj_takedown(dev);
- engine->vram.takedown(dev);
- nouveau_irq_fini(dev);
+ nouveau_gpio_destroy(dev);
+ engine->vram.takedown(dev);
+ engine->fb.takedown(dev);
+ engine->timer.takedown(dev);
+ engine->mc.takedown(dev);
- nouveau_pm_fini(dev);
nouveau_bios_takedown(dev);
+ engine->display.late_takedown(dev);
+
+ nouveau_irq_fini(dev);
vga_client_register(dev->pdev, NULL, NULL, NULL);
}
@@ -990,8 +1097,8 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev)
int nouveau_load(struct drm_device *dev, unsigned long flags)
{
struct drm_nouveau_private *dev_priv;
- uint32_t reg0, strap;
- resource_size_t mmio_start_offs;
+ unsigned long long offset, length;
+ uint32_t reg0 = ~0, strap;
int ret;
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
@@ -1002,83 +1109,90 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = dev_priv;
dev_priv->dev = dev;
+ pci_set_master(dev->pdev);
+
dev_priv->flags = flags & NOUVEAU_FLAGS;
NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
dev->pci_vendor, dev->pci_device, dev->pdev->class);
- /* resource 0 is mmio regs */
- /* resource 1 is linear FB */
- /* resource 2 is RAMIN (mmio regs + 0x1000000) */
- /* resource 6 is bios */
+ /* first up, map the start of mmio and determine the chipset */
+ dev_priv->mmio = ioremap(pci_resource_start(dev->pdev, 0), PAGE_SIZE);
+ if (dev_priv->mmio) {
+#ifdef __BIG_ENDIAN
+ /* put the card into big-endian mode if it's not */
+ if (nv_rd32(dev, NV03_PMC_BOOT_1) != 0x01000001)
+ nv_wr32(dev, NV03_PMC_BOOT_1, 0x01000001);
+ DRM_MEMORYBARRIER();
+#endif
- /* map the mmio regs */
- mmio_start_offs = pci_resource_start(dev->pdev, 0);
- dev_priv->mmio = ioremap(mmio_start_offs, 0x00800000);
- if (!dev_priv->mmio) {
- NV_ERROR(dev, "Unable to initialize the mmio mapping. "
- "Please report your setup to " DRIVER_EMAIL "\n");
+ /* determine chipset and derive architecture from it */
+ reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
+ if ((reg0 & 0x0f000000) > 0) {
+ dev_priv->chipset = (reg0 & 0xff00000) >> 20;
+ switch (dev_priv->chipset & 0xf0) {
+ case 0x10:
+ case 0x20:
+ case 0x30:
+ dev_priv->card_type = dev_priv->chipset & 0xf0;
+ break;
+ case 0x40:
+ case 0x60:
+ dev_priv->card_type = NV_40;
+ break;
+ case 0x50:
+ case 0x80:
+ case 0x90:
+ case 0xa0:
+ dev_priv->card_type = NV_50;
+ break;
+ case 0xc0:
+ dev_priv->card_type = NV_C0;
+ break;
+ case 0xd0:
+ dev_priv->card_type = NV_D0;
+ break;
+ case 0xe0:
+ dev_priv->card_type = NV_E0;
+ break;
+ default:
+ break;
+ }
+ } else
+ if ((reg0 & 0xff00fff0) == 0x20004000) {
+ if (reg0 & 0x00f00000)
+ dev_priv->chipset = 0x05;
+ else
+ dev_priv->chipset = 0x04;
+ dev_priv->card_type = NV_04;
+ }
+
+ iounmap(dev_priv->mmio);
+ }
+
+ if (!dev_priv->card_type) {
+ NV_ERROR(dev, "unsupported chipset 0x%08x\n", reg0);
ret = -EINVAL;
goto err_priv;
}
- NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
- (unsigned long long)mmio_start_offs);
-
-#ifdef __BIG_ENDIAN
- /* Put the card in BE mode if it's not */
- if (nv_rd32(dev, NV03_PMC_BOOT_1) != 0x01000001)
- nv_wr32(dev, NV03_PMC_BOOT_1, 0x01000001);
- DRM_MEMORYBARRIER();
-#endif
+ NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
+ dev_priv->card_type, reg0);
- /* Time to determine the card architecture */
- reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
-
- /* We're dealing with >=NV10 */
- if ((reg0 & 0x0f000000) > 0) {
- /* Bit 27-20 contain the architecture in hex */
- dev_priv->chipset = (reg0 & 0xff00000) >> 20;
- /* NV04 or NV05 */
- } else if ((reg0 & 0xff00fff0) == 0x20004000) {
- if (reg0 & 0x00f00000)
- dev_priv->chipset = 0x05;
- else
- dev_priv->chipset = 0x04;
- } else
- dev_priv->chipset = 0xff;
+ /* map the mmio regs, limiting the amount to preserve vmap space */
+ offset = pci_resource_start(dev->pdev, 0);
+ length = pci_resource_len(dev->pdev, 0);
+ if (dev_priv->card_type < NV_E0)
+ length = min(length, (unsigned long long)0x00800000);
- switch (dev_priv->chipset & 0xf0) {
- case 0x00:
- case 0x10:
- case 0x20:
- case 0x30:
- dev_priv->card_type = dev_priv->chipset & 0xf0;
- break;
- case 0x40:
- case 0x60:
- dev_priv->card_type = NV_40;
- break;
- case 0x50:
- case 0x80:
- case 0x90:
- case 0xa0:
- dev_priv->card_type = NV_50;
- break;
- case 0xc0:
- dev_priv->card_type = NV_C0;
- break;
- case 0xd0:
- dev_priv->card_type = NV_D0;
- break;
- default:
- NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
+ dev_priv->mmio = ioremap(offset, length);
+ if (!dev_priv->mmio) {
+ NV_ERROR(dev, "Unable to initialize the mmio mapping. "
+ "Please report your setup to " DRIVER_EMAIL "\n");
ret = -EINVAL;
- goto err_mmio;
+ goto err_priv;
}
-
- NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
- dev_priv->card_type, reg0);
+ NV_DEBUG(dev, "regs mapped ok at 0x%llx\n", offset);
/* determine frequency of timing crystal */
strap = nv_rd32(dev, 0x101000);
@@ -1136,7 +1250,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
}
} else {
dev_priv->ramin_size = 1 * 1024 * 1024;
- dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN,
+ dev_priv->ramin = ioremap(offset + NV_RAMIN,
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c
index 638cf601c427..d5eedd67afe5 100644
--- a/drivers/gpu/drm/nouveau/nv04_fb.c
+++ b/drivers/gpu/drm/nouveau/nv04_fb.c
@@ -4,6 +4,40 @@
#include "nouveau_drm.h"
int
+nv04_fb_vram_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 boot0 = nv_rd32(dev, NV04_PFB_BOOT_0);
+
+ if (boot0 & 0x00000100) {
+ dev_priv->vram_size = ((boot0 >> 12) & 0xf) * 2 + 2;
+ dev_priv->vram_size *= 1024 * 1024;
+ } else {
+ switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) {
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB:
+ dev_priv->vram_size = 32 * 1024 * 1024;
+ break;
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB:
+ dev_priv->vram_size = 16 * 1024 * 1024;
+ break;
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB:
+ dev_priv->vram_size = 8 * 1024 * 1024;
+ break;
+ case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB:
+ dev_priv->vram_size = 4 * 1024 * 1024;
+ break;
+ }
+ }
+
+ if ((boot0 & 0x00000038) <= 0x10)
+ dev_priv->vram_type = NV_MEM_TYPE_SGRAM;
+ else
+ dev_priv->vram_type = NV_MEM_TYPE_SDRAM;
+
+ return 0;
+}
+
+int
nv04_fb_init(struct drm_device *dev)
{
/* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c
index f78181a59b4a..420b1608536d 100644
--- a/drivers/gpu/drm/nouveau/nv10_fb.c
+++ b/drivers/gpu/drm/nouveau/nv10_fb.c
@@ -3,81 +3,16 @@
#include "nouveau_drv.h"
#include "nouveau_drm.h"
-static struct drm_mm_node *
-nv20_fb_alloc_tag(struct drm_device *dev, uint32_t size)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
- struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
- struct drm_mm_node *mem;
- int ret;
-
- ret = drm_mm_pre_get(&pfb->tag_heap);
- if (ret)
- return NULL;
-
- spin_lock(&dev_priv->tile.lock);
- mem = drm_mm_search_free(&pfb->tag_heap, size, 0, 0);
- if (mem)
- mem = drm_mm_get_block_atomic(mem, size, 0);
- spin_unlock(&dev_priv->tile.lock);
-
- return mem;
-}
-
-static void
-nv20_fb_free_tag(struct drm_device *dev, struct drm_mm_node *mem)
-{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
-
- spin_lock(&dev_priv->tile.lock);
- drm_mm_put_block(mem);
- spin_unlock(&dev_priv->tile.lock);
-}
-
void
nv10_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr,
uint32_t size, uint32_t pitch, uint32_t flags)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
- int bpp = (flags & NOUVEAU_GEM_TILE_32BPP ? 32 : 16);
- tile->addr = addr;
+ tile->addr = 0x80000000 | addr;
tile->limit = max(1u, addr + size) - 1;
tile->pitch = pitch;
-
- if (dev_priv->card_type == NV_20) {
- if (flags & NOUVEAU_GEM_TILE_ZETA) {
- /*
- * Allocate some of the on-die tag memory,
- * used to store Z compression meta-data (most
- * likely just a bitmap determining if a given
- * tile is compressed or not).
- */
- tile->tag_mem = nv20_fb_alloc_tag(dev, size / 256);
-
- if (tile->tag_mem) {
- /* Enable Z compression */
- if (dev_priv->chipset >= 0x25)
- tile->zcomp = tile->tag_mem->start |
- (bpp == 16 ?
- NV25_PFB_ZCOMP_MODE_16 :
- NV25_PFB_ZCOMP_MODE_32);
- else
- tile->zcomp = tile->tag_mem->start |
- NV20_PFB_ZCOMP_EN |
- (bpp == 16 ? 0 :
- NV20_PFB_ZCOMP_MODE_32);
- }
-
- tile->addr |= 3;
- } else {
- tile->addr |= 1;
- }
-
- } else {
- tile->addr |= 1 << 31;
- }
}
void
@@ -86,11 +21,6 @@ nv10_fb_free_tile_region(struct drm_device *dev, int i)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
- if (tile->tag_mem) {
- nv20_fb_free_tag(dev, tile->tag_mem);
- tile->tag_mem = NULL;
- }
-
tile->addr = tile->limit = tile->pitch = tile->zcomp = 0;
}
@@ -103,9 +33,48 @@ nv10_fb_set_tile_region(struct drm_device *dev, int i)
nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit);
nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch);
nv_wr32(dev, NV10_PFB_TILE(i), tile->addr);
+}
+
+int
+nv1a_fb_vram_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct pci_dev *bridge;
+ uint32_t mem, mib;
+
+ bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
+ if (!bridge) {
+ NV_ERROR(dev, "no bridge device\n");
+ return 0;
+ }
+
+ if (dev_priv->chipset == 0x1a) {
+ pci_read_config_dword(bridge, 0x7c, &mem);
+ mib = ((mem >> 6) & 31) + 1;
+ } else {
+ pci_read_config_dword(bridge, 0x84, &mem);
+ mib = ((mem >> 4) & 127) + 1;
+ }
+
+ dev_priv->vram_size = mib * 1024 * 1024;
+ return 0;
+}
+
+int
+nv10_fb_vram_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 fifo_data = nv_rd32(dev, NV04_PFB_FIFO_DATA);
+ u32 cfg0 = nv_rd32(dev, 0x100200);
- if (dev_priv->card_type == NV_20)
- nv_wr32(dev, NV20_PFB_ZCOMP(i), tile->zcomp);
+ dev_priv->vram_size = fifo_data & NV10_PFB_FIFO_DATA_RAM_AMOUNT_MB_MASK;
+
+ if (cfg0 & 0x00000001)
+ dev_priv->vram_type = NV_MEM_TYPE_DDR1;
+ else
+ dev_priv->vram_type = NV_MEM_TYPE_SDRAM;
+
+ return 0;
}
int
@@ -115,14 +84,8 @@ nv10_fb_init(struct drm_device *dev)
struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
int i;
- pfb->num_tiles = NV10_PFB_TILE__SIZE;
-
- if (dev_priv->card_type == NV_20)
- drm_mm_init(&pfb->tag_heap, 0,
- (dev_priv->chipset >= 0x25 ?
- 64 * 1024 : 32 * 1024));
-
/* Turn all the tiling regions off. */
+ pfb->num_tiles = NV10_PFB_TILE__SIZE;
for (i = 0; i < pfb->num_tiles; i++)
pfb->set_tile_region(dev, i);
@@ -138,7 +101,4 @@ nv10_fb_takedown(struct drm_device *dev)
for (i = 0; i < pfb->num_tiles; i++)
pfb->free_tile_region(dev, i);
-
- if (dev_priv->card_type == NV_20)
- drm_mm_takedown(&pfb->tag_heap);
}
diff --git a/drivers/gpu/drm/nouveau/nv20_fb.c b/drivers/gpu/drm/nouveau/nv20_fb.c
new file mode 100644
index 000000000000..19bd64059a66
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv20_fb.c
@@ -0,0 +1,148 @@
+#include "drmP.h"
+#include "drm.h"
+#include "nouveau_drv.h"
+#include "nouveau_drm.h"
+
+static struct drm_mm_node *
+nv20_fb_alloc_tag(struct drm_device *dev, uint32_t size)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+ struct drm_mm_node *mem;
+ int ret;
+
+ ret = drm_mm_pre_get(&pfb->tag_heap);
+ if (ret)
+ return NULL;
+
+ spin_lock(&dev_priv->tile.lock);
+ mem = drm_mm_search_free(&pfb->tag_heap, size, 0, 0);
+ if (mem)
+ mem = drm_mm_get_block_atomic(mem, size, 0);
+ spin_unlock(&dev_priv->tile.lock);
+
+ return mem;
+}
+
+static void
+nv20_fb_free_tag(struct drm_device *dev, struct drm_mm_node **pmem)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct drm_mm_node *mem = *pmem;
+ if (mem) {
+ spin_lock(&dev_priv->tile.lock);
+ drm_mm_put_block(mem);
+ spin_unlock(&dev_priv->tile.lock);
+ *pmem = NULL;
+ }
+}
+
+void
+nv20_fb_init_tile_region(struct drm_device *dev, int i, uint32_t addr,
+ uint32_t size, uint32_t pitch, uint32_t flags)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+ int bpp = (flags & NOUVEAU_GEM_TILE_32BPP ? 32 : 16);
+
+ tile->addr = 0x00000001 | addr;
+ tile->limit = max(1u, addr + size) - 1;
+ tile->pitch = pitch;
+
+ /* Allocate some of the on-die tag memory, used to store Z
+ * compression meta-data (most likely just a bitmap determining
+ * if a given tile is compressed or not).
+ */
+ if (flags & NOUVEAU_GEM_TILE_ZETA) {
+ tile->tag_mem = nv20_fb_alloc_tag(dev, size / 256);
+ if (tile->tag_mem) {
+ /* Enable Z compression */
+ tile->zcomp = tile->tag_mem->start;
+ if (dev_priv->chipset >= 0x25) {
+ if (bpp == 16)
+ tile->zcomp |= NV25_PFB_ZCOMP_MODE_16;
+ else
+ tile->zcomp |= NV25_PFB_ZCOMP_MODE_32;
+ } else {
+ tile->zcomp |= NV20_PFB_ZCOMP_EN;
+ if (bpp != 16)
+ tile->zcomp |= NV20_PFB_ZCOMP_MODE_32;
+ }
+ }
+
+ tile->addr |= 2;
+ }
+}
+
+void
+nv20_fb_free_tile_region(struct drm_device *dev, int i)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+
+ tile->addr = tile->limit = tile->pitch = tile->zcomp = 0;
+ nv20_fb_free_tag(dev, &tile->tag_mem);
+}
+
+void
+nv20_fb_set_tile_region(struct drm_device *dev, int i)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
+
+ nv_wr32(dev, NV10_PFB_TLIMIT(i), tile->limit);
+ nv_wr32(dev, NV10_PFB_TSIZE(i), tile->pitch);
+ nv_wr32(dev, NV10_PFB_TILE(i), tile->addr);
+ nv_wr32(dev, NV20_PFB_ZCOMP(i), tile->zcomp);
+}
+
+int
+nv20_fb_vram_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 mem_size = nv_rd32(dev, 0x10020c);
+ u32 pbus1218 = nv_rd32(dev, 0x001218);
+
+ dev_priv->vram_size = mem_size & 0xff000000;
+ switch (pbus1218 & 0x00000300) {
+ case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_SDRAM; break;
+ case 0x00000100: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000200: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000300: dev_priv->vram_type = NV_MEM_TYPE_GDDR2; break;
+ }
+
+ return 0;
+}
+
+int
+nv20_fb_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+ int i;
+
+ if (dev_priv->chipset >= 0x25)
+ drm_mm_init(&pfb->tag_heap, 0, 64 * 1024);
+ else
+ drm_mm_init(&pfb->tag_heap, 0, 32 * 1024);
+
+ /* Turn all the tiling regions off. */
+ pfb->num_tiles = NV10_PFB_TILE__SIZE;
+ for (i = 0; i < pfb->num_tiles; i++)
+ pfb->set_tile_region(dev, i);
+
+ return 0;
+}
+
+void
+nv20_fb_takedown(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
+ int i;
+
+ for (i = 0; i < pfb->num_tiles; i++)
+ pfb->free_tile_region(dev, i);
+
+ drm_mm_takedown(&pfb->tag_heap);
+}
diff --git a/drivers/gpu/drm/nouveau/nv40_fb.c b/drivers/gpu/drm/nouveau/nv40_fb.c
index f0ac2a768c67..7fbcb334c096 100644
--- a/drivers/gpu/drm/nouveau/nv40_fb.c
+++ b/drivers/gpu/drm/nouveau/nv40_fb.c
@@ -72,6 +72,51 @@ nv44_fb_init_gart(struct drm_device *dev)
}
int
+nv40_fb_vram_init(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+ /* 0x001218 is actually present on a few other NV4X I looked at,
+ * and even contains sane values matching 0x100474. From looking
+ * at various vbios images however, this isn't the case everywhere.
+ * So, I chose to use the same regs I've seen NVIDIA reading around
+ * the memory detection, hopefully that'll get us the right numbers
+ */
+ if (dev_priv->chipset == 0x40) {
+ u32 pbus1218 = nv_rd32(dev, 0x001218);
+ switch (pbus1218 & 0x00000300) {
+ case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_SDRAM; break;
+ case 0x00000100: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000200: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000300: dev_priv->vram_type = NV_MEM_TYPE_DDR2; break;
+ }
+ } else
+ if (dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) {
+ u32 pfb914 = nv_rd32(dev, 0x100914);
+ switch (pfb914 & 0x00000003) {
+ case 0x00000000: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
+ case 0x00000001: dev_priv->vram_type = NV_MEM_TYPE_DDR2; break;
+ case 0x00000002: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
+ case 0x00000003: break;
+ }
+ } else
+ if (dev_priv->chipset != 0x4e) {
+ u32 pfb474 = nv_rd32(dev, 0x100474);
+ if (pfb474 & 0x00000004)
+ dev_priv->vram_type = NV_MEM_TYPE_GDDR3;
+ if (pfb474 & 0x00000002)
+ dev_priv->vram_type = NV_MEM_TYPE_DDR2;
+ if (pfb474 & 0x00000001)
+ dev_priv->vram_type = NV_MEM_TYPE_DDR1;
+ } else {
+ dev_priv->vram_type = NV_MEM_TYPE_STOLEN;
+ }
+
+ dev_priv->vram_size = nv_rd32(dev, 0x10020c) & 0xff000000;
+ return 0;
+}
+
+int
nv40_fb_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 8f6c2ace3adf..701b927998bf 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -170,6 +170,41 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
return ret;
}
+static int
+nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update)
+{
+ struct drm_device *dev = nv_crtc->base.dev;
+ struct nouveau_channel *evo = nv50_display(dev)->master;
+ int ret;
+ int adj;
+ u32 hue, vib;
+
+ NV_DEBUG_KMS(dev, "vibrance = %i, hue = %i\n",
+ nv_crtc->color_vibrance, nv_crtc->vibrant_hue);
+
+ ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
+ if (ret) {
+ NV_ERROR(dev, "no space while setting color vibrance\n");
+ return ret;
+ }
+
+ adj = (nv_crtc->color_vibrance > 0) ? 50 : 0;
+ vib = ((nv_crtc->color_vibrance * 2047 + adj) / 100) & 0xfff;
+
+ hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff;
+
+ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
+ OUT_RING (evo, (hue << 20) | (vib << 8));
+
+ if (update) {
+ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
+ OUT_RING (evo, 0);
+ FIRE_RING (evo);
+ }
+
+ return 0;
+}
+
struct nouveau_connector *
nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
{
@@ -577,8 +612,6 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
OUT_RING (evo, fb->base.depth == 8 ?
NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON);
- BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
- OUT_RING (evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1);
OUT_RING (evo, (y << 16) | x);
@@ -661,6 +694,7 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
nv_crtc->set_dither(nv_crtc, false);
nv_crtc->set_scale(nv_crtc, false);
+ nv_crtc->set_color_vibrance(nv_crtc, false);
return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
}
@@ -721,6 +755,9 @@ nv50_crtc_create(struct drm_device *dev, int index)
if (!nv_crtc)
return -ENOMEM;
+ nv_crtc->color_vibrance = 50;
+ nv_crtc->vibrant_hue = 0;
+
/* Default CLUT parameters, will be activated on the hw upon
* first mode set.
*/
@@ -751,6 +788,7 @@ nv50_crtc_create(struct drm_device *dev, int index)
/* set function pointers */
nv_crtc->set_dither = nv50_crtc_set_dither;
nv_crtc->set_scale = nv50_crtc_set_scale;
+ nv_crtc->set_color_vibrance = nv50_crtc_set_color_vibrance;
drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs);
drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
index a0f2bebf49e3..55c56330be6d 100644
--- a/drivers/gpu/drm/nouveau/nv50_dac.c
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -190,11 +190,8 @@ nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
- connector->native_mode) {
- int id = adjusted_mode->base.id;
- *adjusted_mode = *connector->native_mode;
- adjusted_mode->base.id = id;
- }
+ connector->native_mode)
+ drm_mode_copy(adjusted_mode, connector->native_mode);
return true;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 7ba28e08ee31..8b78b9cfa383 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -50,6 +50,29 @@ nv50_sor_nr(struct drm_device *dev)
return 4;
}
+u32
+nv50_display_active_crtcs(struct drm_device *dev)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 mask = 0;
+ int i;
+
+ if (dev_priv->chipset < 0x90 ||
+ dev_priv->chipset == 0x92 ||
+ dev_priv->chipset == 0xa0) {
+ for (i = 0; i < 2; i++)
+ mask |= nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_C(i));
+ } else {
+ for (i = 0; i < 4; i++)
+ mask |= nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_C(i));
+ }
+
+ for (i = 0; i < 3; i++)
+ mask |= nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_C(i));
+
+ return mask & 3;
+}
+
static int
evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data)
{
@@ -451,15 +474,15 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
}
if (dev_priv->chipset < 0xc0) {
- BEGIN_RING(chan, NvSubSw, 0x0060, 2);
+ BEGIN_RING(chan, 0, 0x0060, 2);
OUT_RING (chan, NvEvoSema0 + nv_crtc->index);
OUT_RING (chan, dispc->sem.offset);
- BEGIN_RING(chan, NvSubSw, 0x006c, 1);
+ BEGIN_RING(chan, 0, 0x006c, 1);
OUT_RING (chan, 0xf00d0000 | dispc->sem.value);
- BEGIN_RING(chan, NvSubSw, 0x0064, 2);
+ BEGIN_RING(chan, 0, 0x0064, 2);
OUT_RING (chan, dispc->sem.offset ^ 0x10);
OUT_RING (chan, 0x74b1e000);
- BEGIN_RING(chan, NvSubSw, 0x0060, 1);
+ BEGIN_RING(chan, 0, 0x0060, 1);
if (dev_priv->chipset < 0x84)
OUT_RING (chan, NvSema);
else
@@ -467,12 +490,12 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
} else {
u64 offset = chan->dispc_vma[nv_crtc->index].offset;
offset += dispc->sem.offset;
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+ BEGIN_NVC0(chan, 2, 0, 0x0010, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
OUT_RING (chan, 0xf00d0000 | dispc->sem.value);
OUT_RING (chan, 0x1002);
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+ BEGIN_NVC0(chan, 2, 0, 0x0010, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset ^ 0x10));
OUT_RING (chan, 0x74b1e000);
@@ -840,9 +863,9 @@ nv50_display_unk20_handler(struct drm_device *dev)
if (type == OUTPUT_DP) {
int link = !(dcb->dpconf.sor.link & 1);
if ((mc & 0x000f0000) == 0x00020000)
- nouveau_dp_tu_update(dev, or, link, pclk, 18);
+ nv50_sor_dp_calc_tu(dev, or, link, pclk, 18);
else
- nouveau_dp_tu_update(dev, or, link, pclk, 24);
+ nv50_sor_dp_calc_tu(dev, or, link, pclk, 24);
}
if (dcb->type != OUTPUT_ANALOG) {
diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h
index 95874f7c043c..5d3dd14d2837 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.h
+++ b/drivers/gpu/drm/nouveau/nv50_display.h
@@ -74,6 +74,8 @@ void nv50_display_destroy(struct drm_device *dev);
int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
+u32 nv50_display_active_crtcs(struct drm_device *);
+
int nv50_display_sync(struct drm_device *);
int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
struct nouveau_channel *chan);
diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h
index 3860ca62cb19..771d879bc834 100644
--- a/drivers/gpu/drm/nouveau/nv50_evo.h
+++ b/drivers/gpu/drm/nouveau/nv50_evo.h
@@ -104,7 +104,8 @@
#define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000
#define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009
#define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8
-#define NV50_EVO_CRTC_COLOR_CTRL_COLOR 0x00040000
+#define NV50_EVO_CRTC_COLOR_CTRL_VIBRANCE 0x000fff00
+#define NV50_EVO_CRTC_COLOR_CTRL_HUE 0xfff00000
#define NV50_EVO_CRTC_FB_POS 0x000008c0
#define NV50_EVO_CRTC_REAL_RES 0x000008c8
#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4
diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c
index ec5481dfcd82..d020ed4979b4 100644
--- a/drivers/gpu/drm/nouveau/nv50_pm.c
+++ b/drivers/gpu/drm/nouveau/nv50_pm.c
@@ -28,6 +28,7 @@
#include "nouveau_hw.h"
#include "nouveau_pm.h"
#include "nouveau_hwsq.h"
+#include "nv50_display.h"
enum clk_src {
clk_src_crystal,
@@ -352,17 +353,13 @@ nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
}
struct nv50_pm_state {
+ struct nouveau_pm_level *perflvl;
+ struct hwsq_ucode eclk_hwsq;
struct hwsq_ucode mclk_hwsq;
u32 mscript;
-
- u32 emast;
- u32 nctrl;
- u32 ncoef;
- u32 sctrl;
- u32 scoef;
-
- u32 amast;
- u32 pdivs;
+ u32 mmast;
+ u32 mctrl;
+ u32 mcoef;
};
static u32
@@ -415,40 +412,153 @@ clk_same(u32 a, u32 b)
return ((a / 1000) == (b / 1000));
}
+static void
+mclk_precharge(struct nouveau_mem_exec_func *exec)
+{
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+
+ hwsq_wr32(hwsq, 0x1002d4, 0x00000001);
+}
+
+static void
+mclk_refresh(struct nouveau_mem_exec_func *exec)
+{
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+
+ hwsq_wr32(hwsq, 0x1002d0, 0x00000001);
+}
+
+static void
+mclk_refresh_auto(struct nouveau_mem_exec_func *exec, bool enable)
+{
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+
+ hwsq_wr32(hwsq, 0x100210, enable ? 0x80000000 : 0x00000000);
+}
+
+static void
+mclk_refresh_self(struct nouveau_mem_exec_func *exec, bool enable)
+{
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+
+ hwsq_wr32(hwsq, 0x1002dc, enable ? 0x00000001 : 0x00000000);
+}
+
+static void
+mclk_wait(struct nouveau_mem_exec_func *exec, u32 nsec)
+{
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+
+ if (nsec > 1000)
+ hwsq_usec(hwsq, (nsec + 500) / 1000);
+}
+
+static u32
+mclk_mrg(struct nouveau_mem_exec_func *exec, int mr)
+{
+ if (mr <= 1)
+ return nv_rd32(exec->dev, 0x1002c0 + ((mr - 0) * 4));
+ if (mr <= 3)
+ return nv_rd32(exec->dev, 0x1002e0 + ((mr - 2) * 4));
+ return 0;
+}
+
+static void
+mclk_mrs(struct nouveau_mem_exec_func *exec, int mr, u32 data)
+{
+ struct drm_nouveau_private *dev_priv = exec->dev->dev_private;
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+
+ if (mr <= 1) {
+ if (dev_priv->vram_rank_B)
+ hwsq_wr32(hwsq, 0x1002c8 + ((mr - 0) * 4), data);
+ hwsq_wr32(hwsq, 0x1002c0 + ((mr - 0) * 4), data);
+ } else
+ if (mr <= 3) {
+ if (dev_priv->vram_rank_B)
+ hwsq_wr32(hwsq, 0x1002e8 + ((mr - 2) * 4), data);
+ hwsq_wr32(hwsq, 0x1002e0 + ((mr - 2) * 4), data);
+ }
+}
+
+static void
+mclk_clock_set(struct nouveau_mem_exec_func *exec)
+{
+ struct nv50_pm_state *info = exec->priv;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+ u32 ctrl = nv_rd32(exec->dev, 0x004008);
+
+ info->mmast = nv_rd32(exec->dev, 0x00c040);
+ info->mmast &= ~0xc0000000; /* get MCLK_2 from HREF */
+ info->mmast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */
+
+ hwsq_wr32(hwsq, 0xc040, info->mmast);
+ hwsq_wr32(hwsq, 0x4008, ctrl | 0x00000200); /* bypass MPLL */
+ if (info->mctrl & 0x80000000)
+ hwsq_wr32(hwsq, 0x400c, info->mcoef);
+ hwsq_wr32(hwsq, 0x4008, info->mctrl);
+}
+
+static void
+mclk_timing_set(struct nouveau_mem_exec_func *exec)
+{
+ struct drm_device *dev = exec->dev;
+ struct nv50_pm_state *info = exec->priv;
+ struct nouveau_pm_level *perflvl = info->perflvl;
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
+ int i;
+
+ for (i = 0; i < 9; i++) {
+ u32 reg = 0x100220 + (i * 4);
+ u32 val = nv_rd32(dev, reg);
+ if (val != perflvl->timing.reg[i])
+ hwsq_wr32(hwsq, reg, perflvl->timing.reg[i]);
+ }
+}
+
static int
-calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq)
+calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl,
+ struct nv50_pm_state *info)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
+ u32 crtc_mask = nv50_display_active_crtcs(dev);
+ struct nouveau_mem_exec_func exec = {
+ .dev = dev,
+ .precharge = mclk_precharge,
+ .refresh = mclk_refresh,
+ .refresh_auto = mclk_refresh_auto,
+ .refresh_self = mclk_refresh_self,
+ .wait = mclk_wait,
+ .mrg = mclk_mrg,
+ .mrs = mclk_mrs,
+ .clock_set = mclk_clock_set,
+ .timing_set = mclk_timing_set,
+ .priv = info
+ };
+ struct hwsq_ucode *hwsq = &info->mclk_hwsq;
struct pll_lims pll;
- u32 mast = nv_rd32(dev, 0x00c040);
- u32 ctrl = nv_rd32(dev, 0x004008);
- u32 coef = nv_rd32(dev, 0x00400c);
- u32 orig = ctrl;
- u32 crtc_mask = 0;
int N, M, P;
- int ret, i;
+ int ret;
/* use pcie refclock if possible, otherwise use mpll */
- ctrl &= ~0x81ff0200;
- if (clk_same(freq, read_clk(dev, clk_src_href))) {
- ctrl |= 0x00000200 | (pll.log2p_bias << 19);
+ info->mctrl = nv_rd32(dev, 0x004008);
+ info->mctrl &= ~0x81ff0200;
+ if (clk_same(perflvl->memory, read_clk(dev, clk_src_href))) {
+ info->mctrl |= 0x00000200 | (pll.log2p_bias << 19);
} else {
- ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P);
+ ret = calc_pll(dev, 0x4008, &pll, perflvl->memory, &N, &M, &P);
if (ret == 0)
return -EINVAL;
- ctrl |= 0x80000000 | (P << 22) | (P << 16);
- ctrl |= pll.log2p_bias << 19;
- coef = (N << 8) | M;
- }
-
- mast &= ~0xc0000000; /* get MCLK_2 from HREF */
- mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */
-
- /* determine active crtcs */
- for (i = 0; i < 2; i++) {
- if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
- crtc_mask |= (1 << i);
+ info->mctrl |= 0x80000000 | (P << 22) | (P << 16);
+ info->mctrl |= pll.log2p_bias << 19;
+ info->mcoef = (N << 8) | M;
}
/* build the ucode which will reclock the memory for us */
@@ -462,25 +572,10 @@ calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq)
hwsq_setf(hwsq, 0x10, 0); /* disable bus access */
hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */
- /* prepare memory controller */
- hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */
- hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */
- hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */
- hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */
-
- /* reclock memory */
- hwsq_wr32(hwsq, 0xc040, mast);
- hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */
- hwsq_wr32(hwsq, 0x400c, coef);
- hwsq_wr32(hwsq, 0x4008, ctrl);
-
- /* restart memory controller */
- hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */
- hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */
- hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */
- hwsq_usec(hwsq, 12); /* wait for the PLL to stabilize */
-
- hwsq_usec(hwsq, 48); /* may be unnecessary: causes flickering */
+ ret = nouveau_mem_exec(&exec, perflvl);
+ if (ret)
+ return ret;
+
hwsq_setf(hwsq, 0x10, 1); /* enable bus access */
hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */
if (dev_priv->chipset >= 0x92)
@@ -494,10 +589,11 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_pm_state *info;
+ struct hwsq_ucode *hwsq;
struct pll_lims pll;
+ u32 out, mast, divs, ctrl;
int clk, ret = -EINVAL;
int N, M, P1, P2;
- u32 out;
if (dev_priv->chipset == 0xaa ||
dev_priv->chipset == 0xac)
@@ -506,54 +602,44 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
+ info->perflvl = perflvl;
- /* core: for the moment at least, always use nvpll */
- clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1);
- if (clk == 0)
- goto error;
+ /* memory: build hwsq ucode which we'll use to reclock memory.
+ * use pcie refclock if possible, otherwise use mpll */
+ info->mclk_hwsq.len = 0;
+ if (perflvl->memory) {
+ ret = calc_mclk(dev, perflvl, info);
+ if (ret)
+ goto error;
+ info->mscript = perflvl->memscript;
+ }
- info->emast = 0x00000003;
- info->nctrl = 0x80000000 | (P1 << 19) | (P1 << 16);
- info->ncoef = (N << 8) | M;
+ divs = read_div(dev);
+ mast = info->mmast;
- /* shader: tie to nvclk if possible, otherwise use spll. have to be
- * very careful that the shader clock is at least twice the core, or
- * some chipsets will be very unhappy. i expect most or all of these
- * cases will be handled by tying to nvclk, but it's possible there's
- * corners
- */
- if (P1-- && perflvl->shader == (perflvl->core << 1)) {
- info->emast |= 0x00000020;
- info->sctrl = 0x00000000 | (P1 << 19) | (P1 << 16);
- info->scoef = nv_rd32(dev, 0x004024);
- } else {
- clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1);
- if (clk == 0)
- goto error;
+ /* start building HWSQ script for engine reclocking */
+ hwsq = &info->eclk_hwsq;
+ hwsq_init(hwsq);
+ hwsq_setf(hwsq, 0x10, 0); /* disable bus access */
+ hwsq_op5f(hwsq, 0x00, 0x01); /* wait for access disabled? */
- info->emast |= 0x00000030;
- info->sctrl = 0x80000000 | (P1 << 19) | (P1 << 16);
- info->scoef = (N << 8) | M;
+ /* vdec/dom6: switch to "safe" clocks temporarily */
+ if (perflvl->vdec) {
+ mast &= ~0x00000c00;
+ divs &= ~0x00000700;
}
- /* memory: build hwsq ucode which we'll use to reclock memory */
- info->mclk_hwsq.len = 0;
- if (perflvl->memory) {
- clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq);
- if (clk < 0) {
- ret = clk;
- goto error;
- }
-
- info->mscript = perflvl->memscript;
+ if (perflvl->dom6) {
+ mast &= ~0x0c000000;
+ divs &= ~0x00000007;
}
+ hwsq_wr32(hwsq, 0x00c040, mast);
+
/* vdec: avoid modifying xpll until we know exactly how the other
* clock domains work, i suspect at least some of them can also be
* tied to xpll...
*/
- info->amast = nv_rd32(dev, 0x00c040);
- info->pdivs = read_div(dev);
if (perflvl->vdec) {
/* see how close we can get using nvclk as a source */
clk = calc_div(perflvl->core, perflvl->vdec, &P1);
@@ -566,16 +652,14 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
out = calc_div(out, perflvl->vdec, &P2);
/* select whichever gets us closest */
- info->amast &= ~0x00000c00;
- info->pdivs &= ~0x00000700;
if (abs((int)perflvl->vdec - clk) <=
abs((int)perflvl->vdec - out)) {
if (dev_priv->chipset != 0x98)
- info->amast |= 0x00000c00;
- info->pdivs |= P1 << 8;
+ mast |= 0x00000c00;
+ divs |= P1 << 8;
} else {
- info->amast |= 0x00000800;
- info->pdivs |= P2 << 8;
+ mast |= 0x00000800;
+ divs |= P2 << 8;
}
}
@@ -583,21 +667,82 @@ nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
* of the host clock frequency
*/
if (perflvl->dom6) {
- info->amast &= ~0x0c000000;
if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) {
- info->amast |= 0x00000000;
+ mast |= 0x00000000;
} else
if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) {
- info->amast |= 0x08000000;
+ mast |= 0x08000000;
} else {
clk = read_clk(dev, clk_src_hclk) * 3;
clk = calc_div(clk, perflvl->dom6, &P1);
- info->amast |= 0x0c000000;
- info->pdivs = (info->pdivs & ~0x00000007) | P1;
+ mast |= 0x0c000000;
+ divs |= P1;
}
}
+ /* vdec/dom6: complete switch to new clocks */
+ switch (dev_priv->chipset) {
+ case 0x92:
+ case 0x94:
+ case 0x96:
+ hwsq_wr32(hwsq, 0x004800, divs);
+ break;
+ default:
+ hwsq_wr32(hwsq, 0x004700, divs);
+ break;
+ }
+
+ hwsq_wr32(hwsq, 0x00c040, mast);
+
+ /* core/shader: make sure sclk/nvclk are disconnected from their
+ * PLLs (nvclk to dom6, sclk to hclk)
+ */
+ if (dev_priv->chipset < 0x92)
+ mast = (mast & ~0x001000b0) | 0x00100080;
+ else
+ mast = (mast & ~0x000000b3) | 0x00000081;
+
+ hwsq_wr32(hwsq, 0x00c040, mast);
+
+ /* core: for the moment at least, always use nvpll */
+ clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1);
+ if (clk == 0)
+ goto error;
+
+ ctrl = nv_rd32(dev, 0x004028) & ~0xc03f0100;
+ mast &= ~0x00100000;
+ mast |= 3;
+
+ hwsq_wr32(hwsq, 0x004028, 0x80000000 | (P1 << 19) | (P1 << 16) | ctrl);
+ hwsq_wr32(hwsq, 0x00402c, (N << 8) | M);
+
+ /* shader: tie to nvclk if possible, otherwise use spll. have to be
+ * very careful that the shader clock is at least twice the core, or
+ * some chipsets will be very unhappy. i expect most or all of these
+ * cases will be handled by tying to nvclk, but it's possible there's
+ * corners
+ */
+ ctrl = nv_rd32(dev, 0x004020) & ~0xc03f0100;
+
+ if (P1-- && perflvl->shader == (perflvl->core << 1)) {
+ hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl);
+ hwsq_wr32(hwsq, 0x00c040, 0x00000020 | mast);
+ } else {
+ clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1);
+ if (clk == 0)
+ goto error;
+ ctrl |= 0x80000000;
+
+ hwsq_wr32(hwsq, 0x004020, (P1 << 19) | (P1 << 16) | ctrl);
+ hwsq_wr32(hwsq, 0x004024, (N << 8) | M);
+ hwsq_wr32(hwsq, 0x00c040, 0x00000030 | mast);
+ }
+
+ hwsq_setf(hwsq, 0x10, 1); /* enable bus access */
+ hwsq_op5f(hwsq, 0x00, 0x00); /* wait for access enabled? */
+ hwsq_fini(hwsq);
+
return info;
error:
kfree(info);
@@ -605,23 +750,24 @@ error:
}
static int
-prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq)
+prog_hwsq(struct drm_device *dev, struct hwsq_ucode *hwsq)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 hwsq_data, hwsq_kick;
int i;
- if (dev_priv->chipset < 0x90) {
+ if (dev_priv->chipset < 0x94) {
hwsq_data = 0x001400;
hwsq_kick = 0x00000003;
} else {
hwsq_data = 0x080000;
hwsq_kick = 0x00000001;
}
-
/* upload hwsq ucode */
nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
nv_wr32(dev, 0x001304, 0x00000000);
+ if (dev_priv->chipset >= 0x92)
+ nv_wr32(dev, 0x001318, 0x00000000);
for (i = 0; i < hwsq->len / 4; i++)
nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]);
nv_mask(dev, 0x001098, 0x00000018, 0x00000018);
@@ -645,20 +791,19 @@ prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq)
int
nv50_pm_clocks_set(struct drm_device *dev, void *data)
{
- struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_pm_state *info = data;
struct bit_entry M;
- int ret = 0;
+ int ret = -EBUSY;
/* halt and idle execution engines */
nv_mask(dev, 0x002504, 0x00000001, 0x00000001);
if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010))
- goto error;
+ goto resume;
+ if (!nv_wait(dev, 0x00251c, 0x0000003f, 0x0000003f))
+ goto resume;
- /* memory: it is *very* important we change this first, the ucode
- * we build in pre() now has hardcoded 0xc040 values, which can't
- * change before we execute it or the engine clocks may end up
- * messed up.
+ /* program memory clock, if necessary - must come before engine clock
+ * reprogramming due to how we construct the hwsq scripts in pre()
*/
if (info->mclk_hwsq.len) {
/* execute some scripts that do ??? from the vbios.. */
@@ -672,42 +817,14 @@ nv50_pm_clocks_set(struct drm_device *dev, void *data)
nouveau_bios_init_exec(dev, info->mscript);
}
- ret = prog_mclk(dev, &info->mclk_hwsq);
+ ret = prog_hwsq(dev, &info->mclk_hwsq);
if (ret)
goto resume;
}
- /* reclock vdec/dom6 */
- nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000);
- switch (dev_priv->chipset) {
- case 0x92:
- case 0x94:
- case 0x96:
- nv_mask(dev, 0x004800, 0x00000707, info->pdivs);
- break;
- default:
- nv_mask(dev, 0x004700, 0x00000707, info->pdivs);
- break;
- }
- nv_mask(dev, 0x00c040, 0x0c000c00, info->amast);
+ /* program engine clocks */
+ ret = prog_hwsq(dev, &info->eclk_hwsq);
- /* core/shader: make sure sclk/nvclk are disconnected from their
- * plls (nvclk to dom6, sclk to hclk), modify the plls, and
- * reconnect sclk/nvclk to their new clock source
- */
- if (dev_priv->chipset < 0x92)
- nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */
- else
- nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081);
- nv_mask(dev, 0x004020, 0xc03f0100, info->sctrl);
- nv_wr32(dev, 0x004024, info->scoef);
- nv_mask(dev, 0x004028, 0xc03f0100, info->nctrl);
- nv_wr32(dev, 0x00402c, info->ncoef);
- nv_mask(dev, 0x00c040, 0x00100033, info->emast);
-
- goto resume;
-error:
- ret = -EBUSY;
resume:
nv_mask(dev, 0x002504, 0x00000001, 0x00000000);
kfree(info);
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index c4423ba9c9bf..a7844ab6a50c 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -36,6 +36,193 @@
#include "nouveau_crtc.h"
#include "nv50_display.h"
+static u32
+nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */
+ static const u8 nv50[] = { 16, 8, 0, 24 };
+ if (dev_priv->card_type == 0xaf)
+ return nvaf[lane];
+ return nv50[lane];
+}
+
+static void
+nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern)
+{
+ u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24);
+}
+
+static void
+nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb,
+ u8 lane, u8 swing, u8 preem)
+{
+ u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane);
+ u32 mask = 0x000000ff << shift;
+ u8 *table, *entry, *config;
+
+ table = nouveau_dp_bios_data(dev, dcb, &entry);
+ if (!table || (table[0] != 0x20 && table[0] != 0x21)) {
+ NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
+ return;
+ }
+
+ config = entry + table[4];
+ while (config[0] != swing || config[1] != preem) {
+ config += table[5];
+ if (config >= entry + table[4] + entry[4] * table[5])
+ return;
+ }
+
+ nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift);
+ nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift);
+ nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8);
+}
+
+static void
+nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc,
+ int link_nr, u32 link_bw, bool enhframe)
+{
+ u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000;
+ u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800)) & ~0x000c0000;
+ u8 *table, *entry, mask;
+ int i;
+
+ table = nouveau_dp_bios_data(dev, dcb, &entry);
+ if (!table || (table[0] != 0x20 && table[0] != 0x21)) {
+ NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
+ return;
+ }
+
+ entry = ROMPTR(dev, entry[10]);
+ if (entry) {
+ while (link_bw < ROM16(entry[0]) * 10)
+ entry += 4;
+
+ nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc);
+ }
+
+ dpctrl |= ((1 << link_nr) - 1) << 16;
+ if (enhframe)
+ dpctrl |= 0x00004000;
+
+ if (link_bw > 162000)
+ clksor |= 0x00040000;
+
+ nv_wr32(dev, 0x614300 + (or * 0x800), clksor);
+ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), dpctrl);
+
+ mask = 0;
+ for (i = 0; i < link_nr; i++)
+ mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3);
+ nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask);
+}
+
+static void
+nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw)
+{
+ u32 dpctrl = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000;
+ u32 clksor = nv_rd32(dev, 0x614300 + (or * 0x800));
+ if (clksor & 0x000c0000)
+ *bw = 270000;
+ else
+ *bw = 162000;
+
+ if (dpctrl > 0x00030000) *nr = 4;
+ else if (dpctrl > 0x00010000) *nr = 2;
+ else *nr = 1;
+}
+
+void
+nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
+{
+ const u32 symbol = 100000;
+ int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0;
+ int TU, VTUi, VTUf, VTUa;
+ u64 link_data_rate, link_ratio, unk;
+ u32 best_diff = 64 * symbol;
+ u32 link_nr, link_bw, r;
+
+ /* calculate packed data rate for each lane */
+ nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw);
+ link_data_rate = (clk * bpp / 8) / link_nr;
+
+ /* calculate ratio of packed data rate to link symbol rate */
+ link_ratio = link_data_rate * symbol;
+ r = do_div(link_ratio, link_bw);
+
+ for (TU = 64; TU >= 32; TU--) {
+ /* calculate average number of valid symbols in each TU */
+ u32 tu_valid = link_ratio * TU;
+ u32 calc, diff;
+
+ /* find a hw representation for the fraction.. */
+ VTUi = tu_valid / symbol;
+ calc = VTUi * symbol;
+ diff = tu_valid - calc;
+ if (diff) {
+ if (diff >= (symbol / 2)) {
+ VTUf = symbol / (symbol - diff);
+ if (symbol - (VTUf * diff))
+ VTUf++;
+
+ if (VTUf <= 15) {
+ VTUa = 1;
+ calc += symbol - (symbol / VTUf);
+ } else {
+ VTUa = 0;
+ VTUf = 1;
+ calc += symbol;
+ }
+ } else {
+ VTUa = 0;
+ VTUf = min((int)(symbol / diff), 15);
+ calc += symbol / VTUf;
+ }
+
+ diff = calc - tu_valid;
+ } else {
+ /* no remainder, but the hw doesn't like the fractional
+ * part to be zero. decrement the integer part and
+ * have the fraction add a whole symbol back
+ */
+ VTUa = 0;
+ VTUf = 1;
+ VTUi--;
+ }
+
+ if (diff < best_diff) {
+ best_diff = diff;
+ bestTU = TU;
+ bestVTUa = VTUa;
+ bestVTUf = VTUf;
+ bestVTUi = VTUi;
+ if (diff == 0)
+ break;
+ }
+ }
+
+ if (!bestTU) {
+ NV_ERROR(dev, "DP: unable to find suitable config\n");
+ return;
+ }
+
+ /* XXX close to vbios numbers, but not right */
+ unk = (symbol - link_ratio) * bestTU;
+ unk *= link_ratio;
+ r = do_div(unk, symbol);
+ r = do_div(unk, symbol);
+ unk += 6;
+
+ nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2);
+ nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 |
+ bestVTUf << 16 |
+ bestVTUi << 8 |
+ unk);
+}
static void
nv50_sor_disconnect(struct drm_encoder *encoder)
{
@@ -117,20 +304,13 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
}
if (nv_encoder->dcb->type == OUTPUT_DP) {
- struct nouveau_i2c_chan *auxch;
-
- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
- if (!auxch)
- return;
+ struct dp_train_func func = {
+ .link_set = nv50_sor_dp_link_set,
+ .train_set = nv50_sor_dp_train_set,
+ .train_adj = nv50_sor_dp_train_adj
+ };
- if (mode == DRM_MODE_DPMS_ON) {
- u8 status = DP_SET_POWER_D0;
- nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
- nouveau_dp_link_train(encoder, nv_encoder->dp.datarate);
- } else {
- u8 status = DP_SET_POWER_D3;
- nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
- }
+ nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func);
}
}
@@ -162,11 +342,8 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
- connector->native_mode) {
- int id = adjusted_mode->base.id;
- *adjusted_mode = *connector->native_mode;
- adjusted_mode->base.id = id;
- }
+ connector->native_mode)
+ drm_mode_copy(adjusted_mode, connector->native_mode);
return true;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_vm.c b/drivers/gpu/drm/nouveau/nv50_vm.c
index 6f38ceae3aa4..44fbac9c7d93 100644
--- a/drivers/gpu/drm/nouveau/nv50_vm.c
+++ b/drivers/gpu/drm/nouveau/nv50_vm.c
@@ -57,27 +57,15 @@ nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde,
}
static inline u64
-nv50_vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target)
+vm_addr(struct nouveau_vma *vma, u64 phys, u32 memtype, u32 target)
{
- struct drm_nouveau_private *dev_priv = vma->vm->dev->dev_private;
-
phys |= 1; /* present */
phys |= (u64)memtype << 40;
-
- /* IGPs don't have real VRAM, re-target to stolen system memory */
- if (target == 0 && dev_priv->vram_sys_base) {
- phys += dev_priv->vram_sys_base;
- target = 3;
- }
-
phys |= target << 4;
-
if (vma->access & NV_MEM_ACCESS_SYS)
phys |= (1 << 6);
-
if (!(vma->access & NV_MEM_ACCESS_WO))
phys |= (1 << 3);
-
return phys;
}
@@ -85,11 +73,19 @@ void
nv50_vm_map(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
struct nouveau_mem *mem, u32 pte, u32 cnt, u64 phys, u64 delta)
{
+ struct drm_nouveau_private *dev_priv = vma->vm->dev->dev_private;
u32 comp = (mem->memtype & 0x180) >> 7;
- u32 block;
+ u32 block, target;
int i;
- phys = nv50_vm_addr(vma, phys, mem->memtype, 0);
+ /* IGPs don't have real VRAM, re-target to stolen system memory */
+ target = 0;
+ if (dev_priv->vram_sys_base) {
+ phys += dev_priv->vram_sys_base;
+ target = 3;
+ }
+
+ phys = vm_addr(vma, phys, mem->memtype, target);
pte <<= 3;
cnt <<= 3;
@@ -125,9 +121,10 @@ void
nv50_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
{
+ u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 3 : 2;
pte <<= 3;
while (cnt--) {
- u64 phys = nv50_vm_addr(vma, (u64)*list++, mem->memtype, 2);
+ u64 phys = vm_addr(vma, (u64)*list++, mem->memtype, target);
nv_wo32(pgt, pte + 0, lower_32_bits(phys));
nv_wo32(pgt, pte + 4, upper_32_bits(phys));
pte += 8;
diff --git a/drivers/gpu/drm/nouveau/nv50_vram.c b/drivers/gpu/drm/nouveau/nv50_vram.c
index 2e45e57fd869..9ed9ae397d75 100644
--- a/drivers/gpu/drm/nouveau/nv50_vram.c
+++ b/drivers/gpu/drm/nouveau/nv50_vram.c
@@ -189,8 +189,25 @@ nv50_vram_init(struct drm_device *dev)
struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
+ u32 pfb714 = nv_rd32(dev, 0x100714);
u32 rblock, length;
+ switch (pfb714 & 0x00000007) {
+ case 0: dev_priv->vram_type = NV_MEM_TYPE_DDR1; break;
+ case 1:
+ if (nouveau_mem_vbios_type(dev) == NV_MEM_TYPE_DDR3)
+ dev_priv->vram_type = NV_MEM_TYPE_DDR3;
+ else
+ dev_priv->vram_type = NV_MEM_TYPE_DDR2;
+ break;
+ case 2: dev_priv->vram_type = NV_MEM_TYPE_GDDR3; break;
+ case 3: dev_priv->vram_type = NV_MEM_TYPE_GDDR4; break;
+ case 4: dev_priv->vram_type = NV_MEM_TYPE_GDDR5; break;
+ default:
+ break;
+ }
+
+ dev_priv->vram_rank_B = !!(nv_rd32(dev, 0x100200) & 0x4);
dev_priv->vram_size = nv_rd32(dev, 0x10020c);
dev_priv->vram_size |= (dev_priv->vram_size & 0xff) << 32;
dev_priv->vram_size &= 0xffffffff00ULL;
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index dcbe0d5d0241..50d68a7a1379 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -436,6 +436,24 @@ nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit)
printk(" on channel 0x%010llx\n", (u64)inst << 12);
}
+static int
+nvc0_fifo_page_flip(struct drm_device *dev, u32 chid)
+{
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan = NULL;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->channels.lock, flags);
+ if (likely(chid >= 0 && chid < dev_priv->engine.fifo.channels)) {
+ chan = dev_priv->channels.ptr[chid];
+ if (likely(chan))
+ ret = nouveau_finish_page_flip(chan, NULL);
+ }
+ spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+ return ret;
+}
+
static void
nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
{
@@ -445,11 +463,21 @@ nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f;
u32 subc = (addr & 0x00070000);
u32 mthd = (addr & 0x00003ffc);
+ u32 show = stat;
- NV_INFO(dev, "PSUBFIFO %d:", unit);
- nouveau_bitfield_print(nvc0_fifo_subfifo_intr, stat);
- NV_INFO(dev, "PSUBFIFO %d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
- unit, chid, subc, mthd, data);
+ if (stat & 0x00200000) {
+ if (mthd == 0x0054) {
+ if (!nvc0_fifo_page_flip(dev, chid))
+ show &= ~0x00200000;
+ }
+ }
+
+ if (show) {
+ NV_INFO(dev, "PFIFO%d:", unit);
+ nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
+ NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
+ unit, chid, subc, mthd, data);
+ }
nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008);
nv_wr32(dev, 0x040108 + (unit * 0x2000), stat);
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
index 8ee3963f9030..9066102d1159 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.c
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -333,14 +333,6 @@ nvc0_graph_fini(struct drm_device *dev, int engine, bool suspend)
return 0;
}
-static int
-nvc0_graph_mthd_page_flip(struct nouveau_channel *chan,
- u32 class, u32 mthd, u32 data)
-{
- nouveau_finish_page_flip(chan, NULL);
- return 0;
-}
-
static void
nvc0_graph_init_obj418880(struct drm_device *dev)
{
@@ -889,7 +881,6 @@ nvc0_graph_create(struct drm_device *dev)
NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */
NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */
- NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip);
NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */
if (fermi >= 0x9197)
NVOBJ_CLASS(dev, 0x9197, GR); /* 3D (NVC1-) */
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
index e9992f62c1c0..ce65f81bb871 100644
--- a/drivers/gpu/drm/nouveau/nvc0_pm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_pm.c
@@ -269,7 +269,7 @@ calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq)
clk0 = calc_div(dev, clk, clk0, freq, &div1D);
/* see if we can get any closer using PLLs */
- if (clk0 != freq) {
+ if (clk0 != freq && (0x00004387 & (1 << clk))) {
if (clk < 7)
clk1 = calc_pll(dev, clk, freq, &info->coef);
else
diff --git a/drivers/gpu/drm/nouveau/nvc0_vm.c b/drivers/gpu/drm/nouveau/nvc0_vm.c
index 9e352944a35a..30d2bd58828f 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vm.c
@@ -77,9 +77,11 @@ void
nvc0_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt,
struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list)
{
+ u32 target = (vma->access & NV_MEM_ACCESS_NOSNOOP) ? 7 : 5;
+
pte <<= 3;
while (cnt--) {
- u64 phys = nvc0_vm_addr(vma, *list++, mem->memtype, 5);
+ u64 phys = nvc0_vm_addr(vma, *list++, mem->memtype, target);
nv_wo32(pgt, pte + 0, lower_32_bits(phys));
nv_wo32(pgt, pte + 4, upper_32_bits(phys));
pte += 8;
diff --git a/drivers/gpu/drm/nouveau/nvc0_vram.c b/drivers/gpu/drm/nouveau/nvc0_vram.c
index ce984d573a51..a7eef8934c07 100644
--- a/drivers/gpu/drm/nouveau/nvc0_vram.c
+++ b/drivers/gpu/drm/nouveau/nvc0_vram.c
@@ -106,31 +106,32 @@ nvc0_vram_init(struct drm_device *dev)
struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
- u32 parts = nv_rd32(dev, 0x121c74);
+ u32 parts = nv_rd32(dev, 0x022438);
+ u32 pmask = nv_rd32(dev, 0x022554);
u32 bsize = nv_rd32(dev, 0x10f20c);
u32 offset, length;
bool uniform = true;
int ret, part;
NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800));
- NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize);
+ NV_DEBUG(dev, "parts 0x%08x mask 0x%08x\n", parts, pmask);
+
+ dev_priv->vram_type = nouveau_mem_vbios_type(dev);
+ dev_priv->vram_rank_B = !!(nv_rd32(dev, 0x10f200) & 0x00000004);
/* read amount of vram attached to each memory controller */
- part = 0;
- while (parts) {
- u32 psize = nv_rd32(dev, 0x11020c + (part++ * 0x1000));
- if (psize == 0)
- continue;
- parts--;
-
- if (psize != bsize) {
- if (psize < bsize)
- bsize = psize;
- uniform = false;
+ for (part = 0; part < parts; part++) {
+ if (!(pmask & (1 << part))) {
+ u32 psize = nv_rd32(dev, 0x11020c + (part * 0x1000));
+ if (psize != bsize) {
+ if (psize < bsize)
+ bsize = psize;
+ uniform = false;
+ }
+
+ NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize);
+ dev_priv->vram_size += (u64)psize << 20;
}
-
- NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", part, psize);
- dev_priv->vram_size += (u64)psize << 20;
}
/* if all controllers have the same amount attached, there's no holes */
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index d2ba2f07400b..0247250939e8 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -284,6 +284,8 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
u32 *push;
int ret;
+ evo_sync(crtc->dev, EVO_MASTER);
+
swap_interval <<= 4;
if (swap_interval == 0)
swap_interval |= 0x100;
@@ -301,12 +303,12 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
offset = chan->dispc_vma[nv_crtc->index].offset;
offset += evo->sem.offset;
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+ BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset));
OUT_RING (chan, 0xf00d0000 | evo->sem.value);
OUT_RING (chan, 0x1002);
- BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+ BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
OUT_RING (chan, upper_32_bits(offset));
OUT_RING (chan, lower_32_bits(offset ^ 0x10));
OUT_RING (chan, 0x74b1e000);
@@ -361,10 +363,12 @@ nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
static int
nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
{
+ struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
struct drm_device *dev = nv_crtc->base.dev;
struct nouveau_connector *nv_connector;
struct drm_connector *connector;
u32 *push, mode = 0x00;
+ u32 mthd;
nv_connector = nouveau_crtc_connector_get(nv_crtc);
connector = &nv_connector->base;
@@ -382,9 +386,14 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
mode |= nv_connector->dithering_depth;
}
+ if (dev_priv->card_type < NV_E0)
+ mthd = 0x0490 + (nv_crtc->index * 0x0300);
+ else
+ mthd = 0x04a0 + (nv_crtc->index * 0x0300);
+
push = evo_wait(dev, EVO_MASTER, 4);
if (push) {
- evo_mthd(push, 0x0490 + (nv_crtc->index * 0x300), 1);
+ evo_mthd(push, mthd, 1);
evo_data(push, mode);
if (update) {
evo_mthd(push, 0x0080, 1);
@@ -593,7 +602,7 @@ nvd0_crtc_commit(struct drm_crtc *crtc)
evo_kick(push, crtc->dev, EVO_MASTER);
}
- nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, false);
+ nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true);
nvd0_display_flip_next(crtc, crtc->fb, NULL, 1);
}
@@ -634,8 +643,7 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks;
u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks;
u32 vblan2e = 0, vblan2s = 1;
- u32 magic = 0x31ec6000;
- u32 syncs, *push;
+ u32 *push;
int ret;
hactive = mode->htotal;
@@ -655,15 +663,8 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
vblan2e = vactive + vsynce + vbackp;
vblan2s = vblan2e + (mode->vdisplay * vscan / ilace);
vactive = (vactive * 2) + 1;
- magic |= 0x00000001;
}
- syncs = 0x00000001;
- if (mode->flags & DRM_MODE_FLAG_NHSYNC)
- syncs |= 0x00000008;
- if (mode->flags & DRM_MODE_FLAG_NVSYNC)
- syncs |= 0x00000010;
-
ret = nvd0_crtc_swap_fbs(crtc, old_fb);
if (ret)
return ret;
@@ -683,9 +684,6 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
evo_data(push, mode->clock * 1000);
evo_data(push, 0x00200000); /* ??? */
evo_data(push, mode->clock * 1000);
- evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
- evo_data(push, syncs);
- evo_data(push, magic);
evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2);
evo_data(push, 0x00000311);
evo_data(push, 0x00000100);
@@ -959,11 +957,6 @@ nvd0_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
static void
-nvd0_dac_prepare(struct drm_encoder *encoder)
-{
-}
-
-static void
nvd0_dac_commit(struct drm_encoder *encoder)
{
}
@@ -974,13 +967,26 @@ nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
- u32 *push;
+ u32 syncs, magic, *push;
+
+ syncs = 0x00000001;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ syncs |= 0x00000008;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ syncs |= 0x00000010;
+
+ magic = 0x31ec6000 | (nv_crtc->index << 25);
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ magic |= 0x00000001;
nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON);
- push = evo_wait(encoder->dev, EVO_MASTER, 4);
+ push = evo_wait(encoder->dev, EVO_MASTER, 8);
if (push) {
- evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 2);
+ evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
+ evo_data(push, syncs);
+ evo_data(push, magic);
+ evo_mthd(push, 0x0180 + (nv_encoder->or * 0x020), 2);
evo_data(push, 1 << nv_crtc->index);
evo_data(push, 0x00ff);
evo_kick(push, encoder->dev, EVO_MASTER);
@@ -1043,7 +1049,7 @@ nvd0_dac_destroy(struct drm_encoder *encoder)
static const struct drm_encoder_helper_funcs nvd0_dac_hfunc = {
.dpms = nvd0_dac_dpms,
.mode_fixup = nvd0_dac_mode_fixup,
- .prepare = nvd0_dac_prepare,
+ .prepare = nvd0_dac_disconnect,
.commit = nvd0_dac_commit,
.mode_set = nvd0_dac_mode_set,
.disable = nvd0_dac_disconnect,
@@ -1183,6 +1189,149 @@ nvd0_hdmi_disconnect(struct drm_encoder *encoder)
/******************************************************************************
* SOR
*****************************************************************************/
+static inline u32
+nvd0_sor_dp_lane_map(struct drm_device *dev, struct dcb_entry *dcb, u8 lane)
+{
+ static const u8 nvd0[] = { 16, 8, 0, 24 };
+ return nvd0[lane];
+}
+
+static void
+nvd0_sor_dp_train_set(struct drm_device *dev, struct dcb_entry *dcb, u8 pattern)
+{
+ const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ const u32 loff = (or * 0x800) + (link * 0x80);
+ nv_mask(dev, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern);
+}
+
+static void
+nvd0_sor_dp_train_adj(struct drm_device *dev, struct dcb_entry *dcb,
+ u8 lane, u8 swing, u8 preem)
+{
+ const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ const u32 loff = (or * 0x800) + (link * 0x80);
+ u32 shift = nvd0_sor_dp_lane_map(dev, dcb, lane);
+ u32 mask = 0x000000ff << shift;
+ u8 *table, *entry, *config = NULL;
+
+ switch (swing) {
+ case 0: preem += 0; break;
+ case 1: preem += 4; break;
+ case 2: preem += 7; break;
+ case 3: preem += 9; break;
+ }
+
+ table = nouveau_dp_bios_data(dev, dcb, &entry);
+ if (table) {
+ if (table[0] == 0x30) {
+ config = entry + table[4];
+ config += table[5] * preem;
+ } else
+ if (table[0] == 0x40) {
+ config = table + table[1];
+ config += table[2] * table[3];
+ config += table[6] * preem;
+ }
+ }
+
+ if (!config) {
+ NV_ERROR(dev, "PDISP: unsupported DP table for chipset\n");
+ return;
+ }
+
+ nv_mask(dev, 0x61c118 + loff, mask, config[1] << shift);
+ nv_mask(dev, 0x61c120 + loff, mask, config[2] << shift);
+ nv_mask(dev, 0x61c130 + loff, 0x0000ff00, config[3] << 8);
+ nv_mask(dev, 0x61c13c + loff, 0x00000000, 0x00000000);
+}
+
+static void
+nvd0_sor_dp_link_set(struct drm_device *dev, struct dcb_entry *dcb, int crtc,
+ int link_nr, u32 link_bw, bool enhframe)
+{
+ const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ const u32 loff = (or * 0x800) + (link * 0x80);
+ const u32 soff = (or * 0x800);
+ u32 dpctrl = nv_rd32(dev, 0x61c10c + loff) & ~0x001f4000;
+ u32 clksor = nv_rd32(dev, 0x612300 + soff) & ~0x007c0000;
+ u32 script = 0x0000, lane_mask = 0;
+ u8 *table, *entry;
+ int i;
+
+ link_bw /= 27000;
+
+ table = nouveau_dp_bios_data(dev, dcb, &entry);
+ if (table) {
+ if (table[0] == 0x30) entry = ROMPTR(dev, entry[10]);
+ else if (table[0] == 0x40) entry = ROMPTR(dev, entry[9]);
+ else entry = NULL;
+
+ while (entry) {
+ if (entry[0] >= link_bw)
+ break;
+ entry += 3;
+ }
+
+ nouveau_bios_run_init_table(dev, script, dcb, crtc);
+ }
+
+ clksor |= link_bw << 18;
+ dpctrl |= ((1 << link_nr) - 1) << 16;
+ if (enhframe)
+ dpctrl |= 0x00004000;
+
+ for (i = 0; i < link_nr; i++)
+ lane_mask |= 1 << (nvd0_sor_dp_lane_map(dev, dcb, i) >> 3);
+
+ nv_wr32(dev, 0x612300 + soff, clksor);
+ nv_wr32(dev, 0x61c10c + loff, dpctrl);
+ nv_mask(dev, 0x61c130 + loff, 0x0000000f, lane_mask);
+}
+
+static void
+nvd0_sor_dp_link_get(struct drm_device *dev, struct dcb_entry *dcb,
+ u32 *link_nr, u32 *link_bw)
+{
+ const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1);
+ const u32 loff = (or * 0x800) + (link * 0x80);
+ const u32 soff = (or * 0x800);
+ u32 dpctrl = nv_rd32(dev, 0x61c10c + loff) & 0x000f0000;
+ u32 clksor = nv_rd32(dev, 0x612300 + soff);
+
+ if (dpctrl > 0x00030000) *link_nr = 4;
+ else if (dpctrl > 0x00010000) *link_nr = 2;
+ else *link_nr = 1;
+
+ *link_bw = (clksor & 0x007c0000) >> 18;
+ *link_bw *= 27000;
+}
+
+static void
+nvd0_sor_dp_calc_tu(struct drm_device *dev, struct dcb_entry *dcb,
+ u32 crtc, u32 datarate)
+{
+ const u32 symbol = 100000;
+ const u32 TU = 64;
+ u32 link_nr, link_bw;
+ u64 ratio, value;
+
+ nvd0_sor_dp_link_get(dev, dcb, &link_nr, &link_bw);
+
+ ratio = datarate;
+ ratio *= symbol;
+ do_div(ratio, link_nr * link_bw);
+
+ value = (symbol - ratio) * TU;
+ value *= ratio;
+ do_div(value, symbol);
+ do_div(value, symbol);
+
+ value += 5;
+ value |= 0x08000000;
+
+ nv_wr32(dev, 0x616610 + (crtc * 0x800), value);
+}
+
static void
nvd0_sor_dpms(struct drm_encoder *encoder, int mode)
{
@@ -1215,6 +1364,16 @@ nvd0_sor_dpms(struct drm_encoder *encoder, int mode)
nv_mask(dev, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl);
nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000);
nv_wait(dev, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000);
+
+ if (nv_encoder->dcb->type == OUTPUT_DP) {
+ struct dp_train_func func = {
+ .link_set = nvd0_sor_dp_link_set,
+ .train_set = nvd0_sor_dp_train_set,
+ .train_adj = nvd0_sor_dp_train_adj
+ };
+
+ nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func);
+ }
}
static bool
@@ -1237,8 +1396,37 @@ nvd0_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
static void
+nvd0_sor_disconnect(struct drm_encoder *encoder)
+{
+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+ struct drm_device *dev = encoder->dev;
+ u32 *push;
+
+ if (nv_encoder->crtc) {
+ nvd0_crtc_prepare(nv_encoder->crtc);
+
+ push = evo_wait(dev, EVO_MASTER, 4);
+ if (push) {
+ evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1);
+ evo_data(push, 0x00000000);
+ evo_mthd(push, 0x0080, 1);
+ evo_data(push, 0x00000000);
+ evo_kick(push, dev, EVO_MASTER);
+ }
+
+ nvd0_hdmi_disconnect(encoder);
+
+ nv_encoder->crtc = NULL;
+ nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
+ }
+}
+
+static void
nvd0_sor_prepare(struct drm_encoder *encoder)
{
+ nvd0_sor_disconnect(encoder);
+ if (nouveau_encoder(encoder)->dcb->type == OUTPUT_DP)
+ evo_sync(encoder->dev, EVO_MASTER);
}
static void
@@ -1257,7 +1445,18 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
struct nouveau_connector *nv_connector;
struct nvbios *bios = &dev_priv->vbios;
u32 mode_ctrl = (1 << nv_crtc->index);
- u32 *push, or_config;
+ u32 syncs, magic, *push;
+ u32 or_config;
+
+ syncs = 0x00000001;
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ syncs |= 0x00000008;
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ syncs |= 0x00000010;
+
+ magic = 0x31ec6000 | (nv_crtc->index << 25);
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ magic |= 0x00000001;
nv_connector = nouveau_encoder_connector_get(nv_encoder);
switch (nv_encoder->dcb->type) {
@@ -1306,6 +1505,22 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
}
break;
+ case OUTPUT_DP:
+ if (nv_connector->base.display_info.bpc == 6) {
+ nv_encoder->dp.datarate = mode->clock * 18 / 8;
+ syncs |= 0x00000140;
+ } else {
+ nv_encoder->dp.datarate = mode->clock * 24 / 8;
+ syncs |= 0x00000180;
+ }
+
+ if (nv_encoder->dcb->sorconf.link & 1)
+ mode_ctrl |= 0x00000800;
+ else
+ mode_ctrl |= 0x00000900;
+
+ or_config = (mode_ctrl & 0x00000f00) >> 8;
+ break;
default:
BUG_ON(1);
break;
@@ -1313,9 +1528,17 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON);
- push = evo_wait(dev, EVO_MASTER, 4);
+ if (nv_encoder->dcb->type == OUTPUT_DP) {
+ nvd0_sor_dp_calc_tu(dev, nv_encoder->dcb, nv_crtc->index,
+ nv_encoder->dp.datarate);
+ }
+
+ push = evo_wait(dev, EVO_MASTER, 8);
if (push) {
- evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2);
+ evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
+ evo_data(push, syncs);
+ evo_data(push, magic);
+ evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 2);
evo_data(push, mode_ctrl);
evo_data(push, or_config);
evo_kick(push, dev, EVO_MASTER);
@@ -1325,32 +1548,6 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
}
static void
-nvd0_sor_disconnect(struct drm_encoder *encoder)
-{
- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
- struct drm_device *dev = encoder->dev;
- u32 *push;
-
- if (nv_encoder->crtc) {
- nvd0_crtc_prepare(nv_encoder->crtc);
-
- push = evo_wait(dev, EVO_MASTER, 4);
- if (push) {
- evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1);
- evo_data(push, 0x00000000);
- evo_mthd(push, 0x0080, 1);
- evo_data(push, 0x00000000);
- evo_kick(push, dev, EVO_MASTER);
- }
-
- nvd0_hdmi_disconnect(encoder);
-
- nv_encoder->crtc = NULL;
- nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
- }
-}
-
-static void
nvd0_sor_destroy(struct drm_encoder *encoder)
{
drm_encoder_cleanup(encoder);
@@ -1402,17 +1599,19 @@ static struct dcb_entry *
lookup_dcb(struct drm_device *dev, int id, u32 mc)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
- int type, or, i;
+ int type, or, i, link = -1;
if (id < 4) {
type = OUTPUT_ANALOG;
or = id;
} else {
switch (mc & 0x00000f00) {
- case 0x00000000: type = OUTPUT_LVDS; break;
- case 0x00000100: type = OUTPUT_TMDS; break;
- case 0x00000200: type = OUTPUT_TMDS; break;
- case 0x00000500: type = OUTPUT_TMDS; break;
+ case 0x00000000: link = 0; type = OUTPUT_LVDS; break;
+ case 0x00000100: link = 0; type = OUTPUT_TMDS; break;
+ case 0x00000200: link = 1; type = OUTPUT_TMDS; break;
+ case 0x00000500: link = 0; type = OUTPUT_TMDS; break;
+ case 0x00000800: link = 0; type = OUTPUT_DP; break;
+ case 0x00000900: link = 1; type = OUTPUT_DP; break;
default:
NV_ERROR(dev, "PDISP: unknown SOR mc 0x%08x\n", mc);
return NULL;
@@ -1423,7 +1622,8 @@ lookup_dcb(struct drm_device *dev, int id, u32 mc)
for (i = 0; i < dev_priv->vbios.dcb.entries; i++) {
struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i];
- if (dcb->type == type && (dcb->or & (1 << or)))
+ if (dcb->type == type && (dcb->or & (1 << or)) &&
+ (link < 0 || link == !(dcb->sorconf.link & 1)))
return dcb;
}
@@ -1474,7 +1674,9 @@ nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask)
}
pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000;
- if (mask & 0x00010000) {
+ NV_DEBUG_KMS(dev, "PDISP: crtc %d pclk %d mask 0x%08x\n",
+ crtc, pclk, mask);
+ if (pclk && (mask & 0x00010000)) {
nv50_crtc_set_clock(dev, crtc, pclk);
}
@@ -1498,6 +1700,7 @@ nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask)
break;
case OUTPUT_TMDS:
case OUTPUT_LVDS:
+ case OUTPUT_DP:
if (cfg & 0x00000100)
tmp = 0x00000101;
else
@@ -1548,7 +1751,7 @@ nvd0_display_bh(unsigned long data)
{
struct drm_device *dev = (struct drm_device *)data;
struct nvd0_display *disp = nvd0_display(dev);
- u32 mask, crtc;
+ u32 mask = 0, crtc = ~0;
int i;
if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) {
@@ -1564,12 +1767,8 @@ nvd0_display_bh(unsigned long data)
}
}
- mask = nv_rd32(dev, 0x6101d4);
- crtc = 0;
- if (!mask) {
- mask = nv_rd32(dev, 0x6109d4);
- crtc = 1;
- }
+ while (!mask && ++crtc < dev->mode_config.num_crtc)
+ mask = nv_rd32(dev, 0x6101d4 + (crtc * 0x800));
if (disp->modeset & 0x00000001)
nvd0_display_unk1_handler(dev, crtc, mask);
@@ -1584,6 +1783,7 @@ nvd0_display_intr(struct drm_device *dev)
{
struct nvd0_display *disp = nvd0_display(dev);
u32 intr = nv_rd32(dev, 0x610088);
+ int i;
if (intr & 0x00000001) {
u32 stat = nv_rd32(dev, 0x61008c);
@@ -1628,16 +1828,13 @@ nvd0_display_intr(struct drm_device *dev)
intr &= ~0x00100000;
}
- if (intr & 0x01000000) {
- u32 stat = nv_rd32(dev, 0x6100bc);
- nv_wr32(dev, 0x6100bc, stat);
- intr &= ~0x01000000;
- }
-
- if (intr & 0x02000000) {
- u32 stat = nv_rd32(dev, 0x6108bc);
- nv_wr32(dev, 0x6108bc, stat);
- intr &= ~0x02000000;
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ u32 mask = 0x01000000 << i;
+ if (intr & mask) {
+ u32 stat = nv_rd32(dev, 0x6100bc + (i * 0x800));
+ nv_wr32(dev, 0x6100bc + (i * 0x800), stat);
+ intr &= ~mask;
+ }
}
if (intr)
@@ -1774,7 +1971,7 @@ nvd0_display_create(struct drm_device *dev)
struct pci_dev *pdev = dev->pdev;
struct nvd0_display *disp;
struct dcb_entry *dcbe;
- int ret, i;
+ int crtcs, ret, i;
disp = kzalloc(sizeof(*disp), GFP_KERNEL);
if (!disp)
@@ -1782,7 +1979,8 @@ nvd0_display_create(struct drm_device *dev)
dev_priv->engine.display.priv = disp;
/* create crtc objects to represent the hw heads */
- for (i = 0; i < 2; i++) {
+ crtcs = nv_rd32(dev, 0x022448);
+ for (i = 0; i < crtcs; i++) {
ret = nvd0_crtc_create(dev, i);
if (ret)
goto out;
@@ -1803,6 +2001,7 @@ nvd0_display_create(struct drm_device *dev)
switch (dcbe->type) {
case OUTPUT_TMDS:
case OUTPUT_LVDS:
+ case OUTPUT_DP:
nvd0_sor_create(connector, dcbe);
break;
case OUTPUT_ANALOG:
diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
index 6a5f4395838f..88718fad5d6d 100644
--- a/drivers/gpu/drm/r128/r128_drv.c
+++ b/drivers/gpu/drm/r128/r128_drv.c
@@ -85,6 +85,7 @@ static struct drm_driver driver = {
int r128_driver_load(struct drm_device *dev, unsigned long flags)
{
+ pci_set_master(dev->pdev);
return drm_vblank_init(dev, 1);
}
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index 2139fe893ec5..9d83729956ff 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -71,7 +71,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \
evergreen.o evergreen_cs.o evergreen_blit_shaders.o evergreen_blit_kms.o \
radeon_trace_points.o ni.o cayman_blit_shaders.o atombios_encoders.o \
- radeon_semaphore.o radeon_sa.o
+ radeon_semaphore.o radeon_sa.o atombios_i2c.o si.o si_blit_shaders.o
radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
radeon-$(CONFIG_VGA_SWITCHEROO) += radeon_atpx_handler.o
diff --git a/drivers/gpu/drm/radeon/ObjectID.h b/drivers/gpu/drm/radeon/ObjectID.h
index c61c3fe9fb98..ca4b038050d2 100644
--- a/drivers/gpu/drm/radeon/ObjectID.h
+++ b/drivers/gpu/drm/radeon/ObjectID.h
@@ -85,6 +85,7 @@
#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA 0x1F
#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 0x20
#define ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 0x21
+#define ENCODER_OBJECT_ID_INTERNAL_VCE 0x24
#define ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO 0xFF
@@ -387,6 +388,10 @@
GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
ENCODER_OBJECT_ID_NUTMEG << OBJECT_ID_SHIFT)
+#define ENCODER_VCE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+ ENCODER_OBJECT_ID_INTERNAL_VCE << OBJECT_ID_SHIFT)
+
/****************************************************/
/* Connector Object ID definition - Shared with BIOS */
/****************************************************/
diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
index 1b50ad8919d5..4b04ba3828e8 100644
--- a/drivers/gpu/drm/radeon/atombios.h
+++ b/drivers/gpu/drm/radeon/atombios.h
@@ -101,6 +101,7 @@
#define ATOM_LCD_SELFTEST_START (ATOM_DISABLE+5)
#define ATOM_LCD_SELFTEST_STOP (ATOM_ENABLE+5)
#define ATOM_ENCODER_INIT (ATOM_DISABLE+7)
+#define ATOM_INIT (ATOM_DISABLE+7)
#define ATOM_GET_STATUS (ATOM_DISABLE+8)
#define ATOM_BLANKING 1
@@ -251,25 +252,25 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
USHORT SetEngineClock; //Function Table,directly used by various SW components,latest version 1.1
USHORT SetMemoryClock; //Function Table,directly used by various SW components,latest version 1.1
USHORT SetPixelClock; //Function Table,directly used by various SW components,latest version 1.2
- USHORT DynamicClockGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init
+ USHORT EnableDispPowerGating; //Atomic Table, indirectly used by various SW components,called from ASIC_Init
USHORT ResetMemoryDLL; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock
USHORT ResetMemoryDevice; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock
- USHORT MemoryPLLInit;
- USHORT AdjustDisplayPll; //only used by Bios
+ USHORT MemoryPLLInit; //Atomic Table, used only by Bios
+ USHORT AdjustDisplayPll; //Atomic Table, used by various SW componentes.
USHORT AdjustMemoryController; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock
USHORT EnableASIC_StaticPwrMgt; //Atomic Table, only used by Bios
USHORT ASIC_StaticPwrMgtStatusChange; //Obsolete , only used by Bios
USHORT DAC_LoadDetection; //Atomic Table, directly used by various SW components,latest version 1.2
USHORT LVTMAEncoderControl; //Atomic Table,directly used by various SW components,latest version 1.3
- USHORT LCD1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1
+ USHORT HW_Misc_Operation; //Atomic Table, directly used by various SW components,latest version 1.1
USHORT DAC1EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1
USHORT DAC2EncoderControl; //Atomic Table, directly used by various SW components,latest version 1.1
USHORT DVOOutputControl; //Atomic Table, directly used by various SW components,latest version 1.1
USHORT CV1OutputControl; //Atomic Table, Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead
- USHORT GetConditionalGoldenSetting; //only used by Bios
+ USHORT GetConditionalGoldenSetting; //Only used by Bios
USHORT TVEncoderControl; //Function Table,directly used by various SW components,latest version 1.1
- USHORT TMDSAEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3
- USHORT LVDSEncoderControl; //Atomic Table, directly used by various SW components,latest version 1.3
+ USHORT PatchMCSetting; //only used by BIOS
+ USHORT MC_SEQ_Control; //only used by BIOS
USHORT TV1OutputControl; //Atomic Table, Obsolete from Ry6xx, use DAC2 Output instead
USHORT EnableScaler; //Atomic Table, used only by Bios
USHORT BlankCRTC; //Atomic Table, directly used by various SW components,latest version 1.1
@@ -282,7 +283,7 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
USHORT SetCRTC_Replication; //Atomic Table, used only by Bios
USHORT SelectCRTC_Source; //Atomic Table, directly used by various SW components,latest version 1.1
USHORT EnableGraphSurfaces; //Atomic Table, used only by Bios
- USHORT UpdateCRTC_DoubleBufferRegisters;
+ USHORT UpdateCRTC_DoubleBufferRegisters; //Atomic Table, used only by Bios
USHORT LUT_AutoFill; //Atomic Table, only used by Bios
USHORT EnableHW_IconCursor; //Atomic Table, only used by Bios
USHORT GetMemoryClock; //Atomic Table, directly used by various SW components,latest version 1.1
@@ -308,27 +309,36 @@ typedef struct _ATOM_MASTER_LIST_OF_COMMAND_TABLES{
USHORT SetVoltage; //Function Table,directly and/or indirectly used by various SW components,latest version 1.1
USHORT DAC1OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1
USHORT DAC2OutputControl; //Atomic Table, directly used by various SW components,latest version 1.1
- USHORT SetupHWAssistedI2CStatus; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C"
+ USHORT ComputeMemoryClockParam; //Function Table,only used by Bios, obsolete soon.Switch to use "ReadEDIDFromHWAssistedI2C"
USHORT ClockSource; //Atomic Table, indirectly used by various SW components,called from ASIC_Init
USHORT MemoryDeviceInit; //Atomic Table, indirectly used by various SW components,called from SetMemoryClock
- USHORT EnableYUV; //Atomic Table, indirectly used by various SW components,called from EnableVGARender
+ USHORT GetDispObjectInfo; //Atomic Table, indirectly used by various SW components,called from EnableVGARender
USHORT DIG1EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1
USHORT DIG2EncoderControl; //Atomic Table,directly used by various SW components,latest version 1.1
USHORT DIG1TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1
USHORT DIG2TransmitterControl; //Atomic Table,directly used by various SW components,latest version 1.1
USHORT ProcessAuxChannelTransaction; //Function Table,only used by Bios
USHORT DPEncoderService; //Function Table,only used by Bios
+ USHORT GetVoltageInfo; //Function Table,only used by Bios since SI
}ATOM_MASTER_LIST_OF_COMMAND_TABLES;
// For backward compatible
#define ReadEDIDFromHWAssistedI2C ProcessI2cChannelTransaction
-#define UNIPHYTransmitterControl DIG1TransmitterControl
-#define LVTMATransmitterControl DIG2TransmitterControl
+#define DPTranslatorControl DIG2EncoderControl
+#define UNIPHYTransmitterControl DIG1TransmitterControl
+#define LVTMATransmitterControl DIG2TransmitterControl
#define SetCRTC_DPM_State GetConditionalGoldenSetting
#define SetUniphyInstance ASIC_StaticPwrMgtStatusChange
#define HPDInterruptService ReadHWAssistedI2CStatus
#define EnableVGA_Access GetSCLKOverMCLKRatio
-#define GetDispObjectInfo EnableYUV
+#define EnableYUV GetDispObjectInfo
+#define DynamicClockGating EnableDispPowerGating
+#define SetupHWAssistedI2CStatus ComputeMemoryClockParam
+
+#define TMDSAEncoderControl PatchMCSetting
+#define LVDSEncoderControl MC_SEQ_Control
+#define LCD1OutputControl HW_Misc_Operation
+
typedef struct _ATOM_MASTER_COMMAND_TABLE
{
@@ -495,6 +505,34 @@ typedef struct _COMPUTE_MEMORY_ENGINE_PLL_PARAMETERS_V5
// ucInputFlag
#define ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN 1 // 1-StrobeMode, 0-PerformanceMode
+// use for ComputeMemoryClockParamTable
+typedef struct _COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1
+{
+ union
+ {
+ ULONG ulClock;
+ ATOM_S_MPLL_FB_DIVIDER ulFbDiv; //Output:UPPER_WORD=FB_DIV_INTEGER, LOWER_WORD=FB_DIV_FRAC shl (16-FB_FRACTION_BITS)
+ };
+ UCHAR ucDllSpeed; //Output
+ UCHAR ucPostDiv; //Output
+ union{
+ UCHAR ucInputFlag; //Input : ATOM_PLL_INPUT_FLAG_PLL_STROBE_MODE_EN: 1-StrobeMode, 0-PerformanceMode
+ UCHAR ucPllCntlFlag; //Output:
+ };
+ UCHAR ucBWCntl;
+}COMPUTE_MEMORY_CLOCK_PARAM_PARAMETERS_V2_1;
+
+// definition of ucInputFlag
+#define MPLL_INPUT_FLAG_STROBE_MODE_EN 0x01
+// definition of ucPllCntlFlag
+#define MPLL_CNTL_FLAG_VCO_MODE_MASK 0x03
+#define MPLL_CNTL_FLAG_BYPASS_DQ_PLL 0x04
+#define MPLL_CNTL_FLAG_QDR_ENABLE 0x08
+#define MPLL_CNTL_FLAG_AD_HALF_RATE 0x10
+
+//MPLL_CNTL_FLAG_BYPASS_AD_PLL has a wrong name, should be BYPASS_DQ_PLL
+#define MPLL_CNTL_FLAG_BYPASS_AD_PLL 0x04
+
typedef struct _DYNAMICE_MEMORY_SETTINGS_PARAMETER
{
ATOM_COMPUTE_CLOCK_FREQ ulClock;
@@ -562,6 +600,16 @@ typedef struct _DYNAMIC_CLOCK_GATING_PARAMETERS
#define DYNAMIC_CLOCK_GATING_PS_ALLOCATION DYNAMIC_CLOCK_GATING_PARAMETERS
/****************************************************************************/
+// Structure used by EnableDispPowerGatingTable.ctb
+/****************************************************************************/
+typedef struct _ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1
+{
+ UCHAR ucDispPipeId; // ATOM_CRTC1, ATOM_CRTC2, ...
+ UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE
+ UCHAR ucPadding[2];
+}ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1;
+
+/****************************************************************************/
// Structure used by EnableASIC_StaticPwrMgtTable.ctb
/****************************************************************************/
typedef struct _ENABLE_ASIC_STATIC_PWR_MGT_PARAMETERS
@@ -807,6 +855,7 @@ typedef struct _ATOM_DIG_ENCODER_CONFIG_V4
#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_1_62GHZ 0x00
#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_2_70GHZ 0x01
#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_5_40GHZ 0x02
+#define ATOM_ENCODER_CONFIG_V4_DPLINKRATE_3_24GHZ 0x03
#define ATOM_ENCODER_CONFIG_V4_ENCODER_SEL 0x70
#define ATOM_ENCODER_CONFIG_V4_DIG0_ENCODER 0x00
#define ATOM_ENCODER_CONFIG_V4_DIG1_ENCODER 0x10
@@ -814,6 +863,7 @@ typedef struct _ATOM_DIG_ENCODER_CONFIG_V4
#define ATOM_ENCODER_CONFIG_V4_DIG3_ENCODER 0x30
#define ATOM_ENCODER_CONFIG_V4_DIG4_ENCODER 0x40
#define ATOM_ENCODER_CONFIG_V4_DIG5_ENCODER 0x50
+#define ATOM_ENCODER_CONFIG_V4_DIG6_ENCODER 0x60
typedef struct _DIG_ENCODER_CONTROL_PARAMETERS_V4
{
@@ -1171,6 +1221,106 @@ typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V4
#define ATOM_TRANSMITTER_CONFIG_V4_TRANSMITTER3 0x80 //EF
+typedef struct _ATOM_DIG_TRANSMITTER_CONFIG_V5
+{
+#if ATOM_BIG_ENDIAN
+ UCHAR ucReservd1:1;
+ UCHAR ucHPDSel:3;
+ UCHAR ucPhyClkSrcId:2;
+ UCHAR ucCoherentMode:1;
+ UCHAR ucReserved:1;
+#else
+ UCHAR ucReserved:1;
+ UCHAR ucCoherentMode:1;
+ UCHAR ucPhyClkSrcId:2;
+ UCHAR ucHPDSel:3;
+ UCHAR ucReservd1:1;
+#endif
+}ATOM_DIG_TRANSMITTER_CONFIG_V5;
+
+typedef struct _DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5
+{
+ USHORT usSymClock; // Encoder Clock in 10kHz,(DP mode)= linkclock/10, (TMDS/LVDS/HDMI)= pixel clock, (HDMI deep color), =pixel clock * deep_color_ratio
+ UCHAR ucPhyId; // 0=UNIPHYA, 1=UNIPHYB, 2=UNIPHYC, 3=UNIPHYD, 4= UNIPHYE 5=UNIPHYF
+ UCHAR ucAction; // define as ATOM_TRANSMITER_ACTION_xxx
+ UCHAR ucLaneNum; // indicate lane number 1-8
+ UCHAR ucConnObjId; // Connector Object Id defined in ObjectId.h
+ UCHAR ucDigMode; // indicate DIG mode
+ union{
+ ATOM_DIG_TRANSMITTER_CONFIG_V5 asConfig;
+ UCHAR ucConfig;
+ };
+ UCHAR ucDigEncoderSel; // indicate DIG front end encoder
+ UCHAR ucDPLaneSet;
+ UCHAR ucReserved;
+ UCHAR ucReserved1;
+}DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5;
+
+//ucPhyId
+#define ATOM_PHY_ID_UNIPHYA 0
+#define ATOM_PHY_ID_UNIPHYB 1
+#define ATOM_PHY_ID_UNIPHYC 2
+#define ATOM_PHY_ID_UNIPHYD 3
+#define ATOM_PHY_ID_UNIPHYE 4
+#define ATOM_PHY_ID_UNIPHYF 5
+#define ATOM_PHY_ID_UNIPHYG 6
+
+// ucDigEncoderSel
+#define ATOM_TRANMSITTER_V5__DIGA_SEL 0x01
+#define ATOM_TRANMSITTER_V5__DIGB_SEL 0x02
+#define ATOM_TRANMSITTER_V5__DIGC_SEL 0x04
+#define ATOM_TRANMSITTER_V5__DIGD_SEL 0x08
+#define ATOM_TRANMSITTER_V5__DIGE_SEL 0x10
+#define ATOM_TRANMSITTER_V5__DIGF_SEL 0x20
+#define ATOM_TRANMSITTER_V5__DIGG_SEL 0x40
+
+// ucDigMode
+#define ATOM_TRANSMITTER_DIGMODE_V5_DP 0
+#define ATOM_TRANSMITTER_DIGMODE_V5_LVDS 1
+#define ATOM_TRANSMITTER_DIGMODE_V5_DVI 2
+#define ATOM_TRANSMITTER_DIGMODE_V5_HDMI 3
+#define ATOM_TRANSMITTER_DIGMODE_V5_SDVO 4
+#define ATOM_TRANSMITTER_DIGMODE_V5_DP_MST 5
+
+// ucDPLaneSet
+#define DP_LANE_SET__0DB_0_4V 0x00
+#define DP_LANE_SET__0DB_0_6V 0x01
+#define DP_LANE_SET__0DB_0_8V 0x02
+#define DP_LANE_SET__0DB_1_2V 0x03
+#define DP_LANE_SET__3_5DB_0_4V 0x08
+#define DP_LANE_SET__3_5DB_0_6V 0x09
+#define DP_LANE_SET__3_5DB_0_8V 0x0a
+#define DP_LANE_SET__6DB_0_4V 0x10
+#define DP_LANE_SET__6DB_0_6V 0x11
+#define DP_LANE_SET__9_5DB_0_4V 0x18
+
+// ATOM_DIG_TRANSMITTER_CONFIG_V5 asConfig;
+// Bit1
+#define ATOM_TRANSMITTER_CONFIG_V5_COHERENT 0x02
+
+// Bit3:2
+#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SEL_MASK 0x0c
+#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SEL_SHIFT 0x02
+
+#define ATOM_TRANSMITTER_CONFIG_V5_P1PLL 0x00
+#define ATOM_TRANSMITTER_CONFIG_V5_P2PLL 0x04
+#define ATOM_TRANSMITTER_CONFIG_V5_P0PLL 0x08
+#define ATOM_TRANSMITTER_CONFIG_V5_REFCLK_SRC_EXT 0x0c
+// Bit6:4
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD_SEL_MASK 0x70
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD_SEL_SHIFT 0x04
+
+#define ATOM_TRANSMITTER_CONFIG_V5_NO_HPD_SEL 0x00
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD1_SEL 0x10
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD2_SEL 0x20
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD3_SEL 0x30
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD4_SEL 0x40
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD5_SEL 0x50
+#define ATOM_TRANSMITTER_CONFIG_V5_HPD6_SEL 0x60
+
+#define DIG_TRANSMITTER_CONTROL_PS_ALLOCATION_V1_5 DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5
+
+
/****************************************************************************/
// Structures used by ExternalEncoderControlTable V1.3
// ASIC Families: Evergreen, Llano, NI
@@ -1793,6 +1943,7 @@ typedef struct _ENABLE_SPREAD_SPECTRUM_ON_PPLL_V2
#define ATOM_PPLL_SS_TYPE_V3_P1PLL 0x00
#define ATOM_PPLL_SS_TYPE_V3_P2PLL 0x04
#define ATOM_PPLL_SS_TYPE_V3_DCPLL 0x08
+#define ATOM_PPLL_SS_TYPE_V3_P0PLL ATOM_PPLL_SS_TYPE_V3_DCPLL
#define ATOM_PPLL_SS_AMOUNT_V3_FBDIV_MASK 0x00FF
#define ATOM_PPLL_SS_AMOUNT_V3_FBDIV_SHIFT 0
#define ATOM_PPLL_SS_AMOUNT_V3_NFRAC_MASK 0x0F00
@@ -2030,12 +2181,77 @@ typedef struct _SET_VOLTAGE_PARAMETERS_V2
USHORT usVoltageLevel; // real voltage level
}SET_VOLTAGE_PARAMETERS_V2;
+
+typedef struct _SET_VOLTAGE_PARAMETERS_V1_3
+{
+ UCHAR ucVoltageType; // To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI
+ UCHAR ucVoltageMode; // Indicate action: Set voltage level
+ USHORT usVoltageLevel; // real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. )
+}SET_VOLTAGE_PARAMETERS_V1_3;
+
+//ucVoltageType
+#define VOLTAGE_TYPE_VDDC 1
+#define VOLTAGE_TYPE_MVDDC 2
+#define VOLTAGE_TYPE_MVDDQ 3
+#define VOLTAGE_TYPE_VDDCI 4
+
+//SET_VOLTAGE_PARAMETERS_V3.ucVoltageMode
+#define ATOM_SET_VOLTAGE 0 //Set voltage Level
+#define ATOM_INIT_VOLTAGE_REGULATOR 3 //Init Regulator
+#define ATOM_SET_VOLTAGE_PHASE 4 //Set Vregulator Phase
+#define ATOM_GET_MAX_VOLTAGE 6 //Get Max Voltage, not used in SetVoltageTable v1.3
+#define ATOM_GET_VOLTAGE_LEVEL 6 //Get Voltage level from vitual voltage ID
+
+// define vitual voltage id in usVoltageLevel
+#define ATOM_VIRTUAL_VOLTAGE_ID0 0xff01
+#define ATOM_VIRTUAL_VOLTAGE_ID1 0xff02
+#define ATOM_VIRTUAL_VOLTAGE_ID2 0xff03
+#define ATOM_VIRTUAL_VOLTAGE_ID3 0xff04
+
typedef struct _SET_VOLTAGE_PS_ALLOCATION
{
SET_VOLTAGE_PARAMETERS sASICSetVoltage;
WRITE_ONE_BYTE_HW_I2C_DATA_PS_ALLOCATION sReserved;
}SET_VOLTAGE_PS_ALLOCATION;
+// New Added from SI for GetVoltageInfoTable, input parameter structure
+typedef struct _GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_1
+{
+ UCHAR ucVoltageType; // Input: To tell which voltage to set up, VDDC/MVDDC/MVDDQ/VDDCI
+ UCHAR ucVoltageMode; // Input: Indicate action: Get voltage info
+ USHORT usVoltageLevel; // Input: real voltage level in unit of mv or Voltage Phase (0, 1, 2, .. ) or Leakage Id
+ ULONG ulReserved;
+}GET_VOLTAGE_INFO_INPUT_PARAMETER_V1_1;
+
+// New Added from SI for GetVoltageInfoTable, output parameter structure when ucVotlageMode == ATOM_GET_VOLTAGE_VID
+typedef struct _GET_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1
+{
+ ULONG ulVotlageGpioState;
+ ULONG ulVoltageGPioMask;
+}GET_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1;
+
+// New Added from SI for GetVoltageInfoTable, output parameter structure when ucVotlageMode == ATOM_GET_VOLTAGE_STATEx_LEAKAGE_VID
+typedef struct _GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1
+{
+ USHORT usVoltageLevel;
+ USHORT usVoltageId; // Voltage Id programmed in Voltage Regulator
+ ULONG ulReseved;
+}GET_LEAKAGE_VOLTAGE_INFO_OUTPUT_PARAMETER_V1_1;
+
+
+// GetVoltageInfo v1.1 ucVoltageMode
+#define ATOM_GET_VOLTAGE_VID 0x00
+#define ATOM_GET_VOTLAGE_INIT_SEQ 0x03
+#define ATOM_GET_VOLTTAGE_PHASE_PHASE_VID 0x04
+// for SI, this state map to 0xff02 voltage state in Power Play table, which is power boost state
+#define ATOM_GET_VOLTAGE_STATE0_LEAKAGE_VID 0x10
+
+// for SI, this state map to 0xff01 voltage state in Power Play table, which is performance state
+#define ATOM_GET_VOLTAGE_STATE1_LEAKAGE_VID 0x11
+// undefined power state
+#define ATOM_GET_VOLTAGE_STATE2_LEAKAGE_VID 0x12
+#define ATOM_GET_VOLTAGE_STATE3_LEAKAGE_VID 0x13
+
/****************************************************************************/
// Structures used by TVEncoderControlTable
/****************************************************************************/
@@ -2065,9 +2281,9 @@ typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES
USHORT MultimediaConfigInfo; // Only used by MM Lib,latest version 2.1, not configuable from Bios, need to include the table to build Bios
USHORT StandardVESA_Timing; // Only used by Bios
USHORT FirmwareInfo; // Shared by various SW components,latest version 1.4
- USHORT DAC_Info; // Will be obsolete from R600
+ USHORT PaletteData; // Only used by BIOS
USHORT LCD_Info; // Shared by various SW components,latest version 1.3, was called LVDS_Info
- USHORT TMDS_Info; // Will be obsolete from R600
+ USHORT DIGTransmitterInfo; // Internal used by VBIOS only version 3.1
USHORT AnalogTV_Info; // Shared by various SW components,latest version 1.1
USHORT SupportedDevicesInfo; // Will be obsolete from R600
USHORT GPIO_I2C_Info; // Shared by various SW components,latest version 1.2 will be used from R600
@@ -2096,15 +2312,16 @@ typedef struct _ATOM_MASTER_LIST_OF_DATA_TABLES
USHORT PowerSourceInfo; // Shared by various SW components, latest versoin 1.1
}ATOM_MASTER_LIST_OF_DATA_TABLES;
-// For backward compatible
-#define LVDS_Info LCD_Info
-
typedef struct _ATOM_MASTER_DATA_TABLE
{
ATOM_COMMON_TABLE_HEADER sHeader;
ATOM_MASTER_LIST_OF_DATA_TABLES ListOfDataTables;
}ATOM_MASTER_DATA_TABLE;
+// For backward compatible
+#define LVDS_Info LCD_Info
+#define DAC_Info PaletteData
+#define TMDS_Info DIGTransmitterInfo
/****************************************************************************/
// Structure used in MultimediaCapabilityInfoTable
@@ -2171,7 +2388,9 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO
typedef struct _ATOM_FIRMWARE_CAPABILITY
{
#if ATOM_BIG_ENDIAN
- USHORT Reserved:3;
+ USHORT Reserved:1;
+ USHORT SCL2Redefined:1;
+ USHORT PostWithoutModeSet:1;
USHORT HyperMemory_Size:4;
USHORT HyperMemory_Support:1;
USHORT PPMode_Assigned:1;
@@ -2193,7 +2412,9 @@ typedef struct _ATOM_FIRMWARE_CAPABILITY
USHORT PPMode_Assigned:1;
USHORT HyperMemory_Support:1;
USHORT HyperMemory_Size:4;
- USHORT Reserved:3;
+ USHORT PostWithoutModeSet:1;
+ USHORT SCL2Redefined:1;
+ USHORT Reserved:1;
#endif
}ATOM_FIRMWARE_CAPABILITY;
@@ -2418,7 +2639,8 @@ typedef struct _ATOM_FIRMWARE_INFO_V2_2
USHORT usLcdMaxPixelClockPLL_Output; // In MHz unit
ULONG ulReserved4; //Was ulAsicMaximumVoltage
ULONG ulMinPixelClockPLL_Output; //In 10Khz unit
- ULONG ulReserved5; //Was usMinEngineClockPLL_Input and usMaxEngineClockPLL_Input
+ UCHAR ucRemoteDisplayConfig;
+ UCHAR ucReserved5[3]; //Was usMinEngineClockPLL_Input and usMaxEngineClockPLL_Input
ULONG ulReserved6; //Was usMinEngineClockPLL_Output and usMinMemoryClockPLL_Input
ULONG ulReserved7; //Was usMaxMemoryClockPLL_Input and usMinMemoryClockPLL_Output
USHORT usReserved11; //Was usMaxPixelClock; //In 10Khz unit, Max. Pclk used only for DAC
@@ -2438,6 +2660,11 @@ typedef struct _ATOM_FIRMWARE_INFO_V2_2
#define ATOM_FIRMWARE_INFO_LAST ATOM_FIRMWARE_INFO_V2_2
+
+// definition of ucRemoteDisplayConfig
+#define REMOTE_DISPLAY_DISABLE 0x00
+#define REMOTE_DISPLAY_ENABLE 0x01
+
/****************************************************************************/
// Structures used in IntegratedSystemInfoTable
/****************************************************************************/
@@ -2660,8 +2887,9 @@ usMinDownStreamHTLinkWidth: same as above.
#define INTEGRATED_SYSTEM_INFO__AMD_CPU__GREYHOUND 2
#define INTEGRATED_SYSTEM_INFO__AMD_CPU__K8 3
#define INTEGRATED_SYSTEM_INFO__AMD_CPU__PHARAOH 4
+#define INTEGRATED_SYSTEM_INFO__AMD_CPU__OROCHI 5
-#define INTEGRATED_SYSTEM_INFO__AMD_CPU__MAX_CODE INTEGRATED_SYSTEM_INFO__AMD_CPU__PHARAOH // this deff reflects max defined CPU code
+#define INTEGRATED_SYSTEM_INFO__AMD_CPU__MAX_CODE INTEGRATED_SYSTEM_INFO__AMD_CPU__OROCHI // this deff reflects max defined CPU code
#define SYSTEM_CONFIG_POWEREXPRESS_ENABLE 0x00000001
#define SYSTEM_CONFIG_RUN_AT_OVERDRIVE_ENGINE 0x00000002
@@ -2753,6 +2981,7 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V5
#define ASIC_INT_DIG4_ENCODER_ID 0x0b
#define ASIC_INT_DIG5_ENCODER_ID 0x0c
#define ASIC_INT_DIG6_ENCODER_ID 0x0d
+#define ASIC_INT_DIG7_ENCODER_ID 0x0e
//define Encoder attribute
#define ATOM_ANALOG_ENCODER 0
@@ -3226,15 +3455,23 @@ typedef struct _ATOM_LCD_INFO_V13
UCHAR ucPowerSequenceDIGONtoDE_in4Ms;
UCHAR ucPowerSequenceDEtoVARY_BL_in4Ms;
- UCHAR ucPowerSequenceDEtoDIGON_in4Ms;
UCHAR ucPowerSequenceVARY_BLtoDE_in4Ms;
+ UCHAR ucPowerSequenceDEtoDIGON_in4Ms;
UCHAR ucOffDelay_in4Ms;
UCHAR ucPowerSequenceVARY_BLtoBLON_in4Ms;
UCHAR ucPowerSequenceBLONtoVARY_BL_in4Ms;
UCHAR ucReserved1;
- ULONG ulReserved[4];
+ UCHAR ucDPCD_eDP_CONFIGURATION_CAP; // dpcd 0dh
+ UCHAR ucDPCD_MAX_LINK_RATE; // dpcd 01h
+ UCHAR ucDPCD_MAX_LANE_COUNT; // dpcd 02h
+ UCHAR ucDPCD_MAX_DOWNSPREAD; // dpcd 03h
+
+ USHORT usMaxPclkFreqInSingleLink; // Max PixelClock frequency in single link mode.
+ UCHAR uceDPToLVDSRxId;
+ UCHAR ucLcdReservd;
+ ULONG ulReserved[2];
}ATOM_LCD_INFO_V13;
#define ATOM_LCD_INFO_LAST ATOM_LCD_INFO_V13
@@ -3273,6 +3510,11 @@ typedef struct _ATOM_LCD_INFO_V13
//Use this cap bit for a quick reference whether an embadded panel (LCD1 ) is LVDS or eDP.
#define LCDPANEL_CAP_V13_eDP 0x4 // = LCDPANEL_CAP_eDP no change comparing to previous version
+//uceDPToLVDSRxId
+#define eDP_TO_LVDS_RX_DISABLE 0x00 // no eDP->LVDS translator chip
+#define eDP_TO_LVDS_COMMON_ID 0x01 // common eDP->LVDS translator chip without AMD SW init
+#define eDP_TO_LVDS_RT_ID 0x02 // RT tanslator which require AMD SW init
+
typedef struct _ATOM_PATCH_RECORD_MODE
{
UCHAR ucRecordType;
@@ -3317,6 +3559,7 @@ typedef struct _ATOM_PANEL_RESOLUTION_PATCH_RECORD
#define LCD_CAP_RECORD_TYPE 3
#define LCD_FAKE_EDID_PATCH_RECORD_TYPE 4
#define LCD_PANEL_RESOLUTION_RECORD_TYPE 5
+#define LCD_EDID_OFFSET_PATCH_RECORD_TYPE 6
#define ATOM_RECORD_END_TYPE 0xFF
/****************************Spread Spectrum Info Table Definitions **********************/
@@ -3528,6 +3771,7 @@ else //Non VGA case
CAIL needs to claim an reserved area defined by FBAccessAreaOffset and usFBUsedbyDrvInKB in non VGA case.*/
+/***********************************************************************************/
#define ATOM_MAX_FIRMWARE_VRAM_USAGE_INFO 1
typedef struct _ATOM_FIRMWARE_VRAM_RESERVE_INFO
@@ -3818,13 +4062,17 @@ typedef struct _EXT_DISPLAY_PATH
ATOM_DP_CONN_CHANNEL_MAPPING asDPMapping;
ATOM_DVI_CONN_CHANNEL_MAPPING asDVIMapping;
};
- UCHAR ucReserved;
- USHORT usReserved[2];
+ UCHAR ucChPNInvert; // bit vector for up to 8 lanes, =0: P and N is not invert, =1 P and N is inverted
+ USHORT usCaps;
+ USHORT usReserved;
}EXT_DISPLAY_PATH;
#define NUMBER_OF_UCHAR_FOR_GUID 16
#define MAX_NUMBER_OF_EXT_DISPLAY_PATH 7
+//usCaps
+#define EXT_DISPLAY_PATH_CAPS__HBR2_DISABLE 0x01
+
typedef struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO
{
ATOM_COMMON_TABLE_HEADER sHeader;
@@ -3832,7 +4080,9 @@ typedef struct _ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO
EXT_DISPLAY_PATH sPath[MAX_NUMBER_OF_EXT_DISPLAY_PATH]; // total of fixed 7 entries.
UCHAR ucChecksum; // a simple Checksum of the sum of whole structure equal to 0x0.
UCHAR uc3DStereoPinId; // use for eDP panel
- UCHAR Reserved [6]; // for potential expansion
+ UCHAR ucRemoteDisplayConfig;
+ UCHAR uceDPToLVDSRxId;
+ UCHAR Reserved[4]; // for potential expansion
}ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO;
//Related definitions, all records are different but they have a commond header
@@ -3977,6 +4227,7 @@ typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD
#define GPIO_PIN_STATE_ACTIVE_HIGH 0x1
// Indexes to GPIO array in GLSync record
+// GLSync record is for Frame Lock/Gen Lock feature.
#define ATOM_GPIO_INDEX_GLSYNC_REFCLK 0
#define ATOM_GPIO_INDEX_GLSYNC_HSYNC 1
#define ATOM_GPIO_INDEX_GLSYNC_VSYNC 2
@@ -3984,7 +4235,9 @@ typedef struct _ATOM_OBJECT_GPIO_CNTL_RECORD
#define ATOM_GPIO_INDEX_GLSYNC_SWAP_GNT 4
#define ATOM_GPIO_INDEX_GLSYNC_INTERRUPT 5
#define ATOM_GPIO_INDEX_GLSYNC_V_RESET 6
-#define ATOM_GPIO_INDEX_GLSYNC_MAX 7
+#define ATOM_GPIO_INDEX_GLSYNC_SWAP_CNTL 7
+#define ATOM_GPIO_INDEX_GLSYNC_SWAP_SEL 8
+#define ATOM_GPIO_INDEX_GLSYNC_MAX 9
typedef struct _ATOM_ENCODER_DVO_CF_RECORD
{
@@ -3994,7 +4247,8 @@ typedef struct _ATOM_ENCODER_DVO_CF_RECORD
}ATOM_ENCODER_DVO_CF_RECORD;
// Bit maps for ATOM_ENCODER_CAP_RECORD.ucEncoderCap
-#define ATOM_ENCODER_CAP_RECORD_HBR2 0x01 // DP1.2 HBR2 is supported by this path
+#define ATOM_ENCODER_CAP_RECORD_HBR2 0x01 // DP1.2 HBR2 is supported by HW encoder
+#define ATOM_ENCODER_CAP_RECORD_HBR2_EN 0x02 // DP1.2 HBR2 setting is qualified and HBR2 can be enabled
typedef struct _ATOM_ENCODER_CAP_RECORD
{
@@ -4003,11 +4257,13 @@ typedef struct _ATOM_ENCODER_CAP_RECORD
USHORT usEncoderCap;
struct {
#if ATOM_BIG_ENDIAN
- USHORT usReserved:15; // Bit1-15 may be defined for other capability in future
+ USHORT usReserved:14; // Bit1-15 may be defined for other capability in future
+ USHORT usHBR2En:1; // Bit1 is for DP1.2 HBR2 enable
USHORT usHBR2Cap:1; // Bit0 is for DP1.2 HBR2 capability.
#else
USHORT usHBR2Cap:1; // Bit0 is for DP1.2 HBR2 capability.
- USHORT usReserved:15; // Bit1-15 may be defined for other capability in future
+ USHORT usHBR2En:1; // Bit1 is for DP1.2 HBR2 enable
+ USHORT usReserved:14; // Bit1-15 may be defined for other capability in future
#endif
};
};
@@ -4157,6 +4413,7 @@ typedef struct _ATOM_VOLTAGE_CONTROL
#define VOLTAGE_CONTROL_ID_VT1556M 0x07
#define VOLTAGE_CONTROL_ID_CHL822x 0x08
#define VOLTAGE_CONTROL_ID_VT1586M 0x09
+#define VOLTAGE_CONTROL_ID_UP1637 0x0A
typedef struct _ATOM_VOLTAGE_OBJECT
{
@@ -4193,6 +4450,69 @@ typedef struct _ATOM_LEAKID_VOLTAGE
USHORT usVoltage;
}ATOM_LEAKID_VOLTAGE;
+typedef struct _ATOM_VOLTAGE_OBJECT_HEADER_V3{
+ UCHAR ucVoltageType; //Indicate Voltage Source: VDDC, MVDDC, MVDDQ or MVDDCI
+ UCHAR ucVoltageMode; //Indicate voltage control mode: Init/Set/Leakage/Set phase
+ USHORT usSize; //Size of Object
+}ATOM_VOLTAGE_OBJECT_HEADER_V3;
+
+typedef struct _VOLTAGE_LUT_ENTRY_V2
+{
+ ULONG ulVoltageId; // The Voltage ID which is used to program GPIO register
+ USHORT usVoltageValue; // The corresponding Voltage Value, in mV
+}VOLTAGE_LUT_ENTRY_V2;
+
+typedef struct _LEAKAGE_VOLTAGE_LUT_ENTRY_V2
+{
+ USHORT usVoltageLevel; // The Voltage ID which is used to program GPIO register
+ USHORT usVoltageId;
+ USHORT usLeakageId; // The corresponding Voltage Value, in mV
+}LEAKAGE_VOLTAGE_LUT_ENTRY_V2;
+
+typedef struct _ATOM_I2C_VOLTAGE_OBJECT_V3
+{
+ ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;
+ UCHAR ucVoltageRegulatorId; //Indicate Voltage Regulator Id
+ UCHAR ucVoltageControlI2cLine;
+ UCHAR ucVoltageControlAddress;
+ UCHAR ucVoltageControlOffset;
+ ULONG ulReserved;
+ VOLTAGE_LUT_ENTRY asVolI2cLut[1]; // end with 0xff
+}ATOM_I2C_VOLTAGE_OBJECT_V3;
+
+typedef struct _ATOM_GPIO_VOLTAGE_OBJECT_V3
+{
+ ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;
+ UCHAR ucVoltageGpioCntlId; // default is 0 which indicate control through CG VID mode
+ UCHAR ucGpioEntryNum; // indiate the entry numbers of Votlage/Gpio value Look up table
+ UCHAR ucPhaseDelay; // phase delay in unit of micro second
+ UCHAR ucReserved;
+ ULONG ulGpioMaskVal; // GPIO Mask value
+ VOLTAGE_LUT_ENTRY_V2 asVolGpioLut[1];
+}ATOM_GPIO_VOLTAGE_OBJECT_V3;
+
+typedef struct _ATOM_LEAKAGE_VOLTAGE_OBJECT_V3
+{
+ ATOM_VOLTAGE_OBJECT_HEADER_V3 sHeader;
+ UCHAR ucLeakageCntlId; // default is 0
+ UCHAR ucLeakageEntryNum; // indicate the entry number of LeakageId/Voltage Lut table
+ UCHAR ucReserved[2];
+ ULONG ulMaxVoltageLevel;
+ LEAKAGE_VOLTAGE_LUT_ENTRY_V2 asLeakageIdLut[1];
+}ATOM_LEAKAGE_VOLTAGE_OBJECT_V3;
+
+typedef union _ATOM_VOLTAGE_OBJECT_V3{
+ ATOM_GPIO_VOLTAGE_OBJECT_V3 asGpioVoltageObj;
+ ATOM_I2C_VOLTAGE_OBJECT_V3 asI2cVoltageObj;
+ ATOM_LEAKAGE_VOLTAGE_OBJECT_V3 asLeakageObj;
+}ATOM_VOLTAGE_OBJECT_V3;
+
+typedef struct _ATOM_VOLTAGE_OBJECT_INFO_V3_1
+{
+ ATOM_COMMON_TABLE_HEADER sHeader;
+ ATOM_VOLTAGE_OBJECT_V3 asVoltageObj[3]; //Info for Voltage control
+}ATOM_VOLTAGE_OBJECT_INFO_V3_1;
+
typedef struct _ATOM_ASIC_PROFILE_VOLTAGE
{
UCHAR ucProfileId;
@@ -4305,7 +4625,18 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6
USHORT usHDMISSpreadRateIn10Hz;
USHORT usDVISSPercentage;
USHORT usDVISSpreadRateIn10Hz;
- ULONG ulReserved3[21];
+ ULONG SclkDpmBoostMargin;
+ ULONG SclkDpmThrottleMargin;
+ USHORT SclkDpmTdpLimitPG;
+ USHORT SclkDpmTdpLimitBoost;
+ ULONG ulBoostEngineCLock;
+ UCHAR ulBoostVid_2bit;
+ UCHAR EnableBoost;
+ USHORT GnbTdpLimit;
+ USHORT usMaxLVDSPclkFreqInSingleLink;
+ UCHAR ucLvdsMisc;
+ UCHAR ucLVDSReserved;
+ ULONG ulReserved3[15];
ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo;
}ATOM_INTEGRATED_SYSTEM_INFO_V6;
@@ -4313,9 +4644,16 @@ typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V6
#define INTEGRATED_SYSTEM_INFO_V6_GPUCAPINFO__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01
#define INTEGRATED_SYSTEM_INFO_V6_GPUCAPINFO__DISABLE_AUX_HW_MODE_DETECTION 0x08
-// ulOtherDisplayMisc
-#define INTEGRATED_SYSTEM_INFO__GET_EDID_CALLBACK_FUNC_SUPPORT 0x01
+//ucLVDSMisc:
+#define SYS_INFO_LVDSMISC__888_FPDI_MODE 0x01
+#define SYS_INFO_LVDSMISC__DL_CH_SWAP 0x02
+#define SYS_INFO_LVDSMISC__888_BPC 0x04
+#define SYS_INFO_LVDSMISC__OVERRIDE_EN 0x08
+#define SYS_INFO_LVDSMISC__BLON_ACTIVE_LOW 0x10
+// not used any more
+#define SYS_INFO_LVDSMISC__VSYNC_ACTIVE_LOW 0x04
+#define SYS_INFO_LVDSMISC__HSYNC_ACTIVE_LOW 0x08
/**********************************************************************************************************************
ATOM_INTEGRATED_SYSTEM_INFO_V6 Description
@@ -4384,7 +4722,208 @@ ucUMAChannelNumber: System memory channel numbers.
ulCSR_M3_ARB_CNTL_DEFAULT[10]: Arrays with values for CSR M3 arbiter for default
ulCSR_M3_ARB_CNTL_UVD[10]: Arrays with values for CSR M3 arbiter for UVD playback.
ulCSR_M3_ARB_CNTL_FS3D[10]: Arrays with values for CSR M3 arbiter for Full Screen 3D applications.
-sAvail_SCLK[5]: Arrays to provide available list of SLCK and corresponding voltage, order from low to high
+sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high
+ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns.
+ulMinimumNClk: Minimum NCLK speed among all NB-Pstates to calcualte data reconnection latency. Unit in 10kHz.
+ulIdleNClk: NCLK speed while memory runs in self-refresh state. Unit in 10kHz.
+ulDDR_DLL_PowerUpTime: DDR PHY DLL power up time. Unit in ns.
+ulDDR_PLL_PowerUpTime: DDR PHY PLL power up time. Unit in ns.
+usPCIEClkSSPercentage: PCIE Clock Spred Spectrum Percentage in unit 0.01%; 100 mean 1%.
+usPCIEClkSSType: PCIE Clock Spred Spectrum Type. 0 for Down spread(default); 1 for Center spread.
+usLvdsSSPercentage: LVDS panel ( not include eDP ) Spread Spectrum Percentage in unit of 0.01%, =0, use VBIOS default setting.
+usLvdsSSpreadRateIn10Hz: LVDS panel ( not include eDP ) Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting.
+usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting.
+usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting.
+usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting.
+usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting.
+usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz
+ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode
+ [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped
+ [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color
+ [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used
+ [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low )
+**********************************************************************************************************************/
+
+// this Table is used for Liano/Ontario APU
+typedef struct _ATOM_FUSION_SYSTEM_INFO_V1
+{
+ ATOM_INTEGRATED_SYSTEM_INFO_V6 sIntegratedSysInfo;
+ ULONG ulPowerplayTable[128];
+}ATOM_FUSION_SYSTEM_INFO_V1;
+/**********************************************************************************************************************
+ ATOM_FUSION_SYSTEM_INFO_V1 Description
+sIntegratedSysInfo: refer to ATOM_INTEGRATED_SYSTEM_INFO_V6 definition.
+ulPowerplayTable[128]: This 512 bytes memory is used to save ATOM_PPLIB_POWERPLAYTABLE3, starting form ulPowerplayTable[0]
+**********************************************************************************************************************/
+
+// this IntegrateSystemInfoTable is used for Trinity APU
+typedef struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7
+{
+ ATOM_COMMON_TABLE_HEADER sHeader;
+ ULONG ulBootUpEngineClock;
+ ULONG ulDentistVCOFreq;
+ ULONG ulBootUpUMAClock;
+ ATOM_CLK_VOLT_CAPABILITY sDISPCLK_Voltage[4];
+ ULONG ulBootUpReqDisplayVector;
+ ULONG ulOtherDisplayMisc;
+ ULONG ulGPUCapInfo;
+ ULONG ulSB_MMIO_Base_Addr;
+ USHORT usRequestedPWMFreqInHz;
+ UCHAR ucHtcTmpLmt;
+ UCHAR ucHtcHystLmt;
+ ULONG ulMinEngineClock;
+ ULONG ulSystemConfig;
+ ULONG ulCPUCapInfo;
+ USHORT usNBP0Voltage;
+ USHORT usNBP1Voltage;
+ USHORT usBootUpNBVoltage;
+ USHORT usExtDispConnInfoOffset;
+ USHORT usPanelRefreshRateRange;
+ UCHAR ucMemoryType;
+ UCHAR ucUMAChannelNumber;
+ UCHAR strVBIOSMsg[40];
+ ULONG ulReserved[20];
+ ATOM_AVAILABLE_SCLK_LIST sAvail_SCLK[5];
+ ULONG ulGMCRestoreResetTime;
+ ULONG ulMinimumNClk;
+ ULONG ulIdleNClk;
+ ULONG ulDDR_DLL_PowerUpTime;
+ ULONG ulDDR_PLL_PowerUpTime;
+ USHORT usPCIEClkSSPercentage;
+ USHORT usPCIEClkSSType;
+ USHORT usLvdsSSPercentage;
+ USHORT usLvdsSSpreadRateIn10Hz;
+ USHORT usHDMISSPercentage;
+ USHORT usHDMISSpreadRateIn10Hz;
+ USHORT usDVISSPercentage;
+ USHORT usDVISSpreadRateIn10Hz;
+ ULONG SclkDpmBoostMargin;
+ ULONG SclkDpmThrottleMargin;
+ USHORT SclkDpmTdpLimitPG;
+ USHORT SclkDpmTdpLimitBoost;
+ ULONG ulBoostEngineCLock;
+ UCHAR ulBoostVid_2bit;
+ UCHAR EnableBoost;
+ USHORT GnbTdpLimit;
+ USHORT usMaxLVDSPclkFreqInSingleLink;
+ UCHAR ucLvdsMisc;
+ UCHAR ucLVDSReserved;
+ UCHAR ucLVDSPwrOnSeqDIGONtoDE_in4Ms;
+ UCHAR ucLVDSPwrOnSeqDEtoVARY_BL_in4Ms;
+ UCHAR ucLVDSPwrOffSeqVARY_BLtoDE_in4Ms;
+ UCHAR ucLVDSPwrOffSeqDEtoDIGON_in4Ms;
+ UCHAR ucLVDSOffToOnDelay_in4Ms;
+ UCHAR ucLVDSPwrOnSeqVARY_BLtoBLON_in4Ms;
+ UCHAR ucLVDSPwrOffSeqBLONtoVARY_BL_in4Ms;
+ UCHAR ucLVDSReserved1;
+ ULONG ulLCDBitDepthControlVal;
+ ULONG ulNbpStateMemclkFreq[4];
+ USHORT usNBP2Voltage;
+ USHORT usNBP3Voltage;
+ ULONG ulNbpStateNClkFreq[4];
+ UCHAR ucNBDPMEnable;
+ UCHAR ucReserved[3];
+ UCHAR ucDPMState0VclkFid;
+ UCHAR ucDPMState0DclkFid;
+ UCHAR ucDPMState1VclkFid;
+ UCHAR ucDPMState1DclkFid;
+ UCHAR ucDPMState2VclkFid;
+ UCHAR ucDPMState2DclkFid;
+ UCHAR ucDPMState3VclkFid;
+ UCHAR ucDPMState3DclkFid;
+ ATOM_EXTERNAL_DISPLAY_CONNECTION_INFO sExtDispConnInfo;
+}ATOM_INTEGRATED_SYSTEM_INFO_V1_7;
+
+// ulOtherDisplayMisc
+#define INTEGRATED_SYSTEM_INFO__GET_EDID_CALLBACK_FUNC_SUPPORT 0x01
+#define INTEGRATED_SYSTEM_INFO__GET_BOOTUP_DISPLAY_CALLBACK_FUNC_SUPPORT 0x02
+#define INTEGRATED_SYSTEM_INFO__GET_EXPANSION_CALLBACK_FUNC_SUPPORT 0x04
+#define INTEGRATED_SYSTEM_INFO__FAST_BOOT_SUPPORT 0x08
+
+// ulGPUCapInfo
+#define SYS_INFO_GPUCAPS__TMDSHDMI_COHERENT_SINGLEPLL_MODE 0x01
+#define SYS_INFO_GPUCAPS__DP_SINGLEPLL_MODE 0x02
+#define SYS_INFO_GPUCAPS__DISABLE_AUX_MODE_DETECT 0x08
+
+/**********************************************************************************************************************
+ ATOM_INTEGRATED_SYSTEM_INFO_V1_7 Description
+ulBootUpEngineClock: VBIOS bootup Engine clock frequency, in 10kHz unit. if it is equal 0, then VBIOS use pre-defined bootup engine clock
+ulDentistVCOFreq: Dentist VCO clock in 10kHz unit.
+ulBootUpUMAClock: System memory boot up clock frequency in 10Khz unit.
+sDISPCLK_Voltage: Report Display clock voltage requirement.
+
+ulBootUpReqDisplayVector: VBIOS boot up display IDs, following are supported devices in Trinity projects:
+ ATOM_DEVICE_CRT1_SUPPORT 0x0001
+ ATOM_DEVICE_DFP1_SUPPORT 0x0008
+ ATOM_DEVICE_DFP6_SUPPORT 0x0040
+ ATOM_DEVICE_DFP2_SUPPORT 0x0080
+ ATOM_DEVICE_DFP3_SUPPORT 0x0200
+ ATOM_DEVICE_DFP4_SUPPORT 0x0400
+ ATOM_DEVICE_DFP5_SUPPORT 0x0800
+ ATOM_DEVICE_LCD1_SUPPORT 0x0002
+ulOtherDisplayMisc: bit[0]=0: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is not supported by SBIOS.
+ =1: INT15 callback function Get LCD EDID ( ax=4e08, bl=1b ) is supported by SBIOS.
+ bit[1]=0: INT15 callback function Get boot display( ax=4e08, bl=01h) is not supported by SBIOS
+ =1: INT15 callback function Get boot display( ax=4e08, bl=01h) is supported by SBIOS
+ bit[2]=0: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is not supported by SBIOS
+ =1: INT15 callback function Get panel Expansion ( ax=4e08, bl=02h) is supported by SBIOS
+ bit[3]=0: VBIOS fast boot is disable
+ =1: VBIOS fast boot is enable. ( VBIOS skip display device detection in every set mode if LCD panel is connect and LID is open)
+ulGPUCapInfo: bit[0]=0: TMDS/HDMI Coherent Mode use cascade PLL mode.
+ =1: TMDS/HDMI Coherent Mode use signel PLL mode.
+ bit[1]=0: DP mode use cascade PLL mode ( New for Trinity )
+ =1: DP mode use single PLL mode
+ bit[3]=0: Enable AUX HW mode detection logic
+ =1: Disable AUX HW mode detection logic
+
+ulSB_MMIO_Base_Addr: Physical Base address to SB MMIO space. Driver needs to initialize it for SMU usage.
+
+usRequestedPWMFreqInHz: When it's set to 0x0 by SBIOS: the LCD BackLight is not controlled by GPU(SW).
+ Any attempt to change BL using VBIOS function or enable VariBri from PP table is not effective since ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==0;
+
+ When it's set to a non-zero frequency, the BackLight is controlled by GPU (SW) in one of two ways below:
+ 1. SW uses the GPU BL PWM output to control the BL, in chis case, this non-zero frequency determines what freq GPU should use;
+ VBIOS will set up proper PWM frequency and ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1,as the result,
+ Changing BL using VBIOS function is functional in both driver and non-driver present environment;
+ and enabling VariBri under the driver environment from PP table is optional.
+
+ 2. SW uses other means to control BL (like DPCD),this non-zero frequency serves as a flag only indicating
+ that BL control from GPU is expected.
+ VBIOS will NOT set up PWM frequency but make ATOM_BIOS_INFO_BL_CONTROLLED_BY_GPU==1
+ Changing BL using VBIOS function could be functional in both driver and non-driver present environment,but
+ it's per platform
+ and enabling VariBri under the driver environment from PP table is optional.
+
+ucHtcTmpLmt: Refer to D18F3x64 bit[22:16], HtcTmpLmt.
+ Threshold on value to enter HTC_active state.
+ucHtcHystLmt: Refer to D18F3x64 bit[27:24], HtcHystLmt.
+ To calculate threshold off value to exit HTC_active state, which is Threshold on vlaue minus ucHtcHystLmt.
+ulMinEngineClock: Minimum SCLK allowed in 10kHz unit. This is calculated based on WRCK Fuse settings.
+ulSystemConfig: Bit[0]=0: PCIE Power Gating Disabled
+ =1: PCIE Power Gating Enabled
+ Bit[1]=0: DDR-DLL shut-down feature disabled.
+ 1: DDR-DLL shut-down feature enabled.
+ Bit[2]=0: DDR-PLL Power down feature disabled.
+ 1: DDR-PLL Power down feature enabled.
+ulCPUCapInfo: TBD
+usNBP0Voltage: VID for voltage on NB P0 State
+usNBP1Voltage: VID for voltage on NB P1 State
+usNBP2Voltage: VID for voltage on NB P2 State
+usNBP3Voltage: VID for voltage on NB P3 State
+usBootUpNBVoltage: Voltage Index of GNB voltage configured by SBIOS, which is suffcient to support VBIOS DISPCLK requirement.
+usExtDispConnInfoOffset: Offset to sExtDispConnInfo inside the structure
+usPanelRefreshRateRange: Bit vector for LCD supported refresh rate range. If DRR is requestd by the platform, at least two bits need to be set
+ to indicate a range.
+ SUPPORTED_LCD_REFRESHRATE_30Hz 0x0004
+ SUPPORTED_LCD_REFRESHRATE_40Hz 0x0008
+ SUPPORTED_LCD_REFRESHRATE_50Hz 0x0010
+ SUPPORTED_LCD_REFRESHRATE_60Hz 0x0020
+ucMemoryType: [3:0]=1:DDR1;=2:DDR2;=3:DDR3.[7:4] is reserved.
+ucUMAChannelNumber: System memory channel numbers.
+ulCSR_M3_ARB_CNTL_DEFAULT[10]: Arrays with values for CSR M3 arbiter for default
+ulCSR_M3_ARB_CNTL_UVD[10]: Arrays with values for CSR M3 arbiter for UVD playback.
+ulCSR_M3_ARB_CNTL_FS3D[10]: Arrays with values for CSR M3 arbiter for Full Screen 3D applications.
+sAvail_SCLK[5]: Arrays to provide availabe list of SLCK and corresponding voltage, order from low to high
ulGMCRestoreResetTime: GMC power restore and GMC reset time to calculate data reconnection latency. Unit in ns.
ulMinimumNClk: Minimum NCLK speed among all NB-Pstates to calcualte data reconnection latency. Unit in 10kHz.
ulIdleNClk: NCLK speed while memory runs in self-refresh state. Unit in 10kHz.
@@ -4398,6 +4937,41 @@ usHDMISSPercentage: HDMI Spread Spectrum Percentage in unit 0.01%;
usHDMISSpreadRateIn10Hz: HDMI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting.
usDVISSPercentage: DVI Spread Spectrum Percentage in unit 0.01%; 100 mean 1%, =0, use VBIOS default setting.
usDVISSpreadRateIn10Hz: DVI Spread Spectrum frequency in unit of 10Hz, =0, use VBIOS default setting.
+usMaxLVDSPclkFreqInSingleLink: Max pixel clock LVDS panel single link, if=0 means VBIOS use default threhold, right now it is 85Mhz
+ucLVDSMisc: [bit0] LVDS 888bit panel mode =0: LVDS 888 panel in LDI mode, =1: LVDS 888 panel in FPDI mode
+ [bit1] LVDS panel lower and upper link mapping =0: lower link and upper link not swap, =1: lower link and upper link are swapped
+ [bit2] LVDS 888bit per color mode =0: 666 bit per color =1:888 bit per color
+ [bit3] LVDS parameter override enable =0: ucLvdsMisc parameter are not used =1: ucLvdsMisc parameter should be used
+ [bit4] Polarity of signal sent to digital BLON output pin. =0: not inverted(active high) =1: inverted ( active low )
+ucLVDSPwrOnSeqDIGONtoDE_in4Ms: LVDS power up sequence time in unit of 4ms, time delay from DIGON signal active to data enable signal active( DE ).
+ =0 mean use VBIOS default which is 8 ( 32ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON.
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+ucLVDSPwrOnDEtoVARY_BL_in4Ms: LVDS power up sequence time in unit of 4ms., time delay from DE( data enable ) active to Vary Brightness enable signal active( VARY_BL ).
+ =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power up sequence is as following: DIGON->DE->VARY_BL->BLON.
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ucLVDSPwrOffVARY_BLtoDE_in4Ms: LVDS power down sequence time in unit of 4ms, time delay from data enable ( DE ) signal off to LCDVCC (DIGON) off.
+ =0 mean use VBIOS default delay which is 8 ( 32ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ucLVDSPwrOffDEtoDIGON_in4Ms: LVDS power down sequence time in unit of 4ms, time delay from vary brightness enable signal( VARY_BL) off to data enable ( DE ) signal off.
+ =0 mean use VBIOS default which is 90 ( 360ms ). The LVDS power down sequence is as following: BLON->VARY_BL->DE->DIGON
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ucLVDSOffToOnDelay_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from DIGON signal off to DIGON signal active.
+ =0 means to use VBIOS default delay which is 125 ( 500ms ).
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ucLVDSPwrOnVARY_BLtoBLON_in4Ms: LVDS power up sequence time in unit of 4ms. Time delay from VARY_BL signal on to DLON signal active.
+ =0 means to use VBIOS default delay which is 0 ( 0ms ).
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ucLVDSPwrOffBLONtoVARY_BL_in4Ms: LVDS power down sequence time in unit of 4ms. Time delay from BLON signal off to VARY_BL signal off.
+ =0 means to use VBIOS default delay which is 0 ( 0ms ).
+ This parameter is used by VBIOS only. VBIOS will patch LVDS_InfoTable.
+
+ulNbpStateMemclkFreq[4]: system memory clock frequncey in unit of 10Khz in different NB pstate.
+
**********************************************************************************************************************/
/**************************************************************************/
@@ -4459,6 +5033,7 @@ typedef struct _ATOM_ASIC_SS_ASSIGNMENT
#define ASIC_INTERNAL_SS_ON_DP 7
#define ASIC_INTERNAL_SS_ON_DCPLL 8
#define ASIC_EXTERNAL_SS_ON_DP_CLOCK 9
+#define ASIC_INTERNAL_VCE_SS 10
typedef struct _ATOM_ASIC_SS_ASSIGNMENT_V2
{
@@ -4520,7 +5095,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3
#define ATOM_DOS_MODE_INFO_DEF 7
#define ATOM_I2C_CHANNEL_STATUS_DEF 8
#define ATOM_I2C_CHANNEL_STATUS1_DEF 9
-
+#define ATOM_INTERNAL_TIMER_DEF 10
// BIOS_0_SCRATCH Definition
#define ATOM_S0_CRT1_MONO 0x00000001L
@@ -4648,6 +5223,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO_V3
#define ATOM_S2_DEVICE_DPMS_MASKw1 0x3FF
#define ATOM_S2_FORCEDLOWPWRMODE_STATE_MASKb3 0x0C
#define ATOM_S2_FORCEDLOWPWRMODE_STATE_CHANGEb3 0x10
+#define ATOM_S2_TMDS_COHERENT_MODEb3 0x10 // used by VBIOS code only, use coherent mode for TMDS/HDMI mode
#define ATOM_S2_VRI_BRIGHT_ENABLEb3 0x20
#define ATOM_S2_ROTATION_STATE_MASKb3 0xC0
@@ -5038,6 +5614,23 @@ typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3
USHORT usDeviceId; // Active Device Id for this surface. If no device, set to 0.
}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_3;
+typedef struct _ENABLE_GRAPH_SURFACE_PARAMETERS_V1_4
+{
+ USHORT usHight; // Image Hight
+ USHORT usWidth; // Image Width
+ USHORT usGraphPitch;
+ UCHAR ucColorDepth;
+ UCHAR ucPixelFormat;
+ UCHAR ucSurface; // Surface 1 or 2
+ UCHAR ucEnable; // ATOM_ENABLE or ATOM_DISABLE
+ UCHAR ucModeType;
+ UCHAR ucReserved;
+}ENABLE_GRAPH_SURFACE_PARAMETERS_V1_4;
+
+// ucEnable
+#define ATOM_GRAPH_CONTROL_SET_PITCH 0x0f
+#define ATOM_GRAPH_CONTROL_SET_DISP_START 0x10
+
typedef struct _ENABLE_GRAPH_SURFACE_PS_ALLOCATION
{
ENABLE_GRAPH_SURFACE_PARAMETERS sSetSurface;
@@ -5057,6 +5650,58 @@ typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS
USHORT usY_Size;
}GET_DISPLAY_SURFACE_SIZE_PARAMETERS;
+typedef struct _GET_DISPLAY_SURFACE_SIZE_PARAMETERS_V2
+{
+ union{
+ USHORT usX_Size; //When use as input parameter, usX_Size indicates which CRTC
+ USHORT usSurface;
+ };
+ USHORT usY_Size;
+ USHORT usDispXStart;
+ USHORT usDispYStart;
+}GET_DISPLAY_SURFACE_SIZE_PARAMETERS_V2;
+
+
+typedef struct _PALETTE_DATA_CONTROL_PARAMETERS_V3
+{
+ UCHAR ucLutId;
+ UCHAR ucAction;
+ USHORT usLutStartIndex;
+ USHORT usLutLength;
+ USHORT usLutOffsetInVram;
+}PALETTE_DATA_CONTROL_PARAMETERS_V3;
+
+// ucAction:
+#define PALETTE_DATA_AUTO_FILL 1
+#define PALETTE_DATA_READ 2
+#define PALETTE_DATA_WRITE 3
+
+
+typedef struct _INTERRUPT_SERVICE_PARAMETERS_V2
+{
+ UCHAR ucInterruptId;
+ UCHAR ucServiceId;
+ UCHAR ucStatus;
+ UCHAR ucReserved;
+}INTERRUPT_SERVICE_PARAMETER_V2;
+
+// ucInterruptId
+#define HDP1_INTERRUPT_ID 1
+#define HDP2_INTERRUPT_ID 2
+#define HDP3_INTERRUPT_ID 3
+#define HDP4_INTERRUPT_ID 4
+#define HDP5_INTERRUPT_ID 5
+#define HDP6_INTERRUPT_ID 6
+#define SW_INTERRUPT_ID 11
+
+// ucAction
+#define INTERRUPT_SERVICE_GEN_SW_INT 1
+#define INTERRUPT_SERVICE_GET_STATUS 2
+
+ // ucStatus
+#define INTERRUPT_STATUS__INT_TRIGGER 1
+#define INTERRUPT_STATUS__HPD_HIGH 2
+
typedef struct _INDIRECT_IO_ACCESS
{
ATOM_COMMON_TABLE_HEADER sHeader;
@@ -5189,7 +5834,7 @@ typedef struct _ATOM_INIT_REG_BLOCK{
#define END_OF_REG_INDEX_BLOCK 0x0ffff
#define END_OF_REG_DATA_BLOCK 0x00000000
-#define ATOM_INIT_REG_MASK_FLAG 0x80
+#define ATOM_INIT_REG_MASK_FLAG 0x80 //Not used in BIOS
#define CLOCK_RANGE_HIGHEST 0x00ffffff
#define VALUE_DWORD SIZEOF ULONG
@@ -5229,6 +5874,7 @@ typedef struct _ATOM_MC_INIT_PARAM_TABLE
#define _128Mx8 0x51
#define _128Mx16 0x52
#define _256Mx8 0x61
+#define _256Mx16 0x62
#define SAMSUNG 0x1
#define INFINEON 0x2
@@ -5585,7 +6231,7 @@ typedef struct _ATOM_VRAM_MODULE_V7
ULONG ulChannelMapCfg; // mmMC_SHARED_CHREMAP
USHORT usModuleSize; // Size of ATOM_VRAM_MODULE_V7
USHORT usPrivateReserved; // MC_ARB_RAMCFG (includes NOOFBANK,NOOFRANKS,NOOFROWS,NOOFCOLS)
- USHORT usReserved;
+ USHORT usEnableChannels; // bit vector which indicate which channels are enabled
UCHAR ucExtMemoryID; // Current memory module ID
UCHAR ucMemoryType; // MEM_TYPE_DDR2/DDR3/GDDR3/GDDR5
UCHAR ucChannelNum; // Number of mem. channels supported in this module
@@ -5597,7 +6243,8 @@ typedef struct _ATOM_VRAM_MODULE_V7
UCHAR ucNPL_RT; // Round trip delay (MC_SEQ_CAS_TIMING [28:24]:TCL=CL+NPL_RT-2). Always 2.
UCHAR ucPreamble; // [7:4] Write Preamble, [3:0] Read Preamble
UCHAR ucMemorySize; // Total memory size in unit of 16MB for CONFIG_MEMSIZE - bit[23:0] zeros
- UCHAR ucReserved[3];
+ USHORT usSEQSettingOffset;
+ UCHAR ucReserved;
// Memory Module specific values
USHORT usEMRS2Value; // EMRS2/MR2 Value.
USHORT usEMRS3Value; // EMRS3/MR3 Value.
@@ -5633,10 +6280,10 @@ typedef struct _ATOM_VRAM_INFO_V3
typedef struct _ATOM_VRAM_INFO_V4
{
ATOM_COMMON_TABLE_HEADER sHeader;
- USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting
- USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting
- USHORT usRerseved;
- UCHAR ucMemDQ7_0ByteRemap; // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3
+ USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting
+ USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting
+ USHORT usRerseved;
+ UCHAR ucMemDQ7_0ByteRemap; // DQ line byte remap, =0: Memory Data line BYTE0, =1: BYTE1, =2: BYTE2, =3: BYTE3
ULONG ulMemDQ7_0BitRemap; // each DQ line ( 7~0) use 3bits, like: DQ0=Bit[2:0], DQ1:[5:3], ... DQ7:[23:21]
UCHAR ucReservde[4];
UCHAR ucNumOfVRAMModule;
@@ -5648,9 +6295,10 @@ typedef struct _ATOM_VRAM_INFO_V4
typedef struct _ATOM_VRAM_INFO_HEADER_V2_1
{
ATOM_COMMON_TABLE_HEADER sHeader;
- USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting
- USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting
- USHORT usReserved[4];
+ USHORT usMemAdjustTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory vendor specific MC adjust setting
+ USHORT usMemClkPatchTblOffset; // offset of ATOM_INIT_REG_BLOCK structure for memory clock specific MC setting
+ USHORT usPerBytePresetOffset; // offset of ATOM_INIT_REG_BLOCK structure for Per Byte Offset Preset Settings
+ USHORT usReserved[3];
UCHAR ucNumOfVRAMModule; // indicate number of VRAM module
UCHAR ucMemoryClkPatchTblVer; // version of memory AC timing register list
UCHAR ucVramModuleVer; // indicate ATOM_VRAM_MODUE version
@@ -5935,6 +6583,52 @@ typedef struct _ATOM_DISP_OUT_INFO_V2
ASIC_ENCODER_INFO asEncoderInfo[1];
}ATOM_DISP_OUT_INFO_V2;
+
+typedef struct _ATOM_DISP_CLOCK_ID {
+ UCHAR ucPpllId;
+ UCHAR ucPpllAttribute;
+}ATOM_DISP_CLOCK_ID;
+
+// ucPpllAttribute
+#define CLOCK_SOURCE_SHAREABLE 0x01
+#define CLOCK_SOURCE_DP_MODE 0x02
+#define CLOCK_SOURCE_NONE_DP_MODE 0x04
+
+//DispOutInfoTable
+typedef struct _ASIC_TRANSMITTER_INFO_V2
+{
+ USHORT usTransmitterObjId;
+ USHORT usDispClkIdOffset; // point to clock source id list supported by Encoder Object
+ UCHAR ucTransmitterCmdTblId;
+ UCHAR ucConfig;
+ UCHAR ucEncoderID; // available 1st encoder ( default )
+ UCHAR ucOptionEncoderID; // available 2nd encoder ( optional )
+ UCHAR uc2ndEncoderID;
+ UCHAR ucReserved;
+}ASIC_TRANSMITTER_INFO_V2;
+
+typedef struct _ATOM_DISP_OUT_INFO_V3
+{
+ ATOM_COMMON_TABLE_HEADER sHeader;
+ USHORT ptrTransmitterInfo;
+ USHORT ptrEncoderInfo;
+ USHORT ptrMainCallParserFar; // direct address of main parser call in VBIOS binary.
+ USHORT usReserved;
+ UCHAR ucDCERevision;
+ UCHAR ucMaxDispEngineNum;
+ UCHAR ucMaxActiveDispEngineNum;
+ UCHAR ucMaxPPLLNum;
+ UCHAR ucCoreRefClkSource; // value of CORE_REF_CLK_SOURCE
+ UCHAR ucReserved[3];
+ ASIC_TRANSMITTER_INFO_V2 asTransmitterInfo[1]; // for alligment only
+}ATOM_DISP_OUT_INFO_V3;
+
+typedef enum CORE_REF_CLK_SOURCE{
+ CLOCK_SRC_XTALIN=0,
+ CLOCK_SRC_XO_IN=1,
+ CLOCK_SRC_XO_IN2=2,
+}CORE_REF_CLK_SOURCE;
+
// DispDevicePriorityInfo
typedef struct _ATOM_DISPLAY_DEVICE_PRIORITY_INFO
{
@@ -6070,6 +6764,39 @@ typedef struct _PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS
#define HW_I2C_READ 0
#define I2C_2BYTE_ADDR 0x02
+/****************************************************************************/
+// Structures used by HW_Misc_OperationTable
+/****************************************************************************/
+typedef struct _ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1
+{
+ UCHAR ucCmd; // Input: To tell which action to take
+ UCHAR ucReserved[3];
+ ULONG ulReserved;
+}ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1;
+
+typedef struct _ATOM_HW_MISC_OPERATION_OUTPUT_PARAMETER_V1_1
+{
+ UCHAR ucReturnCode; // Output: Return value base on action was taken
+ UCHAR ucReserved[3];
+ ULONG ulReserved;
+}ATOM_HW_MISC_OPERATION_OUTPUT_PARAMETER_V1_1;
+
+// Actions code
+#define ATOM_GET_SDI_SUPPORT 0xF0
+
+// Return code
+#define ATOM_UNKNOWN_CMD 0
+#define ATOM_FEATURE_NOT_SUPPORTED 1
+#define ATOM_FEATURE_SUPPORTED 2
+
+typedef struct _ATOM_HW_MISC_OPERATION_PS_ALLOCATION
+{
+ ATOM_HW_MISC_OPERATION_INPUT_PARAMETER_V1_1 sInput_Output;
+ PROCESS_I2C_CHANNEL_TRANSACTION_PARAMETERS sReserved;
+}ATOM_HW_MISC_OPERATION_PS_ALLOCATION;
+
+/****************************************************************************/
+
typedef struct _SET_HWBLOCK_INSTANCE_PARAMETER_V2
{
UCHAR ucHWBlkInst; // HW block instance, 0, 1, 2, ...
@@ -6090,6 +6817,52 @@ typedef struct _SET_HWBLOCK_INSTANCE_PARAMETER_V2
#define SELECT_CRTC_PIXEL_RATE 7
#define SELECT_VGA_BLK 8
+// DIGTransmitterInfoTable structure used to program UNIPHY settings
+typedef struct _DIG_TRANSMITTER_INFO_HEADER_V3_1{
+ ATOM_COMMON_TABLE_HEADER sHeader;
+ USHORT usDPVsPreEmphSettingOffset; // offset of PHY_ANALOG_SETTING_INFO * with DP Voltage Swing and Pre-Emphasis for each Link clock
+ USHORT usPhyAnalogRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with None-DP mode Analog Setting's register Info
+ USHORT usPhyAnalogSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with None-DP mode Analog Setting for each link clock range
+ USHORT usPhyPllRegListOffset; // offset of CLOCK_CONDITION_REGESTER_INFO* with Phy Pll register Info
+ USHORT usPhyPllSettingOffset; // offset of CLOCK_CONDITION_SETTING_ENTRY* with Phy Pll Settings
+}DIG_TRANSMITTER_INFO_HEADER_V3_1;
+
+typedef struct _CLOCK_CONDITION_REGESTER_INFO{
+ USHORT usRegisterIndex;
+ UCHAR ucStartBit;
+ UCHAR ucEndBit;
+}CLOCK_CONDITION_REGESTER_INFO;
+
+typedef struct _CLOCK_CONDITION_SETTING_ENTRY{
+ USHORT usMaxClockFreq;
+ UCHAR ucEncodeMode;
+ UCHAR ucPhySel;
+ ULONG ulAnalogSetting[1];
+}CLOCK_CONDITION_SETTING_ENTRY;
+
+typedef struct _CLOCK_CONDITION_SETTING_INFO{
+ USHORT usEntrySize;
+ CLOCK_CONDITION_SETTING_ENTRY asClkCondSettingEntry[1];
+}CLOCK_CONDITION_SETTING_INFO;
+
+typedef struct _PHY_CONDITION_REG_VAL{
+ ULONG ulCondition;
+ ULONG ulRegVal;
+}PHY_CONDITION_REG_VAL;
+
+typedef struct _PHY_CONDITION_REG_INFO{
+ USHORT usRegIndex;
+ USHORT usSize;
+ PHY_CONDITION_REG_VAL asRegVal[1];
+}PHY_CONDITION_REG_INFO;
+
+typedef struct _PHY_ANALOG_SETTING_INFO{
+ UCHAR ucEncodeMode;
+ UCHAR ucPhySel;
+ USHORT usSize;
+ PHY_CONDITION_REG_INFO asAnalogSetting[1];
+}PHY_ANALOG_SETTING_INFO;
+
/****************************************************************************/
//Portion VI: Definitinos for vbios MC scratch registers that driver used
/****************************************************************************/
@@ -6497,6 +7270,8 @@ typedef struct _ATOM_PPLIB_THERMALCONTROLLER
#define ATOM_PP_THERMALCONTROLLER_EMC2103 13 /* 0x0D */ // Only fan control will be implemented, do NOT show this in PPGen.
#define ATOM_PP_THERMALCONTROLLER_SUMO 14 /* 0x0E */ // Sumo type, used internally
#define ATOM_PP_THERMALCONTROLLER_NISLANDS 15
+#define ATOM_PP_THERMALCONTROLLER_SISLANDS 16
+#define ATOM_PP_THERMALCONTROLLER_LM96163 17
// Thermal controller 'combo type' to use an external controller for Fan control and an internal controller for thermal.
// We probably should reserve the bit 0x80 for this use.
@@ -6512,6 +7287,7 @@ typedef struct _ATOM_PPLIB_STATE
UCHAR ucClockStateIndices[1]; // variable-sized
} ATOM_PPLIB_STATE;
+
typedef struct _ATOM_PPLIB_FANTABLE
{
UCHAR ucFanTableFormat; // Change this if the table format changes or version changes so that the other fields are not the same.
@@ -6524,12 +7300,20 @@ typedef struct _ATOM_PPLIB_FANTABLE
USHORT usPWMHigh; // The PWM value at THigh.
} ATOM_PPLIB_FANTABLE;
+typedef struct _ATOM_PPLIB_FANTABLE2
+{
+ ATOM_PPLIB_FANTABLE basicTable;
+ USHORT usTMax; // The max temperature
+} ATOM_PPLIB_FANTABLE2;
+
typedef struct _ATOM_PPLIB_EXTENDEDHEADER
{
USHORT usSize;
ULONG ulMaxEngineClock; // For Overdrive.
ULONG ulMaxMemoryClock; // For Overdrive.
// Add extra system parameters here, always adjust size to include all fields.
+ USHORT usVCETableOffset; //points to ATOM_PPLIB_VCE_Table
+ USHORT usUVDTableOffset; //points to ATOM_PPLIB_UVD_Table
} ATOM_PPLIB_EXTENDEDHEADER;
//// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps
@@ -6552,6 +7336,7 @@ typedef struct _ATOM_PPLIB_EXTENDEDHEADER
#define ATOM_PP_PLATFORM_CAP_REGULATOR_HOT 0x00010000 // Enable the 'regulator hot' feature.
#define ATOM_PP_PLATFORM_CAP_BACO 0x00020000 // Does the driver supports BACO state.
+
typedef struct _ATOM_PPLIB_POWERPLAYTABLE
{
ATOM_COMMON_TABLE_HEADER sHeader;
@@ -6610,7 +7395,8 @@ typedef struct _ATOM_PPLIB_POWERPLAYTABLE4
USHORT usVddciDependencyOnMCLKOffset;
USHORT usVddcDependencyOnMCLKOffset;
USHORT usMaxClockVoltageOnDCOffset;
- USHORT usReserved[2];
+ USHORT usVddcPhaseShedLimitsTableOffset; // Points to ATOM_PPLIB_PhaseSheddingLimits_Table
+ USHORT usReserved;
} ATOM_PPLIB_POWERPLAYTABLE4, *LPATOM_PPLIB_POWERPLAYTABLE4;
typedef struct _ATOM_PPLIB_POWERPLAYTABLE5
@@ -6620,8 +7406,9 @@ typedef struct _ATOM_PPLIB_POWERPLAYTABLE5
ULONG ulNearTDPLimit;
ULONG ulSQRampingThreshold;
USHORT usCACLeakageTableOffset; // Points to ATOM_PPLIB_CAC_Leakage_Table
- ULONG ulCACLeakage; // TBD, this parameter is still under discussion. Change to ulReserved if not needed.
- ULONG ulReserved;
+ ULONG ulCACLeakage; // The iLeakage for driver calculated CAC leakage table
+ USHORT usTDPODLimit;
+ USHORT usLoadLineSlope; // in milliOhms * 100
} ATOM_PPLIB_POWERPLAYTABLE5, *LPATOM_PPLIB_POWERPLAYTABLE5;
//// ATOM_PPLIB_NONCLOCK_INFO::usClassification
@@ -6650,6 +7437,7 @@ typedef struct _ATOM_PPLIB_POWERPLAYTABLE5
//// ATOM_PPLIB_NONCLOCK_INFO::usClassification2
#define ATOM_PPLIB_CLASSIFICATION2_LIMITEDPOWERSOURCE_2 0x0001
#define ATOM_PPLIB_CLASSIFICATION2_ULV 0x0002
+#define ATOM_PPLIB_CLASSIFICATION2_MVC 0x0004 //Multi-View Codec (BD-3D)
//// ATOM_PPLIB_NONCLOCK_INFO::ulCapsAndSettings
#define ATOM_PPLIB_SINGLE_DISPLAY_ONLY 0x00000001
@@ -6673,7 +7461,9 @@ typedef struct _ATOM_PPLIB_POWERPLAYTABLE5
#define ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING 0x00001000
#define ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS 0x00002000
-#define ATOM_PPLIB_DISALLOW_ON_DC 0x00004000
+
+#define ATOM_PPLIB_DISALLOW_ON_DC 0x00004000
+
#define ATOM_PPLIB_ENABLE_VARIBRIGHT 0x00008000
//memory related flags
@@ -6735,7 +7525,7 @@ typedef struct _ATOM_PPLIB_R600_CLOCK_INFO
#define ATOM_PPLIB_R600_FLAGS_UVDSAFE 2
#define ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE 4
#define ATOM_PPLIB_R600_FLAGS_MEMORY_ODT_OFF 8
-#define ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF 16
+#define ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF 16
#define ATOM_PPLIB_R600_FLAGS_LOWPOWER 32 // On the RV770 use 'low power' setting (sequencer S0).
typedef struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO
@@ -6754,6 +7544,24 @@ typedef struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO
} ATOM_PPLIB_EVERGREEN_CLOCK_INFO;
+typedef struct _ATOM_PPLIB_SI_CLOCK_INFO
+{
+ USHORT usEngineClockLow;
+ UCHAR ucEngineClockHigh;
+
+ USHORT usMemoryClockLow;
+ UCHAR ucMemoryClockHigh;
+
+ USHORT usVDDC;
+ USHORT usVDDCI;
+ UCHAR ucPCIEGen;
+ UCHAR ucUnused1;
+
+ ULONG ulFlags; // ATOM_PPLIB_SI_FLAGS_*, no flag is necessary for now
+
+} ATOM_PPLIB_SI_CLOCK_INFO;
+
+
typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
{
@@ -6766,7 +7574,7 @@ typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
UCHAR ucPadding; // For proper alignment and size.
USHORT usVDDC; // For the 780, use: None, Low, High, Variable
UCHAR ucMaxHTLinkWidth; // From SBIOS - {2, 4, 8, 16}
- UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requirement.
+ UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement.
USHORT usHTLinkFreq; // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200).
ULONG ulFlags;
} ATOM_PPLIB_RS780_CLOCK_INFO;
@@ -6788,9 +7596,7 @@ typedef struct _ATOM_PPLIB_SUMO_CLOCK_INFO{
USHORT usEngineClockLow; //clockfrequency & 0xFFFF. The unit is in 10khz
UCHAR ucEngineClockHigh; //clockfrequency >> 16.
UCHAR vddcIndex; //2-bit vddc index;
- UCHAR leakage; //please use 8-bit absolute value, not the 6-bit % value
- //please initalize to 0
- UCHAR rsv;
+ USHORT tdpLimit;
//please initalize to 0
USHORT rsv1;
//please initialize to 0s
@@ -6813,7 +7619,7 @@ typedef struct _ATOM_PPLIB_STATE_V2
UCHAR clockInfoIndex[1];
} ATOM_PPLIB_STATE_V2;
-typedef struct StateArray{
+typedef struct _StateArray{
//how many states we have
UCHAR ucNumEntries;
@@ -6821,18 +7627,17 @@ typedef struct StateArray{
}StateArray;
-typedef struct ClockInfoArray{
+typedef struct _ClockInfoArray{
//how many clock levels we have
UCHAR ucNumEntries;
- //sizeof(ATOM_PPLIB_SUMO_CLOCK_INFO)
+ //sizeof(ATOM_PPLIB_CLOCK_INFO)
UCHAR ucEntrySize;
- //this is for Sumo
- ATOM_PPLIB_SUMO_CLOCK_INFO clockInfo[1];
+ UCHAR clockInfo[1];
}ClockInfoArray;
-typedef struct NonClockInfoArray{
+typedef struct _NonClockInfoArray{
//how many non-clock levels we have. normally should be same as number of states
UCHAR ucNumEntries;
@@ -6871,6 +7676,124 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table
ATOM_PPLIB_Clock_Voltage_Limit_Record entries[1]; // Dynamically allocate entries.
}ATOM_PPLIB_Clock_Voltage_Limit_Table;
+typedef struct _ATOM_PPLIB_CAC_Leakage_Record
+{
+ USHORT usVddc; // We use this field for the "fake" standardized VDDC for power calculations
+ ULONG ulLeakageValue;
+}ATOM_PPLIB_CAC_Leakage_Record;
+
+typedef struct _ATOM_PPLIB_CAC_Leakage_Table
+{
+ UCHAR ucNumEntries; // Number of entries.
+ ATOM_PPLIB_CAC_Leakage_Record entries[1]; // Dynamically allocate entries.
+}ATOM_PPLIB_CAC_Leakage_Table;
+
+typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Record
+{
+ USHORT usVoltage;
+ USHORT usSclkLow;
+ UCHAR ucSclkHigh;
+ USHORT usMclkLow;
+ UCHAR ucMclkHigh;
+}ATOM_PPLIB_PhaseSheddingLimits_Record;
+
+typedef struct _ATOM_PPLIB_PhaseSheddingLimits_Table
+{
+ UCHAR ucNumEntries; // Number of entries.
+ ATOM_PPLIB_PhaseSheddingLimits_Record entries[1]; // Dynamically allocate entries.
+}ATOM_PPLIB_PhaseSheddingLimits_Table;
+
+typedef struct _VCEClockInfo{
+ USHORT usEVClkLow;
+ UCHAR ucEVClkHigh;
+ USHORT usECClkLow;
+ UCHAR ucECClkHigh;
+}VCEClockInfo;
+
+typedef struct _VCEClockInfoArray{
+ UCHAR ucNumEntries;
+ VCEClockInfo entries[1];
+}VCEClockInfoArray;
+
+typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record
+{
+ USHORT usVoltage;
+ UCHAR ucVCEClockInfoIndex;
+}ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record;
+
+typedef struct _ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table
+{
+ UCHAR numEntries;
+ ATOM_PPLIB_VCE_Clock_Voltage_Limit_Record entries[1];
+}ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table;
+
+typedef struct _ATOM_PPLIB_VCE_State_Record
+{
+ UCHAR ucVCEClockInfoIndex;
+ UCHAR ucClockInfoIndex; //highest 2 bits indicates memory p-states, lower 6bits indicates index to ClockInfoArrary
+}ATOM_PPLIB_VCE_State_Record;
+
+typedef struct _ATOM_PPLIB_VCE_State_Table
+{
+ UCHAR numEntries;
+ ATOM_PPLIB_VCE_State_Record entries[1];
+}ATOM_PPLIB_VCE_State_Table;
+
+
+typedef struct _ATOM_PPLIB_VCE_Table
+{
+ UCHAR revid;
+// VCEClockInfoArray array;
+// ATOM_PPLIB_VCE_Clock_Voltage_Limit_Table limits;
+// ATOM_PPLIB_VCE_State_Table states;
+}ATOM_PPLIB_VCE_Table;
+
+
+typedef struct _UVDClockInfo{
+ USHORT usVClkLow;
+ UCHAR ucVClkHigh;
+ USHORT usDClkLow;
+ UCHAR ucDClkHigh;
+}UVDClockInfo;
+
+typedef struct _UVDClockInfoArray{
+ UCHAR ucNumEntries;
+ UVDClockInfo entries[1];
+}UVDClockInfoArray;
+
+typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record
+{
+ USHORT usVoltage;
+ UCHAR ucUVDClockInfoIndex;
+}ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record;
+
+typedef struct _ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table
+{
+ UCHAR numEntries;
+ ATOM_PPLIB_UVD_Clock_Voltage_Limit_Record entries[1];
+}ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table;
+
+typedef struct _ATOM_PPLIB_UVD_State_Record
+{
+ UCHAR ucUVDClockInfoIndex;
+ UCHAR ucClockInfoIndex; //highest 2 bits indicates memory p-states, lower 6bits indicates index to ClockInfoArrary
+}ATOM_PPLIB_UVD_State_Record;
+
+typedef struct _ATOM_PPLIB_UVD_State_Table
+{
+ UCHAR numEntries;
+ ATOM_PPLIB_UVD_State_Record entries[1];
+}ATOM_PPLIB_UVD_State_Table;
+
+
+typedef struct _ATOM_PPLIB_UVD_Table
+{
+ UCHAR revid;
+// UVDClockInfoArray array;
+// ATOM_PPLIB_UVD_Clock_Voltage_Limit_Table limits;
+// ATOM_PPLIB_UVD_State_Table states;
+}ATOM_PPLIB_UVD_Table;
+
/**************************************************************************/
@@ -7020,4 +7943,68 @@ typedef struct _ATOM_PPLIB_Clock_Voltage_Limit_Table
#pragma pack() // BIOS data must use byte aligment
+//
+// AMD ACPI Table
+//
+#pragma pack(1)
+
+typedef struct {
+ ULONG Signature;
+ ULONG TableLength; //Length
+ UCHAR Revision;
+ UCHAR Checksum;
+ UCHAR OemId[6];
+ UCHAR OemTableId[8]; //UINT64 OemTableId;
+ ULONG OemRevision;
+ ULONG CreatorId;
+ ULONG CreatorRevision;
+} AMD_ACPI_DESCRIPTION_HEADER;
+/*
+//EFI_ACPI_DESCRIPTION_HEADER from AcpiCommon.h
+typedef struct {
+ UINT32 Signature; //0x0
+ UINT32 Length; //0x4
+ UINT8 Revision; //0x8
+ UINT8 Checksum; //0x9
+ UINT8 OemId[6]; //0xA
+ UINT64 OemTableId; //0x10
+ UINT32 OemRevision; //0x18
+ UINT32 CreatorId; //0x1C
+ UINT32 CreatorRevision; //0x20
+}EFI_ACPI_DESCRIPTION_HEADER;
+*/
+typedef struct {
+ AMD_ACPI_DESCRIPTION_HEADER SHeader;
+ UCHAR TableUUID[16]; //0x24
+ ULONG VBIOSImageOffset; //0x34. Offset to the first GOP_VBIOS_CONTENT block from the beginning of the stucture.
+ ULONG Lib1ImageOffset; //0x38. Offset to the first GOP_LIB1_CONTENT block from the beginning of the stucture.
+ ULONG Reserved[4]; //0x3C
+}UEFI_ACPI_VFCT;
+
+typedef struct {
+ ULONG PCIBus; //0x4C
+ ULONG PCIDevice; //0x50
+ ULONG PCIFunction; //0x54
+ USHORT VendorID; //0x58
+ USHORT DeviceID; //0x5A
+ USHORT SSVID; //0x5C
+ USHORT SSID; //0x5E
+ ULONG Revision; //0x60
+ ULONG ImageLength; //0x64
+}VFCT_IMAGE_HEADER;
+
+
+typedef struct {
+ VFCT_IMAGE_HEADER VbiosHeader;
+ UCHAR VbiosContent[1];
+}GOP_VBIOS_CONTENT;
+
+typedef struct {
+ VFCT_IMAGE_HEADER Lib1Header;
+ UCHAR Lib1Content[1];
+}GOP_LIB1_CONTENT;
+
+#pragma pack()
+
+
#endif /* _ATOMBIOS_H */
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 742f17f009a9..b5ff1f7b6f7e 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -231,6 +231,22 @@ static void atombios_blank_crtc(struct drm_crtc *crtc, int state)
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
}
+static void atombios_powergate_crtc(struct drm_crtc *crtc, int state)
+{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ int index = GetIndexIntoMasterTable(COMMAND, EnableDispPowerGating);
+ ENABLE_DISP_POWER_GATING_PARAMETERS_V2_1 args;
+
+ memset(&args, 0, sizeof(args));
+
+ args.ucDispPipeId = radeon_crtc->crtc_id;
+ args.ucEnable = state;
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+}
+
void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
{
struct drm_device *dev = crtc->dev;
@@ -242,8 +258,11 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
radeon_crtc->enabled = true;
/* adjust pm to dpms changes BEFORE enabling crtcs */
radeon_pm_compute_clocks(rdev);
+ /* disable crtc pair power gating before programming */
+ if (ASIC_IS_DCE6(rdev))
+ atombios_powergate_crtc(crtc, ATOM_DISABLE);
atombios_enable_crtc(crtc, ATOM_ENABLE);
- if (ASIC_IS_DCE3(rdev))
+ if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev))
atombios_enable_crtc_memreq(crtc, ATOM_ENABLE);
atombios_blank_crtc(crtc, ATOM_DISABLE);
drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
@@ -255,10 +274,29 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
if (radeon_crtc->enabled)
atombios_blank_crtc(crtc, ATOM_ENABLE);
- if (ASIC_IS_DCE3(rdev))
+ if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev))
atombios_enable_crtc_memreq(crtc, ATOM_DISABLE);
atombios_enable_crtc(crtc, ATOM_DISABLE);
radeon_crtc->enabled = false;
+ /* power gating is per-pair */
+ if (ASIC_IS_DCE6(rdev)) {
+ struct drm_crtc *other_crtc;
+ struct radeon_crtc *other_radeon_crtc;
+ list_for_each_entry(other_crtc, &rdev->ddev->mode_config.crtc_list, head) {
+ other_radeon_crtc = to_radeon_crtc(other_crtc);
+ if (((radeon_crtc->crtc_id == 0) && (other_radeon_crtc->crtc_id == 1)) ||
+ ((radeon_crtc->crtc_id == 1) && (other_radeon_crtc->crtc_id == 0)) ||
+ ((radeon_crtc->crtc_id == 2) && (other_radeon_crtc->crtc_id == 3)) ||
+ ((radeon_crtc->crtc_id == 3) && (other_radeon_crtc->crtc_id == 2)) ||
+ ((radeon_crtc->crtc_id == 4) && (other_radeon_crtc->crtc_id == 5)) ||
+ ((radeon_crtc->crtc_id == 5) && (other_radeon_crtc->crtc_id == 4))) {
+ /* if both crtcs in the pair are off, enable power gating */
+ if (other_radeon_crtc->enabled == false)
+ atombios_powergate_crtc(crtc, ATOM_ENABLE);
+ break;
+ }
+ }
+ }
/* adjust pm to dpms changes AFTER disabling crtcs */
radeon_pm_compute_clocks(rdev);
break;
@@ -436,7 +474,7 @@ static void atombios_crtc_program_ss(struct radeon_device *rdev,
return;
}
args.v3.ucEnable = enable;
- if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK))
+ if ((ss->percentage == 0) || (ss->type & ATOM_EXTERNAL_SS_MASK) || ASIC_IS_DCE61(rdev))
args.v3.ucEnable = ATOM_DISABLE;
} else if (ASIC_IS_DCE4(rdev)) {
args.v2.usSpreadSpectrumPercentage = cpu_to_le16(ss->percentage);
@@ -550,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)) ||
@@ -699,7 +737,7 @@ union set_pixel_clock {
/* on DCE5, make sure the voltage is high enough to support the
* required disp clk.
*/
-static void atombios_crtc_set_dcpll(struct radeon_device *rdev,
+static void atombios_crtc_set_disp_eng_pll(struct radeon_device *rdev,
u32 dispclk)
{
u8 frev, crev;
@@ -729,7 +767,12 @@ static void atombios_crtc_set_dcpll(struct radeon_device *rdev,
* SetPixelClock provides the dividers
*/
args.v6.ulDispEngClkFreq = cpu_to_le32(dispclk);
- args.v6.ucPpll = ATOM_DCPLL;
+ if (ASIC_IS_DCE61(rdev))
+ args.v6.ucPpll = ATOM_EXT_PLL1;
+ else if (ASIC_IS_DCE6(rdev))
+ args.v6.ucPpll = ATOM_PPLL0;
+ else
+ args.v6.ucPpll = ATOM_DCPLL;
break;
default:
DRM_ERROR("Unknown table version %d %d\n", frev, crev);
@@ -922,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:
@@ -1031,6 +1076,7 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
struct radeon_bo *rbo;
uint64_t fb_location;
uint32_t fb_format, fb_pitch_pixels, tiling_flags;
+ unsigned bankw, bankh, mtaspect, tile_split;
u32 fb_swap = EVERGREEN_GRPH_ENDIAN_SWAP(EVERGREEN_GRPH_ENDIAN_NONE);
u32 tmp, viewport_w, viewport_h;
int r;
@@ -1121,20 +1167,13 @@ static int dce4_crtc_do_set_base(struct drm_crtc *crtc,
break;
}
- switch ((tmp & 0xf000) >> 12) {
- case 0: /* 1KB rows */
- default:
- fb_format |= EVERGREEN_GRPH_TILE_SPLIT(EVERGREEN_ADDR_SURF_TILE_SPLIT_1KB);
- break;
- case 1: /* 2KB rows */
- fb_format |= EVERGREEN_GRPH_TILE_SPLIT(EVERGREEN_ADDR_SURF_TILE_SPLIT_2KB);
- break;
- case 2: /* 4KB rows */
- fb_format |= EVERGREEN_GRPH_TILE_SPLIT(EVERGREEN_ADDR_SURF_TILE_SPLIT_4KB);
- break;
- }
-
fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_2D_TILED_THIN1);
+
+ evergreen_tiling_fields(tiling_flags, &bankw, &bankh, &mtaspect, &tile_split);
+ fb_format |= EVERGREEN_GRPH_TILE_SPLIT(tile_split);
+ fb_format |= EVERGREEN_GRPH_BANK_WIDTH(bankw);
+ fb_format |= EVERGREEN_GRPH_BANK_HEIGHT(bankh);
+ fb_format |= EVERGREEN_GRPH_MACRO_TILE_ASPECT(mtaspect);
} else if (tiling_flags & RADEON_TILING_MICRO)
fb_format |= EVERGREEN_GRPH_ARRAY_MODE(EVERGREEN_GRPH_ARRAY_1D_TILED_THIN1);
@@ -1450,7 +1489,36 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
struct drm_crtc *test_crtc;
uint32_t pll_in_use = 0;
- if (ASIC_IS_DCE4(rdev)) {
+ if (ASIC_IS_DCE61(rdev)) {
+ list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
+ if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
+ struct radeon_encoder *test_radeon_encoder =
+ to_radeon_encoder(test_encoder);
+ struct radeon_encoder_atom_dig *dig =
+ test_radeon_encoder->enc_priv;
+
+ if ((test_radeon_encoder->encoder_id ==
+ ENCODER_OBJECT_ID_INTERNAL_UNIPHY) &&
+ (dig->linkb == false)) /* UNIPHY A uses PPLL2 */
+ return ATOM_PPLL2;
+ }
+ }
+ /* UNIPHY B/C/D/E/F */
+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+ struct radeon_crtc *radeon_test_crtc;
+
+ if (crtc == test_crtc)
+ continue;
+
+ radeon_test_crtc = to_radeon_crtc(test_crtc);
+ if ((radeon_test_crtc->pll_id == ATOM_PPLL0) ||
+ (radeon_test_crtc->pll_id == ATOM_PPLL1))
+ pll_in_use |= (1 << radeon_test_crtc->pll_id);
+ }
+ if (!(pll_in_use & 4))
+ return ATOM_PPLL0;
+ return ATOM_PPLL1;
+ } else if (ASIC_IS_DCE4(rdev)) {
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
/* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock,
@@ -1489,10 +1557,12 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
}
-void radeon_atom_dcpll_init(struct radeon_device *rdev)
+void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev)
{
/* always set DCPLL */
- if (ASIC_IS_DCE4(rdev)) {
+ if (ASIC_IS_DCE6(rdev))
+ atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk);
+ else if (ASIC_IS_DCE4(rdev)) {
struct radeon_atom_ss ss;
bool ss_enabled = radeon_atombios_get_asic_ss_info(rdev, &ss,
ASIC_INTERNAL_SS_ON_DCPLL,
@@ -1500,7 +1570,7 @@ void radeon_atom_dcpll_init(struct radeon_device *rdev)
if (ss_enabled)
atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, &ss);
/* XXX: DCE5, make sure voltage, dispclk is high enough */
- atombios_crtc_set_dcpll(rdev, rdev->clock.default_dispclk);
+ atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk);
if (ss_enabled)
atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, &ss);
}
@@ -1578,6 +1648,8 @@ static void atombios_crtc_commit(struct drm_crtc *crtc)
static void atombios_crtc_disable(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct radeon_atom_ss ss;
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
@@ -1589,6 +1661,12 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
break;
+ case ATOM_PPLL0:
+ /* disable the ppll */
+ if (ASIC_IS_DCE61(rdev))
+ atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
+ 0, 0, ATOM_DISABLE, 0, 0, 0, 0, 0, false, &ss);
+ break;
default:
break;
}
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 552b436451fd..c57d85664e77 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -63,12 +63,12 @@ static int radeon_process_aux_ch(struct radeon_i2c_chan *chan,
memset(&args, 0, sizeof(args));
- base = (unsigned char *)rdev->mode_info.atom_context->scratch;
+ base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1);
memcpy(base, send, send_bytes);
- args.v1.lpAuxRequest = 0;
- args.v1.lpDataOut = 16;
+ args.v1.lpAuxRequest = 0 + 4;
+ args.v1.lpDataOut = 16 + 4;
args.v1.ucDataOutLen = 0;
args.v1.ucChannelID = chan->rec.i2c_id;
args.v1.ucDelay = delay / 10;
@@ -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 */
@@ -746,7 +749,8 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
/* set the lane count on the sink */
tmp = dp_info->dp_lane_count;
- if (dp_info->dpcd[0] >= 0x11)
+ if (dp_info->dpcd[DP_DPCD_REV] >= 0x11 &&
+ dp_info->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)
tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index b88c4608731b..e607c4d7dd98 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -479,7 +479,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
* - 2 DIG encoder blocks.
* DIG1/2 can drive UNIPHY0/1/2 link A or link B
*
- * DCE 4.0/5.0
+ * DCE 4.0/5.0/6.0
* - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B).
* Supports up to 6 digital outputs
* - 6 DIG encoder blocks.
@@ -495,7 +495,11 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
* - 3 DIG transmitter blocks UNIPHY0/1/2 (links A and B).
* Supports up to 6 digital outputs
* - 2 DIG encoder blocks.
+ * llano
* DIG1/2 can drive UNIPHY0/1/2 link A or link B
+ * ontario
+ * DIG1 drives UNIPHY0/1/2 link A
+ * DIG2 drives UNIPHY0/1/2 link B
*
* Routing
* crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
@@ -537,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 */
@@ -703,6 +707,7 @@ union dig_transmitter_control {
DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
DIG_TRANSMITTER_CONTROL_PARAMETERS_V3 v3;
DIG_TRANSMITTER_CONTROL_PARAMETERS_V4 v4;
+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V1_5 v5;
};
void
@@ -723,6 +728,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
int connector_object_id = 0;
int igp_lane_info = 0;
int dig_encoder = dig->dig_encoder;
+ int hpd_id = RADEON_HPD_NONE;
if (action == ATOM_TRANSMITTER_ACTION_INIT) {
connector = radeon_get_connector_for_encoder_init(encoder);
@@ -738,6 +744,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
struct radeon_connector_atom_dig *dig_connector =
radeon_connector->con_priv;
+ hpd_id = radeon_connector->hpd.hpd;
dp_clock = dig_connector->dp_clock;
dp_lane_count = dig_connector->dp_lane_count;
connector_object_id =
@@ -1003,6 +1010,60 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t
args.v4.acConfig.fDualLinkConnector = 1;
}
break;
+ case 5:
+ args.v5.ucAction = action;
+ if (is_dp)
+ args.v5.usSymClock = cpu_to_le16(dp_clock / 10);
+ else
+ args.v5.usSymClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ if (dig->linkb)
+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYB;
+ else
+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYA;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ if (dig->linkb)
+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYD;
+ else
+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYC;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ if (dig->linkb)
+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYF;
+ else
+ args.v5.ucPhyId = ATOM_PHY_ID_UNIPHYE;
+ break;
+ }
+ if (is_dp)
+ args.v5.ucLaneNum = dp_lane_count;
+ else if (radeon_encoder->pixel_clock > 165000)
+ args.v5.ucLaneNum = 8;
+ else
+ args.v5.ucLaneNum = 4;
+ args.v5.ucConnObjId = connector_object_id;
+ args.v5.ucDigMode = atombios_get_encoder_mode(encoder);
+
+ if (is_dp && rdev->clock.dp_extclk)
+ args.v5.asConfig.ucPhyClkSrcId = ENCODER_REFCLK_SRC_EXTCLK;
+ else
+ args.v5.asConfig.ucPhyClkSrcId = pll_id;
+
+ if (is_dp)
+ args.v5.asConfig.ucCoherentMode = 1; /* DP requires coherent */
+ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v5.asConfig.ucCoherentMode = 1;
+ }
+ if (hpd_id == RADEON_HPD_NONE)
+ args.v5.asConfig.ucHPDSel = 0;
+ else
+ args.v5.asConfig.ucHPDSel = hpd_id + 1;
+ args.v5.ucDigEncoderSel = 1 << dig_encoder;
+ args.v5.ucDPLaneSet = lane_set;
+ break;
default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
break;
@@ -1098,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));
@@ -1377,7 +1438,7 @@ radeon_atom_encoder_dpms_ext(struct drm_encoder *encoder,
switch (mode) {
case DRM_MODE_DPMS_ON:
default:
- if (ASIC_IS_DCE41(rdev)) {
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) {
atombios_external_encoder_setup(encoder, ext_encoder,
EXTERNAL_ENCODER_ACTION_V3_ENABLE_OUTPUT);
atombios_external_encoder_setup(encoder, ext_encoder,
@@ -1388,7 +1449,7 @@ radeon_atom_encoder_dpms_ext(struct drm_encoder *encoder,
case DRM_MODE_DPMS_STANDBY:
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- if (ASIC_IS_DCE41(rdev)) {
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)) {
atombios_external_encoder_setup(encoder, ext_encoder,
EXTERNAL_ENCODER_ACTION_V3_ENCODER_BLANKING);
atombios_external_encoder_setup(encoder, ext_encoder,
@@ -1761,7 +1822,7 @@ radeon_atom_encoder_init(struct radeon_device *rdev)
break;
}
- if (ext_encoder && ASIC_IS_DCE41(rdev))
+ if (ext_encoder && (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev)))
atombios_external_encoder_setup(encoder, ext_encoder,
EXTERNAL_ENCODER_ACTION_V3_ENCODER_INIT);
}
@@ -1850,7 +1911,7 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
}
if (ext_encoder) {
- if (ASIC_IS_DCE41(rdev))
+ if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE61(rdev))
atombios_external_encoder_setup(encoder, ext_encoder,
EXTERNAL_ENCODER_ACTION_V3_ENCODER_SETUP);
else
diff --git a/drivers/gpu/drm/radeon/atombios_i2c.c b/drivers/gpu/drm/radeon/atombios_i2c.c
new file mode 100644
index 000000000000..44d87b6b4220
--- /dev/null
+++ b/drivers/gpu/drm/radeon/atombios_i2c.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Alex Deucher
+ *
+ */
+#include "drmP.h"
+#include "radeon_drm.h"
+#include "radeon.h"
+#include "atom.h"
+
+#define TARGET_HW_I2C_CLOCK 50
+
+/* these are a limitation of ProcessI2cChannelTransaction not the hw */
+#define ATOM_MAX_HW_I2C_WRITE 2
+#define ATOM_MAX_HW_I2C_READ 255
+
+static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan,
+ u8 slave_addr, u8 flags,
+ u8 *buf, u8 num)
+{
+ struct drm_device *dev = chan->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args;
+ int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction);
+ unsigned char *base;
+ u16 out;
+
+ memset(&args, 0, sizeof(args));
+
+ base = (unsigned char *)rdev->mode_info.atom_context->scratch;
+
+ if (flags & HW_I2C_WRITE) {
+ if (num > ATOM_MAX_HW_I2C_WRITE) {
+ DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 2)\n", num);
+ return -EINVAL;
+ }
+ memcpy(&out, buf, num);
+ args.lpI2CDataOut = cpu_to_le16(out);
+ } else {
+ if (num > ATOM_MAX_HW_I2C_READ) {
+ DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num);
+ return -EINVAL;
+ }
+ }
+
+ args.ucI2CSpeed = TARGET_HW_I2C_CLOCK;
+ args.ucRegIndex = 0;
+ args.ucTransBytes = num;
+ args.ucSlaveAddr = slave_addr << 1;
+ args.ucLineNumber = chan->rec.i2c_id;
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ /* error */
+ if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) {
+ DRM_DEBUG_KMS("hw_i2c error\n");
+ return -EIO;
+ }
+
+ if (!(flags & HW_I2C_WRITE))
+ memcpy(buf, base, num);
+
+ return 0;
+}
+
+int radeon_atom_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct radeon_i2c_chan *i2c = i2c_get_adapdata(i2c_adap);
+ struct i2c_msg *p;
+ int i, remaining, current_count, buffer_offset, max_bytes, ret;
+ u8 buf = 0, flags;
+
+ /* check for bus probe */
+ p = &msgs[0];
+ if ((num == 1) && (p->len == 0)) {
+ ret = radeon_process_i2c_ch(i2c,
+ p->addr, HW_I2C_WRITE,
+ &buf, 1);
+ if (ret)
+ return ret;
+ else
+ return num;
+ }
+
+ for (i = 0; i < num; i++) {
+ p = &msgs[i];
+ remaining = p->len;
+ buffer_offset = 0;
+ /* max_bytes are a limitation of ProcessI2cChannelTransaction not the hw */
+ if (p->flags & I2C_M_RD) {
+ max_bytes = ATOM_MAX_HW_I2C_READ;
+ flags = HW_I2C_READ;
+ } else {
+ max_bytes = ATOM_MAX_HW_I2C_WRITE;
+ flags = HW_I2C_WRITE;
+ }
+ while (remaining) {
+ if (remaining > max_bytes)
+ current_count = max_bytes;
+ else
+ current_count = remaining;
+ ret = radeon_process_i2c_ch(i2c,
+ p->addr, flags,
+ &p->buf[buffer_offset], current_count);
+ if (ret)
+ return ret;
+ remaining -= current_count;
+ buffer_offset += current_count;
+ }
+ }
+
+ return num;
+}
+
+u32 radeon_atom_hw_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
diff --git a/drivers/gpu/drm/radeon/cayman_blit_shaders.c b/drivers/gpu/drm/radeon/cayman_blit_shaders.c
index 7b4eeb7b4a8c..19a0114d2e3b 100644
--- a/drivers/gpu/drm/radeon/cayman_blit_shaders.c
+++ b/drivers/gpu/drm/radeon/cayman_blit_shaders.c
@@ -24,6 +24,7 @@
* Alex Deucher <alexander.deucher@amd.com>
*/
+#include <linux/bug.h>
#include <linux/types.h>
#include <linux/kernel.h>
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index f58254a3fb01..cfa372cb1cb3 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -43,6 +43,37 @@ void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
extern void cayman_cp_int_cntl_setup(struct radeon_device *rdev,
int ring, u32 cp_int_cntl);
+void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw,
+ unsigned *bankh, unsigned *mtaspect,
+ unsigned *tile_split)
+{
+ *bankw = (tiling_flags >> RADEON_TILING_EG_BANKW_SHIFT) & RADEON_TILING_EG_BANKW_MASK;
+ *bankh = (tiling_flags >> RADEON_TILING_EG_BANKH_SHIFT) & RADEON_TILING_EG_BANKH_MASK;
+ *mtaspect = (tiling_flags >> RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK;
+ *tile_split = (tiling_flags >> RADEON_TILING_EG_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_TILE_SPLIT_MASK;
+ switch (*bankw) {
+ default:
+ case 1: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_1; break;
+ case 2: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_2; break;
+ case 4: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_4; break;
+ case 8: *bankw = EVERGREEN_ADDR_SURF_BANK_WIDTH_8; break;
+ }
+ switch (*bankh) {
+ default:
+ case 1: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_1; break;
+ case 2: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_2; break;
+ case 4: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_4; break;
+ case 8: *bankh = EVERGREEN_ADDR_SURF_BANK_HEIGHT_8; break;
+ }
+ switch (*mtaspect) {
+ default:
+ case 1: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_1; break;
+ case 2: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_2; break;
+ case 4: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_4; break;
+ case 8: *mtaspect = EVERGREEN_ADDR_SURF_MACRO_TILE_ASPECT_8; break;
+ }
+}
+
void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
{
u16 ctl, v;
@@ -68,6 +99,25 @@ void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
}
}
+void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc];
+ int i;
+
+ if (RREG32(EVERGREEN_CRTC_CONTROL + radeon_crtc->crtc_offset) & EVERGREEN_CRTC_MASTER_EN) {
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (!(RREG32(EVERGREEN_CRTC_STATUS + radeon_crtc->crtc_offset) & EVERGREEN_CRTC_V_BLANK))
+ break;
+ udelay(1);
+ }
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(EVERGREEN_CRTC_STATUS + radeon_crtc->crtc_offset) & EVERGREEN_CRTC_V_BLANK)
+ break;
+ udelay(1);
+ }
+ }
+}
+
void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc)
{
/* enable the pflip int */
@@ -531,7 +581,7 @@ static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev,
return 0;
}
-static u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev)
+u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev)
{
u32 tmp = RREG32(MC_SHARED_CHMAP);
@@ -1278,7 +1328,10 @@ void evergreen_mc_program(struct radeon_device *rdev)
rdev->mc.vram_end >> 12);
}
WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR, rdev->vram_scratch.gpu_addr >> 12);
- if (rdev->flags & RADEON_IS_IGP) {
+ /* llano/ontario only */
+ if ((rdev->family == CHIP_PALM) ||
+ (rdev->family == CHIP_SUMO) ||
+ (rdev->family == CHIP_SUMO2)) {
tmp = RREG32(MC_FUS_VM_FB_OFFSET) & 0x000FFFFF;
tmp |= ((rdev->mc.vram_end >> 20) & 0xF) << 24;
tmp |= ((rdev->mc.vram_start >> 20) & 0xF) << 20;
@@ -1489,7 +1542,7 @@ int evergreen_cp_resume(struct radeon_device *rdev)
evergreen_cp_start(rdev);
ring->ready = true;
- r = radeon_ring_test(rdev, ring);
+ r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring);
if (r) {
ring->ready = false;
return r;
@@ -1922,7 +1975,9 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
- if (rdev->flags & RADEON_IS_IGP)
+ if ((rdev->family == CHIP_PALM) ||
+ (rdev->family == CHIP_SUMO) ||
+ (rdev->family == CHIP_SUMO2))
mc_arb_ramcfg = RREG32(FUS_MC_ARB_RAMCFG);
else
mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
@@ -2312,7 +2367,9 @@ int evergreen_mc_init(struct radeon_device *rdev)
/* Get VRAM informations */
rdev->mc.vram_is_ddr = true;
- if (rdev->flags & RADEON_IS_IGP)
+ if ((rdev->family == CHIP_PALM) ||
+ (rdev->family == CHIP_SUMO) ||
+ (rdev->family == CHIP_SUMO2))
tmp = RREG32(FUS_MC_ARB_RAMCFG);
else
tmp = RREG32(MC_ARB_RAMCFG);
@@ -2344,12 +2401,14 @@ int evergreen_mc_init(struct radeon_device *rdev)
rdev->mc.aper_base = pci_resource_start(rdev->pdev, 0);
rdev->mc.aper_size = pci_resource_len(rdev->pdev, 0);
/* Setup GPU memory space */
- if (rdev->flags & RADEON_IS_IGP) {
+ if ((rdev->family == CHIP_PALM) ||
+ (rdev->family == CHIP_SUMO) ||
+ (rdev->family == CHIP_SUMO2)) {
/* size in bytes on fusion */
rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE);
rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE);
} else {
- /* size in MB on evergreen */
+ /* size in MB on evergreen/cayman/tn */
rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
}
@@ -2507,7 +2566,9 @@ void evergreen_disable_interrupt_state(struct radeon_device *rdev)
WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
}
- WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
+ /* only one DAC on DCE6 */
+ if (!ASIC_IS_DCE6(rdev))
+ WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
WREG32(DACB_AUTODETECT_INT_CONTROL, 0);
tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY;
@@ -3147,7 +3208,7 @@ static int evergreen_startup(struct radeon_device *rdev)
r = evergreen_blit_init(rdev);
if (r) {
r600_blit_fini(rdev);
- rdev->asic->copy = NULL;
+ rdev->asic->copy.copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
@@ -3187,7 +3248,7 @@ static int evergreen_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r600_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
DRM_ERROR("radeon: failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/evergreen_blit_kms.c b/drivers/gpu/drm/radeon/evergreen_blit_kms.c
index 2379849515c7..222acd2d33df 100644
--- a/drivers/gpu/drm/radeon/evergreen_blit_kms.c
+++ b/drivers/gpu/drm/radeon/evergreen_blit_kms.c
@@ -32,17 +32,7 @@
#include "evergreend.h"
#include "evergreen_blit_shaders.h"
#include "cayman_blit_shaders.h"
-
-#define DI_PT_RECTLIST 0x11
-#define DI_INDEX_SIZE_16_BIT 0x0
-#define DI_SRC_SEL_AUTO_INDEX 0x2
-
-#define FMT_8 0x1
-#define FMT_5_6_5 0x8
-#define FMT_8_8_8_8 0x1a
-#define COLOR_8 0x1
-#define COLOR_5_6_5 0x8
-#define COLOR_8_8_8_8 0x1a
+#include "radeon_blit_common.h"
/* emits 17 */
static void
@@ -236,7 +226,7 @@ set_scissors(struct radeon_device *rdev, int x1, int y1,
x1 = 1;
if (y2 == 0)
y1 = 1;
- if (rdev->family == CHIP_CAYMAN) {
+ if (rdev->family >= CHIP_CAYMAN) {
if ((x2 == 1) && (y2 == 1))
x2 = 2;
}
diff --git a/drivers/gpu/drm/radeon/evergreen_blit_shaders.c b/drivers/gpu/drm/radeon/evergreen_blit_shaders.c
index 3a10399e0066..f85c0af115b5 100644
--- a/drivers/gpu/drm/radeon/evergreen_blit_shaders.c
+++ b/drivers/gpu/drm/radeon/evergreen_blit_shaders.c
@@ -24,6 +24,7 @@
* Alex Deucher <alexander.deucher@amd.com>
*/
+#include <linux/bug.h>
#include <linux/types.h>
#include <linux/kernel.h>
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index 8e8cd85e5c00..70089d32b80f 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -31,6 +31,9 @@
#include "evergreen_reg_safe.h"
#include "cayman_reg_safe.h"
+#define MAX(a,b) (((a)>(b))?(a):(b))
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
static int evergreen_cs_packet_next_reloc(struct radeon_cs_parser *p,
struct radeon_cs_reloc **cs_reloc);
@@ -40,42 +43,46 @@ struct evergreen_cs_track {
u32 npipes;
u32 row_size;
/* value we track */
- u32 nsamples;
- u32 cb_color_base_last[12];
+ u32 nsamples; /* unused */
struct radeon_bo *cb_color_bo[12];
u32 cb_color_bo_offset[12];
- struct radeon_bo *cb_color_fmask_bo[8];
- struct radeon_bo *cb_color_cmask_bo[8];
+ struct radeon_bo *cb_color_fmask_bo[8]; /* unused */
+ struct radeon_bo *cb_color_cmask_bo[8]; /* unused */
u32 cb_color_info[12];
u32 cb_color_view[12];
- u32 cb_color_pitch_idx[12];
- u32 cb_color_slice_idx[12];
- u32 cb_color_dim_idx[12];
- u32 cb_color_dim[12];
u32 cb_color_pitch[12];
u32 cb_color_slice[12];
- u32 cb_color_cmask_slice[8];
- u32 cb_color_fmask_slice[8];
+ u32 cb_color_attrib[12];
+ u32 cb_color_cmask_slice[8];/* unused */
+ u32 cb_color_fmask_slice[8];/* unused */
u32 cb_target_mask;
- u32 cb_shader_mask;
+ u32 cb_shader_mask; /* unused */
u32 vgt_strmout_config;
u32 vgt_strmout_buffer_config;
+ struct radeon_bo *vgt_strmout_bo[4];
+ u32 vgt_strmout_bo_offset[4];
+ u32 vgt_strmout_size[4];
u32 db_depth_control;
u32 db_depth_view;
+ u32 db_depth_slice;
u32 db_depth_size;
- u32 db_depth_size_idx;
u32 db_z_info;
- u32 db_z_idx;
u32 db_z_read_offset;
u32 db_z_write_offset;
struct radeon_bo *db_z_read_bo;
struct radeon_bo *db_z_write_bo;
u32 db_s_info;
- u32 db_s_idx;
u32 db_s_read_offset;
u32 db_s_write_offset;
struct radeon_bo *db_s_read_bo;
struct radeon_bo *db_s_write_bo;
+ bool sx_misc_kill_all_prims;
+ 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)
@@ -103,19 +110,6 @@ static u32 evergreen_cs_get_num_banks(u32 nbanks)
}
}
-static u32 evergreen_cs_get_tile_split(u32 row_size)
-{
- switch (row_size) {
- case 1:
- default:
- return ADDR_SURF_TILE_SPLIT_1KB;
- case 2:
- return ADDR_SURF_TILE_SPLIT_2KB;
- case 4:
- return ADDR_SURF_TILE_SPLIT_4KB;
- }
-}
-
static void evergreen_cs_track_init(struct evergreen_cs_track *track)
{
int i;
@@ -128,50 +122,820 @@ static void evergreen_cs_track_init(struct evergreen_cs_track *track)
}
for (i = 0; i < 12; i++) {
- track->cb_color_base_last[i] = 0;
track->cb_color_bo[i] = NULL;
track->cb_color_bo_offset[i] = 0xFFFFFFFF;
track->cb_color_info[i] = 0;
- track->cb_color_view[i] = 0;
- track->cb_color_pitch_idx[i] = 0;
- track->cb_color_slice_idx[i] = 0;
- track->cb_color_dim[i] = 0;
+ track->cb_color_view[i] = 0xFFFFFFFF;
track->cb_color_pitch[i] = 0;
track->cb_color_slice[i] = 0;
- track->cb_color_dim[i] = 0;
}
track->cb_target_mask = 0xFFFFFFFF;
track->cb_shader_mask = 0xFFFFFFFF;
+ track->cb_dirty = true;
track->db_depth_view = 0xFFFFC000;
track->db_depth_size = 0xFFFFFFFF;
- track->db_depth_size_idx = 0;
track->db_depth_control = 0xFFFFFFFF;
track->db_z_info = 0xFFFFFFFF;
- track->db_z_idx = 0xFFFFFFFF;
track->db_z_read_offset = 0xFFFFFFFF;
track->db_z_write_offset = 0xFFFFFFFF;
track->db_z_read_bo = NULL;
track->db_z_write_bo = NULL;
track->db_s_info = 0xFFFFFFFF;
- track->db_s_idx = 0xFFFFFFFF;
track->db_s_read_offset = 0xFFFFFFFF;
track->db_s_write_offset = 0xFFFFFFFF;
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;
+ track->vgt_strmout_bo[i] = NULL;
+ track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF;
+ }
+ track->streamout_dirty = true;
+ track->sx_misc_kill_all_prims = false;
}
-static int evergreen_cs_track_check(struct radeon_cs_parser *p)
+struct eg_surface {
+ /* value gathered from cs */
+ unsigned nbx;
+ unsigned nby;
+ unsigned format;
+ unsigned mode;
+ unsigned nbanks;
+ unsigned bankw;
+ unsigned bankh;
+ unsigned tsplit;
+ unsigned mtilea;
+ unsigned nsamples;
+ /* output value */
+ unsigned bpe;
+ unsigned layer_size;
+ unsigned palign;
+ unsigned halign;
+ unsigned long base_align;
+};
+
+static int evergreen_surface_check_linear(struct radeon_cs_parser *p,
+ struct eg_surface *surf,
+ const char *prefix)
+{
+ surf->layer_size = surf->nbx * surf->nby * surf->bpe * surf->nsamples;
+ surf->base_align = surf->bpe;
+ surf->palign = 1;
+ surf->halign = 1;
+ return 0;
+}
+
+static int evergreen_surface_check_linear_aligned(struct radeon_cs_parser *p,
+ struct eg_surface *surf,
+ const char *prefix)
+{
+ struct evergreen_cs_track *track = p->track;
+ unsigned palign;
+
+ palign = MAX(64, track->group_size / surf->bpe);
+ surf->layer_size = surf->nbx * surf->nby * surf->bpe * surf->nsamples;
+ surf->base_align = track->group_size;
+ surf->palign = palign;
+ surf->halign = 1;
+ if (surf->nbx & (palign - 1)) {
+ if (prefix) {
+ dev_warn(p->dev, "%s:%d %s pitch %d invalid must be aligned with %d\n",
+ __func__, __LINE__, prefix, surf->nbx, palign);
+ }
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int evergreen_surface_check_1d(struct radeon_cs_parser *p,
+ struct eg_surface *surf,
+ const char *prefix)
+{
+ struct evergreen_cs_track *track = p->track;
+ unsigned palign;
+
+ palign = track->group_size / (8 * surf->bpe * surf->nsamples);
+ palign = MAX(8, palign);
+ surf->layer_size = surf->nbx * surf->nby * surf->bpe;
+ surf->base_align = track->group_size;
+ surf->palign = palign;
+ surf->halign = 8;
+ if ((surf->nbx & (palign - 1))) {
+ if (prefix) {
+ dev_warn(p->dev, "%s:%d %s pitch %d invalid must be aligned with %d (%d %d %d)\n",
+ __func__, __LINE__, prefix, surf->nbx, palign,
+ track->group_size, surf->bpe, surf->nsamples);
+ }
+ return -EINVAL;
+ }
+ if ((surf->nby & (8 - 1))) {
+ if (prefix) {
+ dev_warn(p->dev, "%s:%d %s height %d invalid must be aligned with 8\n",
+ __func__, __LINE__, prefix, surf->nby);
+ }
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int evergreen_surface_check_2d(struct radeon_cs_parser *p,
+ struct eg_surface *surf,
+ const char *prefix)
+{
+ struct evergreen_cs_track *track = p->track;
+ unsigned palign, halign, tileb, slice_pt;
+
+ tileb = 64 * surf->bpe * surf->nsamples;
+ palign = track->group_size / (8 * surf->bpe * surf->nsamples);
+ palign = MAX(8, palign);
+ slice_pt = 1;
+ if (tileb > surf->tsplit) {
+ slice_pt = tileb / surf->tsplit;
+ }
+ tileb = tileb / slice_pt;
+ /* macro tile width & height */
+ palign = (8 * surf->bankw * track->npipes) * surf->mtilea;
+ halign = (8 * surf->bankh * surf->nbanks) / surf->mtilea;
+ surf->layer_size = surf->nbx * surf->nby * surf->bpe * slice_pt;
+ surf->base_align = (palign / 8) * (halign / 8) * tileb;
+ surf->palign = palign;
+ surf->halign = halign;
+
+ if ((surf->nbx & (palign - 1))) {
+ if (prefix) {
+ dev_warn(p->dev, "%s:%d %s pitch %d invalid must be aligned with %d\n",
+ __func__, __LINE__, prefix, surf->nbx, palign);
+ }
+ return -EINVAL;
+ }
+ if ((surf->nby & (halign - 1))) {
+ if (prefix) {
+ dev_warn(p->dev, "%s:%d %s height %d invalid must be aligned with %d\n",
+ __func__, __LINE__, prefix, surf->nby, halign);
+ }
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int evergreen_surface_check(struct radeon_cs_parser *p,
+ struct eg_surface *surf,
+ const char *prefix)
+{
+ /* some common value computed here */
+ surf->bpe = r600_fmt_get_blocksize(surf->format);
+
+ switch (surf->mode) {
+ case ARRAY_LINEAR_GENERAL:
+ return evergreen_surface_check_linear(p, surf, prefix);
+ case ARRAY_LINEAR_ALIGNED:
+ return evergreen_surface_check_linear_aligned(p, surf, prefix);
+ case ARRAY_1D_TILED_THIN1:
+ return evergreen_surface_check_1d(p, surf, prefix);
+ case ARRAY_2D_TILED_THIN1:
+ return evergreen_surface_check_2d(p, surf, prefix);
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid array mode %d\n",
+ __func__, __LINE__, prefix, surf->mode);
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+static int evergreen_surface_value_conv_check(struct radeon_cs_parser *p,
+ struct eg_surface *surf,
+ const char *prefix)
+{
+ switch (surf->mode) {
+ case ARRAY_2D_TILED_THIN1:
+ break;
+ case ARRAY_LINEAR_GENERAL:
+ case ARRAY_LINEAR_ALIGNED:
+ case ARRAY_1D_TILED_THIN1:
+ return 0;
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid array mode %d\n",
+ __func__, __LINE__, prefix, surf->mode);
+ return -EINVAL;
+ }
+
+ switch (surf->nbanks) {
+ case 0: surf->nbanks = 2; break;
+ case 1: surf->nbanks = 4; break;
+ case 2: surf->nbanks = 8; break;
+ case 3: surf->nbanks = 16; break;
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid number of banks %d\n",
+ __func__, __LINE__, prefix, surf->nbanks);
+ return -EINVAL;
+ }
+ switch (surf->bankw) {
+ case 0: surf->bankw = 1; break;
+ case 1: surf->bankw = 2; break;
+ case 2: surf->bankw = 4; break;
+ case 3: surf->bankw = 8; break;
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid bankw %d\n",
+ __func__, __LINE__, prefix, surf->bankw);
+ return -EINVAL;
+ }
+ switch (surf->bankh) {
+ case 0: surf->bankh = 1; break;
+ case 1: surf->bankh = 2; break;
+ case 2: surf->bankh = 4; break;
+ case 3: surf->bankh = 8; break;
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid bankh %d\n",
+ __func__, __LINE__, prefix, surf->bankh);
+ return -EINVAL;
+ }
+ switch (surf->mtilea) {
+ case 0: surf->mtilea = 1; break;
+ case 1: surf->mtilea = 2; break;
+ case 2: surf->mtilea = 4; break;
+ case 3: surf->mtilea = 8; break;
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid macro tile aspect %d\n",
+ __func__, __LINE__, prefix, surf->mtilea);
+ return -EINVAL;
+ }
+ switch (surf->tsplit) {
+ case 0: surf->tsplit = 64; break;
+ case 1: surf->tsplit = 128; break;
+ case 2: surf->tsplit = 256; break;
+ case 3: surf->tsplit = 512; break;
+ case 4: surf->tsplit = 1024; break;
+ case 5: surf->tsplit = 2048; break;
+ case 6: surf->tsplit = 4096; break;
+ default:
+ dev_warn(p->dev, "%s:%d %s invalid tile split %d\n",
+ __func__, __LINE__, prefix, surf->tsplit);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int evergreen_cs_track_validate_cb(struct radeon_cs_parser *p, unsigned id)
{
struct evergreen_cs_track *track = p->track;
+ struct eg_surface surf;
+ unsigned pitch, slice, mslice;
+ unsigned long offset;
+ int r;
- /* we don't support stream out buffer yet */
- if (track->vgt_strmout_config || track->vgt_strmout_buffer_config) {
- dev_warn(p->dev, "this kernel doesn't support SMX output buffer\n");
+ mslice = G_028C6C_SLICE_MAX(track->cb_color_view[id]) + 1;
+ pitch = track->cb_color_pitch[id];
+ slice = track->cb_color_slice[id];
+ surf.nbx = (pitch + 1) * 8;
+ surf.nby = ((slice + 1) * 64) / surf.nbx;
+ surf.mode = G_028C70_ARRAY_MODE(track->cb_color_info[id]);
+ surf.format = G_028C70_FORMAT(track->cb_color_info[id]);
+ surf.tsplit = G_028C74_TILE_SPLIT(track->cb_color_attrib[id]);
+ surf.nbanks = G_028C74_NUM_BANKS(track->cb_color_attrib[id]);
+ surf.bankw = G_028C74_BANK_WIDTH(track->cb_color_attrib[id]);
+ surf.bankh = G_028C74_BANK_HEIGHT(track->cb_color_attrib[id]);
+ surf.mtilea = G_028C74_MACRO_TILE_ASPECT(track->cb_color_attrib[id]);
+ surf.nsamples = 1;
+
+ if (!r600_fmt_is_valid_color(surf.format)) {
+ dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08x)\n",
+ __func__, __LINE__, surf.format,
+ id, track->cb_color_info[id]);
+ return -EINVAL;
+ }
+
+ r = evergreen_surface_value_conv_check(p, &surf, "cb");
+ if (r) {
+ return r;
+ }
+
+ r = evergreen_surface_check(p, &surf, "cb");
+ if (r) {
+ dev_warn(p->dev, "%s:%d cb[%d] invalid (0x%08x 0x%08x 0x%08x 0x%08x)\n",
+ __func__, __LINE__, id, track->cb_color_pitch[id],
+ track->cb_color_slice[id], track->cb_color_attrib[id],
+ track->cb_color_info[id]);
+ return r;
+ }
+
+ offset = track->cb_color_bo_offset[id] << 8;
+ if (offset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d cb[%d] bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, id, offset, surf.base_align);
+ return -EINVAL;
+ }
+
+ offset += surf.layer_size * mslice;
+ if (offset > radeon_bo_size(track->cb_color_bo[id])) {
+ dev_warn(p->dev, "%s:%d cb[%d] bo too small (layer size %d, "
+ "offset %d, max layer %d, bo size %ld, slice %d)\n",
+ __func__, __LINE__, id, surf.layer_size,
+ track->cb_color_bo_offset[id] << 8, mslice,
+ radeon_bo_size(track->cb_color_bo[id]), slice);
+ dev_warn(p->dev, "%s:%d problematic surf: (%d %d) (%d %d %d %d %d %d %d)\n",
+ __func__, __LINE__, surf.nbx, surf.nby,
+ surf.mode, surf.bpe, surf.nsamples,
+ surf.bankw, surf.bankh,
+ surf.tsplit, surf.mtilea);
return -EINVAL;
}
- /* XXX fill in */
+ 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;
+ struct eg_surface surf;
+ unsigned pitch, slice, mslice;
+ unsigned long offset;
+ int r;
+
+ mslice = G_028008_SLICE_MAX(track->db_depth_view) + 1;
+ pitch = G_028058_PITCH_TILE_MAX(track->db_depth_size);
+ slice = track->db_depth_slice;
+ surf.nbx = (pitch + 1) * 8;
+ surf.nby = ((slice + 1) * 64) / surf.nbx;
+ surf.mode = G_028040_ARRAY_MODE(track->db_z_info);
+ surf.format = G_028044_FORMAT(track->db_s_info);
+ surf.tsplit = G_028044_TILE_SPLIT(track->db_s_info);
+ surf.nbanks = G_028040_NUM_BANKS(track->db_z_info);
+ surf.bankw = G_028040_BANK_WIDTH(track->db_z_info);
+ surf.bankh = G_028040_BANK_HEIGHT(track->db_z_info);
+ surf.mtilea = G_028040_MACRO_TILE_ASPECT(track->db_z_info);
+ surf.nsamples = 1;
+
+ if (surf.format != 1) {
+ dev_warn(p->dev, "%s:%d stencil invalid format %d\n",
+ __func__, __LINE__, surf.format);
+ return -EINVAL;
+ }
+ /* replace by color format so we can use same code */
+ surf.format = V_028C70_COLOR_8;
+
+ r = evergreen_surface_value_conv_check(p, &surf, "stencil");
+ if (r) {
+ return r;
+ }
+
+ r = evergreen_surface_check(p, &surf, NULL);
+ if (r) {
+ /* old userspace doesn't compute proper depth/stencil alignment
+ * check that alignment against a bigger byte per elements and
+ * only report if that alignment is wrong too.
+ */
+ surf.format = V_028C70_COLOR_8_8_8_8;
+ r = evergreen_surface_check(p, &surf, "stencil");
+ if (r) {
+ dev_warn(p->dev, "%s:%d stencil invalid (0x%08x 0x%08x 0x%08x 0x%08x)\n",
+ __func__, __LINE__, track->db_depth_size,
+ track->db_depth_slice, track->db_s_info, track->db_z_info);
+ }
+ return r;
+ }
+
+ offset = track->db_s_read_offset << 8;
+ if (offset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d stencil read bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, offset, surf.base_align);
+ return -EINVAL;
+ }
+ offset += surf.layer_size * mslice;
+ if (offset > radeon_bo_size(track->db_s_read_bo)) {
+ dev_warn(p->dev, "%s:%d stencil read bo too small (layer size %d, "
+ "offset %ld, max layer %d, bo size %ld)\n",
+ __func__, __LINE__, surf.layer_size,
+ (unsigned long)track->db_s_read_offset << 8, mslice,
+ radeon_bo_size(track->db_s_read_bo));
+ dev_warn(p->dev, "%s:%d stencil invalid (0x%08x 0x%08x 0x%08x 0x%08x)\n",
+ __func__, __LINE__, track->db_depth_size,
+ track->db_depth_slice, track->db_s_info, track->db_z_info);
+ return -EINVAL;
+ }
+
+ offset = track->db_s_write_offset << 8;
+ if (offset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d stencil write bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, offset, surf.base_align);
+ return -EINVAL;
+ }
+ offset += surf.layer_size * mslice;
+ if (offset > radeon_bo_size(track->db_s_write_bo)) {
+ dev_warn(p->dev, "%s:%d stencil write bo too small (layer size %d, "
+ "offset %ld, max layer %d, bo size %ld)\n",
+ __func__, __LINE__, surf.layer_size,
+ (unsigned long)track->db_s_write_offset << 8, mslice,
+ radeon_bo_size(track->db_s_write_bo));
+ 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;
+}
+
+static int evergreen_cs_track_validate_depth(struct radeon_cs_parser *p)
+{
+ struct evergreen_cs_track *track = p->track;
+ struct eg_surface surf;
+ unsigned pitch, slice, mslice;
+ unsigned long offset;
+ int r;
+
+ mslice = G_028008_SLICE_MAX(track->db_depth_view) + 1;
+ pitch = G_028058_PITCH_TILE_MAX(track->db_depth_size);
+ slice = track->db_depth_slice;
+ surf.nbx = (pitch + 1) * 8;
+ surf.nby = ((slice + 1) * 64) / surf.nbx;
+ surf.mode = G_028040_ARRAY_MODE(track->db_z_info);
+ surf.format = G_028040_FORMAT(track->db_z_info);
+ surf.tsplit = G_028040_TILE_SPLIT(track->db_z_info);
+ surf.nbanks = G_028040_NUM_BANKS(track->db_z_info);
+ surf.bankw = G_028040_BANK_WIDTH(track->db_z_info);
+ surf.bankh = G_028040_BANK_HEIGHT(track->db_z_info);
+ surf.mtilea = G_028040_MACRO_TILE_ASPECT(track->db_z_info);
+ surf.nsamples = 1;
+
+ switch (surf.format) {
+ case V_028040_Z_16:
+ surf.format = V_028C70_COLOR_16;
+ break;
+ case V_028040_Z_24:
+ case V_028040_Z_32_FLOAT:
+ surf.format = V_028C70_COLOR_8_8_8_8;
+ break;
+ default:
+ dev_warn(p->dev, "%s:%d depth invalid format %d\n",
+ __func__, __LINE__, surf.format);
+ return -EINVAL;
+ }
+
+ r = evergreen_surface_value_conv_check(p, &surf, "depth");
+ if (r) {
+ dev_warn(p->dev, "%s:%d depth invalid (0x%08x 0x%08x 0x%08x)\n",
+ __func__, __LINE__, track->db_depth_size,
+ track->db_depth_slice, track->db_z_info);
+ return r;
+ }
+
+ r = evergreen_surface_check(p, &surf, "depth");
+ if (r) {
+ dev_warn(p->dev, "%s:%d depth invalid (0x%08x 0x%08x 0x%08x)\n",
+ __func__, __LINE__, track->db_depth_size,
+ track->db_depth_slice, track->db_z_info);
+ return r;
+ }
+
+ offset = track->db_z_read_offset << 8;
+ if (offset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d stencil read bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, offset, surf.base_align);
+ return -EINVAL;
+ }
+ offset += surf.layer_size * mslice;
+ if (offset > radeon_bo_size(track->db_z_read_bo)) {
+ dev_warn(p->dev, "%s:%d depth read bo too small (layer size %d, "
+ "offset %ld, max layer %d, bo size %ld)\n",
+ __func__, __LINE__, surf.layer_size,
+ (unsigned long)track->db_z_read_offset << 8, mslice,
+ radeon_bo_size(track->db_z_read_bo));
+ return -EINVAL;
+ }
+
+ offset = track->db_z_write_offset << 8;
+ if (offset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d stencil write bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, offset, surf.base_align);
+ return -EINVAL;
+ }
+ offset += surf.layer_size * mslice;
+ if (offset > radeon_bo_size(track->db_z_write_bo)) {
+ dev_warn(p->dev, "%s:%d depth write bo too small (layer size %d, "
+ "offset %ld, max layer %d, bo size %ld)\n",
+ __func__, __LINE__, surf.layer_size,
+ (unsigned long)track->db_z_write_offset << 8, mslice,
+ radeon_bo_size(track->db_z_write_bo));
+ 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;
+}
+
+static int evergreen_cs_track_validate_texture(struct radeon_cs_parser *p,
+ struct radeon_bo *texture,
+ struct radeon_bo *mipmap,
+ unsigned idx)
+{
+ struct eg_surface surf;
+ unsigned long toffset, moffset;
+ unsigned dim, llevel, mslice, width, height, depth, i;
+ u32 texdw[8];
+ int r;
+
+ texdw[0] = radeon_get_ib_value(p, idx + 0);
+ texdw[1] = radeon_get_ib_value(p, idx + 1);
+ texdw[2] = radeon_get_ib_value(p, idx + 2);
+ texdw[3] = radeon_get_ib_value(p, idx + 3);
+ texdw[4] = radeon_get_ib_value(p, idx + 4);
+ texdw[5] = radeon_get_ib_value(p, idx + 5);
+ texdw[6] = radeon_get_ib_value(p, idx + 6);
+ texdw[7] = radeon_get_ib_value(p, idx + 7);
+ dim = G_030000_DIM(texdw[0]);
+ llevel = G_030014_LAST_LEVEL(texdw[5]);
+ mslice = G_030014_LAST_ARRAY(texdw[5]) + 1;
+ width = G_030000_TEX_WIDTH(texdw[0]) + 1;
+ height = G_030004_TEX_HEIGHT(texdw[1]) + 1;
+ depth = G_030004_TEX_DEPTH(texdw[1]) + 1;
+ surf.format = G_03001C_DATA_FORMAT(texdw[7]);
+ surf.nbx = (G_030000_PITCH(texdw[0]) + 1) * 8;
+ surf.nbx = r600_fmt_get_nblocksx(surf.format, surf.nbx);
+ surf.nby = r600_fmt_get_nblocksy(surf.format, height);
+ surf.mode = G_030004_ARRAY_MODE(texdw[1]);
+ surf.tsplit = G_030018_TILE_SPLIT(texdw[6]);
+ surf.nbanks = G_03001C_NUM_BANKS(texdw[7]);
+ surf.bankw = G_03001C_BANK_WIDTH(texdw[7]);
+ surf.bankh = G_03001C_BANK_HEIGHT(texdw[7]);
+ surf.mtilea = G_03001C_MACRO_TILE_ASPECT(texdw[7]);
+ surf.nsamples = 1;
+ toffset = texdw[2] << 8;
+ moffset = texdw[3] << 8;
+
+ if (!r600_fmt_is_valid_texture(surf.format, p->family)) {
+ dev_warn(p->dev, "%s:%d texture invalid format %d\n",
+ __func__, __LINE__, surf.format);
+ return -EINVAL;
+ }
+ switch (dim) {
+ case V_030000_SQ_TEX_DIM_1D:
+ case V_030000_SQ_TEX_DIM_2D:
+ case V_030000_SQ_TEX_DIM_CUBEMAP:
+ case V_030000_SQ_TEX_DIM_1D_ARRAY:
+ case V_030000_SQ_TEX_DIM_2D_ARRAY:
+ depth = 1;
+ case V_030000_SQ_TEX_DIM_3D:
+ break;
+ default:
+ dev_warn(p->dev, "%s:%d texture invalid dimension %d\n",
+ __func__, __LINE__, dim);
+ return -EINVAL;
+ }
+
+ r = evergreen_surface_value_conv_check(p, &surf, "texture");
+ if (r) {
+ return r;
+ }
+
+ /* align height */
+ evergreen_surface_check(p, &surf, NULL);
+ surf.nby = ALIGN(surf.nby, surf.halign);
+
+ r = evergreen_surface_check(p, &surf, "texture");
+ if (r) {
+ dev_warn(p->dev, "%s:%d texture invalid 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n",
+ __func__, __LINE__, texdw[0], texdw[1], texdw[4],
+ texdw[5], texdw[6], texdw[7]);
+ return r;
+ }
+
+ /* check texture size */
+ if (toffset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d texture bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, toffset, surf.base_align);
+ return -EINVAL;
+ }
+ if (moffset & (surf.base_align - 1)) {
+ dev_warn(p->dev, "%s:%d mipmap bo base %ld not aligned with %ld\n",
+ __func__, __LINE__, moffset, surf.base_align);
+ return -EINVAL;
+ }
+ if (dim == SQ_TEX_DIM_3D) {
+ toffset += surf.layer_size * depth;
+ } else {
+ toffset += surf.layer_size * mslice;
+ }
+ if (toffset > radeon_bo_size(texture)) {
+ dev_warn(p->dev, "%s:%d texture bo too small (layer size %d, "
+ "offset %ld, max layer %d, depth %d, bo size %ld) (%d %d)\n",
+ __func__, __LINE__, surf.layer_size,
+ (unsigned long)texdw[2] << 8, mslice,
+ depth, radeon_bo_size(texture),
+ surf.nbx, surf.nby);
+ return -EINVAL;
+ }
+
+ /* check mipmap size */
+ for (i = 1; i <= llevel; i++) {
+ unsigned w, h, d;
+
+ w = r600_mip_minify(width, i);
+ h = r600_mip_minify(height, i);
+ d = r600_mip_minify(depth, i);
+ surf.nbx = r600_fmt_get_nblocksx(surf.format, w);
+ surf.nby = r600_fmt_get_nblocksy(surf.format, h);
+
+ switch (surf.mode) {
+ case ARRAY_2D_TILED_THIN1:
+ if (surf.nbx < surf.palign || surf.nby < surf.halign) {
+ surf.mode = ARRAY_1D_TILED_THIN1;
+ }
+ /* recompute alignment */
+ evergreen_surface_check(p, &surf, NULL);
+ break;
+ case ARRAY_LINEAR_GENERAL:
+ case ARRAY_LINEAR_ALIGNED:
+ case ARRAY_1D_TILED_THIN1:
+ break;
+ default:
+ dev_warn(p->dev, "%s:%d invalid array mode %d\n",
+ __func__, __LINE__, surf.mode);
+ return -EINVAL;
+ }
+ surf.nbx = ALIGN(surf.nbx, surf.palign);
+ surf.nby = ALIGN(surf.nby, surf.halign);
+
+ r = evergreen_surface_check(p, &surf, "mipmap");
+ if (r) {
+ return r;
+ }
+
+ if (dim == SQ_TEX_DIM_3D) {
+ moffset += surf.layer_size * d;
+ } else {
+ moffset += surf.layer_size * mslice;
+ }
+ if (moffset > radeon_bo_size(mipmap)) {
+ dev_warn(p->dev, "%s:%d mipmap [%d] bo too small (layer size %d, "
+ "offset %ld, coffset %ld, max layer %d, depth %d, "
+ "bo size %ld) level0 (%d %d %d)\n",
+ __func__, __LINE__, i, surf.layer_size,
+ (unsigned long)texdw[3] << 8, moffset, mslice,
+ d, radeon_bo_size(mipmap),
+ width, height, depth);
+ dev_warn(p->dev, "%s:%d problematic surf: (%d %d) (%d %d %d %d %d %d %d)\n",
+ __func__, __LINE__, surf.nbx, surf.nby,
+ surf.mode, surf.bpe, surf.nsamples,
+ surf.bankw, surf.bankh,
+ surf.tsplit, surf.mtilea);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int evergreen_cs_track_check(struct radeon_cs_parser *p)
+{
+ struct evergreen_cs_track *track = p->track;
+ unsigned tmp, i;
+ int r;
+ unsigned buffer_mask = 0;
+
+ /* check streamout */
+ if (track->streamout_dirty && track->vgt_strmout_config) {
+ for (i = 0; i < 4; i++) {
+ if (track->vgt_strmout_config & (1 << i)) {
+ buffer_mask |= (track->vgt_strmout_buffer_config >> (i * 4)) & 0xf;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (buffer_mask & (1 << i)) {
+ if (track->vgt_strmout_bo[i]) {
+ u64 offset = (u64)track->vgt_strmout_bo_offset[i] +
+ (u64)track->vgt_strmout_size[i];
+ if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) {
+ DRM_ERROR("streamout %d bo too small: 0x%llx, 0x%lx\n",
+ i, offset,
+ radeon_bo_size(track->vgt_strmout_bo[i]));
+ return -EINVAL;
+ }
+ } else {
+ dev_warn(p->dev, "No buffer for streamout %d\n", i);
+ return -EINVAL;
+ }
+ }
+ }
+ track->streamout_dirty = false;
+ }
+
+ if (track->sx_misc_kill_all_prims)
+ return 0;
+
+ /* check that we have a cb for each enabled target
+ */
+ if (track->cb_dirty) {
+ tmp = track->cb_target_mask;
+ for (i = 0; i < 8; i++) {
+ if ((tmp >> (i * 4)) & 0xF) {
+ /* at least one component is enabled */
+ if (track->cb_color_bo[i] == NULL) {
+ dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
+ __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
+ return -EINVAL;
+ }
+ /* check cb */
+ r = evergreen_cs_track_validate_cb(p, i);
+ if (r) {
+ return r;
+ }
+ }
+ }
+ track->cb_dirty = false;
+ }
+
+ if (track->db_dirty) {
+ /* Check stencil buffer */
+ if (G_028800_STENCIL_ENABLE(track->db_depth_control)) {
+ r = evergreen_cs_track_validate_stencil(p);
+ if (r)
+ return r;
+ }
+ /* Check depth buffer */
+ if (G_028800_Z_ENABLE(track->db_depth_control)) {
+ r = evergreen_cs_track_validate_depth(p);
+ if (r)
+ return r;
+ }
+ track->db_dirty = false;
+ }
+
return 0;
}
@@ -503,6 +1267,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
break;
case DB_DEPTH_CONTROL:
track->db_depth_control = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case CAYMAN_DB_EQAA:
if (p->rdev->family < CHIP_CAYMAN) {
@@ -532,20 +1297,35 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
track->db_z_info |= Z_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ unsigned bankw, bankh, mtaspect, tile_split;
+
+ evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ &bankw, &bankh, &mtaspect,
+ &tile_split);
ib[idx] |= DB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
- ib[idx] |= DB_TILE_SPLIT(evergreen_cs_get_tile_split(track->row_size));
+ ib[idx] |= DB_TILE_SPLIT(tile_split) |
+ DB_BANK_WIDTH(bankw) |
+ DB_BANK_HEIGHT(bankh) |
+ DB_MACRO_TILE_ASPECT(mtaspect);
}
}
+ track->db_dirty = true;
break;
case DB_STENCIL_INFO:
track->db_s_info = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case DB_DEPTH_VIEW:
track->db_depth_view = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case DB_DEPTH_SIZE:
track->db_depth_size = radeon_get_ib_value(p, idx);
- track->db_depth_size_idx = idx;
+ track->db_dirty = true;
+ break;
+ case R_02805C_DB_DEPTH_SLICE:
+ track->db_depth_slice = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case DB_Z_READ_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -557,6 +1337,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_z_read_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_z_read_bo = reloc->robj;
+ track->db_dirty = true;
break;
case DB_Z_WRITE_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -568,6 +1349,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_z_write_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_z_write_bo = reloc->robj;
+ track->db_dirty = true;
break;
case DB_STENCIL_READ_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -579,6 +1361,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_s_read_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_s_read_bo = reloc->robj;
+ track->db_dirty = true;
break;
case DB_STENCIL_WRITE_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -590,18 +1373,56 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_s_write_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_s_write_bo = reloc->robj;
+ track->db_dirty = true;
break;
case VGT_STRMOUT_CONFIG:
track->vgt_strmout_config = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_CONFIG:
track->vgt_strmout_buffer_config = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
+ case VGT_STRMOUT_BUFFER_BASE_0:
+ case VGT_STRMOUT_BUFFER_BASE_1:
+ case VGT_STRMOUT_BUFFER_BASE_2:
+ case VGT_STRMOUT_BUFFER_BASE_3:
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16;
+ track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ track->vgt_strmout_bo[tmp] = reloc->robj;
+ track->streamout_dirty = true;
+ break;
+ case VGT_STRMOUT_BUFFER_SIZE_0:
+ case VGT_STRMOUT_BUFFER_SIZE_1:
+ case VGT_STRMOUT_BUFFER_SIZE_2:
+ case VGT_STRMOUT_BUFFER_SIZE_3:
+ tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16;
+ /* size in register is DWs, convert to bytes */
+ track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4;
+ track->streamout_dirty = true;
+ break;
+ case CP_COHER_BASE:
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "missing reloc for CP_COHER_BASE "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
case CB_TARGET_MASK:
track->cb_target_mask = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case CB_SHADER_MASK:
track->cb_shader_mask = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case PA_SC_AA_CONFIG:
if (p->rdev->family >= CHIP_CAYMAN) {
@@ -631,6 +1452,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR7_VIEW:
tmp = (reg - CB_COLOR0_VIEW) / 0x3c;
track->cb_color_view[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case CB_COLOR8_VIEW:
case CB_COLOR9_VIEW:
@@ -638,6 +1460,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR11_VIEW:
tmp = ((reg - CB_COLOR8_VIEW) / 0x1c) + 8;
track->cb_color_view[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case CB_COLOR0_INFO:
case CB_COLOR1_INFO:
@@ -659,6 +1482,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
}
+ track->cb_dirty = true;
break;
case CB_COLOR8_INFO:
case CB_COLOR9_INFO:
@@ -676,6 +1500,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
}
+ track->cb_dirty = true;
break;
case CB_COLOR0_PITCH:
case CB_COLOR1_PITCH:
@@ -687,7 +1512,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR7_PITCH:
tmp = (reg - CB_COLOR0_PITCH) / 0x3c;
track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_pitch_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR8_PITCH:
case CB_COLOR9_PITCH:
@@ -695,7 +1520,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR11_PITCH:
tmp = ((reg - CB_COLOR8_PITCH) / 0x1c) + 8;
track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_pitch_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR0_SLICE:
case CB_COLOR1_SLICE:
@@ -707,7 +1532,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR7_SLICE:
tmp = (reg - CB_COLOR0_SLICE) / 0x3c;
track->cb_color_slice[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_slice_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR8_SLICE:
case CB_COLOR9_SLICE:
@@ -715,7 +1540,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR11_SLICE:
tmp = ((reg - CB_COLOR8_SLICE) / 0x1c) + 8;
track->cb_color_slice[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_slice_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR0_ATTRIB:
case CB_COLOR1_ATTRIB:
@@ -725,6 +1550,30 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR5_ATTRIB:
case CB_COLOR6_ATTRIB:
case CB_COLOR7_ATTRIB:
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ unsigned bankw, bankh, mtaspect, tile_split;
+
+ evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ &bankw, &bankh, &mtaspect,
+ &tile_split);
+ ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
+ ib[idx] |= CB_TILE_SPLIT(tile_split) |
+ CB_BANK_WIDTH(bankw) |
+ CB_BANK_HEIGHT(bankh) |
+ CB_MACRO_TILE_ASPECT(mtaspect);
+ }
+ }
+ tmp = ((reg - CB_COLOR0_ATTRIB) / 0x3c);
+ track->cb_color_attrib[tmp] = ib[idx];
+ track->cb_dirty = true;
+ break;
case CB_COLOR8_ATTRIB:
case CB_COLOR9_ATTRIB:
case CB_COLOR10_ATTRIB:
@@ -735,30 +1584,23 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
"0x%04X\n", reg);
return -EINVAL;
}
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
- ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
- ib[idx] |= CB_TILE_SPLIT(evergreen_cs_get_tile_split(track->row_size));
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
+ unsigned bankw, bankh, mtaspect, tile_split;
+
+ evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ &bankw, &bankh, &mtaspect,
+ &tile_split);
+ ib[idx] |= CB_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
+ ib[idx] |= CB_TILE_SPLIT(tile_split) |
+ CB_BANK_WIDTH(bankw) |
+ CB_BANK_HEIGHT(bankh) |
+ CB_MACRO_TILE_ASPECT(mtaspect);
+ }
}
- break;
- case CB_COLOR0_DIM:
- case CB_COLOR1_DIM:
- case CB_COLOR2_DIM:
- case CB_COLOR3_DIM:
- case CB_COLOR4_DIM:
- case CB_COLOR5_DIM:
- case CB_COLOR6_DIM:
- case CB_COLOR7_DIM:
- tmp = (reg - CB_COLOR0_DIM) / 0x3c;
- track->cb_color_dim[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_dim_idx[tmp] = idx;
- break;
- case CB_COLOR8_DIM:
- case CB_COLOR9_DIM:
- case CB_COLOR10_DIM:
- case CB_COLOR11_DIM:
- tmp = ((reg - CB_COLOR8_DIM) / 0x1c) + 8;
- track->cb_color_dim[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_dim_idx[tmp] = idx;
+ tmp = ((reg - CB_COLOR8_ATTRIB) / 0x1c) + 8;
+ track->cb_color_attrib[tmp] = ib[idx];
+ track->cb_dirty = true;
break;
case CB_COLOR0_FMASK:
case CB_COLOR1_FMASK:
@@ -833,8 +1675,8 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - CB_COLOR0_BASE) / 0x3c;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
- track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
+ track->cb_dirty = true;
break;
case CB_COLOR8_BASE:
case CB_COLOR9_BASE:
@@ -849,8 +1691,25 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = ((reg - CB_COLOR8_BASE) / 0x1c) + 8;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
- track->cb_color_base_last[tmp] = ib[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:
@@ -864,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:
@@ -989,6 +1847,9 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
+ case SX_MISC:
+ track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0;
+ break;
default:
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
@@ -996,22 +1857,30 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return 0;
}
-/**
- * evergreen_check_texture_resource() - check if register is authorized or not
- * @p: parser structure holding parsing context
- * @idx: index into the cs buffer
- * @texture: texture's bo structure
- * @mipmap: mipmap's bo structure
- *
- * This function will check that the resource has valid field and that
- * the texture and mipmap bo object are big enough to cover this resource.
- */
-static int evergreen_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
- struct radeon_bo *texture,
- struct radeon_bo *mipmap)
+static bool evergreen_is_safe_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
{
- /* XXX fill in */
- return 0;
+ u32 last_reg, m, i;
+
+ if (p->rdev->family >= CHIP_CAYMAN)
+ last_reg = ARRAY_SIZE(cayman_reg_safe_bm);
+ else
+ last_reg = ARRAY_SIZE(evergreen_reg_safe_bm);
+
+ i = (reg >> 7);
+ if (i >= last_reg) {
+ dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
+ return false;
+ }
+ m = 1 << ((reg >> 2) & 31);
+ if (p->rdev->family >= CHIP_CAYMAN) {
+ if (!(cayman_reg_safe_bm[i] & m))
+ return true;
+ } else {
+ if (!(evergreen_reg_safe_bm[i] & m))
+ return true;
+ }
+ dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
+ return false;
}
static int evergreen_packet3_check(struct radeon_cs_parser *p,
@@ -1036,6 +1905,8 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
{
int pred_op;
int tmp;
+ uint64_t offset;
+
if (pkt->count != 1) {
DRM_ERROR("bad SET PREDICATION\n");
return -EINVAL;
@@ -1059,8 +1930,12 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- ib[idx + 0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx + 1] = tmp + (upper_32_bits(reloc->lobj.gpu_offset) & 0xff);
+ offset = reloc->lobj.gpu_offset +
+ (idx_value & 0xfffffff0) +
+ ((u64)(tmp & 0xff) << 32);
+
+ ib[idx + 0] = offset;
+ ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff);
}
break;
case PACKET3_CONTEXT_CONTROL:
@@ -1088,6 +1963,9 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
break;
case PACKET3_INDEX_BASE:
+ {
+ uint64_t offset;
+
if (pkt->count != 1) {
DRM_ERROR("bad INDEX_BASE\n");
return -EINVAL;
@@ -1097,15 +1975,24 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad INDEX_BASE\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ idx_value +
+ ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
+
+ ib[idx+0] = offset;
+ ib[idx+1] = upper_32_bits(offset) & 0xff;
+
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX:
+ {
+ uint64_t offset;
if (pkt->count != 3) {
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
@@ -1115,15 +2002,25 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ idx_value +
+ ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
+
+ ib[idx+0] = offset;
+ ib[idx+1] = upper_32_bits(offset) & 0xff;
+
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX_2:
+ {
+ uint64_t offset;
+
if (pkt->count != 4) {
DRM_ERROR("bad DRAW_INDEX_2\n");
return -EINVAL;
@@ -1133,14 +2030,21 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DRAW_INDEX_2\n");
return -EINVAL;
}
- ib[idx+1] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ radeon_get_ib_value(p, idx+1) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
+
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX_AUTO:
if (pkt->count != 1) {
DRM_ERROR("bad DRAW_INDEX_AUTO\n");
@@ -1231,13 +2135,20 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
/* bit 4 is reg (0) or mem (1) */
if (idx_value & 0x10) {
+ uint64_t offset;
+
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad WAIT_REG_MEM\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffffc);
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_SURFACE_SYNC:
@@ -1262,16 +2173,25 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
if (pkt->count) {
+ uint64_t offset;
+
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffff8;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_EVENT_WRITE_EOP:
+ {
+ uint64_t offset;
+
if (pkt->count != 4) {
DRM_ERROR("bad EVENT_WRITE_EOP\n");
return -EINVAL;
@@ -1281,10 +2201,19 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE_EOP\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffffc;
+ ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
break;
+ }
case PACKET3_EVENT_WRITE_EOS:
+ {
+ uint64_t offset;
+
if (pkt->count != 3) {
DRM_ERROR("bad EVENT_WRITE_EOS\n");
return -EINVAL;
@@ -1294,9 +2223,15 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE_EOS\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffffc;
+ ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
break;
+ }
case PACKET3_SET_CONFIG_REG:
start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START;
end_reg = 4 * pkt->count + start_reg - 4;
@@ -1344,6 +2279,7 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
for (i = 0; i < (pkt->count / 8); i++) {
struct radeon_bo *texture, *mipmap;
+ u32 toffset, moffset;
u32 size, offset;
switch (G__SQ_CONSTANT_TYPE(radeon_get_ib_value(p, idx+1+(i*8)+7))) {
@@ -1354,32 +2290,42 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad SET_RESOURCE (tex)\n");
return -EINVAL;
}
- ib[idx+1+(i*8)+2] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
ib[idx+1+(i*8)+1] |=
TEX_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) {
- ib[idx+1+(i*8)+6] |=
- TEX_TILE_SPLIT(evergreen_cs_get_tile_split(track->row_size));
+ unsigned bankw, bankh, mtaspect, tile_split;
+
+ evergreen_tiling_fields(reloc->lobj.tiling_flags,
+ &bankw, &bankh, &mtaspect,
+ &tile_split);
+ ib[idx+1+(i*8)+6] |= TEX_TILE_SPLIT(tile_split);
ib[idx+1+(i*8)+7] |=
+ TEX_BANK_WIDTH(bankw) |
+ TEX_BANK_HEIGHT(bankh) |
+ MACRO_TILE_ASPECT(mtaspect) |
TEX_NUM_BANKS(evergreen_cs_get_num_banks(track->nbanks));
}
}
texture = reloc->robj;
+ toffset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
/* tex mip base */
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad SET_RESOURCE (tex)\n");
return -EINVAL;
}
- ib[idx+1+(i*8)+3] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ moffset = (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
mipmap = reloc->robj;
- r = evergreen_check_texture_resource(p, idx+1+(i*8),
- texture, mipmap);
+ r = evergreen_cs_track_validate_texture(p, texture, mipmap, idx+1+(i*8));
if (r)
return r;
+ ib[idx+1+(i*8)+2] += toffset;
+ ib[idx+1+(i*8)+3] += moffset;
break;
case SQ_TEX_VTX_VALID_BUFFER:
+ {
+ uint64_t offset64;
/* vtx base */
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
@@ -1391,11 +2337,15 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
/* force size to size of the buffer */
dev_warn(p->dev, "vbo resource seems too big for the bo\n");
- ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj);
+ ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj) - offset;
}
- ib[idx+1+(i*8)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
- ib[idx+1+(i*8)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset64 = reloc->lobj.gpu_offset + offset;
+ ib[idx+1+(i*8)+0] = offset64;
+ ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
+ (upper_32_bits(offset64) & 0xff);
break;
+ }
case SQ_TEX_VTX_INVALID_TEXTURE:
case SQ_TEX_VTX_INVALID_BUFFER:
default:
@@ -1451,6 +2401,104 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
break;
+ case PACKET3_STRMOUT_BUFFER_UPDATE:
+ if (pkt->count != 4) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (invalid count)\n");
+ return -EINVAL;
+ }
+ /* Updating memory at DST_ADDRESS. */
+ if (idx_value & 0x1) {
+ u64 offset;
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing dst reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+1);
+ offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE dst bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
+ }
+ /* Reading data from SRC_ADDRESS. */
+ if (((idx_value >> 1) & 0x3) == 2) {
+ u64 offset;
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing src reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+3);
+ offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE src bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
+ }
+ break;
+ case PACKET3_COPY_DW:
+ if (pkt->count != 4) {
+ DRM_ERROR("bad COPY_DW (invalid count)\n");
+ return -EINVAL;
+ }
+ if (idx_value & 0x1) {
+ u64 offset;
+ /* SRC is memory. */
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad COPY_DW (missing src reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+1);
+ offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad COPY_DW src bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
+ } else {
+ /* SRC is a reg. */
+ reg = radeon_get_ib_value(p, idx+1) << 2;
+ if (!evergreen_is_safe_reg(p, reg, idx+1))
+ return -EINVAL;
+ }
+ if (idx_value & 0x2) {
+ u64 offset;
+ /* DST is memory. */
+ r = evergreen_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad COPY_DW (missing dst reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+3);
+ offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad COPY_DW dst bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
+ } else {
+ /* DST is a reg. */
+ reg = radeon_get_ib_value(p, idx+3) << 2;
+ if (!evergreen_is_safe_reg(p, reg, idx+3))
+ return -EINVAL;
+ }
+ break;
case PACKET3_NOP:
break;
default:
diff --git a/drivers/gpu/drm/radeon/evergreen_reg.h b/drivers/gpu/drm/radeon/evergreen_reg.h
index 4215de95477e..96c10b3991aa 100644
--- a/drivers/gpu/drm/radeon/evergreen_reg.h
+++ b/drivers/gpu/drm/radeon/evergreen_reg.h
@@ -219,6 +219,7 @@
# define EVERGREEN_CRTC_MASTER_EN (1 << 0)
# define EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE (1 << 24)
#define EVERGREEN_CRTC_STATUS 0x6e8c
+# define EVERGREEN_CRTC_V_BLANK (1 << 0)
#define EVERGREEN_CRTC_STATUS_POSITION 0x6e90
#define EVERGREEN_MASTER_UPDATE_MODE 0x6ef8
#define EVERGREEN_CRTC_UPDATE_LOCK 0x6ed4
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index 74713d42df29..b4eefc355f16 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -77,6 +77,7 @@
#define CONFIG_MEMSIZE 0x5428
+#define CP_COHER_BASE 0x85F8
#define CP_ME_CNTL 0x86D8
#define CP_ME_HALT (1 << 28)
#define CP_PFP_HALT (1 << 26)
@@ -925,20 +926,138 @@
#define DB_DEBUG4 0x983C
#define DB_WATERMARKS 0x9854
#define DB_DEPTH_CONTROL 0x28800
+#define R_028800_DB_DEPTH_CONTROL 0x028800
+#define S_028800_STENCIL_ENABLE(x) (((x) & 0x1) << 0)
+#define G_028800_STENCIL_ENABLE(x) (((x) >> 0) & 0x1)
+#define C_028800_STENCIL_ENABLE 0xFFFFFFFE
+#define S_028800_Z_ENABLE(x) (((x) & 0x1) << 1)
+#define G_028800_Z_ENABLE(x) (((x) >> 1) & 0x1)
+#define C_028800_Z_ENABLE 0xFFFFFFFD
+#define S_028800_Z_WRITE_ENABLE(x) (((x) & 0x1) << 2)
+#define G_028800_Z_WRITE_ENABLE(x) (((x) >> 2) & 0x1)
+#define C_028800_Z_WRITE_ENABLE 0xFFFFFFFB
+#define S_028800_ZFUNC(x) (((x) & 0x7) << 4)
+#define G_028800_ZFUNC(x) (((x) >> 4) & 0x7)
+#define C_028800_ZFUNC 0xFFFFFF8F
+#define S_028800_BACKFACE_ENABLE(x) (((x) & 0x1) << 7)
+#define G_028800_BACKFACE_ENABLE(x) (((x) >> 7) & 0x1)
+#define C_028800_BACKFACE_ENABLE 0xFFFFFF7F
+#define S_028800_STENCILFUNC(x) (((x) & 0x7) << 8)
+#define G_028800_STENCILFUNC(x) (((x) >> 8) & 0x7)
+#define C_028800_STENCILFUNC 0xFFFFF8FF
+#define V_028800_STENCILFUNC_NEVER 0x00000000
+#define V_028800_STENCILFUNC_LESS 0x00000001
+#define V_028800_STENCILFUNC_EQUAL 0x00000002
+#define V_028800_STENCILFUNC_LEQUAL 0x00000003
+#define V_028800_STENCILFUNC_GREATER 0x00000004
+#define V_028800_STENCILFUNC_NOTEQUAL 0x00000005
+#define V_028800_STENCILFUNC_GEQUAL 0x00000006
+#define V_028800_STENCILFUNC_ALWAYS 0x00000007
+#define S_028800_STENCILFAIL(x) (((x) & 0x7) << 11)
+#define G_028800_STENCILFAIL(x) (((x) >> 11) & 0x7)
+#define C_028800_STENCILFAIL 0xFFFFC7FF
+#define V_028800_STENCIL_KEEP 0x00000000
+#define V_028800_STENCIL_ZERO 0x00000001
+#define V_028800_STENCIL_REPLACE 0x00000002
+#define V_028800_STENCIL_INCR 0x00000003
+#define V_028800_STENCIL_DECR 0x00000004
+#define V_028800_STENCIL_INVERT 0x00000005
+#define V_028800_STENCIL_INCR_WRAP 0x00000006
+#define V_028800_STENCIL_DECR_WRAP 0x00000007
+#define S_028800_STENCILZPASS(x) (((x) & 0x7) << 14)
+#define G_028800_STENCILZPASS(x) (((x) >> 14) & 0x7)
+#define C_028800_STENCILZPASS 0xFFFE3FFF
+#define S_028800_STENCILZFAIL(x) (((x) & 0x7) << 17)
+#define G_028800_STENCILZFAIL(x) (((x) >> 17) & 0x7)
+#define C_028800_STENCILZFAIL 0xFFF1FFFF
+#define S_028800_STENCILFUNC_BF(x) (((x) & 0x7) << 20)
+#define G_028800_STENCILFUNC_BF(x) (((x) >> 20) & 0x7)
+#define C_028800_STENCILFUNC_BF 0xFF8FFFFF
+#define S_028800_STENCILFAIL_BF(x) (((x) & 0x7) << 23)
+#define G_028800_STENCILFAIL_BF(x) (((x) >> 23) & 0x7)
+#define C_028800_STENCILFAIL_BF 0xFC7FFFFF
+#define S_028800_STENCILZPASS_BF(x) (((x) & 0x7) << 26)
+#define G_028800_STENCILZPASS_BF(x) (((x) >> 26) & 0x7)
+#define C_028800_STENCILZPASS_BF 0xE3FFFFFF
+#define S_028800_STENCILZFAIL_BF(x) (((x) & 0x7) << 29)
+#define G_028800_STENCILZFAIL_BF(x) (((x) >> 29) & 0x7)
+#define C_028800_STENCILZFAIL_BF 0x1FFFFFFF
#define DB_DEPTH_VIEW 0x28008
+#define R_028008_DB_DEPTH_VIEW 0x00028008
+#define S_028008_SLICE_START(x) (((x) & 0x7FF) << 0)
+#define G_028008_SLICE_START(x) (((x) >> 0) & 0x7FF)
+#define C_028008_SLICE_START 0xFFFFF800
+#define S_028008_SLICE_MAX(x) (((x) & 0x7FF) << 13)
+#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)
# define DB_NUM_BANKS(x) (((x) & 0x3) << 12)
# define DB_BANK_WIDTH(x) (((x) & 0x3) << 16)
# define DB_BANK_HEIGHT(x) (((x) & 0x3) << 20)
+# define DB_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 24)
+#define R_028040_DB_Z_INFO 0x028040
+#define S_028040_FORMAT(x) (((x) & 0x3) << 0)
+#define G_028040_FORMAT(x) (((x) >> 0) & 0x3)
+#define C_028040_FORMAT 0xFFFFFFFC
+#define V_028040_Z_INVALID 0x00000000
+#define V_028040_Z_16 0x00000001
+#define V_028040_Z_24 0x00000002
+#define V_028040_Z_32_FLOAT 0x00000003
+#define S_028040_ARRAY_MODE(x) (((x) & 0xF) << 4)
+#define G_028040_ARRAY_MODE(x) (((x) >> 4) & 0xF)
+#define C_028040_ARRAY_MODE 0xFFFFFF0F
+#define S_028040_READ_SIZE(x) (((x) & 0x1) << 28)
+#define G_028040_READ_SIZE(x) (((x) >> 28) & 0x1)
+#define C_028040_READ_SIZE 0xEFFFFFFF
+#define S_028040_TILE_SURFACE_ENABLE(x) (((x) & 0x1) << 29)
+#define G_028040_TILE_SURFACE_ENABLE(x) (((x) >> 29) & 0x1)
+#define C_028040_TILE_SURFACE_ENABLE 0xDFFFFFFF
+#define S_028040_ZRANGE_PRECISION(x) (((x) & 0x1) << 31)
+#define G_028040_ZRANGE_PRECISION(x) (((x) >> 31) & 0x1)
+#define C_028040_ZRANGE_PRECISION 0x7FFFFFFF
+#define S_028040_TILE_SPLIT(x) (((x) & 0x7) << 8)
+#define G_028040_TILE_SPLIT(x) (((x) >> 8) & 0x7)
+#define S_028040_NUM_BANKS(x) (((x) & 0x3) << 12)
+#define G_028040_NUM_BANKS(x) (((x) >> 12) & 0x3)
+#define S_028040_BANK_WIDTH(x) (((x) & 0x3) << 16)
+#define G_028040_BANK_WIDTH(x) (((x) >> 16) & 0x3)
+#define S_028040_BANK_HEIGHT(x) (((x) & 0x3) << 20)
+#define G_028040_BANK_HEIGHT(x) (((x) >> 20) & 0x3)
+#define S_028040_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 24)
+#define G_028040_MACRO_TILE_ASPECT(x) (((x) >> 24) & 0x3)
#define DB_STENCIL_INFO 0x28044
+#define R_028044_DB_STENCIL_INFO 0x028044
+#define S_028044_FORMAT(x) (((x) & 0x1) << 0)
+#define G_028044_FORMAT(x) (((x) >> 0) & 0x1)
+#define C_028044_FORMAT 0xFFFFFFFE
+#define G_028044_TILE_SPLIT(x) (((x) >> 8) & 0x7)
#define DB_Z_READ_BASE 0x28048
#define DB_STENCIL_READ_BASE 0x2804c
#define DB_Z_WRITE_BASE 0x28050
#define DB_STENCIL_WRITE_BASE 0x28054
#define DB_DEPTH_SIZE 0x28058
+#define R_028058_DB_DEPTH_SIZE 0x028058
+#define S_028058_PITCH_TILE_MAX(x) (((x) & 0x7FF) << 0)
+#define G_028058_PITCH_TILE_MAX(x) (((x) >> 0) & 0x7FF)
+#define C_028058_PITCH_TILE_MAX 0xFFFFF800
+#define S_028058_HEIGHT_TILE_MAX(x) (((x) & 0x7FF) << 11)
+#define G_028058_HEIGHT_TILE_MAX(x) (((x) >> 11) & 0x7FF)
+#define C_028058_HEIGHT_TILE_MAX 0xFFC007FF
+#define R_02805C_DB_DEPTH_SLICE 0x02805C
+#define S_02805C_SLICE_TILE_MAX(x) (((x) & 0x3FFFFF) << 0)
+#define G_02805C_SLICE_TILE_MAX(x) (((x) >> 0) & 0x3FFFFF)
+#define C_02805C_SLICE_TILE_MAX 0xFFC00000
#define SQ_PGM_START_PS 0x28840
#define SQ_PGM_START_VS 0x2885c
@@ -948,6 +1067,14 @@
#define SQ_PGM_START_HS 0x288b8
#define SQ_PGM_START_LS 0x288d0
+#define VGT_STRMOUT_BUFFER_BASE_0 0x28AD8
+#define VGT_STRMOUT_BUFFER_BASE_1 0x28AE8
+#define VGT_STRMOUT_BUFFER_BASE_2 0x28AF8
+#define VGT_STRMOUT_BUFFER_BASE_3 0x28B08
+#define VGT_STRMOUT_BUFFER_SIZE_0 0x28AD0
+#define VGT_STRMOUT_BUFFER_SIZE_1 0x28AE0
+#define VGT_STRMOUT_BUFFER_SIZE_2 0x28AF0
+#define VGT_STRMOUT_BUFFER_SIZE_3 0x28B00
#define VGT_STRMOUT_CONFIG 0x28b94
#define VGT_STRMOUT_BUFFER_CONFIG 0x28b98
@@ -974,6 +1101,114 @@
#define CB_COLOR0_PITCH 0x28c64
#define CB_COLOR0_SLICE 0x28c68
#define CB_COLOR0_VIEW 0x28c6c
+#define R_028C6C_CB_COLOR0_VIEW 0x00028C6C
+#define S_028C6C_SLICE_START(x) (((x) & 0x7FF) << 0)
+#define G_028C6C_SLICE_START(x) (((x) >> 0) & 0x7FF)
+#define C_028C6C_SLICE_START 0xFFFFF800
+#define S_028C6C_SLICE_MAX(x) (((x) & 0x7FF) << 13)
+#define G_028C6C_SLICE_MAX(x) (((x) >> 13) & 0x7FF)
+#define C_028C6C_SLICE_MAX 0xFF001FFF
+#define R_028C70_CB_COLOR0_INFO 0x028C70
+#define S_028C70_ENDIAN(x) (((x) & 0x3) << 0)
+#define G_028C70_ENDIAN(x) (((x) >> 0) & 0x3)
+#define C_028C70_ENDIAN 0xFFFFFFFC
+#define S_028C70_FORMAT(x) (((x) & 0x3F) << 2)
+#define G_028C70_FORMAT(x) (((x) >> 2) & 0x3F)
+#define C_028C70_FORMAT 0xFFFFFF03
+#define V_028C70_COLOR_INVALID 0x00000000
+#define V_028C70_COLOR_8 0x00000001
+#define V_028C70_COLOR_4_4 0x00000002
+#define V_028C70_COLOR_3_3_2 0x00000003
+#define V_028C70_COLOR_16 0x00000005
+#define V_028C70_COLOR_16_FLOAT 0x00000006
+#define V_028C70_COLOR_8_8 0x00000007
+#define V_028C70_COLOR_5_6_5 0x00000008
+#define V_028C70_COLOR_6_5_5 0x00000009
+#define V_028C70_COLOR_1_5_5_5 0x0000000A
+#define V_028C70_COLOR_4_4_4_4 0x0000000B
+#define V_028C70_COLOR_5_5_5_1 0x0000000C
+#define V_028C70_COLOR_32 0x0000000D
+#define V_028C70_COLOR_32_FLOAT 0x0000000E
+#define V_028C70_COLOR_16_16 0x0000000F
+#define V_028C70_COLOR_16_16_FLOAT 0x00000010
+#define V_028C70_COLOR_8_24 0x00000011
+#define V_028C70_COLOR_8_24_FLOAT 0x00000012
+#define V_028C70_COLOR_24_8 0x00000013
+#define V_028C70_COLOR_24_8_FLOAT 0x00000014
+#define V_028C70_COLOR_10_11_11 0x00000015
+#define V_028C70_COLOR_10_11_11_FLOAT 0x00000016
+#define V_028C70_COLOR_11_11_10 0x00000017
+#define V_028C70_COLOR_11_11_10_FLOAT 0x00000018
+#define V_028C70_COLOR_2_10_10_10 0x00000019
+#define V_028C70_COLOR_8_8_8_8 0x0000001A
+#define V_028C70_COLOR_10_10_10_2 0x0000001B
+#define V_028C70_COLOR_X24_8_32_FLOAT 0x0000001C
+#define V_028C70_COLOR_32_32 0x0000001D
+#define V_028C70_COLOR_32_32_FLOAT 0x0000001E
+#define V_028C70_COLOR_16_16_16_16 0x0000001F
+#define V_028C70_COLOR_16_16_16_16_FLOAT 0x00000020
+#define V_028C70_COLOR_32_32_32_32 0x00000022
+#define V_028C70_COLOR_32_32_32_32_FLOAT 0x00000023
+#define V_028C70_COLOR_32_32_32_FLOAT 0x00000030
+#define S_028C70_ARRAY_MODE(x) (((x) & 0xF) << 8)
+#define G_028C70_ARRAY_MODE(x) (((x) >> 8) & 0xF)
+#define C_028C70_ARRAY_MODE 0xFFFFF0FF
+#define V_028C70_ARRAY_LINEAR_GENERAL 0x00000000
+#define V_028C70_ARRAY_LINEAR_ALIGNED 0x00000001
+#define V_028C70_ARRAY_1D_TILED_THIN1 0x00000002
+#define V_028C70_ARRAY_2D_TILED_THIN1 0x00000004
+#define S_028C70_NUMBER_TYPE(x) (((x) & 0x7) << 12)
+#define G_028C70_NUMBER_TYPE(x) (((x) >> 12) & 0x7)
+#define C_028C70_NUMBER_TYPE 0xFFFF8FFF
+#define V_028C70_NUMBER_UNORM 0x00000000
+#define V_028C70_NUMBER_SNORM 0x00000001
+#define V_028C70_NUMBER_USCALED 0x00000002
+#define V_028C70_NUMBER_SSCALED 0x00000003
+#define V_028C70_NUMBER_UINT 0x00000004
+#define V_028C70_NUMBER_SINT 0x00000005
+#define V_028C70_NUMBER_SRGB 0x00000006
+#define V_028C70_NUMBER_FLOAT 0x00000007
+#define S_028C70_COMP_SWAP(x) (((x) & 0x3) << 15)
+#define G_028C70_COMP_SWAP(x) (((x) >> 15) & 0x3)
+#define C_028C70_COMP_SWAP 0xFFFE7FFF
+#define V_028C70_SWAP_STD 0x00000000
+#define V_028C70_SWAP_ALT 0x00000001
+#define V_028C70_SWAP_STD_REV 0x00000002
+#define V_028C70_SWAP_ALT_REV 0x00000003
+#define S_028C70_FAST_CLEAR(x) (((x) & 0x1) << 17)
+#define G_028C70_FAST_CLEAR(x) (((x) >> 17) & 0x1)
+#define C_028C70_FAST_CLEAR 0xFFFDFFFF
+#define S_028C70_COMPRESSION(x) (((x) & 0x3) << 18)
+#define G_028C70_COMPRESSION(x) (((x) >> 18) & 0x3)
+#define C_028C70_COMPRESSION 0xFFF3FFFF
+#define S_028C70_BLEND_CLAMP(x) (((x) & 0x1) << 19)
+#define G_028C70_BLEND_CLAMP(x) (((x) >> 19) & 0x1)
+#define C_028C70_BLEND_CLAMP 0xFFF7FFFF
+#define S_028C70_BLEND_BYPASS(x) (((x) & 0x1) << 20)
+#define G_028C70_BLEND_BYPASS(x) (((x) >> 20) & 0x1)
+#define C_028C70_BLEND_BYPASS 0xFFEFFFFF
+#define S_028C70_SIMPLE_FLOAT(x) (((x) & 0x1) << 21)
+#define G_028C70_SIMPLE_FLOAT(x) (((x) >> 21) & 0x1)
+#define C_028C70_SIMPLE_FLOAT 0xFFDFFFFF
+#define S_028C70_ROUND_MODE(x) (((x) & 0x1) << 22)
+#define G_028C70_ROUND_MODE(x) (((x) >> 22) & 0x1)
+#define C_028C70_ROUND_MODE 0xFFBFFFFF
+#define S_028C70_TILE_COMPACT(x) (((x) & 0x1) << 23)
+#define G_028C70_TILE_COMPACT(x) (((x) >> 23) & 0x1)
+#define C_028C70_TILE_COMPACT 0xFF7FFFFF
+#define S_028C70_SOURCE_FORMAT(x) (((x) & 0x3) << 24)
+#define G_028C70_SOURCE_FORMAT(x) (((x) >> 24) & 0x3)
+#define C_028C70_SOURCE_FORMAT 0xFCFFFFFF
+#define V_028C70_EXPORT_4C_32BPC 0x0
+#define V_028C70_EXPORT_4C_16BPC 0x1
+#define V_028C70_EXPORT_2C_32BPC 0x2 /* Do not use */
+#define S_028C70_RAT(x) (((x) & 0x1) << 26)
+#define G_028C70_RAT(x) (((x) >> 26) & 0x1)
+#define C_028C70_RAT 0xFBFFFFFF
+#define S_028C70_RESOURCE_TYPE(x) (((x) & 0x7) << 27)
+#define G_028C70_RESOURCE_TYPE(x) (((x) >> 27) & 0x7)
+#define C_028C70_RESOURCE_TYPE 0xC7FFFFFF
+
#define CB_COLOR0_INFO 0x28c70
# define CB_FORMAT(x) ((x) << 2)
# define CB_ARRAY_MODE(x) ((x) << 8)
@@ -984,6 +1219,20 @@
# define CB_SOURCE_FORMAT(x) ((x) << 24)
# define CB_SF_EXPORT_FULL 0
# define CB_SF_EXPORT_NORM 1
+#define R_028C74_CB_COLOR0_ATTRIB 0x028C74
+#define S_028C74_NON_DISP_TILING_ORDER(x) (((x) & 0x1) << 4)
+#define G_028C74_NON_DISP_TILING_ORDER(x) (((x) >> 4) & 0x1)
+#define C_028C74_NON_DISP_TILING_ORDER 0xFFFFFFEF
+#define S_028C74_TILE_SPLIT(x) (((x) & 0xf) << 5)
+#define G_028C74_TILE_SPLIT(x) (((x) >> 5) & 0xf)
+#define S_028C74_NUM_BANKS(x) (((x) & 0x3) << 10)
+#define G_028C74_NUM_BANKS(x) (((x) >> 10) & 0x3)
+#define S_028C74_BANK_WIDTH(x) (((x) & 0x3) << 13)
+#define G_028C74_BANK_WIDTH(x) (((x) >> 13) & 0x3)
+#define S_028C74_BANK_HEIGHT(x) (((x) & 0x3) << 16)
+#define G_028C74_BANK_HEIGHT(x) (((x) >> 16) & 0x3)
+#define S_028C74_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 19)
+#define G_028C74_MACRO_TILE_ASPECT(x) (((x) >> 19) & 0x3)
#define CB_COLOR0_ATTRIB 0x28c74
# define CB_TILE_SPLIT(x) (((x) & 0x7) << 5)
# define ADDR_SURF_TILE_SPLIT_64B 0
@@ -1008,6 +1257,7 @@
# define ADDR_SURF_BANK_HEIGHT_2 1
# define ADDR_SURF_BANK_HEIGHT_4 2
# define ADDR_SURF_BANK_HEIGHT_8 3
+# define CB_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 19)
#define CB_COLOR0_DIM 0x28c78
/* only CB0-7 blocks have these regs */
#define CB_COLOR0_CMASK 0x28c7c
@@ -1196,9 +1446,144 @@
#define SQ_TEX_RESOURCE_WORD6_0 0x30018
# define TEX_TILE_SPLIT(x) (((x) & 0x7) << 29)
#define SQ_TEX_RESOURCE_WORD7_0 0x3001c
+# define MACRO_TILE_ASPECT(x) (((x) & 0x3) << 6)
# define TEX_BANK_WIDTH(x) (((x) & 0x3) << 8)
# define TEX_BANK_HEIGHT(x) (((x) & 0x3) << 10)
# define TEX_NUM_BANKS(x) (((x) & 0x3) << 16)
+#define R_030000_SQ_TEX_RESOURCE_WORD0_0 0x030000
+#define S_030000_DIM(x) (((x) & 0x7) << 0)
+#define G_030000_DIM(x) (((x) >> 0) & 0x7)
+#define C_030000_DIM 0xFFFFFFF8
+#define V_030000_SQ_TEX_DIM_1D 0x00000000
+#define V_030000_SQ_TEX_DIM_2D 0x00000001
+#define V_030000_SQ_TEX_DIM_3D 0x00000002
+#define V_030000_SQ_TEX_DIM_CUBEMAP 0x00000003
+#define V_030000_SQ_TEX_DIM_1D_ARRAY 0x00000004
+#define V_030000_SQ_TEX_DIM_2D_ARRAY 0x00000005
+#define V_030000_SQ_TEX_DIM_2D_MSAA 0x00000006
+#define V_030000_SQ_TEX_DIM_2D_ARRAY_MSAA 0x00000007
+#define S_030000_NON_DISP_TILING_ORDER(x) (((x) & 0x1) << 5)
+#define G_030000_NON_DISP_TILING_ORDER(x) (((x) >> 5) & 0x1)
+#define C_030000_NON_DISP_TILING_ORDER 0xFFFFFFDF
+#define S_030000_PITCH(x) (((x) & 0xFFF) << 6)
+#define G_030000_PITCH(x) (((x) >> 6) & 0xFFF)
+#define C_030000_PITCH 0xFFFC003F
+#define S_030000_TEX_WIDTH(x) (((x) & 0x3FFF) << 18)
+#define G_030000_TEX_WIDTH(x) (((x) >> 18) & 0x3FFF)
+#define C_030000_TEX_WIDTH 0x0003FFFF
+#define R_030004_SQ_TEX_RESOURCE_WORD1_0 0x030004
+#define S_030004_TEX_HEIGHT(x) (((x) & 0x3FFF) << 0)
+#define G_030004_TEX_HEIGHT(x) (((x) >> 0) & 0x3FFF)
+#define C_030004_TEX_HEIGHT 0xFFFFC000
+#define S_030004_TEX_DEPTH(x) (((x) & 0x1FFF) << 14)
+#define G_030004_TEX_DEPTH(x) (((x) >> 14) & 0x1FFF)
+#define C_030004_TEX_DEPTH 0xF8003FFF
+#define S_030004_ARRAY_MODE(x) (((x) & 0xF) << 28)
+#define G_030004_ARRAY_MODE(x) (((x) >> 28) & 0xF)
+#define C_030004_ARRAY_MODE 0x0FFFFFFF
+#define R_030008_SQ_TEX_RESOURCE_WORD2_0 0x030008
+#define S_030008_BASE_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0)
+#define G_030008_BASE_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF)
+#define C_030008_BASE_ADDRESS 0x00000000
+#define R_03000C_SQ_TEX_RESOURCE_WORD3_0 0x03000C
+#define S_03000C_MIP_ADDRESS(x) (((x) & 0xFFFFFFFF) << 0)
+#define G_03000C_MIP_ADDRESS(x) (((x) >> 0) & 0xFFFFFFFF)
+#define C_03000C_MIP_ADDRESS 0x00000000
+#define R_030010_SQ_TEX_RESOURCE_WORD4_0 0x030010
+#define S_030010_FORMAT_COMP_X(x) (((x) & 0x3) << 0)
+#define G_030010_FORMAT_COMP_X(x) (((x) >> 0) & 0x3)
+#define C_030010_FORMAT_COMP_X 0xFFFFFFFC
+#define V_030010_SQ_FORMAT_COMP_UNSIGNED 0x00000000
+#define V_030010_SQ_FORMAT_COMP_SIGNED 0x00000001
+#define V_030010_SQ_FORMAT_COMP_UNSIGNED_BIASED 0x00000002
+#define S_030010_FORMAT_COMP_Y(x) (((x) & 0x3) << 2)
+#define G_030010_FORMAT_COMP_Y(x) (((x) >> 2) & 0x3)
+#define C_030010_FORMAT_COMP_Y 0xFFFFFFF3
+#define S_030010_FORMAT_COMP_Z(x) (((x) & 0x3) << 4)
+#define G_030010_FORMAT_COMP_Z(x) (((x) >> 4) & 0x3)
+#define C_030010_FORMAT_COMP_Z 0xFFFFFFCF
+#define S_030010_FORMAT_COMP_W(x) (((x) & 0x3) << 6)
+#define G_030010_FORMAT_COMP_W(x) (((x) >> 6) & 0x3)
+#define C_030010_FORMAT_COMP_W 0xFFFFFF3F
+#define S_030010_NUM_FORMAT_ALL(x) (((x) & 0x3) << 8)
+#define G_030010_NUM_FORMAT_ALL(x) (((x) >> 8) & 0x3)
+#define C_030010_NUM_FORMAT_ALL 0xFFFFFCFF
+#define V_030010_SQ_NUM_FORMAT_NORM 0x00000000
+#define V_030010_SQ_NUM_FORMAT_INT 0x00000001
+#define V_030010_SQ_NUM_FORMAT_SCALED 0x00000002
+#define S_030010_SRF_MODE_ALL(x) (((x) & 0x1) << 10)
+#define G_030010_SRF_MODE_ALL(x) (((x) >> 10) & 0x1)
+#define C_030010_SRF_MODE_ALL 0xFFFFFBFF
+#define V_030010_SRF_MODE_ZERO_CLAMP_MINUS_ONE 0x00000000
+#define V_030010_SRF_MODE_NO_ZERO 0x00000001
+#define S_030010_FORCE_DEGAMMA(x) (((x) & 0x1) << 11)
+#define G_030010_FORCE_DEGAMMA(x) (((x) >> 11) & 0x1)
+#define C_030010_FORCE_DEGAMMA 0xFFFFF7FF
+#define S_030010_ENDIAN_SWAP(x) (((x) & 0x3) << 12)
+#define G_030010_ENDIAN_SWAP(x) (((x) >> 12) & 0x3)
+#define C_030010_ENDIAN_SWAP 0xFFFFCFFF
+#define S_030010_DST_SEL_X(x) (((x) & 0x7) << 16)
+#define G_030010_DST_SEL_X(x) (((x) >> 16) & 0x7)
+#define C_030010_DST_SEL_X 0xFFF8FFFF
+#define V_030010_SQ_SEL_X 0x00000000
+#define V_030010_SQ_SEL_Y 0x00000001
+#define V_030010_SQ_SEL_Z 0x00000002
+#define V_030010_SQ_SEL_W 0x00000003
+#define V_030010_SQ_SEL_0 0x00000004
+#define V_030010_SQ_SEL_1 0x00000005
+#define S_030010_DST_SEL_Y(x) (((x) & 0x7) << 19)
+#define G_030010_DST_SEL_Y(x) (((x) >> 19) & 0x7)
+#define C_030010_DST_SEL_Y 0xFFC7FFFF
+#define S_030010_DST_SEL_Z(x) (((x) & 0x7) << 22)
+#define G_030010_DST_SEL_Z(x) (((x) >> 22) & 0x7)
+#define C_030010_DST_SEL_Z 0xFE3FFFFF
+#define S_030010_DST_SEL_W(x) (((x) & 0x7) << 25)
+#define G_030010_DST_SEL_W(x) (((x) >> 25) & 0x7)
+#define C_030010_DST_SEL_W 0xF1FFFFFF
+#define S_030010_BASE_LEVEL(x) (((x) & 0xF) << 28)
+#define G_030010_BASE_LEVEL(x) (((x) >> 28) & 0xF)
+#define C_030010_BASE_LEVEL 0x0FFFFFFF
+#define R_030014_SQ_TEX_RESOURCE_WORD5_0 0x030014
+#define S_030014_LAST_LEVEL(x) (((x) & 0xF) << 0)
+#define G_030014_LAST_LEVEL(x) (((x) >> 0) & 0xF)
+#define C_030014_LAST_LEVEL 0xFFFFFFF0
+#define S_030014_BASE_ARRAY(x) (((x) & 0x1FFF) << 4)
+#define G_030014_BASE_ARRAY(x) (((x) >> 4) & 0x1FFF)
+#define C_030014_BASE_ARRAY 0xFFFE000F
+#define S_030014_LAST_ARRAY(x) (((x) & 0x1FFF) << 17)
+#define G_030014_LAST_ARRAY(x) (((x) >> 17) & 0x1FFF)
+#define C_030014_LAST_ARRAY 0xC001FFFF
+#define R_030018_SQ_TEX_RESOURCE_WORD6_0 0x030018
+#define S_030018_MAX_ANISO(x) (((x) & 0x7) << 0)
+#define G_030018_MAX_ANISO(x) (((x) >> 0) & 0x7)
+#define C_030018_MAX_ANISO 0xFFFFFFF8
+#define S_030018_PERF_MODULATION(x) (((x) & 0x7) << 3)
+#define G_030018_PERF_MODULATION(x) (((x) >> 3) & 0x7)
+#define C_030018_PERF_MODULATION 0xFFFFFFC7
+#define S_030018_INTERLACED(x) (((x) & 0x1) << 6)
+#define G_030018_INTERLACED(x) (((x) >> 6) & 0x1)
+#define C_030018_INTERLACED 0xFFFFFFBF
+#define S_030018_TILE_SPLIT(x) (((x) & 0x7) << 29)
+#define G_030018_TILE_SPLIT(x) (((x) >> 29) & 0x7)
+#define R_03001C_SQ_TEX_RESOURCE_WORD7_0 0x03001C
+#define S_03001C_MACRO_TILE_ASPECT(x) (((x) & 0x3) << 6)
+#define G_03001C_MACRO_TILE_ASPECT(x) (((x) >> 6) & 0x3)
+#define S_03001C_BANK_WIDTH(x) (((x) & 0x3) << 8)
+#define G_03001C_BANK_WIDTH(x) (((x) >> 8) & 0x3)
+#define S_03001C_BANK_HEIGHT(x) (((x) & 0x3) << 10)
+#define G_03001C_BANK_HEIGHT(x) (((x) >> 10) & 0x3)
+#define S_03001C_NUM_BANKS(x) (((x) & 0x3) << 16)
+#define G_03001C_NUM_BANKS(x) (((x) >> 16) & 0x3)
+#define S_03001C_TYPE(x) (((x) & 0x3) << 30)
+#define G_03001C_TYPE(x) (((x) >> 30) & 0x3)
+#define C_03001C_TYPE 0x3FFFFFFF
+#define V_03001C_SQ_TEX_VTX_INVALID_TEXTURE 0x00000000
+#define V_03001C_SQ_TEX_VTX_INVALID_BUFFER 0x00000001
+#define V_03001C_SQ_TEX_VTX_VALID_TEXTURE 0x00000002
+#define V_03001C_SQ_TEX_VTX_VALID_BUFFER 0x00000003
+#define S_03001C_DATA_FORMAT(x) (((x) & 0x3F) << 0)
+#define G_03001C_DATA_FORMAT(x) (((x) >> 0) & 0x3F)
+#define C_03001C_DATA_FORMAT 0xFFFFFFC0
#define SQ_VTX_CONSTANT_WORD0_0 0x30000
#define SQ_VTX_CONSTANT_WORD1_0 0x30004
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 2509c505acb8..a48ca53fcd6a 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -42,6 +42,8 @@ extern void evergreen_irq_suspend(struct radeon_device *rdev);
extern int evergreen_mc_init(struct radeon_device *rdev);
extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev);
extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
+extern void si_rlc_fini(struct radeon_device *rdev);
+extern int si_rlc_init(struct radeon_device *rdev);
#define EVERGREEN_PFP_UCODE_SIZE 1120
#define EVERGREEN_PM4_UCODE_SIZE 1376
@@ -53,6 +55,8 @@ extern void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
#define CAYMAN_RLC_UCODE_SIZE 1024
#define CAYMAN_MC_UCODE_SIZE 6037
+#define ARUBA_RLC_UCODE_SIZE 1536
+
/* Firmware Names */
MODULE_FIRMWARE("radeon/BARTS_pfp.bin");
MODULE_FIRMWARE("radeon/BARTS_me.bin");
@@ -68,6 +72,9 @@ MODULE_FIRMWARE("radeon/CAYMAN_pfp.bin");
MODULE_FIRMWARE("radeon/CAYMAN_me.bin");
MODULE_FIRMWARE("radeon/CAYMAN_mc.bin");
MODULE_FIRMWARE("radeon/CAYMAN_rlc.bin");
+MODULE_FIRMWARE("radeon/ARUBA_pfp.bin");
+MODULE_FIRMWARE("radeon/ARUBA_me.bin");
+MODULE_FIRMWARE("radeon/ARUBA_rlc.bin");
#define BTC_IO_MC_REGS_SIZE 29
@@ -326,6 +333,15 @@ int ni_init_microcode(struct radeon_device *rdev)
rlc_req_size = CAYMAN_RLC_UCODE_SIZE * 4;
mc_req_size = CAYMAN_MC_UCODE_SIZE * 4;
break;
+ case CHIP_ARUBA:
+ chip_name = "ARUBA";
+ rlc_chip_name = "ARUBA";
+ /* pfp/me same size as CAYMAN */
+ pfp_req_size = CAYMAN_PFP_UCODE_SIZE * 4;
+ me_req_size = CAYMAN_PM4_UCODE_SIZE * 4;
+ rlc_req_size = ARUBA_RLC_UCODE_SIZE * 4;
+ mc_req_size = 0;
+ break;
default: BUG();
}
@@ -365,15 +381,18 @@ int ni_init_microcode(struct radeon_device *rdev)
err = -EINVAL;
}
- snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
- err = request_firmware(&rdev->mc_fw, fw_name, &pdev->dev);
- if (err)
- goto out;
- if (rdev->mc_fw->size != mc_req_size) {
- printk(KERN_ERR
- "ni_mc: Bogus length %zu in firmware \"%s\"\n",
- rdev->mc_fw->size, fw_name);
- err = -EINVAL;
+ /* no MC ucode on TN */
+ if (!(rdev->flags & RADEON_IS_IGP)) {
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+ err = request_firmware(&rdev->mc_fw, fw_name, &pdev->dev);
+ if (err)
+ goto out;
+ if (rdev->mc_fw->size != mc_req_size) {
+ printk(KERN_ERR
+ "ni_mc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->mc_fw->size, fw_name);
+ err = -EINVAL;
+ }
}
out:
platform_device_unregister(pdev);
@@ -478,6 +497,7 @@ static u32 cayman_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * CAYMAN_MAX_PIPES);
switch (rdev->family) {
case CHIP_CAYMAN:
+ case CHIP_ARUBA:
force_no_swizzle = true;
break;
default:
@@ -610,7 +630,6 @@ static void cayman_gpu_init(struct radeon_device *rdev)
switch (rdev->family) {
case CHIP_CAYMAN:
- default:
rdev->config.cayman.max_shader_engines = 2;
rdev->config.cayman.max_pipes_per_simd = 4;
rdev->config.cayman.max_tile_pipes = 8;
@@ -632,6 +651,43 @@ static void cayman_gpu_init(struct radeon_device *rdev)
rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30;
rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130;
break;
+ case CHIP_ARUBA:
+ default:
+ rdev->config.cayman.max_shader_engines = 1;
+ rdev->config.cayman.max_pipes_per_simd = 4;
+ rdev->config.cayman.max_tile_pipes = 2;
+ if ((rdev->pdev->device == 0x9900) ||
+ (rdev->pdev->device == 0x9901)) {
+ rdev->config.cayman.max_simds_per_se = 6;
+ rdev->config.cayman.max_backends_per_se = 2;
+ } else if ((rdev->pdev->device == 0x9903) ||
+ (rdev->pdev->device == 0x9904)) {
+ rdev->config.cayman.max_simds_per_se = 4;
+ rdev->config.cayman.max_backends_per_se = 2;
+ } else if ((rdev->pdev->device == 0x9990) ||
+ (rdev->pdev->device == 0x9991)) {
+ rdev->config.cayman.max_simds_per_se = 3;
+ rdev->config.cayman.max_backends_per_se = 1;
+ } else {
+ rdev->config.cayman.max_simds_per_se = 2;
+ rdev->config.cayman.max_backends_per_se = 1;
+ }
+ rdev->config.cayman.max_texture_channel_caches = 2;
+ rdev->config.cayman.max_gprs = 256;
+ rdev->config.cayman.max_threads = 256;
+ rdev->config.cayman.max_gs_threads = 32;
+ rdev->config.cayman.max_stack_entries = 512;
+ rdev->config.cayman.sx_num_of_sets = 8;
+ rdev->config.cayman.sx_max_export_size = 256;
+ rdev->config.cayman.sx_max_export_pos_size = 64;
+ rdev->config.cayman.sx_max_export_smx_size = 192;
+ rdev->config.cayman.max_hw_contexts = 8;
+ rdev->config.cayman.sq_num_cf_insts = 2;
+
+ rdev->config.cayman.sc_prim_fifo_size = 0x40;
+ rdev->config.cayman.sc_hiz_tile_fifo_size = 0x30;
+ rdev->config.cayman.sc_earlyz_tile_fifo_size = 0x130;
+ break;
}
/* Initialize HDP */
@@ -652,7 +708,9 @@ static void cayman_gpu_init(struct radeon_device *rdev)
cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE);
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG);
- cgts_tcc_disable = 0xff000000;
+ cgts_tcc_disable = 0xffff0000;
+ for (i = 0; i < rdev->config.cayman.max_texture_channel_caches; i++)
+ cgts_tcc_disable &= ~(1 << (16 + i));
gc_user_rb_backend_disable = RREG32(GC_USER_RB_BACKEND_DISABLE);
gc_user_shader_pipe_config = RREG32(GC_USER_SHADER_PIPE_CONFIG);
cgts_user_tcc_disable = RREG32(CGTS_USER_TCC_DISABLE);
@@ -804,8 +862,13 @@ static void cayman_gpu_init(struct radeon_device *rdev)
rdev->config.cayman.tile_config |= (3 << 0);
break;
}
- rdev->config.cayman.tile_config |=
- ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
+
+ /* num banks is 8 on all fusion asics. 0 = 4, 1 = 8, 2 = 16 */
+ if (rdev->flags & RADEON_IS_IGP)
+ rdev->config.evergreen.tile_config |= 1 << 4;
+ else
+ rdev->config.cayman.tile_config |=
+ ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
rdev->config.cayman.tile_config |=
((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
rdev->config.cayman.tile_config |=
@@ -1318,7 +1381,7 @@ int cayman_cp_resume(struct radeon_device *rdev)
rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false;
rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false;
/* this only test cp0 */
- r = radeon_ring_test(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+ r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false;
rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false;
@@ -1440,18 +1503,29 @@ static int cayman_startup(struct radeon_device *rdev)
/* enable pcie gen2 link */
evergreen_pcie_gen2_enable(rdev);
- if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) {
- r = ni_init_microcode(rdev);
+ if (rdev->flags & RADEON_IS_IGP) {
+ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
+ r = ni_init_microcode(rdev);
+ if (r) {
+ DRM_ERROR("Failed to load firmware!\n");
+ return r;
+ }
+ }
+ } else {
+ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) {
+ r = ni_init_microcode(rdev);
+ if (r) {
+ DRM_ERROR("Failed to load firmware!\n");
+ return r;
+ }
+ }
+
+ r = ni_mc_load_microcode(rdev);
if (r) {
- DRM_ERROR("Failed to load firmware!\n");
+ DRM_ERROR("Failed to load MC firmware!\n");
return r;
}
}
- r = ni_mc_load_microcode(rdev);
- if (r) {
- DRM_ERROR("Failed to load MC firmware!\n");
- return r;
- }
r = r600_vram_scratch_init(rdev);
if (r)
@@ -1466,10 +1540,19 @@ static int cayman_startup(struct radeon_device *rdev)
r = evergreen_blit_init(rdev);
if (r) {
r600_blit_fini(rdev);
- rdev->asic->copy = NULL;
+ rdev->asic->copy.copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
+ /* allocate rlc buffers */
+ if (rdev->flags & RADEON_IS_IGP) {
+ r = si_rlc_init(rdev);
+ if (r) {
+ DRM_ERROR("Failed to init rlc BOs!\n");
+ return r;
+ }
+ }
+
/* allocate wb buffer */
r = radeon_wb_init(rdev);
if (r)
@@ -1518,7 +1601,7 @@ static int cayman_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r600_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
DRM_ERROR("radeon: failed testing IB (%d).\n", r);
rdev->accel_working = false;
@@ -1654,6 +1737,8 @@ int cayman_init(struct radeon_device *rdev)
dev_err(rdev->dev, "disabling GPU acceleration\n");
cayman_cp_fini(rdev);
r600_irq_fini(rdev);
+ if (rdev->flags & RADEON_IS_IGP)
+ si_rlc_fini(rdev);
radeon_wb_fini(rdev);
r100_ib_fini(rdev);
radeon_vm_manager_fini(rdev);
@@ -1665,8 +1750,11 @@ int cayman_init(struct radeon_device *rdev)
/* Don't start up if the MC ucode is missing.
* The default clocks and voltages before the MC ucode
* is loaded are not suffient for advanced operations.
+ *
+ * We can skip this check for TN, because there is no MC
+ * ucode.
*/
- if (!rdev->mc_fw) {
+ if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) {
DRM_ERROR("radeon: MC ucode required for NI+.\n");
return -EINVAL;
}
@@ -1679,6 +1767,8 @@ void cayman_fini(struct radeon_device *rdev)
r600_blit_fini(rdev);
cayman_cp_fini(rdev);
r600_irq_fini(rdev);
+ if (rdev->flags & RADEON_IS_IGP)
+ si_rlc_fini(rdev);
radeon_wb_fini(rdev);
radeon_vm_manager_fini(rdev);
r100_ib_fini(rdev);
@@ -1702,7 +1792,12 @@ int cayman_vm_init(struct radeon_device *rdev)
/* number of VMs */
rdev->vm_manager.nvm = 8;
/* base offset of vram pages */
- rdev->vm_manager.vram_base_offset = 0;
+ if (rdev->flags & RADEON_IS_IGP) {
+ u64 tmp = RREG32(FUS_MC_VM_FB_OFFSET);
+ tmp <<= 22;
+ rdev->vm_manager.vram_base_offset = tmp;
+ } else
+ rdev->vm_manager.vram_base_offset = 0;
return 0;
}
diff --git a/drivers/gpu/drm/radeon/nid.h b/drivers/gpu/drm/radeon/nid.h
index 9a7f3b6e02de..2aa7046ada56 100644
--- a/drivers/gpu/drm/radeon/nid.h
+++ b/drivers/gpu/drm/radeon/nid.h
@@ -106,6 +106,7 @@
#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3)
#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5)
#define ENABLE_ADVANCED_DRIVER_MODEL (1 << 6)
+#define FUS_MC_VM_FB_OFFSET 0x2068
#define MC_SHARED_BLACKOUT_CNTL 0x20ac
#define MC_ARB_RAMCFG 0x2760
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 333cde9d4e7b..81801c176aa5 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -65,6 +65,40 @@ MODULE_FIRMWARE(FIRMWARE_R520);
#include "r100_track.h"
+void r100_wait_for_vblank(struct radeon_device *rdev, int crtc)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc];
+ int i;
+
+ if (radeon_crtc->crtc_id == 0) {
+ if (RREG32(RADEON_CRTC_GEN_CNTL) & RADEON_CRTC_EN) {
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (!(RREG32(RADEON_CRTC_STATUS) & RADEON_CRTC_VBLANK_CUR))
+ break;
+ udelay(1);
+ }
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(RADEON_CRTC_STATUS) & RADEON_CRTC_VBLANK_CUR)
+ break;
+ udelay(1);
+ }
+ }
+ } else {
+ if (RREG32(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_EN) {
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (!(RREG32(RADEON_CRTC2_STATUS) & RADEON_CRTC2_VBLANK_CUR))
+ break;
+ udelay(1);
+ }
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(RADEON_CRTC2_STATUS) & RADEON_CRTC2_VBLANK_CUR)
+ break;
+ udelay(1);
+ }
+ }
+ }
+}
+
/* This files gather functions specifics to:
* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
*/
@@ -87,23 +121,27 @@ int r100_reloc_pitch_offset(struct radeon_cs_parser *p,
r100_cs_dump_packet(p, pkt);
return r;
}
+
value = radeon_get_ib_value(p, idx);
tmp = value & 0x003fffff;
tmp += (((u32)reloc->lobj.gpu_offset) >> 10);
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= RADEON_DST_TILE_MACRO;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
- if (reg == RADEON_SRC_PITCH_OFFSET) {
- DRM_ERROR("Cannot src blit from microtiled surface\n");
- r100_cs_dump_packet(p, pkt);
- return -EINVAL;
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= RADEON_DST_TILE_MACRO;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) {
+ if (reg == RADEON_SRC_PITCH_OFFSET) {
+ DRM_ERROR("Cannot src blit from microtiled surface\n");
+ r100_cs_dump_packet(p, pkt);
+ return -EINVAL;
+ }
+ tile_flags |= RADEON_DST_TILE_MICRO;
}
- tile_flags |= RADEON_DST_TILE_MICRO;
- }
- tmp |= tile_flags;
- p->ib->ptr[idx] = (value & 0x3fc00000) | tmp;
+ tmp |= tile_flags;
+ p->ib->ptr[idx] = (value & 0x3fc00000) | tmp;
+ } else
+ p->ib->ptr[idx] = (value & 0xffc00000) | tmp;
return 0;
}
@@ -412,7 +450,7 @@ void r100_pm_misc(struct radeon_device *rdev)
/* set pcie lanes */
if ((rdev->flags & RADEON_IS_PCIE) &&
!(rdev->flags & RADEON_IS_IGP) &&
- rdev->asic->set_pcie_lanes &&
+ rdev->asic->pm.set_pcie_lanes &&
(ps->pcie_lanes !=
rdev->pm.power_state[rdev->pm.current_power_state_index].pcie_lanes)) {
radeon_set_pcie_lanes(rdev,
@@ -592,8 +630,8 @@ int r100_pci_gart_init(struct radeon_device *rdev)
if (r)
return r;
rdev->gart.table_size = rdev->gart.num_gpu_pages * 4;
- rdev->asic->gart_tlb_flush = &r100_pci_gart_tlb_flush;
- rdev->asic->gart_set_page = &r100_pci_gart_set_page;
+ rdev->asic->gart.tlb_flush = &r100_pci_gart_tlb_flush;
+ rdev->asic->gart.set_page = &r100_pci_gart_set_page;
return radeon_gart_table_ram_alloc(rdev);
}
@@ -930,9 +968,8 @@ static int r100_cp_wait_for_idle(struct radeon_device *rdev)
return -1;
}
-void r100_ring_start(struct radeon_device *rdev)
+void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring)
{
- struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
int r;
r = radeon_ring_lock(rdev, ring, 2);
@@ -1143,8 +1180,8 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
WREG32(RADEON_CP_RB_WPTR_DELAY, 0);
WREG32(RADEON_CP_CSQ_MODE, 0x00004D4D);
WREG32(RADEON_CP_CSQ_CNTL, RADEON_CSQ_PRIBM_INDBM);
- radeon_ring_start(rdev);
- r = radeon_ring_test(rdev, ring);
+ radeon_ring_start(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+ r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring);
if (r) {
DRM_ERROR("radeon: cp isn't working (%d).\n", r);
return r;
@@ -1552,7 +1589,17 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
r100_cs_dump_packet(p, pkt);
return r;
}
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= RADEON_TXO_MACRO_TILE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= RADEON_TXO_MICRO_TILE_X2;
+
+ tmp = idx_value & ~(0x7 << 2);
+ tmp |= tile_flags;
+ ib[idx] = tmp + ((u32)reloc->lobj.gpu_offset);
+ } else
+ ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
track->textures[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -1623,15 +1670,17 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
r100_cs_dump_packet(p, pkt);
return r;
}
-
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= RADEON_COLOR_TILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- tile_flags |= RADEON_COLOR_MICROTILE_ENABLE;
-
- tmp = idx_value & ~(0x7 << 16);
- tmp |= tile_flags;
- ib[idx] = tmp;
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= RADEON_COLOR_TILE_ENABLE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= RADEON_COLOR_MICROTILE_ENABLE;
+
+ tmp = idx_value & ~(0x7 << 16);
+ tmp |= tile_flags;
+ ib[idx] = tmp;
+ } else
+ ib[idx] = idx_value;
track->cb[0].pitch = idx_value & RADEON_COLORPITCH_MASK;
track->cb_dirty = true;
@@ -3691,7 +3740,7 @@ void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
radeon_ring_write(ring, ib->length_dw);
}
-int r100_ib_test(struct radeon_device *rdev)
+int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
{
struct radeon_ib *ib;
uint32_t scratch;
@@ -3916,7 +3965,7 @@ static int r100_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
index eba4cbfa78f6..a59cc474d537 100644
--- a/drivers/gpu/drm/radeon/r200.c
+++ b/drivers/gpu/drm/radeon/r200.c
@@ -215,7 +215,17 @@ int r200_packet0_check(struct radeon_cs_parser *p,
r100_cs_dump_packet(p, pkt);
return r;
}
- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= R200_TXO_MACRO_TILE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= R200_TXO_MICRO_TILE;
+
+ tmp = idx_value & ~(0x7 << 2);
+ tmp |= tile_flags;
+ ib[idx] = tmp + ((u32)reloc->lobj.gpu_offset);
+ } else
+ ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
track->textures[i].robj = reloc->robj;
track->tex_dirty = true;
break;
@@ -277,14 +287,17 @@ int r200_packet0_check(struct radeon_cs_parser *p,
return r;
}
- if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
- tile_flags |= RADEON_COLOR_TILE_ENABLE;
- if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
- tile_flags |= RADEON_COLOR_MICROTILE_ENABLE;
+ if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS)) {
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
+ tile_flags |= RADEON_COLOR_TILE_ENABLE;
+ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
+ tile_flags |= RADEON_COLOR_MICROTILE_ENABLE;
- tmp = idx_value & ~(0x7 << 16);
- tmp |= tile_flags;
- ib[idx] = tmp;
+ tmp = idx_value & ~(0x7 << 16);
+ tmp |= tile_flags;
+ ib[idx] = tmp;
+ } else
+ ib[idx] = idx_value;
track->cb[0].pitch = idx_value & RADEON_COLORPITCH_MASK;
track->cb_dirty = true;
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 6829638cca40..fa14383f9ca0 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -105,8 +105,8 @@ int rv370_pcie_gart_init(struct radeon_device *rdev)
if (r)
DRM_ERROR("Failed to register debugfs file for PCIE gart !\n");
rdev->gart.table_size = rdev->gart.num_gpu_pages * 4;
- rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush;
- rdev->asic->gart_set_page = &rv370_pcie_gart_set_page;
+ rdev->asic->gart.tlb_flush = &rv370_pcie_gart_tlb_flush;
+ rdev->asic->gart.set_page = &rv370_pcie_gart_set_page;
return radeon_gart_table_vram_alloc(rdev);
}
@@ -206,9 +206,8 @@ void r300_fence_ring_emit(struct radeon_device *rdev,
radeon_ring_write(ring, RADEON_SW_INT_FIRE);
}
-void r300_ring_start(struct radeon_device *rdev)
+void r300_ring_start(struct radeon_device *rdev, struct radeon_ring *ring)
{
- struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
unsigned gb_tile_config;
int r;
@@ -1419,7 +1418,7 @@ static int r300_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
index b14323053bad..f3fcaacfea01 100644
--- a/drivers/gpu/drm/radeon/r420.c
+++ b/drivers/gpu/drm/radeon/r420.c
@@ -279,7 +279,7 @@ static int r420_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h
index 3bd8f1b1c606..ec576aaafb73 100644
--- a/drivers/gpu/drm/radeon/r500_reg.h
+++ b/drivers/gpu/drm/radeon/r500_reg.h
@@ -351,6 +351,8 @@
#define AVIVO_D1CRTC_BLANK_CONTROL 0x6084
#define AVIVO_D1CRTC_INTERLACE_CONTROL 0x6088
#define AVIVO_D1CRTC_INTERLACE_STATUS 0x608c
+#define AVIVO_D1CRTC_STATUS 0x609c
+# define AVIVO_D1CRTC_V_BLANK (1 << 0)
#define AVIVO_D1CRTC_STATUS_POSITION 0x60a0
#define AVIVO_D1CRTC_FRAME_COUNT 0x60a4
#define AVIVO_D1CRTC_STEREO_CONTROL 0x60c4
diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
index 25084e824dbc..ebcc15b03c9f 100644
--- a/drivers/gpu/drm/radeon/r520.c
+++ b/drivers/gpu/drm/radeon/r520.c
@@ -33,7 +33,7 @@
/* This files gather functions specifics to: r520,rv530,rv560,rv570,r580 */
-static int r520_mc_wait_for_idle(struct radeon_device *rdev)
+int r520_mc_wait_for_idle(struct radeon_device *rdev)
{
unsigned i;
uint32_t tmp;
@@ -207,7 +207,7 @@ static int r520_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 17ca72ce3027..391bd2636a80 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -49,6 +49,7 @@
#define EVERGREEN_PM4_UCODE_SIZE 1376
#define EVERGREEN_RLC_UCODE_SIZE 768
#define CAYMAN_RLC_UCODE_SIZE 1024
+#define ARUBA_RLC_UCODE_SIZE 1536
/* Firmware Names */
MODULE_FIRMWARE("radeon/R600_pfp.bin");
@@ -2226,7 +2227,7 @@ int r600_cp_resume(struct radeon_device *rdev)
r600_cp_start(rdev);
ring->ready = true;
- r = radeon_ring_test(rdev, ring);
+ r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, ring);
if (r) {
ring->ready = false;
return r;
@@ -2452,7 +2453,7 @@ int r600_startup(struct radeon_device *rdev)
r = r600_blit_init(rdev);
if (r) {
r600_blit_fini(rdev);
- rdev->asic->copy = NULL;
+ rdev->asic->copy.copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
@@ -2493,7 +2494,7 @@ int r600_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r600_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
DRM_ERROR("radeon: failed testing IB (%d).\n", r);
rdev->accel_working = false;
@@ -2701,13 +2702,14 @@ void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
radeon_ring_write(ring, ib->length_dw);
}
-int r600_ib_test(struct radeon_device *rdev, int ring)
+int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring)
{
struct radeon_ib *ib;
uint32_t scratch;
uint32_t tmp = 0;
unsigned i;
int r;
+ int ring_index = radeon_ring_index(rdev, ring);
r = radeon_scratch_get(rdev, &scratch);
if (r) {
@@ -2715,7 +2717,7 @@ int r600_ib_test(struct radeon_device *rdev, int ring)
return r;
}
WREG32(scratch, 0xCAFEDEAD);
- r = radeon_ib_get(rdev, ring, &ib, 256);
+ r = radeon_ib_get(rdev, ring_index, &ib, 256);
if (r) {
DRM_ERROR("radeon: failed to get ib (%d).\n", r);
return r;
@@ -2723,20 +2725,7 @@ int r600_ib_test(struct radeon_device *rdev, int ring)
ib->ptr[0] = PACKET3(PACKET3_SET_CONFIG_REG, 1);
ib->ptr[1] = ((scratch - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
ib->ptr[2] = 0xDEADBEEF;
- ib->ptr[3] = PACKET2(0);
- ib->ptr[4] = PACKET2(0);
- ib->ptr[5] = PACKET2(0);
- ib->ptr[6] = PACKET2(0);
- ib->ptr[7] = PACKET2(0);
- ib->ptr[8] = PACKET2(0);
- ib->ptr[9] = PACKET2(0);
- ib->ptr[10] = PACKET2(0);
- ib->ptr[11] = PACKET2(0);
- ib->ptr[12] = PACKET2(0);
- ib->ptr[13] = PACKET2(0);
- ib->ptr[14] = PACKET2(0);
- ib->ptr[15] = PACKET2(0);
- ib->length_dw = 16;
+ ib->length_dw = 3;
r = radeon_ib_schedule(rdev, ib);
if (r) {
radeon_scratch_free(rdev, scratch);
@@ -2790,7 +2779,7 @@ void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size)
rdev->ih.rptr = 0;
}
-static int r600_ih_ring_alloc(struct radeon_device *rdev)
+int r600_ih_ring_alloc(struct radeon_device *rdev)
{
int r;
@@ -2826,7 +2815,7 @@ static int r600_ih_ring_alloc(struct radeon_device *rdev)
return 0;
}
-static void r600_ih_ring_fini(struct radeon_device *rdev)
+void r600_ih_ring_fini(struct radeon_device *rdev)
{
int r;
if (rdev->ih.ring_obj) {
@@ -2873,10 +2862,17 @@ static int r600_rlc_init(struct radeon_device *rdev)
r600_rlc_stop(rdev);
- WREG32(RLC_HB_BASE, 0);
WREG32(RLC_HB_CNTL, 0);
- WREG32(RLC_HB_RPTR, 0);
- WREG32(RLC_HB_WPTR, 0);
+
+ if (rdev->family == CHIP_ARUBA) {
+ WREG32(TN_RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
+ WREG32(TN_RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
+ }
+ if (rdev->family <= CHIP_CAYMAN) {
+ WREG32(RLC_HB_BASE, 0);
+ WREG32(RLC_HB_RPTR, 0);
+ WREG32(RLC_HB_WPTR, 0);
+ }
if (rdev->family <= CHIP_CAICOS) {
WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
@@ -2885,7 +2881,12 @@ static int r600_rlc_init(struct radeon_device *rdev)
WREG32(RLC_UCODE_CNTL, 0);
fw_data = (const __be32 *)rdev->rlc_fw->data;
- if (rdev->family >= CHIP_CAYMAN) {
+ if (rdev->family >= CHIP_ARUBA) {
+ for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) {
+ WREG32(RLC_UCODE_ADDR, i);
+ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+ }
+ } else if (rdev->family >= CHIP_CAYMAN) {
for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) {
WREG32(RLC_UCODE_ADDR, i);
WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
index accc032c103f..db38f587f27a 100644
--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
+++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
@@ -30,20 +30,7 @@
#include "r600d.h"
#include "r600_blit_shaders.h"
-
-#define DI_PT_RECTLIST 0x11
-#define DI_INDEX_SIZE_16_BIT 0x0
-#define DI_SRC_SEL_AUTO_INDEX 0x2
-
-#define FMT_8 0x1
-#define FMT_5_6_5 0x8
-#define FMT_8_8_8_8 0x1a
-#define COLOR_8 0x1
-#define COLOR_5_6_5 0x8
-#define COLOR_8_8_8_8 0x1a
-
-#define RECT_UNIT_H 32
-#define RECT_UNIT_W (RADEON_GPU_PAGE_SIZE / 4 / RECT_UNIT_H)
+#include "radeon_blit_common.h"
/* emits 21 on rv770+, 23 on r600 */
static void
diff --git a/drivers/gpu/drm/radeon/r600_blit_shaders.c b/drivers/gpu/drm/radeon/r600_blit_shaders.c
index 73e2c7c6edbc..34c8b2340f33 100644
--- a/drivers/gpu/drm/radeon/r600_blit_shaders.c
+++ b/drivers/gpu/drm/radeon/r600_blit_shaders.c
@@ -24,6 +24,7 @@
* Alex Deucher <alexander.deucher@amd.com>
*/
+#include <linux/bug.h>
#include <linux/types.h>
#include <linux/kernel.h>
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 387fcc9f03ef..b8e12af304a9 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -52,15 +52,20 @@ struct r600_cs_track {
struct radeon_bo *cb_color_bo[8];
u64 cb_color_bo_mc[8];
u32 cb_color_bo_offset[8];
- struct radeon_bo *cb_color_frag_bo[8];
- struct radeon_bo *cb_color_tile_bo[8];
+ struct radeon_bo *cb_color_frag_bo[8]; /* unused */
+ struct radeon_bo *cb_color_tile_bo[8]; /* unused */
u32 cb_color_info[8];
- u32 cb_color_size_idx[8];
+ u32 cb_color_view[8];
+ u32 cb_color_size_idx[8]; /* unused */
u32 cb_target_mask;
- u32 cb_shader_mask;
+ u32 cb_shader_mask; /* unused */
u32 cb_color_size[8];
u32 vgt_strmout_en;
u32 vgt_strmout_buffer_en;
+ struct radeon_bo *vgt_strmout_bo[4];
+ u64 vgt_strmout_bo_mc[4]; /* unused */
+ u32 vgt_strmout_bo_offset[4];
+ u32 vgt_strmout_size[4];
u32 db_depth_control;
u32 db_depth_info;
u32 db_depth_size_idx;
@@ -69,13 +74,20 @@ struct r600_cs_track {
u32 db_offset;
struct radeon_bo *db_bo;
u64 db_bo_mc;
+ bool sx_misc_kill_all_prims;
+ 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 }
#define FMT_16_BIT(fmt, vc) [fmt] = { 1, 1, 2, vc, CHIP_R600 }
-#define FMT_24_BIT(fmt) [fmt] = { 1, 1, 3, 0, CHIP_R600 }
+#define FMT_24_BIT(fmt) [fmt] = { 1, 1, 4, 0, CHIP_R600 }
#define FMT_32_BIT(fmt, vc) [fmt] = { 1, 1, 4, vc, CHIP_R600 }
-#define FMT_48_BIT(fmt) [fmt] = { 1, 1, 6, 0, CHIP_R600 }
+#define FMT_48_BIT(fmt) [fmt] = { 1, 1, 8, 0, CHIP_R600 }
#define FMT_64_BIT(fmt, vc) [fmt] = { 1, 1, 8, vc, CHIP_R600 }
#define FMT_96_BIT(fmt) [fmt] = { 1, 1, 12, 0, CHIP_R600 }
#define FMT_128_BIT(fmt, vc) [fmt] = { 1, 1, 16,vc, CHIP_R600 }
@@ -107,7 +119,7 @@ static const struct gpu_formats color_formats_table[] = {
/* 24-bit */
FMT_24_BIT(V_038004_FMT_8_8_8),
-
+
/* 32-bit */
FMT_32_BIT(V_038004_COLOR_32, 1),
FMT_32_BIT(V_038004_COLOR_32_FLOAT, 1),
@@ -162,22 +174,22 @@ static const struct gpu_formats color_formats_table[] = {
[V_038004_FMT_32_AS_32_32_32_32] = { 1, 1, 4, 0, CHIP_CEDAR},
};
-static bool fmt_is_valid_color(u32 format)
+bool r600_fmt_is_valid_color(u32 format)
{
if (format >= ARRAY_SIZE(color_formats_table))
return false;
-
+
if (color_formats_table[format].valid_color)
return true;
return false;
}
-static bool fmt_is_valid_texture(u32 format, enum radeon_family family)
+bool r600_fmt_is_valid_texture(u32 format, enum radeon_family family)
{
if (format >= ARRAY_SIZE(color_formats_table))
return false;
-
+
if (family < color_formats_table[format].min_family)
return false;
@@ -187,7 +199,7 @@ static bool fmt_is_valid_texture(u32 format, enum radeon_family family)
return false;
}
-static int fmt_get_blocksize(u32 format)
+int r600_fmt_get_blocksize(u32 format)
{
if (format >= ARRAY_SIZE(color_formats_table))
return 0;
@@ -195,7 +207,7 @@ static int fmt_get_blocksize(u32 format)
return color_formats_table[format].blocksize;
}
-static int fmt_get_nblocksx(u32 format, u32 w)
+int r600_fmt_get_nblocksx(u32 format, u32 w)
{
unsigned bw;
@@ -209,7 +221,7 @@ static int fmt_get_nblocksx(u32 format, u32 w)
return (w + bw - 1) / bw;
}
-static int fmt_get_nblocksy(u32 format, u32 h)
+int r600_fmt_get_nblocksy(u32 format, u32 h)
{
unsigned bh;
@@ -256,7 +268,7 @@ static int r600_get_array_mode_alignment(struct array_mode_checker *values,
break;
case ARRAY_LINEAR_ALIGNED:
*pitch_align = max((u32)64, (u32)(values->group_size / values->blocksize));
- *height_align = tile_height;
+ *height_align = 1;
*depth_align = 1;
*base_align = values->group_size;
break;
@@ -269,10 +281,9 @@ static int r600_get_array_mode_alignment(struct array_mode_checker *values,
*base_align = values->group_size;
break;
case ARRAY_2D_TILED_THIN1:
- *pitch_align = max((u32)macro_tile_width,
- (u32)(((values->group_size / tile_height) /
- (values->blocksize * values->nsamples)) *
- values->nbanks)) * tile_width;
+ *pitch_align = max((u32)macro_tile_width * tile_width,
+ (u32)((values->group_size * values->nbanks) /
+ (values->blocksize * values->nsamples * tile_width)));
*height_align = macro_tile_height * tile_height;
*depth_align = 1;
*base_align = max(macro_tile_bytes,
@@ -296,12 +307,14 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->cb_color_size[i] = 0;
track->cb_color_size_idx[i] = 0;
track->cb_color_info[i] = 0;
+ track->cb_color_view[i] = 0xFFFFFFFF;
track->cb_color_bo[i] = NULL;
track->cb_color_bo_offset[i] = 0xFFFFFFFF;
track->cb_color_bo_mc[i] = 0xFFFFFFFF;
}
track->cb_target_mask = 0xFFFFFFFF;
track->cb_shader_mask = 0xFFFFFFFF;
+ track->cb_dirty = true;
track->db_bo = NULL;
track->db_bo_mc = 0xFFFFFFFF;
/* assume the biggest format and that htile is enabled */
@@ -310,6 +323,19 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->db_depth_size = 0xFFFFFFFF;
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;
+ track->vgt_strmout_bo[i] = NULL;
+ track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF;
+ track->vgt_strmout_bo_mc[i] = 0xFFFFFFFF;
+ }
+ track->streamout_dirty = true;
+ track->sx_misc_kill_all_prims = false;
}
static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
@@ -322,13 +348,14 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
volatile u32 *ib = p->ib->ptr;
unsigned array_mode;
u32 format;
+
if (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
return -EINVAL;
}
size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i];
format = G_0280A0_FORMAT(track->cb_color_info[i]);
- if (!fmt_is_valid_color(format)) {
+ if (!r600_fmt_is_valid_color(format)) {
dev_warn(p->dev, "%s:%d cb invalid format %d for %d (0x%08X)\n",
__func__, __LINE__, format,
i, track->cb_color_info[i]);
@@ -349,7 +376,7 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
array_check.nbanks = track->nbanks;
array_check.npipes = track->npipes;
array_check.nsamples = track->nsamples;
- array_check.blocksize = fmt_get_blocksize(format);
+ array_check.blocksize = r600_fmt_get_blocksize(format);
if (r600_get_array_mode_alignment(&array_check,
&pitch_align, &height_align, &depth_align, &base_align)) {
dev_warn(p->dev, "%s invalid tiling %d for %d (0x%08X)\n", __func__,
@@ -393,7 +420,18 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
}
/* check offset */
- tmp = fmt_get_nblocksy(format, height) * fmt_get_nblocksx(format, pitch) * fmt_get_blocksize(format);
+ tmp = r600_fmt_get_nblocksy(format, height) * r600_fmt_get_nblocksx(format, pitch) * r600_fmt_get_blocksize(format);
+ switch (array_mode) {
+ default:
+ case V_0280A0_ARRAY_LINEAR_GENERAL:
+ case V_0280A0_ARRAY_LINEAR_ALIGNED:
+ tmp += track->cb_color_view[i] & 0xFF;
+ break;
+ case V_0280A0_ARRAY_1D_TILED_THIN1:
+ case V_0280A0_ARRAY_2D_TILED_THIN1:
+ tmp += G_028080_SLICE_MAX(track->cb_color_view[i]) * tmp;
+ break;
+ }
if ((tmp + track->cb_color_bo_offset[i]) > radeon_bo_size(track->cb_color_bo[i])) {
if (array_mode == V_0280A0_ARRAY_LINEAR_GENERAL) {
/* the initial DDX does bad things with the CB size occasionally */
@@ -403,10 +441,13 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
* broken userspace.
*/
} else {
- dev_warn(p->dev, "%s offset[%d] %d %d %d %lu too big\n", __func__, i,
- array_mode,
+ dev_warn(p->dev, "%s offset[%d] %d %d %d %lu too big (%d %d) (%d %d %d)\n",
+ __func__, i, array_mode,
track->cb_color_bo_offset[i], tmp,
- radeon_bo_size(track->cb_color_bo[i]));
+ radeon_bo_size(track->cb_color_bo[i]),
+ pitch, height, r600_fmt_get_nblocksx(format, pitch),
+ r600_fmt_get_nblocksy(format, height),
+ r600_fmt_get_blocksize(format));
return -EINVAL;
}
}
@@ -420,154 +461,316 @@ static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
return 0;
}
-static int r600_cs_track_check(struct radeon_cs_parser *p)
+static int r600_cs_track_validate_db(struct radeon_cs_parser *p)
{
struct r600_cs_track *track = p->track;
- u32 tmp;
- int r, i;
+ 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;
- /* on legacy kernel we don't perform advanced check */
- if (p->rdev == NULL)
- return 0;
- /* we don't support out buffer yet */
- if (track->vgt_strmout_en || track->vgt_strmout_buffer_en) {
- dev_warn(p->dev, "this kernel doesn't support SMX output buffer\n");
+
+ if (track->db_bo == NULL) {
+ dev_warn(p->dev, "z/stencil with no depth buffer\n");
return -EINVAL;
}
- /* check that we have a cb for each enabled target, we don't check
- * shader_mask because it seems mesa isn't always setting it :(
- */
- tmp = track->cb_target_mask;
- for (i = 0; i < 8; i++) {
- if ((tmp >> (i * 4)) & 0xF) {
- /* at least one component is enabled */
- if (track->cb_color_bo[i] == NULL) {
- dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
- __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
- return -EINVAL;
- }
- /* perform rewrite of CB_COLOR[0-7]_SIZE */
- r = r600_cs_track_validate_cb(p, i);
- if (r)
- return r;
- }
+ 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;
}
- /* 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");
+ 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;
}
- if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) {
- dev_warn(p->dev, "this kernel doesn't support z/stencil htile\n");
+ 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;
}
- 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;
+ 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_DEPTH_X24_8_32_FLOAT:
- bpe = 8;
+ case V_028010_ARRAY_2D_TILED_THIN1:
break;
default:
- dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info));
+ 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) {
- 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);
+ 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 {
- 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;
+ /* 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 V_028010_ARRAY_2D_TILED_THIN1:
+ 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 invalid tiling %d (0x%08X)\n", __func__,
- G_028010_ARRAY_MODE(track->db_depth_info),
- track->db_depth_info);
+ 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;
+ }
+ }
- 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;
+ 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;
+
+ /* on legacy kernel we don't perform advanced check */
+ if (p->rdev == NULL)
+ return 0;
+
+ /* check streamout */
+ if (track->streamout_dirty && track->vgt_strmout_en) {
+ for (i = 0; i < 4; i++) {
+ if (track->vgt_strmout_buffer_en & (1 << i)) {
+ if (track->vgt_strmout_bo[i]) {
+ u64 offset = (u64)track->vgt_strmout_bo_offset[i] +
+ (u64)track->vgt_strmout_size[i];
+ if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) {
+ DRM_ERROR("streamout %d bo too small: 0x%llx, 0x%lx\n",
+ i, offset,
+ radeon_bo_size(track->vgt_strmout_bo[i]));
+ return -EINVAL;
+ }
+ } else {
+ dev_warn(p->dev, "No buffer for streamout %d\n", i);
+ return -EINVAL;
+ }
}
+ }
+ track->streamout_dirty = false;
+ }
- 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;
+ if (track->sx_misc_kill_all_prims)
+ return 0;
+
+ /* check that we have a cb for each enabled target, we don't check
+ * shader_mask because it seems mesa isn't always setting it :(
+ */
+ if (track->cb_dirty) {
+ tmp = track->cb_target_mask;
+ for (i = 0; i < 8; i++) {
+ if ((tmp >> (i * 4)) & 0xF) {
+ /* at least one component is enabled */
+ if (track->cb_color_bo[i] == NULL) {
+ dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
+ __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
+ return -EINVAL;
+ }
+ /* perform rewrite of CB_COLOR[0-7]_SIZE */
+ r = r600_cs_track_validate_cb(p, i);
+ if (r)
+ return r;
}
}
+ track->cb_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;
}
@@ -939,6 +1142,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
break;
case R_028800_DB_DEPTH_CONTROL:
track->db_depth_control = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case R_028010_DB_DEPTH_INFO:
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS) &&
@@ -959,24 +1163,66 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1);
track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1);
}
- } else
+ } else {
track->db_depth_info = radeon_get_ib_value(p, idx);
+ }
+ track->db_dirty = true;
break;
case R_028004_DB_DEPTH_VIEW:
track->db_depth_view = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case R_028000_DB_DEPTH_SIZE:
track->db_depth_size = radeon_get_ib_value(p, idx);
track->db_depth_size_idx = idx;
+ track->db_dirty = true;
break;
case R_028AB0_VGT_STRMOUT_EN:
track->vgt_strmout_en = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
case R_028B20_VGT_STRMOUT_BUFFER_EN:
track->vgt_strmout_buffer_en = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
+ break;
+ case VGT_STRMOUT_BUFFER_BASE_0:
+ case VGT_STRMOUT_BUFFER_BASE_1:
+ case VGT_STRMOUT_BUFFER_BASE_2:
+ case VGT_STRMOUT_BUFFER_BASE_3:
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "bad SET_CONTEXT_REG "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ tmp = (reg - VGT_STRMOUT_BUFFER_BASE_0) / 16;
+ track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ track->vgt_strmout_bo[tmp] = reloc->robj;
+ track->vgt_strmout_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->streamout_dirty = true;
+ break;
+ case VGT_STRMOUT_BUFFER_SIZE_0:
+ case VGT_STRMOUT_BUFFER_SIZE_1:
+ case VGT_STRMOUT_BUFFER_SIZE_2:
+ case VGT_STRMOUT_BUFFER_SIZE_3:
+ tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16;
+ /* size in register is DWs, convert to bytes */
+ track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4;
+ track->streamout_dirty = true;
+ break;
+ case CP_COHER_BASE:
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ dev_warn(p->dev, "missing reloc for CP_COHER_BASE "
+ "0x%04X\n", reg);
+ return -EINVAL;
+ }
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
case R_028238_CB_TARGET_MASK:
track->cb_target_mask = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case R_02823C_CB_SHADER_MASK:
track->cb_shader_mask = radeon_get_ib_value(p, idx);
@@ -984,6 +1230,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case R_028C04_PA_SC_AA_CONFIG:
tmp = G_028C04_MSAA_NUM_SAMPLES(radeon_get_ib_value(p, idx));
track->nsamples = 1 << tmp;
+ track->cb_dirty = true;
break;
case R_0280A0_CB_COLOR0_INFO:
case R_0280A4_CB_COLOR1_INFO:
@@ -1013,6 +1260,19 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4;
track->cb_color_info[tmp] = radeon_get_ib_value(p, idx);
}
+ track->cb_dirty = true;
+ break;
+ case R_028080_CB_COLOR0_VIEW:
+ case R_028084_CB_COLOR1_VIEW:
+ case R_028088_CB_COLOR2_VIEW:
+ case R_02808C_CB_COLOR3_VIEW:
+ case R_028090_CB_COLOR4_VIEW:
+ case R_028094_CB_COLOR5_VIEW:
+ case R_028098_CB_COLOR6_VIEW:
+ case R_02809C_CB_COLOR7_VIEW:
+ tmp = (reg - R_028080_CB_COLOR0_VIEW) / 4;
+ track->cb_color_view[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case R_028060_CB_COLOR0_SIZE:
case R_028064_CB_COLOR1_SIZE:
@@ -1025,6 +1285,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - R_028060_CB_COLOR0_SIZE) / 4;
track->cb_color_size[tmp] = radeon_get_ib_value(p, idx);
track->cb_color_size_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
/* This register were added late, there is userspace
* which does provide relocation for those but set
@@ -1107,6 +1368,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
track->cb_color_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->cb_dirty = true;
break;
case DB_DEPTH_BASE:
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -1119,8 +1381,24 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_bo = reloc->robj;
track->db_bo_mc = reloc->lobj.gpu_offset;
+ 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:
@@ -1191,6 +1469,9 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
+ case SX_MISC:
+ track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0;
+ break;
default:
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
@@ -1198,7 +1479,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
return 0;
}
-static unsigned mip_minify(unsigned size, unsigned level)
+unsigned r600_mip_minify(unsigned size, unsigned level)
{
unsigned val;
@@ -1220,22 +1501,22 @@ static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel,
unsigned nlevels = llevel - blevel + 1;
*l0_size = -1;
- blocksize = fmt_get_blocksize(format);
+ blocksize = r600_fmt_get_blocksize(format);
- w0 = mip_minify(w0, 0);
- h0 = mip_minify(h0, 0);
- d0 = mip_minify(d0, 0);
+ w0 = r600_mip_minify(w0, 0);
+ h0 = r600_mip_minify(h0, 0);
+ d0 = r600_mip_minify(d0, 0);
for(i = 0, offset = 0, level = blevel; i < nlevels; i++, level++) {
- width = mip_minify(w0, i);
- nbx = fmt_get_nblocksx(format, width);
+ width = r600_mip_minify(w0, i);
+ nbx = r600_fmt_get_nblocksx(format, width);
nbx = round_up(nbx, block_align);
- height = mip_minify(h0, i);
- nby = fmt_get_nblocksy(format, height);
+ height = r600_mip_minify(h0, i);
+ nby = r600_fmt_get_nblocksy(format, height);
nby = round_up(nby, height_align);
- depth = mip_minify(d0, i);
+ depth = r600_mip_minify(d0, i);
size = nbx * nby * blocksize;
if (nfaces)
@@ -1327,7 +1608,7 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
return -EINVAL;
}
format = G_038004_DATA_FORMAT(word1);
- if (!fmt_is_valid_texture(format, p->family)) {
+ if (!r600_fmt_is_valid_texture(format, p->family)) {
dev_warn(p->dev, "%s:%d texture invalid format %d\n",
__func__, __LINE__, format);
return -EINVAL;
@@ -1340,7 +1621,7 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
array_check.nbanks = track->nbanks;
array_check.npipes = track->npipes;
array_check.nsamples = 1;
- array_check.blocksize = fmt_get_blocksize(format);
+ array_check.blocksize = r600_fmt_get_blocksize(format);
if (r600_get_array_mode_alignment(&array_check,
&pitch_align, &height_align, &depth_align, &base_align)) {
dev_warn(p->dev, "%s:%d tex array mode (%d) invalid\n",
@@ -1373,6 +1654,10 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
word1 = radeon_get_ib_value(p, idx + 5);
blevel = G_038010_BASE_LEVEL(word0);
llevel = G_038014_LAST_LEVEL(word1);
+ if (blevel > llevel) {
+ dev_warn(p->dev, "texture blevel %d > llevel %d\n",
+ blevel, llevel);
+ }
if (array == 1) {
barray = G_038014_BASE_ARRAY(word1);
larray = G_038014_LAST_ARRAY(word1);
@@ -1384,8 +1669,10 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
&l0_size, &mipmap_size);
/* using get ib will give us the offset into the texture bo */
if ((l0_size + word2) > radeon_bo_size(texture)) {
- dev_warn(p->dev, "texture bo too small (%d %d %d %d -> %d have %ld)\n",
- w0, h0, format, word2, l0_size, radeon_bo_size(texture));
+ dev_warn(p->dev, "texture bo too small ((%d %d) (%d %d) %d %d %d -> %d have %ld)\n",
+ w0, h0, pitch_align, height_align,
+ array_check.array_mode, format, word2,
+ l0_size, radeon_bo_size(texture));
dev_warn(p->dev, "alignments %d %d %d %lld\n", pitch, pitch_align, height_align, base_align);
return -EINVAL;
}
@@ -1398,6 +1685,22 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
return 0;
}
+static bool r600_is_safe_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
+{
+ u32 m, i;
+
+ i = (reg >> 7);
+ if (i >= ARRAY_SIZE(r600_reg_safe_bm)) {
+ dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
+ return false;
+ }
+ m = 1 << ((reg >> 2) & 31);
+ if (!(r600_reg_safe_bm[i] & m))
+ return true;
+ dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
+ return false;
+}
+
static int r600_packet3_check(struct radeon_cs_parser *p,
struct radeon_cs_packet *pkt)
{
@@ -1420,6 +1723,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
{
int pred_op;
int tmp;
+ uint64_t offset;
+
if (pkt->count != 1) {
DRM_ERROR("bad SET PREDICATION\n");
return -EINVAL;
@@ -1443,8 +1748,12 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- ib[idx + 0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx + 1] = tmp + (upper_32_bits(reloc->lobj.gpu_offset) & 0xff);
+ offset = reloc->lobj.gpu_offset +
+ (idx_value & 0xfffffff0) +
+ ((u64)(tmp & 0xff) << 32);
+
+ ib[idx + 0] = offset;
+ ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff);
}
break;
@@ -1468,6 +1777,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
}
break;
case PACKET3_DRAW_INDEX:
+ {
+ uint64_t offset;
if (pkt->count != 3) {
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
@@ -1477,14 +1788,21 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ idx_value +
+ ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
+
+ ib[idx+0] = offset;
+ ib[idx+1] = upper_32_bits(offset) & 0xff;
+
r = r600_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX_AUTO:
if (pkt->count != 1) {
DRM_ERROR("bad DRAW_INDEX_AUTO\n");
@@ -1515,13 +1833,20 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
}
/* bit 4 is reg (0) or mem (1) */
if (idx_value & 0x10) {
+ uint64_t offset;
+
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad WAIT_REG_MEM\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffff0) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffff0);
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_SURFACE_SYNC:
@@ -1546,16 +1871,25 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
if (pkt->count) {
+ uint64_t offset;
+
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffff8;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_EVENT_WRITE_EOP:
+ {
+ uint64_t offset;
+
if (pkt->count != 4) {
DRM_ERROR("bad EVENT_WRITE_EOP\n");
return -EINVAL;
@@ -1565,9 +1899,15 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffffc;
+ ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
break;
+ }
case PACKET3_SET_CONFIG_REG:
start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_OFFSET;
end_reg = 4 * pkt->count + start_reg - 4;
@@ -1652,6 +1992,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
ib[idx+1+(i*7)+3] += mip_offset;
break;
case SQ_TEX_VTX_VALID_BUFFER:
+ {
+ uint64_t offset64;
/* vtx base */
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
@@ -1664,11 +2006,15 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
/* force size to size of the buffer */
dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n",
size + offset, radeon_bo_size(reloc->robj));
- ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj);
+ ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj) - offset;
}
- ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
- ib[idx+1+(i*7)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset64 = reloc->lobj.gpu_offset + offset;
+ ib[idx+1+(i*8)+0] = offset64;
+ ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
+ (upper_32_bits(offset64) & 0xff);
break;
+ }
case SQ_TEX_VTX_INVALID_TEXTURE:
case SQ_TEX_VTX_INVALID_BUFFER:
default:
@@ -1743,6 +2089,104 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
break;
+ case PACKET3_STRMOUT_BUFFER_UPDATE:
+ if (pkt->count != 4) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (invalid count)\n");
+ return -EINVAL;
+ }
+ /* Updating memory at DST_ADDRESS. */
+ if (idx_value & 0x1) {
+ u64 offset;
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing dst reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+1);
+ offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE dst bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
+ }
+ /* Reading data from SRC_ADDRESS. */
+ if (((idx_value >> 1) & 0x3) == 2) {
+ u64 offset;
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE (missing src reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+3);
+ offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad STRMOUT_BUFFER_UPDATE src bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
+ }
+ break;
+ case PACKET3_COPY_DW:
+ if (pkt->count != 4) {
+ DRM_ERROR("bad COPY_DW (invalid count)\n");
+ return -EINVAL;
+ }
+ if (idx_value & 0x1) {
+ u64 offset;
+ /* SRC is memory. */
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad COPY_DW (missing src reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+1);
+ offset += ((u64)(radeon_get_ib_value(p, idx+2) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad COPY_DW src bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
+ } else {
+ /* SRC is a reg. */
+ reg = radeon_get_ib_value(p, idx+1) << 2;
+ if (!r600_is_safe_reg(p, reg, idx+1))
+ return -EINVAL;
+ }
+ if (idx_value & 0x2) {
+ u64 offset;
+ /* DST is memory. */
+ r = r600_cs_packet_next_reloc(p, &reloc);
+ if (r) {
+ DRM_ERROR("bad COPY_DW (missing dst reloc)\n");
+ return -EINVAL;
+ }
+ offset = radeon_get_ib_value(p, idx+3);
+ offset += ((u64)(radeon_get_ib_value(p, idx+4) & 0xff)) << 32;
+ if ((offset + 4) > radeon_bo_size(reloc->robj)) {
+ DRM_ERROR("bad COPY_DW dst bo too small: 0x%llx, 0x%lx\n",
+ offset + 4, radeon_bo_size(reloc->robj));
+ return -EINVAL;
+ }
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
+ } else {
+ /* DST is a reg. */
+ reg = radeon_get_ib_value(p, idx+3) << 2;
+ if (!r600_is_safe_reg(p, reg, idx+3))
+ return -EINVAL;
+ }
+ break;
case PACKET3_NOP:
break;
default:
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 9b23670716f1..59f9c993cc31 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -78,6 +78,20 @@
#define CB_COLOR0_SIZE 0x28060
#define CB_COLOR0_VIEW 0x28080
+#define R_028080_CB_COLOR0_VIEW 0x028080
+#define S_028080_SLICE_START(x) (((x) & 0x7FF) << 0)
+#define G_028080_SLICE_START(x) (((x) >> 0) & 0x7FF)
+#define C_028080_SLICE_START 0xFFFFF800
+#define S_028080_SLICE_MAX(x) (((x) & 0x7FF) << 13)
+#define G_028080_SLICE_MAX(x) (((x) >> 13) & 0x7FF)
+#define C_028080_SLICE_MAX 0xFF001FFF
+#define R_028084_CB_COLOR1_VIEW 0x028084
+#define R_028088_CB_COLOR2_VIEW 0x028088
+#define R_02808C_CB_COLOR3_VIEW 0x02808C
+#define R_028090_CB_COLOR4_VIEW 0x028090
+#define R_028094_CB_COLOR5_VIEW 0x028094
+#define R_028098_CB_COLOR6_VIEW 0x028098
+#define R_02809C_CB_COLOR7_VIEW 0x02809C
#define CB_COLOR0_INFO 0x280a0
# define CB_FORMAT(x) ((x) << 2)
# define CB_ARRAY_MODE(x) ((x) << 8)
@@ -181,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)
@@ -493,6 +515,11 @@
#define VGT_STRMOUT_BUFFER_OFFSET_1 0x28AEC
#define VGT_STRMOUT_BUFFER_OFFSET_2 0x28AFC
#define VGT_STRMOUT_BUFFER_OFFSET_3 0x28B0C
+#define VGT_STRMOUT_BUFFER_SIZE_0 0x28AD0
+#define VGT_STRMOUT_BUFFER_SIZE_1 0x28AE0
+#define VGT_STRMOUT_BUFFER_SIZE_2 0x28AF0
+#define VGT_STRMOUT_BUFFER_SIZE_3 0x28B00
+
#define VGT_STRMOUT_EN 0x28AB0
#define VGT_VERTEX_REUSE_BLOCK_CNTL 0x28C58
#define VTX_REUSE_DEPTH_MASK 0x000000FF
@@ -574,6 +601,10 @@
#define RLC_UCODE_ADDR 0x3f2c
#define RLC_UCODE_DATA 0x3f30
+/* new for TN */
+#define TN_RLC_SAVE_AND_RESTORE_BASE 0x3f10
+#define TN_RLC_CLEAR_STATE_RESTORE_BASE 0x3f20
+
#define SRBM_SOFT_RESET 0xe60
# define SOFT_RESET_RLC (1 << 13)
@@ -835,6 +866,7 @@
# define PACKET3_SEM_SEL_SIGNAL (0x6 << 29)
# define PACKET3_SEM_SEL_WAIT (0x7 << 29)
#define PACKET3_MPEG_INDEX 0x3A
+#define PACKET3_COPY_DW 0x3B
#define PACKET3_WAIT_REG_MEM 0x3C
#define PACKET3_MEM_WRITE 0x3D
#define PACKET3_INDIRECT_BUFFER 0x32
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 1668ec1ee770..138b95216d8d 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -236,12 +236,15 @@ void radeon_pm_resume(struct radeon_device *rdev);
void radeon_combios_get_power_modes(struct radeon_device *rdev);
void radeon_atombios_get_power_modes(struct radeon_device *rdev);
void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type);
-int radeon_atom_get_max_vddc(struct radeon_device *rdev, u16 *voltage);
void rs690_pm_info(struct radeon_device *rdev);
extern int rv6xx_get_temp(struct radeon_device *rdev);
extern int rv770_get_temp(struct radeon_device *rdev);
extern int evergreen_get_temp(struct radeon_device *rdev);
extern int sumo_get_temp(struct radeon_device *rdev);
+extern int si_get_temp(struct radeon_device *rdev);
+extern void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw,
+ unsigned *bankh, unsigned *mtaspect,
+ unsigned *tile_split);
/*
* Fences.
@@ -411,9 +414,6 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
int alignment, int initial_domain,
bool discardable, bool kernel,
struct drm_gem_object **obj);
-int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
- uint64_t *gpu_addr);
-void radeon_gem_object_unpin(struct drm_gem_object *obj);
int radeon_mode_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
@@ -632,6 +632,7 @@ struct radeon_ib {
uint32_t *ptr;
struct radeon_fence *fence;
unsigned vm_id;
+ bool is_const_ib;
};
/*
@@ -771,6 +772,18 @@ struct r600_blit {
void r600_blit_suspend(struct radeon_device *rdev);
+/*
+ * SI RLC stuff
+ */
+struct si_rlc {
+ /* for power gating */
+ struct radeon_bo *save_restore_obj;
+ uint64_t save_restore_gpu_addr;
+ /* for clear state */
+ struct radeon_bo *clear_state_obj;
+ uint64_t clear_state_gpu_addr;
+};
+
int radeon_ib_get(struct radeon_device *rdev, int ring,
struct radeon_ib **ib, unsigned size);
void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib);
@@ -780,7 +793,6 @@ int radeon_ib_pool_init(struct radeon_device *rdev);
void radeon_ib_pool_fini(struct radeon_device *rdev);
int radeon_ib_pool_start(struct radeon_device *rdev);
int radeon_ib_pool_suspend(struct radeon_device *rdev);
-int radeon_ib_test(struct radeon_device *rdev);
/* Ring access between begin & end cannot sleep */
int radeon_ring_index(struct radeon_device *rdev, struct radeon_ring *cp);
void radeon_ring_free_size(struct radeon_device *rdev, struct radeon_ring *cp);
@@ -833,12 +845,13 @@ struct radeon_cs_parser {
struct radeon_cs_reloc *relocs;
struct radeon_cs_reloc **relocs_ptr;
struct list_head validated;
- bool sync_to_ring[RADEON_NUM_RINGS];
/* indices of various chunks */
int chunk_ib_idx;
int chunk_relocs_idx;
int chunk_flags_idx;
+ int chunk_const_ib_idx;
struct radeon_ib *ib;
+ struct radeon_ib *const_ib;
void *track;
unsigned family;
int parser_error;
@@ -980,6 +993,7 @@ enum radeon_int_thermal_type {
THERMAL_TYPE_EVERGREEN,
THERMAL_TYPE_SUMO,
THERMAL_TYPE_NI,
+ THERMAL_TYPE_SI,
};
struct radeon_voltage {
@@ -1132,57 +1146,6 @@ struct radeon_asic {
void (*vga_set_state)(struct radeon_device *rdev, bool state);
bool (*gpu_is_lockup)(struct radeon_device *rdev, struct radeon_ring *cp);
int (*asic_reset)(struct radeon_device *rdev);
- void (*gart_tlb_flush)(struct radeon_device *rdev);
- int (*gart_set_page)(struct radeon_device *rdev, int i, uint64_t addr);
- int (*cp_init)(struct radeon_device *rdev, unsigned ring_size);
- void (*cp_fini)(struct radeon_device *rdev);
- void (*cp_disable)(struct radeon_device *rdev);
- void (*ring_start)(struct radeon_device *rdev);
-
- struct {
- void (*ib_execute)(struct radeon_device *rdev, struct radeon_ib *ib);
- int (*ib_parse)(struct radeon_device *rdev, struct radeon_ib *ib);
- void (*emit_fence)(struct radeon_device *rdev, struct radeon_fence *fence);
- void (*emit_semaphore)(struct radeon_device *rdev, struct radeon_ring *cp,
- struct radeon_semaphore *semaphore, bool emit_wait);
- } ring[RADEON_NUM_RINGS];
-
- int (*ring_test)(struct radeon_device *rdev, struct radeon_ring *cp);
- int (*irq_set)(struct radeon_device *rdev);
- int (*irq_process)(struct radeon_device *rdev);
- u32 (*get_vblank_counter)(struct radeon_device *rdev, int crtc);
- int (*cs_parse)(struct radeon_cs_parser *p);
- int (*copy_blit)(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence *fence);
- int (*copy_dma)(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence *fence);
- int (*copy)(struct radeon_device *rdev,
- uint64_t src_offset,
- uint64_t dst_offset,
- unsigned num_gpu_pages,
- struct radeon_fence *fence);
- uint32_t (*get_engine_clock)(struct radeon_device *rdev);
- void (*set_engine_clock)(struct radeon_device *rdev, uint32_t eng_clock);
- uint32_t (*get_memory_clock)(struct radeon_device *rdev);
- void (*set_memory_clock)(struct radeon_device *rdev, uint32_t mem_clock);
- int (*get_pcie_lanes)(struct radeon_device *rdev);
- void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes);
- void (*set_clock_gating)(struct radeon_device *rdev, int enable);
- int (*set_surface_reg)(struct radeon_device *rdev, int reg,
- uint32_t tiling_flags, uint32_t pitch,
- uint32_t offset, uint32_t obj_size);
- void (*clear_surface_reg)(struct radeon_device *rdev, int reg);
- void (*bandwidth_update)(struct radeon_device *rdev);
- void (*hpd_init)(struct radeon_device *rdev);
- void (*hpd_fini)(struct radeon_device *rdev);
- bool (*hpd_sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
- void (*hpd_set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
/* ioctl hw specific callback. Some hw might want to perform special
* operation on specific ioctl. For instance on wait idle some hw
* might want to perform and HDP flush through MMIO as it seems that
@@ -1190,17 +1153,99 @@ struct radeon_asic {
* through ring.
*/
void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo);
+ /* check if 3D engine is idle */
bool (*gui_idle)(struct radeon_device *rdev);
+ /* wait for mc_idle */
+ int (*mc_wait_for_idle)(struct radeon_device *rdev);
+ /* gart */
+ struct {
+ void (*tlb_flush)(struct radeon_device *rdev);
+ int (*set_page)(struct radeon_device *rdev, int i, uint64_t addr);
+ } gart;
+ /* ring specific callbacks */
+ struct {
+ void (*ib_execute)(struct radeon_device *rdev, struct radeon_ib *ib);
+ int (*ib_parse)(struct radeon_device *rdev, struct radeon_ib *ib);
+ void (*emit_fence)(struct radeon_device *rdev, struct radeon_fence *fence);
+ void (*emit_semaphore)(struct radeon_device *rdev, struct radeon_ring *cp,
+ struct radeon_semaphore *semaphore, bool emit_wait);
+ int (*cs_parse)(struct radeon_cs_parser *p);
+ void (*ring_start)(struct radeon_device *rdev, struct radeon_ring *cp);
+ int (*ring_test)(struct radeon_device *rdev, struct radeon_ring *cp);
+ int (*ib_test)(struct radeon_device *rdev, struct radeon_ring *cp);
+ } ring[RADEON_NUM_RINGS];
+ /* irqs */
+ struct {
+ int (*set)(struct radeon_device *rdev);
+ int (*process)(struct radeon_device *rdev);
+ } irq;
+ /* displays */
+ struct {
+ /* display watermarks */
+ void (*bandwidth_update)(struct radeon_device *rdev);
+ /* get frame count */
+ u32 (*get_vblank_counter)(struct radeon_device *rdev, int crtc);
+ /* wait for vblank */
+ void (*wait_for_vblank)(struct radeon_device *rdev, int crtc);
+ } display;
+ /* copy functions for bo handling */
+ struct {
+ int (*blit)(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct radeon_fence *fence);
+ u32 blit_ring_index;
+ int (*dma)(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct radeon_fence *fence);
+ u32 dma_ring_index;
+ /* method used for bo copy */
+ int (*copy)(struct radeon_device *rdev,
+ uint64_t src_offset,
+ uint64_t dst_offset,
+ unsigned num_gpu_pages,
+ struct radeon_fence *fence);
+ /* ring used for bo copies */
+ u32 copy_ring_index;
+ } copy;
+ /* surfaces */
+ struct {
+ int (*set_reg)(struct radeon_device *rdev, int reg,
+ uint32_t tiling_flags, uint32_t pitch,
+ uint32_t offset, uint32_t obj_size);
+ void (*clear_reg)(struct radeon_device *rdev, int reg);
+ } surface;
+ /* hotplug detect */
+ struct {
+ void (*init)(struct radeon_device *rdev);
+ void (*fini)(struct radeon_device *rdev);
+ bool (*sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+ void (*set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
+ } hpd;
/* power management */
- void (*pm_misc)(struct radeon_device *rdev);
- void (*pm_prepare)(struct radeon_device *rdev);
- void (*pm_finish)(struct radeon_device *rdev);
- void (*pm_init_profile)(struct radeon_device *rdev);
- void (*pm_get_dynpm_state)(struct radeon_device *rdev);
+ struct {
+ void (*misc)(struct radeon_device *rdev);
+ void (*prepare)(struct radeon_device *rdev);
+ void (*finish)(struct radeon_device *rdev);
+ void (*init_profile)(struct radeon_device *rdev);
+ void (*get_dynpm_state)(struct radeon_device *rdev);
+ uint32_t (*get_engine_clock)(struct radeon_device *rdev);
+ void (*set_engine_clock)(struct radeon_device *rdev, uint32_t eng_clock);
+ uint32_t (*get_memory_clock)(struct radeon_device *rdev);
+ void (*set_memory_clock)(struct radeon_device *rdev, uint32_t mem_clock);
+ int (*get_pcie_lanes)(struct radeon_device *rdev);
+ void (*set_pcie_lanes)(struct radeon_device *rdev, int lanes);
+ void (*set_clock_gating)(struct radeon_device *rdev, int enable);
+ } pm;
/* pageflipping */
- void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
- u32 (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base);
- void (*post_page_flip)(struct radeon_device *rdev, int crtc);
+ struct {
+ void (*pre_page_flip)(struct radeon_device *rdev, int crtc);
+ u32 (*page_flip)(struct radeon_device *rdev, int crtc, u64 crtc_base);
+ void (*post_page_flip)(struct radeon_device *rdev, int crtc);
+ } pflip;
};
/*
@@ -1340,6 +1385,37 @@ struct cayman_asic {
struct r100_gpu_lockup lockup;
};
+struct si_asic {
+ unsigned max_shader_engines;
+ unsigned max_pipes_per_simd;
+ unsigned max_tile_pipes;
+ unsigned max_simds_per_se;
+ unsigned max_backends_per_se;
+ unsigned max_texture_channel_caches;
+ unsigned max_gprs;
+ unsigned max_gs_threads;
+ unsigned max_hw_contexts;
+ unsigned sc_prim_fifo_size_frontend;
+ unsigned sc_prim_fifo_size_backend;
+ unsigned sc_hiz_tile_fifo_size;
+ unsigned sc_earlyz_tile_fifo_size;
+
+ unsigned num_shader_engines;
+ unsigned num_tile_pipes;
+ unsigned num_backends_per_se;
+ unsigned backend_disable_mask_per_asic;
+ unsigned backend_map;
+ unsigned num_texture_channel_caches;
+ unsigned mem_max_burst_length_bytes;
+ unsigned mem_row_size_in_kb;
+ unsigned shader_engine_tile_size;
+ unsigned num_gpus;
+ unsigned multi_gpu_tile_size;
+
+ unsigned tile_config;
+ struct r100_gpu_lockup lockup;
+};
+
union radeon_asic_config {
struct r300_asic r300;
struct r100_asic r100;
@@ -1347,6 +1423,7 @@ union radeon_asic_config {
struct rv770_asic rv770;
struct evergreen_asic evergreen;
struct cayman_asic cayman;
+ struct si_asic si;
};
/*
@@ -1462,10 +1539,12 @@ struct radeon_device {
const struct firmware *pfp_fw; /* r6/700 PFP firmware */
const struct firmware *rlc_fw; /* r6/700 RLC firmware */
const struct firmware *mc_fw; /* NI MC firmware */
+ const struct firmware *ce_fw; /* SI CE firmware */
struct r600_blit r600_blit;
struct r600_vram_scratch vram_scratch;
int msi_enabled; /* msi enabled */
struct r600_ih ih; /* r6/700 interrupt ring */
+ struct si_rlc rlc;
struct work_struct hotplug_work;
int num_crtc; /* number of crtcs */
struct mutex dc_hw_i2c_mutex; /* display controller hw i2c mutex */
@@ -1491,8 +1570,6 @@ struct radeon_device {
unsigned debugfs_count;
/* virtual memory */
struct radeon_vm_manager vm_manager;
- /* ring used for bo copies */
- u32 copy_ring;
};
int radeon_device_init(struct radeon_device *rdev,
@@ -1611,6 +1688,9 @@ void r100_pll_errata_after_index(struct radeon_device *rdev);
#define ASIC_IS_DCE41(rdev) ((rdev->family >= CHIP_PALM) && \
(rdev->flags & RADEON_IS_IGP))
#define ASIC_IS_DCE5(rdev) ((rdev->family >= CHIP_BARTS))
+#define ASIC_IS_DCE6(rdev) ((rdev->family >= CHIP_ARUBA))
+#define ASIC_IS_DCE61(rdev) ((rdev->family >= CHIP_ARUBA) && \
+ (rdev->flags & RADEON_IS_IGP))
/*
* BIOS helpers.
@@ -1648,47 +1728,53 @@ void radeon_ring_write(struct radeon_ring *ring, uint32_t v);
#define radeon_fini(rdev) (rdev)->asic->fini((rdev))
#define radeon_resume(rdev) (rdev)->asic->resume((rdev))
#define radeon_suspend(rdev) (rdev)->asic->suspend((rdev))
-#define radeon_cs_parse(p) rdev->asic->cs_parse((p))
+#define radeon_cs_parse(rdev, r, p) (rdev)->asic->ring[(r)].cs_parse((p))
#define radeon_vga_set_state(rdev, state) (rdev)->asic->vga_set_state((rdev), (state))
#define radeon_gpu_is_lockup(rdev, cp) (rdev)->asic->gpu_is_lockup((rdev), (cp))
#define radeon_asic_reset(rdev) (rdev)->asic->asic_reset((rdev))
-#define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart_tlb_flush((rdev))
-#define radeon_gart_set_page(rdev, i, p) (rdev)->asic->gart_set_page((rdev), (i), (p))
-#define radeon_ring_start(rdev) (rdev)->asic->ring_start((rdev))
-#define radeon_ring_test(rdev, cp) (rdev)->asic->ring_test((rdev), (cp))
+#define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart.tlb_flush((rdev))
+#define radeon_gart_set_page(rdev, i, p) (rdev)->asic->gart.set_page((rdev), (i), (p))
+#define radeon_ring_start(rdev, r, cp) (rdev)->asic->ring[(r)].ring_start((rdev), (cp))
+#define radeon_ring_test(rdev, r, cp) (rdev)->asic->ring[(r)].ring_test((rdev), (cp))
+#define radeon_ib_test(rdev, r, cp) (rdev)->asic->ring[(r)].ib_test((rdev), (cp))
#define radeon_ring_ib_execute(rdev, r, ib) (rdev)->asic->ring[(r)].ib_execute((rdev), (ib))
#define radeon_ring_ib_parse(rdev, r, ib) (rdev)->asic->ring[(r)].ib_parse((rdev), (ib))
-#define radeon_irq_set(rdev) (rdev)->asic->irq_set((rdev))
-#define radeon_irq_process(rdev) (rdev)->asic->irq_process((rdev))
-#define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->get_vblank_counter((rdev), (crtc))
+#define radeon_irq_set(rdev) (rdev)->asic->irq.set((rdev))
+#define radeon_irq_process(rdev) (rdev)->asic->irq.process((rdev))
+#define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->display.get_vblank_counter((rdev), (crtc))
#define radeon_fence_ring_emit(rdev, r, fence) (rdev)->asic->ring[(r)].emit_fence((rdev), (fence))
#define radeon_semaphore_ring_emit(rdev, r, cp, semaphore, emit_wait) (rdev)->asic->ring[(r)].emit_semaphore((rdev), (cp), (semaphore), (emit_wait))
-#define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy_blit((rdev), (s), (d), (np), (f))
-#define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy_dma((rdev), (s), (d), (np), (f))
-#define radeon_copy(rdev, s, d, np, f) (rdev)->asic->copy((rdev), (s), (d), (np), (f))
-#define radeon_get_engine_clock(rdev) (rdev)->asic->get_engine_clock((rdev))
-#define radeon_set_engine_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
-#define radeon_get_memory_clock(rdev) (rdev)->asic->get_memory_clock((rdev))
-#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_memory_clock((rdev), (e))
-#define radeon_get_pcie_lanes(rdev) (rdev)->asic->get_pcie_lanes((rdev))
-#define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l))
-#define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e))
-#define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->set_surface_reg((rdev), (r), (f), (p), (o), (s)))
-#define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->clear_surface_reg((rdev), (r)))
-#define radeon_bandwidth_update(rdev) (rdev)->asic->bandwidth_update((rdev))
-#define radeon_hpd_init(rdev) (rdev)->asic->hpd_init((rdev))
-#define radeon_hpd_fini(rdev) (rdev)->asic->hpd_fini((rdev))
-#define radeon_hpd_sense(rdev, hpd) (rdev)->asic->hpd_sense((rdev), (hpd))
-#define radeon_hpd_set_polarity(rdev, hpd) (rdev)->asic->hpd_set_polarity((rdev), (hpd))
+#define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy.blit((rdev), (s), (d), (np), (f))
+#define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy.dma((rdev), (s), (d), (np), (f))
+#define radeon_copy(rdev, s, d, np, f) (rdev)->asic->copy.copy((rdev), (s), (d), (np), (f))
+#define radeon_copy_blit_ring_index(rdev) (rdev)->asic->copy.blit_ring_index
+#define radeon_copy_dma_ring_index(rdev) (rdev)->asic->copy.dma_ring_index
+#define radeon_copy_ring_index(rdev) (rdev)->asic->copy.copy_ring_index
+#define radeon_get_engine_clock(rdev) (rdev)->asic->pm.get_engine_clock((rdev))
+#define radeon_set_engine_clock(rdev, e) (rdev)->asic->pm.set_engine_clock((rdev), (e))
+#define radeon_get_memory_clock(rdev) (rdev)->asic->pm.get_memory_clock((rdev))
+#define radeon_set_memory_clock(rdev, e) (rdev)->asic->pm.set_memory_clock((rdev), (e))
+#define radeon_get_pcie_lanes(rdev) (rdev)->asic->pm.get_pcie_lanes((rdev))
+#define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->pm.set_pcie_lanes((rdev), (l))
+#define radeon_set_clock_gating(rdev, e) (rdev)->asic->pm.set_clock_gating((rdev), (e))
+#define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->surface.set_reg((rdev), (r), (f), (p), (o), (s)))
+#define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->surface.clear_reg((rdev), (r)))
+#define radeon_bandwidth_update(rdev) (rdev)->asic->display.bandwidth_update((rdev))
+#define radeon_hpd_init(rdev) (rdev)->asic->hpd.init((rdev))
+#define radeon_hpd_fini(rdev) (rdev)->asic->hpd.fini((rdev))
+#define radeon_hpd_sense(rdev, h) (rdev)->asic->hpd.sense((rdev), (h))
+#define radeon_hpd_set_polarity(rdev, h) (rdev)->asic->hpd.set_polarity((rdev), (h))
#define radeon_gui_idle(rdev) (rdev)->asic->gui_idle((rdev))
-#define radeon_pm_misc(rdev) (rdev)->asic->pm_misc((rdev))
-#define radeon_pm_prepare(rdev) (rdev)->asic->pm_prepare((rdev))
-#define radeon_pm_finish(rdev) (rdev)->asic->pm_finish((rdev))
-#define radeon_pm_init_profile(rdev) (rdev)->asic->pm_init_profile((rdev))
-#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm_get_dynpm_state((rdev))
-#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pre_page_flip((rdev), (crtc))
-#define radeon_page_flip(rdev, crtc, base) rdev->asic->page_flip((rdev), (crtc), (base))
-#define radeon_post_page_flip(rdev, crtc) rdev->asic->post_page_flip((rdev), (crtc))
+#define radeon_pm_misc(rdev) (rdev)->asic->pm.misc((rdev))
+#define radeon_pm_prepare(rdev) (rdev)->asic->pm.prepare((rdev))
+#define radeon_pm_finish(rdev) (rdev)->asic->pm.finish((rdev))
+#define radeon_pm_init_profile(rdev) (rdev)->asic->pm.init_profile((rdev))
+#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm.get_dynpm_state((rdev))
+#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pflip.pre_page_flip((rdev), (crtc))
+#define radeon_page_flip(rdev, crtc, base) rdev->asic->pflip.page_flip((rdev), (crtc), (base))
+#define radeon_post_page_flip(rdev, crtc) rdev->asic->pflip.post_page_flip((rdev), (crtc))
+#define radeon_wait_for_vblank(rdev, crtc) rdev->asic->display.wait_for_vblank((rdev), (crtc))
+#define radeon_mc_wait_for_idle(rdev) rdev->asic->mc_wait_for_idle((rdev))
/* Common functions */
/* AGP */
@@ -1750,6 +1836,16 @@ int r600_vram_scratch_init(struct radeon_device *rdev);
void r600_vram_scratch_fini(struct radeon_device *rdev);
/*
+ * r600 cs checking helper
+ */
+unsigned r600_mip_minify(unsigned size, unsigned level);
+bool r600_fmt_is_valid_color(u32 format);
+bool r600_fmt_is_valid_texture(u32 format, enum radeon_family family);
+int r600_fmt_get_blocksize(u32 format);
+int r600_fmt_get_nblocksx(u32 format, u32 w);
+int r600_fmt_get_nblocksy(u32 format, u32 h);
+
+/*
* r600 functions used by radeon_encoder.c
*/
extern void r600_hdmi_enable(struct drm_encoder *encoder);
diff --git a/drivers/gpu/drm/radeon/radeon_asic.c b/drivers/gpu/drm/radeon/radeon_asic.c
index 36a6192ce862..be4dc2ff0e40 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.c
+++ b/drivers/gpu/drm/radeon/radeon_asic.c
@@ -114,13 +114,13 @@ void radeon_agp_disable(struct radeon_device *rdev)
rdev->family == CHIP_R423) {
DRM_INFO("Forcing AGP to PCIE mode\n");
rdev->flags |= RADEON_IS_PCIE;
- rdev->asic->gart_tlb_flush = &rv370_pcie_gart_tlb_flush;
- rdev->asic->gart_set_page = &rv370_pcie_gart_set_page;
+ rdev->asic->gart.tlb_flush = &rv370_pcie_gart_tlb_flush;
+ rdev->asic->gart.set_page = &rv370_pcie_gart_set_page;
} else {
DRM_INFO("Forcing AGP to PCI mode\n");
rdev->flags |= RADEON_IS_PCI;
- rdev->asic->gart_tlb_flush = &r100_pci_gart_tlb_flush;
- rdev->asic->gart_set_page = &r100_pci_gart_set_page;
+ rdev->asic->gart.tlb_flush = &r100_pci_gart_tlb_flush;
+ rdev->asic->gart.set_page = &r100_pci_gart_set_page;
}
rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
}
@@ -136,48 +136,70 @@ static struct radeon_asic r100_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r100_gpu_is_lockup,
.asic_reset = &r100_asic_reset,
- .gart_tlb_flush = &r100_pci_gart_tlb_flush,
- .gart_set_page = &r100_pci_gart_set_page,
- .ring_start = &r100_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &r100_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &r100_pci_gart_tlb_flush,
+ .set_page = &r100_pci_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r100_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r100_cs_parse,
+ .ring_start = &r100_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
- .get_vblank_counter = &r100_get_vblank_counter,
- .cs_parse = &r100_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = NULL,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_legacy_get_engine_clock,
- .set_engine_clock = &radeon_legacy_set_engine_clock,
- .get_memory_clock = &radeon_legacy_get_memory_clock,
- .set_memory_clock = NULL,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = &radeon_legacy_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &r100_bandwidth_update,
- .hpd_init = &r100_hpd_init,
- .hpd_fini = &r100_hpd_fini,
- .hpd_sense = &r100_hpd_sense,
- .hpd_set_polarity = &r100_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &r100_pm_misc,
- .pm_prepare = &r100_pm_prepare,
- .pm_finish = &r100_pm_finish,
- .pm_init_profile = &r100_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &r100_pre_page_flip,
- .page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .irq = {
+ .set = &r100_irq_set,
+ .process = &r100_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &r100_bandwidth_update,
+ .get_vblank_counter = &r100_get_vblank_counter,
+ .wait_for_vblank = &r100_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r100_hpd_init,
+ .fini = &r100_hpd_fini,
+ .sense = &r100_hpd_sense,
+ .set_polarity = &r100_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r100_pm_misc,
+ .prepare = &r100_pm_prepare,
+ .finish = &r100_pm_finish,
+ .init_profile = &r100_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
+ },
};
static struct radeon_asic r200_asic = {
@@ -188,47 +210,70 @@ static struct radeon_asic r200_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r100_gpu_is_lockup,
.asic_reset = &r100_asic_reset,
- .gart_tlb_flush = &r100_pci_gart_tlb_flush,
- .gart_set_page = &r100_pci_gart_set_page,
- .ring_start = &r100_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &r100_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &r100_pci_gart_tlb_flush,
+ .set_page = &r100_pci_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r100_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r100_cs_parse,
+ .ring_start = &r100_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
- .get_vblank_counter = &r100_get_vblank_counter,
- .cs_parse = &r100_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_legacy_get_engine_clock,
- .set_engine_clock = &radeon_legacy_set_engine_clock,
- .get_memory_clock = &radeon_legacy_get_memory_clock,
- .set_memory_clock = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = &radeon_legacy_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &r100_bandwidth_update,
- .hpd_init = &r100_hpd_init,
- .hpd_fini = &r100_hpd_fini,
- .hpd_sense = &r100_hpd_sense,
- .hpd_set_polarity = &r100_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &r100_pm_misc,
- .pm_prepare = &r100_pm_prepare,
- .pm_finish = &r100_pm_finish,
- .pm_init_profile = &r100_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &r100_pre_page_flip,
- .page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .irq = {
+ .set = &r100_irq_set,
+ .process = &r100_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &r100_bandwidth_update,
+ .get_vblank_counter = &r100_get_vblank_counter,
+ .wait_for_vblank = &r100_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r100_hpd_init,
+ .fini = &r100_hpd_fini,
+ .sense = &r100_hpd_sense,
+ .set_polarity = &r100_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r100_pm_misc,
+ .prepare = &r100_pm_prepare,
+ .finish = &r100_pm_finish,
+ .init_profile = &r100_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
+ },
};
static struct radeon_asic r300_asic = {
@@ -239,48 +284,70 @@ static struct radeon_asic r300_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &r300_asic_reset,
- .gart_tlb_flush = &r100_pci_gart_tlb_flush,
- .gart_set_page = &r100_pci_gart_set_page,
- .ring_start = &r300_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &r300_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &r100_pci_gart_tlb_flush,
+ .set_page = &r100_pci_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &r300_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
- .get_vblank_counter = &r100_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_legacy_get_engine_clock,
- .set_engine_clock = &radeon_legacy_set_engine_clock,
- .get_memory_clock = &radeon_legacy_get_memory_clock,
- .set_memory_clock = NULL,
- .get_pcie_lanes = &rv370_get_pcie_lanes,
- .set_pcie_lanes = &rv370_set_pcie_lanes,
- .set_clock_gating = &radeon_legacy_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &r100_bandwidth_update,
- .hpd_init = &r100_hpd_init,
- .hpd_fini = &r100_hpd_fini,
- .hpd_sense = &r100_hpd_sense,
- .hpd_set_polarity = &r100_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &r100_pm_misc,
- .pm_prepare = &r100_pm_prepare,
- .pm_finish = &r100_pm_finish,
- .pm_init_profile = &r100_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &r100_pre_page_flip,
- .page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .irq = {
+ .set = &r100_irq_set,
+ .process = &r100_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &r100_bandwidth_update,
+ .get_vblank_counter = &r100_get_vblank_counter,
+ .wait_for_vblank = &r100_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r100_hpd_init,
+ .fini = &r100_hpd_fini,
+ .sense = &r100_hpd_sense,
+ .set_polarity = &r100_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r100_pm_misc,
+ .prepare = &r100_pm_prepare,
+ .finish = &r100_pm_finish,
+ .init_profile = &r100_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = &rv370_get_pcie_lanes,
+ .set_pcie_lanes = &rv370_set_pcie_lanes,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
+ },
};
static struct radeon_asic r300_asic_pcie = {
@@ -291,47 +358,70 @@ static struct radeon_asic r300_asic_pcie = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &r300_asic_reset,
- .gart_tlb_flush = &rv370_pcie_gart_tlb_flush,
- .gart_set_page = &rv370_pcie_gart_set_page,
- .ring_start = &r300_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &r300_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rv370_pcie_gart_tlb_flush,
+ .set_page = &rv370_pcie_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &r300_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
- .get_vblank_counter = &r100_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_legacy_get_engine_clock,
- .set_engine_clock = &radeon_legacy_set_engine_clock,
- .get_memory_clock = &radeon_legacy_get_memory_clock,
- .set_memory_clock = NULL,
- .set_pcie_lanes = &rv370_set_pcie_lanes,
- .set_clock_gating = &radeon_legacy_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &r100_bandwidth_update,
- .hpd_init = &r100_hpd_init,
- .hpd_fini = &r100_hpd_fini,
- .hpd_sense = &r100_hpd_sense,
- .hpd_set_polarity = &r100_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &r100_pm_misc,
- .pm_prepare = &r100_pm_prepare,
- .pm_finish = &r100_pm_finish,
- .pm_init_profile = &r100_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &r100_pre_page_flip,
- .page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .irq = {
+ .set = &r100_irq_set,
+ .process = &r100_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &r100_bandwidth_update,
+ .get_vblank_counter = &r100_get_vblank_counter,
+ .wait_for_vblank = &r100_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r100_hpd_init,
+ .fini = &r100_hpd_fini,
+ .sense = &r100_hpd_sense,
+ .set_polarity = &r100_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r100_pm_misc,
+ .prepare = &r100_pm_prepare,
+ .finish = &r100_pm_finish,
+ .init_profile = &r100_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = &rv370_get_pcie_lanes,
+ .set_pcie_lanes = &rv370_set_pcie_lanes,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
+ },
};
static struct radeon_asic r420_asic = {
@@ -342,48 +432,70 @@ static struct radeon_asic r420_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &r300_asic_reset,
- .gart_tlb_flush = &rv370_pcie_gart_tlb_flush,
- .gart_set_page = &rv370_pcie_gart_set_page,
- .ring_start = &r300_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &r300_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rv370_pcie_gart_tlb_flush,
+ .set_page = &rv370_pcie_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &r300_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
- .get_vblank_counter = &r100_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = &rv370_get_pcie_lanes,
- .set_pcie_lanes = &rv370_set_pcie_lanes,
- .set_clock_gating = &radeon_atom_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &r100_bandwidth_update,
- .hpd_init = &r100_hpd_init,
- .hpd_fini = &r100_hpd_fini,
- .hpd_sense = &r100_hpd_sense,
- .hpd_set_polarity = &r100_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &r100_pm_misc,
- .pm_prepare = &r100_pm_prepare,
- .pm_finish = &r100_pm_finish,
- .pm_init_profile = &r420_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &r100_pre_page_flip,
- .page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .irq = {
+ .set = &r100_irq_set,
+ .process = &r100_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &r100_bandwidth_update,
+ .get_vblank_counter = &r100_get_vblank_counter,
+ .wait_for_vblank = &r100_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r100_hpd_init,
+ .fini = &r100_hpd_fini,
+ .sense = &r100_hpd_sense,
+ .set_polarity = &r100_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r100_pm_misc,
+ .prepare = &r100_pm_prepare,
+ .finish = &r100_pm_finish,
+ .init_profile = &r420_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = &rv370_get_pcie_lanes,
+ .set_pcie_lanes = &rv370_set_pcie_lanes,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
+ },
};
static struct radeon_asic rs400_asic = {
@@ -394,48 +506,70 @@ static struct radeon_asic rs400_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &r300_asic_reset,
- .gart_tlb_flush = &rs400_gart_tlb_flush,
- .gart_set_page = &rs400_gart_set_page,
- .ring_start = &r300_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &rs400_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rs400_gart_tlb_flush,
+ .set_page = &rs400_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &r300_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &r100_irq_set,
- .irq_process = &r100_irq_process,
- .get_vblank_counter = &r100_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_legacy_get_engine_clock,
- .set_engine_clock = &radeon_legacy_set_engine_clock,
- .get_memory_clock = &radeon_legacy_get_memory_clock,
- .set_memory_clock = NULL,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = &radeon_legacy_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &r100_bandwidth_update,
- .hpd_init = &r100_hpd_init,
- .hpd_fini = &r100_hpd_fini,
- .hpd_sense = &r100_hpd_sense,
- .hpd_set_polarity = &r100_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &r100_pm_misc,
- .pm_prepare = &r100_pm_prepare,
- .pm_finish = &r100_pm_finish,
- .pm_init_profile = &r100_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &r100_pre_page_flip,
- .page_flip = &r100_page_flip,
- .post_page_flip = &r100_post_page_flip,
+ .irq = {
+ .set = &r100_irq_set,
+ .process = &r100_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &r100_bandwidth_update,
+ .get_vblank_counter = &r100_get_vblank_counter,
+ .wait_for_vblank = &r100_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r100_hpd_init,
+ .fini = &r100_hpd_fini,
+ .sense = &r100_hpd_sense,
+ .set_polarity = &r100_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r100_pm_misc,
+ .prepare = &r100_pm_prepare,
+ .finish = &r100_pm_finish,
+ .init_profile = &r100_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &r100_pre_page_flip,
+ .page_flip = &r100_page_flip,
+ .post_page_flip = &r100_post_page_flip,
+ },
};
static struct radeon_asic rs600_asic = {
@@ -446,48 +580,70 @@ static struct radeon_asic rs600_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &rs600_asic_reset,
- .gart_tlb_flush = &rs600_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_start = &r300_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &rs600_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rs600_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &r300_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &rs600_irq_set,
- .irq_process = &rs600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = &radeon_atom_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &rs600_bandwidth_update,
- .hpd_init = &rs600_hpd_init,
- .hpd_fini = &rs600_hpd_fini,
- .hpd_sense = &rs600_hpd_sense,
- .hpd_set_polarity = &rs600_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &rs600_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &r420_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &rs600_irq_set,
+ .process = &rs600_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &rs600_bandwidth_update,
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &rs600_hpd_init,
+ .fini = &rs600_hpd_fini,
+ .sense = &rs600_hpd_sense,
+ .set_polarity = &rs600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &rs600_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &r420_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic rs690_asic = {
@@ -498,48 +654,70 @@ static struct radeon_asic rs690_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &rs600_asic_reset,
- .gart_tlb_flush = &rs400_gart_tlb_flush,
- .gart_set_page = &rs400_gart_set_page,
- .ring_start = &r300_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &rs690_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rs400_gart_tlb_flush,
+ .set_page = &rs400_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &r300_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &rs600_irq_set,
- .irq_process = &rs600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r200_copy_dma,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = &radeon_atom_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &rs690_bandwidth_update,
- .hpd_init = &rs600_hpd_init,
- .hpd_fini = &rs600_hpd_fini,
- .hpd_sense = &rs600_hpd_sense,
- .hpd_set_polarity = &rs600_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &rs600_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &r420_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &rs600_irq_set,
+ .process = &rs600_irq_process,
+ },
+ .display = {
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .bandwidth_update = &rs690_bandwidth_update,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r200_copy_dma,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &rs600_hpd_init,
+ .fini = &rs600_hpd_fini,
+ .sense = &rs600_hpd_sense,
+ .set_polarity = &rs600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &rs600_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &r420_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic rv515_asic = {
@@ -550,48 +728,70 @@ static struct radeon_asic rv515_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &rs600_asic_reset,
- .gart_tlb_flush = &rv370_pcie_gart_tlb_flush,
- .gart_set_page = &rv370_pcie_gart_set_page,
- .ring_start = &rv515_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &rv515_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rv370_pcie_gart_tlb_flush,
+ .set_page = &rv370_pcie_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &rv515_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &rs600_irq_set,
- .irq_process = &rs600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = &rv370_get_pcie_lanes,
- .set_pcie_lanes = &rv370_set_pcie_lanes,
- .set_clock_gating = &radeon_atom_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &rv515_bandwidth_update,
- .hpd_init = &rs600_hpd_init,
- .hpd_fini = &rs600_hpd_fini,
- .hpd_sense = &rs600_hpd_sense,
- .hpd_set_polarity = &rs600_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &rs600_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &r420_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &rs600_irq_set,
+ .process = &rs600_irq_process,
+ },
+ .display = {
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .bandwidth_update = &rv515_bandwidth_update,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &rs600_hpd_init,
+ .fini = &rs600_hpd_fini,
+ .sense = &rs600_hpd_sense,
+ .set_polarity = &rs600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &rs600_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &r420_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = &rv370_get_pcie_lanes,
+ .set_pcie_lanes = &rv370_set_pcie_lanes,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic r520_asic = {
@@ -602,48 +802,70 @@ static struct radeon_asic r520_asic = {
.vga_set_state = &r100_vga_set_state,
.gpu_is_lockup = &r300_gpu_is_lockup,
.asic_reset = &rs600_asic_reset,
- .gart_tlb_flush = &rv370_pcie_gart_tlb_flush,
- .gart_set_page = &rv370_pcie_gart_set_page,
- .ring_start = &rv515_ring_start,
- .ring_test = &r100_ring_test,
+ .ioctl_wait_idle = NULL,
+ .gui_idle = &r100_gui_idle,
+ .mc_wait_for_idle = &r520_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &rv370_pcie_gart_tlb_flush,
+ .set_page = &rv370_pcie_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r100_ring_ib_execute,
.emit_fence = &r300_fence_ring_emit,
.emit_semaphore = &r100_semaphore_ring_emit,
+ .cs_parse = &r300_cs_parse,
+ .ring_start = &rv515_ring_start,
+ .ring_test = &r100_ring_test,
+ .ib_test = &r100_ib_test,
}
},
- .irq_set = &rs600_irq_set,
- .irq_process = &rs600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r300_cs_parse,
- .copy_blit = &r100_copy_blit,
- .copy_dma = &r200_copy_dma,
- .copy = &r100_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = &rv370_get_pcie_lanes,
- .set_pcie_lanes = &rv370_set_pcie_lanes,
- .set_clock_gating = &radeon_atom_set_clock_gating,
- .set_surface_reg = r100_set_surface_reg,
- .clear_surface_reg = r100_clear_surface_reg,
- .bandwidth_update = &rv515_bandwidth_update,
- .hpd_init = &rs600_hpd_init,
- .hpd_fini = &rs600_hpd_fini,
- .hpd_sense = &rs600_hpd_sense,
- .hpd_set_polarity = &rs600_hpd_set_polarity,
- .ioctl_wait_idle = NULL,
- .gui_idle = &r100_gui_idle,
- .pm_misc = &rs600_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &r420_pm_init_profile,
- .pm_get_dynpm_state = &r100_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &rs600_irq_set,
+ .process = &rs600_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &rv515_bandwidth_update,
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r100_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = &r200_copy_dma,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r100_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r100_set_surface_reg,
+ .clear_reg = r100_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &rs600_hpd_init,
+ .fini = &rs600_hpd_fini,
+ .sense = &rs600_hpd_sense,
+ .set_polarity = &rs600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &rs600_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &r420_pm_init_profile,
+ .get_dynpm_state = &r100_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = &rv370_get_pcie_lanes,
+ .set_pcie_lanes = &rv370_set_pcie_lanes,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic r600_asic = {
@@ -654,47 +876,69 @@ static struct radeon_asic r600_asic = {
.vga_set_state = &r600_vga_set_state,
.gpu_is_lockup = &r600_gpu_is_lockup,
.asic_reset = &r600_asic_reset,
- .gart_tlb_flush = &r600_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &r600_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &r600_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r600_ring_ib_execute,
.emit_fence = &r600_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &r600_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
}
},
- .irq_set = &r600_irq_set,
- .irq_process = &r600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r600_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = &r600_get_pcie_lanes,
- .set_pcie_lanes = &r600_set_pcie_lanes,
- .set_clock_gating = NULL,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &rv515_bandwidth_update,
- .hpd_init = &r600_hpd_init,
- .hpd_fini = &r600_hpd_fini,
- .hpd_sense = &r600_hpd_sense,
- .hpd_set_polarity = &r600_hpd_set_polarity,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
- .gui_idle = &r600_gui_idle,
- .pm_misc = &r600_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &r600_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &r600_irq_set,
+ .process = &r600_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &rv515_bandwidth_update,
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r600_hpd_init,
+ .fini = &r600_hpd_fini,
+ .sense = &r600_hpd_sense,
+ .set_polarity = &r600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r600_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &r600_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = &r600_get_pcie_lanes,
+ .set_pcie_lanes = &r600_set_pcie_lanes,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic rs780_asic = {
@@ -705,47 +949,69 @@ static struct radeon_asic rs780_asic = {
.gpu_is_lockup = &r600_gpu_is_lockup,
.vga_set_state = &r600_vga_set_state,
.asic_reset = &r600_asic_reset,
- .gart_tlb_flush = &r600_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &r600_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &r600_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r600_ring_ib_execute,
.emit_fence = &r600_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &r600_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
}
},
- .irq_set = &r600_irq_set,
- .irq_process = &r600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r600_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = NULL,
- .set_memory_clock = NULL,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = NULL,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &rs690_bandwidth_update,
- .hpd_init = &r600_hpd_init,
- .hpd_fini = &r600_hpd_fini,
- .hpd_sense = &r600_hpd_sense,
- .hpd_set_polarity = &r600_hpd_set_polarity,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
- .gui_idle = &r600_gui_idle,
- .pm_misc = &r600_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &rs780_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rs600_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &r600_irq_set,
+ .process = &r600_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &rs690_bandwidth_update,
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r600_hpd_init,
+ .fini = &r600_hpd_fini,
+ .sense = &r600_hpd_sense,
+ .set_polarity = &r600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &r600_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &rs780_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = NULL,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rs600_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic rv770_asic = {
@@ -756,47 +1022,69 @@ static struct radeon_asic rv770_asic = {
.asic_reset = &r600_asic_reset,
.gpu_is_lockup = &r600_gpu_is_lockup,
.vga_set_state = &r600_vga_set_state,
- .gart_tlb_flush = &r600_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &r600_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &r600_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &r600_ring_ib_execute,
.emit_fence = &r600_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &r600_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
}
},
- .irq_set = &r600_irq_set,
- .irq_process = &r600_irq_process,
- .get_vblank_counter = &rs600_get_vblank_counter,
- .cs_parse = &r600_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = &r600_get_pcie_lanes,
- .set_pcie_lanes = &r600_set_pcie_lanes,
- .set_clock_gating = &radeon_atom_set_clock_gating,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &rv515_bandwidth_update,
- .hpd_init = &r600_hpd_init,
- .hpd_fini = &r600_hpd_fini,
- .hpd_sense = &r600_hpd_sense,
- .hpd_set_polarity = &r600_hpd_set_polarity,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
- .gui_idle = &r600_gui_idle,
- .pm_misc = &rv770_pm_misc,
- .pm_prepare = &rs600_pm_prepare,
- .pm_finish = &rs600_pm_finish,
- .pm_init_profile = &r600_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &rs600_pre_page_flip,
- .page_flip = &rv770_page_flip,
- .post_page_flip = &rs600_post_page_flip,
+ .irq = {
+ .set = &r600_irq_set,
+ .process = &r600_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &rv515_bandwidth_update,
+ .get_vblank_counter = &rs600_get_vblank_counter,
+ .wait_for_vblank = &avivo_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &r600_hpd_init,
+ .fini = &r600_hpd_fini,
+ .sense = &r600_hpd_sense,
+ .set_polarity = &r600_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &rv770_pm_misc,
+ .prepare = &rs600_pm_prepare,
+ .finish = &rs600_pm_finish,
+ .init_profile = &r600_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = &r600_get_pcie_lanes,
+ .set_pcie_lanes = &r600_set_pcie_lanes,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ },
+ .pflip = {
+ .pre_page_flip = &rs600_pre_page_flip,
+ .page_flip = &rv770_page_flip,
+ .post_page_flip = &rs600_post_page_flip,
+ },
};
static struct radeon_asic evergreen_asic = {
@@ -807,47 +1095,69 @@ static struct radeon_asic evergreen_asic = {
.gpu_is_lockup = &evergreen_gpu_is_lockup,
.asic_reset = &evergreen_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .gart_tlb_flush = &evergreen_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &evergreen_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &evergreen_ring_ib_execute,
.emit_fence = &r600_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
}
},
- .irq_set = &evergreen_irq_set,
- .irq_process = &evergreen_irq_process,
- .get_vblank_counter = &evergreen_get_vblank_counter,
- .cs_parse = &evergreen_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = &r600_get_pcie_lanes,
- .set_pcie_lanes = &r600_set_pcie_lanes,
- .set_clock_gating = NULL,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &evergreen_bandwidth_update,
- .hpd_init = &evergreen_hpd_init,
- .hpd_fini = &evergreen_hpd_fini,
- .hpd_sense = &evergreen_hpd_sense,
- .hpd_set_polarity = &evergreen_hpd_set_polarity,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
- .gui_idle = &r600_gui_idle,
- .pm_misc = &evergreen_pm_misc,
- .pm_prepare = &evergreen_pm_prepare,
- .pm_finish = &evergreen_pm_finish,
- .pm_init_profile = &r600_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &evergreen_pre_page_flip,
- .page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .irq = {
+ .set = &evergreen_irq_set,
+ .process = &evergreen_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &evergreen_bandwidth_update,
+ .get_vblank_counter = &evergreen_get_vblank_counter,
+ .wait_for_vblank = &dce4_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &evergreen_hpd_init,
+ .fini = &evergreen_hpd_fini,
+ .sense = &evergreen_hpd_sense,
+ .set_polarity = &evergreen_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &evergreen_pm_misc,
+ .prepare = &evergreen_pm_prepare,
+ .finish = &evergreen_pm_finish,
+ .init_profile = &r600_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = &r600_get_pcie_lanes,
+ .set_pcie_lanes = &r600_set_pcie_lanes,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &evergreen_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &evergreen_post_page_flip,
+ },
};
static struct radeon_asic sumo_asic = {
@@ -858,47 +1168,69 @@ static struct radeon_asic sumo_asic = {
.gpu_is_lockup = &evergreen_gpu_is_lockup,
.asic_reset = &evergreen_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .gart_tlb_flush = &evergreen_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &evergreen_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &evergreen_ring_ib_execute,
.emit_fence = &r600_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
- }
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ },
+ },
+ .irq = {
+ .set = &evergreen_irq_set,
+ .process = &evergreen_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &evergreen_bandwidth_update,
+ .get_vblank_counter = &evergreen_get_vblank_counter,
+ .wait_for_vblank = &dce4_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &evergreen_hpd_init,
+ .fini = &evergreen_hpd_fini,
+ .sense = &evergreen_hpd_sense,
+ .set_polarity = &evergreen_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &evergreen_pm_misc,
+ .prepare = &evergreen_pm_prepare,
+ .finish = &evergreen_pm_finish,
+ .init_profile = &sumo_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = NULL,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &evergreen_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &evergreen_post_page_flip,
},
- .irq_set = &evergreen_irq_set,
- .irq_process = &evergreen_irq_process,
- .get_vblank_counter = &evergreen_get_vblank_counter,
- .cs_parse = &evergreen_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = NULL,
- .set_memory_clock = NULL,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = NULL,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &evergreen_bandwidth_update,
- .hpd_init = &evergreen_hpd_init,
- .hpd_fini = &evergreen_hpd_fini,
- .hpd_sense = &evergreen_hpd_sense,
- .hpd_set_polarity = &evergreen_hpd_set_polarity,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
- .gui_idle = &r600_gui_idle,
- .pm_misc = &evergreen_pm_misc,
- .pm_prepare = &evergreen_pm_prepare,
- .pm_finish = &evergreen_pm_finish,
- .pm_init_profile = &sumo_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &evergreen_pre_page_flip,
- .page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
};
static struct radeon_asic btc_asic = {
@@ -909,47 +1241,69 @@ static struct radeon_asic btc_asic = {
.gpu_is_lockup = &evergreen_gpu_is_lockup,
.asic_reset = &evergreen_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .gart_tlb_flush = &evergreen_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &evergreen_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &evergreen_ring_ib_execute,
.emit_fence = &r600_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
}
},
- .irq_set = &evergreen_irq_set,
- .irq_process = &evergreen_irq_process,
- .get_vblank_counter = &evergreen_get_vblank_counter,
- .cs_parse = &evergreen_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = NULL,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &evergreen_bandwidth_update,
- .hpd_init = &evergreen_hpd_init,
- .hpd_fini = &evergreen_hpd_fini,
- .hpd_sense = &evergreen_hpd_sense,
- .hpd_set_polarity = &evergreen_hpd_set_polarity,
- .ioctl_wait_idle = r600_ioctl_wait_idle,
- .gui_idle = &r600_gui_idle,
- .pm_misc = &evergreen_pm_misc,
- .pm_prepare = &evergreen_pm_prepare,
- .pm_finish = &evergreen_pm_finish,
- .pm_init_profile = &r600_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &evergreen_pre_page_flip,
- .page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .irq = {
+ .set = &evergreen_irq_set,
+ .process = &evergreen_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &evergreen_bandwidth_update,
+ .get_vblank_counter = &evergreen_get_vblank_counter,
+ .wait_for_vblank = &dce4_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &evergreen_hpd_init,
+ .fini = &evergreen_hpd_fini,
+ .sense = &evergreen_hpd_sense,
+ .set_polarity = &evergreen_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &evergreen_pm_misc,
+ .prepare = &evergreen_pm_prepare,
+ .finish = &evergreen_pm_finish,
+ .init_profile = &r600_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &evergreen_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &evergreen_post_page_flip,
+ },
};
static const struct radeon_vm_funcs cayman_vm_funcs = {
@@ -970,60 +1324,282 @@ static struct radeon_asic cayman_asic = {
.gpu_is_lockup = &cayman_gpu_is_lockup,
.asic_reset = &cayman_asic_reset,
.vga_set_state = &r600_vga_set_state,
- .gart_tlb_flush = &cayman_pcie_gart_tlb_flush,
- .gart_set_page = &rs600_gart_set_page,
- .ring_test = &r600_ring_test,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &cayman_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
+ .ring = {
+ [RADEON_RING_TYPE_GFX_INDEX] = {
+ .ib_execute = &cayman_ring_ib_execute,
+ .ib_parse = &evergreen_ib_parse,
+ .emit_fence = &cayman_fence_ring_emit,
+ .emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ },
+ [CAYMAN_RING_TYPE_CP1_INDEX] = {
+ .ib_execute = &cayman_ring_ib_execute,
+ .ib_parse = &evergreen_ib_parse,
+ .emit_fence = &cayman_fence_ring_emit,
+ .emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ },
+ [CAYMAN_RING_TYPE_CP2_INDEX] = {
+ .ib_execute = &cayman_ring_ib_execute,
+ .ib_parse = &evergreen_ib_parse,
+ .emit_fence = &cayman_fence_ring_emit,
+ .emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ }
+ },
+ .irq = {
+ .set = &evergreen_irq_set,
+ .process = &evergreen_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &evergreen_bandwidth_update,
+ .get_vblank_counter = &evergreen_get_vblank_counter,
+ .wait_for_vblank = &dce4_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &evergreen_hpd_init,
+ .fini = &evergreen_hpd_fini,
+ .sense = &evergreen_hpd_sense,
+ .set_polarity = &evergreen_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &evergreen_pm_misc,
+ .prepare = &evergreen_pm_prepare,
+ .finish = &evergreen_pm_finish,
+ .init_profile = &r600_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &evergreen_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &evergreen_post_page_flip,
+ },
+};
+
+static struct radeon_asic trinity_asic = {
+ .init = &cayman_init,
+ .fini = &cayman_fini,
+ .suspend = &cayman_suspend,
+ .resume = &cayman_resume,
+ .gpu_is_lockup = &cayman_gpu_is_lockup,
+ .asic_reset = &cayman_asic_reset,
+ .vga_set_state = &r600_vga_set_state,
+ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ .gui_idle = &r600_gui_idle,
+ .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &cayman_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
.ring = {
[RADEON_RING_TYPE_GFX_INDEX] = {
.ib_execute = &cayman_ring_ib_execute,
.ib_parse = &evergreen_ib_parse,
.emit_fence = &cayman_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
},
[CAYMAN_RING_TYPE_CP1_INDEX] = {
.ib_execute = &cayman_ring_ib_execute,
.ib_parse = &evergreen_ib_parse,
.emit_fence = &cayman_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
},
[CAYMAN_RING_TYPE_CP2_INDEX] = {
.ib_execute = &cayman_ring_ib_execute,
.ib_parse = &evergreen_ib_parse,
.emit_fence = &cayman_fence_ring_emit,
.emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = &evergreen_cs_parse,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
}
},
- .irq_set = &evergreen_irq_set,
- .irq_process = &evergreen_irq_process,
- .get_vblank_counter = &evergreen_get_vblank_counter,
- .cs_parse = &evergreen_cs_parse,
- .copy_blit = &r600_copy_blit,
- .copy_dma = NULL,
- .copy = &r600_copy_blit,
- .get_engine_clock = &radeon_atom_get_engine_clock,
- .set_engine_clock = &radeon_atom_set_engine_clock,
- .get_memory_clock = &radeon_atom_get_memory_clock,
- .set_memory_clock = &radeon_atom_set_memory_clock,
- .get_pcie_lanes = NULL,
- .set_pcie_lanes = NULL,
- .set_clock_gating = NULL,
- .set_surface_reg = r600_set_surface_reg,
- .clear_surface_reg = r600_clear_surface_reg,
- .bandwidth_update = &evergreen_bandwidth_update,
- .hpd_init = &evergreen_hpd_init,
- .hpd_fini = &evergreen_hpd_fini,
- .hpd_sense = &evergreen_hpd_sense,
- .hpd_set_polarity = &evergreen_hpd_set_polarity,
+ .irq = {
+ .set = &evergreen_irq_set,
+ .process = &evergreen_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &dce6_bandwidth_update,
+ .get_vblank_counter = &evergreen_get_vblank_counter,
+ .wait_for_vblank = &dce4_wait_for_vblank,
+ },
+ .copy = {
+ .blit = &r600_copy_blit,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = &r600_copy_blit,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &evergreen_hpd_init,
+ .fini = &evergreen_hpd_fini,
+ .sense = &evergreen_hpd_sense,
+ .set_polarity = &evergreen_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &evergreen_pm_misc,
+ .prepare = &evergreen_pm_prepare,
+ .finish = &evergreen_pm_finish,
+ .init_profile = &sumo_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = NULL,
+ .set_memory_clock = NULL,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &evergreen_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &evergreen_post_page_flip,
+ },
+};
+
+static const struct radeon_vm_funcs si_vm_funcs = {
+ .init = &si_vm_init,
+ .fini = &si_vm_fini,
+ .bind = &si_vm_bind,
+ .unbind = &si_vm_unbind,
+ .tlb_flush = &si_vm_tlb_flush,
+ .page_flags = &cayman_vm_page_flags,
+ .set_page = &cayman_vm_set_page,
+};
+
+static struct radeon_asic si_asic = {
+ .init = &si_init,
+ .fini = &si_fini,
+ .suspend = &si_suspend,
+ .resume = &si_resume,
+ .gpu_is_lockup = &si_gpu_is_lockup,
+ .asic_reset = &si_asic_reset,
+ .vga_set_state = &r600_vga_set_state,
.ioctl_wait_idle = r600_ioctl_wait_idle,
.gui_idle = &r600_gui_idle,
- .pm_misc = &evergreen_pm_misc,
- .pm_prepare = &evergreen_pm_prepare,
- .pm_finish = &evergreen_pm_finish,
- .pm_init_profile = &r600_pm_init_profile,
- .pm_get_dynpm_state = &r600_pm_get_dynpm_state,
- .pre_page_flip = &evergreen_pre_page_flip,
- .page_flip = &evergreen_page_flip,
- .post_page_flip = &evergreen_post_page_flip,
+ .mc_wait_for_idle = &evergreen_mc_wait_for_idle,
+ .gart = {
+ .tlb_flush = &si_pcie_gart_tlb_flush,
+ .set_page = &rs600_gart_set_page,
+ },
+ .ring = {
+ [RADEON_RING_TYPE_GFX_INDEX] = {
+ .ib_execute = &si_ring_ib_execute,
+ .ib_parse = &si_ib_parse,
+ .emit_fence = &si_fence_ring_emit,
+ .emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = NULL,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ },
+ [CAYMAN_RING_TYPE_CP1_INDEX] = {
+ .ib_execute = &si_ring_ib_execute,
+ .ib_parse = &si_ib_parse,
+ .emit_fence = &si_fence_ring_emit,
+ .emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = NULL,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ },
+ [CAYMAN_RING_TYPE_CP2_INDEX] = {
+ .ib_execute = &si_ring_ib_execute,
+ .ib_parse = &si_ib_parse,
+ .emit_fence = &si_fence_ring_emit,
+ .emit_semaphore = &r600_semaphore_ring_emit,
+ .cs_parse = NULL,
+ .ring_test = &r600_ring_test,
+ .ib_test = &r600_ib_test,
+ }
+ },
+ .irq = {
+ .set = &si_irq_set,
+ .process = &si_irq_process,
+ },
+ .display = {
+ .bandwidth_update = &dce6_bandwidth_update,
+ .get_vblank_counter = &evergreen_get_vblank_counter,
+ .wait_for_vblank = &dce4_wait_for_vblank,
+ },
+ .copy = {
+ .blit = NULL,
+ .blit_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .dma = NULL,
+ .dma_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ .copy = NULL,
+ .copy_ring_index = RADEON_RING_TYPE_GFX_INDEX,
+ },
+ .surface = {
+ .set_reg = r600_set_surface_reg,
+ .clear_reg = r600_clear_surface_reg,
+ },
+ .hpd = {
+ .init = &evergreen_hpd_init,
+ .fini = &evergreen_hpd_fini,
+ .sense = &evergreen_hpd_sense,
+ .set_polarity = &evergreen_hpd_set_polarity,
+ },
+ .pm = {
+ .misc = &evergreen_pm_misc,
+ .prepare = &evergreen_pm_prepare,
+ .finish = &evergreen_pm_finish,
+ .init_profile = &sumo_pm_init_profile,
+ .get_dynpm_state = &r600_pm_get_dynpm_state,
+ .get_engine_clock = &radeon_atom_get_engine_clock,
+ .set_engine_clock = &radeon_atom_set_engine_clock,
+ .get_memory_clock = &radeon_atom_get_memory_clock,
+ .set_memory_clock = &radeon_atom_set_memory_clock,
+ .get_pcie_lanes = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = NULL,
+ },
+ .pflip = {
+ .pre_page_flip = &evergreen_pre_page_flip,
+ .page_flip = &evergreen_page_flip,
+ .post_page_flip = &evergreen_post_page_flip,
+ },
};
int radeon_asic_init(struct radeon_device *rdev)
@@ -1036,9 +1612,6 @@ int radeon_asic_init(struct radeon_device *rdev)
else
rdev->num_crtc = 2;
- /* set the ring used for bo copies */
- rdev->copy_ring = RADEON_RING_TYPE_GFX_INDEX;
-
switch (rdev->family) {
case CHIP_R100:
case CHIP_RV100:
@@ -1068,10 +1641,10 @@ int radeon_asic_init(struct radeon_device *rdev)
rdev->asic = &r420_asic;
/* handle macs */
if (rdev->bios == NULL) {
- rdev->asic->get_engine_clock = &radeon_legacy_get_engine_clock;
- rdev->asic->set_engine_clock = &radeon_legacy_set_engine_clock;
- rdev->asic->get_memory_clock = &radeon_legacy_get_memory_clock;
- rdev->asic->set_memory_clock = NULL;
+ rdev->asic->pm.get_engine_clock = &radeon_legacy_get_engine_clock;
+ rdev->asic->pm.set_engine_clock = &radeon_legacy_set_engine_clock;
+ rdev->asic->pm.get_memory_clock = &radeon_legacy_get_memory_clock;
+ rdev->asic->pm.set_memory_clock = NULL;
}
break;
case CHIP_RS400:
@@ -1146,14 +1719,28 @@ int radeon_asic_init(struct radeon_device *rdev)
rdev->num_crtc = 6;
rdev->vm_manager.funcs = &cayman_vm_funcs;
break;
+ case CHIP_ARUBA:
+ rdev->asic = &trinity_asic;
+ /* set num crtcs */
+ rdev->num_crtc = 4;
+ rdev->vm_manager.funcs = &cayman_vm_funcs;
+ break;
+ case CHIP_TAHITI:
+ case CHIP_PITCAIRN:
+ case CHIP_VERDE:
+ rdev->asic = &si_asic;
+ /* set num crtcs */
+ rdev->num_crtc = 6;
+ rdev->vm_manager.funcs = &si_vm_funcs;
+ break;
default:
/* FIXME: not supported yet */
return -EINVAL;
}
if (rdev->flags & RADEON_IS_IGP) {
- rdev->asic->get_memory_clock = NULL;
- rdev->asic->set_memory_clock = NULL;
+ rdev->asic->pm.get_memory_clock = NULL;
+ rdev->asic->pm.set_memory_clock = NULL;
}
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index 6304aef0d9b2..3d9f9f1d8f90 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -63,7 +63,7 @@ int r100_asic_reset(struct radeon_device *rdev);
u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc);
void r100_pci_gart_tlb_flush(struct radeon_device *rdev);
int r100_pci_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
-void r100_ring_start(struct radeon_device *rdev);
+void r100_ring_start(struct radeon_device *rdev, struct radeon_ring *ring);
int r100_irq_set(struct radeon_device *rdev);
int r100_irq_process(struct radeon_device *rdev);
void r100_fence_ring_emit(struct radeon_device *rdev,
@@ -109,7 +109,7 @@ bool r100_gpu_cp_is_lockup(struct radeon_device *rdev,
struct r100_gpu_lockup *lockup,
struct radeon_ring *cp);
void r100_ib_fini(struct radeon_device *rdev);
-int r100_ib_test(struct radeon_device *rdev);
+int r100_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
void r100_irq_disable(struct radeon_device *rdev);
void r100_mc_stop(struct radeon_device *rdev, struct r100_mc_save *save);
void r100_mc_resume(struct radeon_device *rdev, struct r100_mc_save *save);
@@ -139,6 +139,8 @@ extern void r100_pm_get_dynpm_state(struct radeon_device *rdev);
extern void r100_pre_page_flip(struct radeon_device *rdev, int crtc);
extern u32 r100_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
extern void r100_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void r100_wait_for_vblank(struct radeon_device *rdev, int crtc);
+extern int r100_mc_wait_for_idle(struct radeon_device *rdev);
/*
* r200,rv250,rs300,rv280
@@ -159,7 +161,7 @@ extern int r300_suspend(struct radeon_device *rdev);
extern int r300_resume(struct radeon_device *rdev);
extern bool r300_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp);
extern int r300_asic_reset(struct radeon_device *rdev);
-extern void r300_ring_start(struct radeon_device *rdev);
+extern void r300_ring_start(struct radeon_device *rdev, struct radeon_ring *ring);
extern void r300_fence_ring_emit(struct radeon_device *rdev,
struct radeon_fence *fence);
extern int r300_cs_parse(struct radeon_cs_parser *p);
@@ -176,6 +178,7 @@ extern int rv370_pcie_gart_init(struct radeon_device *rdev);
extern void rv370_pcie_gart_fini(struct radeon_device *rdev);
extern int rv370_pcie_gart_enable(struct radeon_device *rdev);
extern void rv370_pcie_gart_disable(struct radeon_device *rdev);
+extern int r300_mc_wait_for_idle(struct radeon_device *rdev);
/*
* r420,r423,rv410
@@ -206,6 +209,7 @@ int rs400_gart_enable(struct radeon_device *rdev);
void rs400_gart_adjust_size(struct radeon_device *rdev);
void rs400_gart_disable(struct radeon_device *rdev);
void rs400_gart_fini(struct radeon_device *rdev);
+extern int rs400_mc_wait_for_idle(struct radeon_device *rdev);
/*
* rs600.
@@ -236,7 +240,8 @@ extern void rs600_pre_page_flip(struct radeon_device *rdev, int crtc);
extern u32 rs600_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
extern void rs600_post_page_flip(struct radeon_device *rdev, int crtc);
void rs600_set_safe_registers(struct radeon_device *rdev);
-
+extern void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc);
+extern int rs600_mc_wait_for_idle(struct radeon_device *rdev);
/*
* rs690,rs740
@@ -251,6 +256,7 @@ void rs690_bandwidth_update(struct radeon_device *rdev);
void rs690_line_buffer_adjust(struct radeon_device *rdev,
struct drm_display_mode *mode1,
struct drm_display_mode *mode2);
+extern int rs690_mc_wait_for_idle(struct radeon_device *rdev);
/*
* rv515
@@ -267,7 +273,7 @@ int rv515_init(struct radeon_device *rdev);
void rv515_fini(struct radeon_device *rdev);
uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg);
void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
-void rv515_ring_start(struct radeon_device *rdev);
+void rv515_ring_start(struct radeon_device *rdev, struct radeon_ring *ring);
void rv515_bandwidth_update(struct radeon_device *rdev);
int rv515_resume(struct radeon_device *rdev);
int rv515_suspend(struct radeon_device *rdev);
@@ -278,13 +284,14 @@ void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save);
void rv515_mc_resume(struct radeon_device *rdev, struct rv515_mc_save *save);
void rv515_clock_startup(struct radeon_device *rdev);
void rv515_debugfs(struct radeon_device *rdev);
-
+int rv515_mc_wait_for_idle(struct radeon_device *rdev);
/*
* r520,rv530,rv560,rv570,r580
*/
int r520_init(struct radeon_device *rdev);
int r520_resume(struct radeon_device *rdev);
+int r520_mc_wait_for_idle(struct radeon_device *rdev);
/*
* r600,rv610,rv630,rv620,rv635,rv670,rs780,rs880
@@ -312,7 +319,7 @@ int r600_set_surface_reg(struct radeon_device *rdev, int reg,
uint32_t tiling_flags, uint32_t pitch,
uint32_t offset, uint32_t obj_size);
void r600_clear_surface_reg(struct radeon_device *rdev, int reg);
-int r600_ib_test(struct radeon_device *rdev, int ring);
+int r600_ib_test(struct radeon_device *rdev, struct radeon_ring *ring);
void r600_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
int r600_ring_test(struct radeon_device *rdev, struct radeon_ring *cp);
int r600_copy_blit(struct radeon_device *rdev,
@@ -375,6 +382,7 @@ void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
void r600_kms_blit_copy(struct radeon_device *rdev,
u64 src_gpu_addr, u64 dst_gpu_addr,
unsigned num_gpu_pages);
+int r600_mc_wait_for_idle(struct radeon_device *rdev);
/*
* rv770,rv730,rv710,rv740
@@ -423,8 +431,10 @@ extern void sumo_pm_init_profile(struct radeon_device *rdev);
extern void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc);
extern u32 evergreen_page_flip(struct radeon_device *rdev, int crtc, u64 crtc_base);
extern void evergreen_post_page_flip(struct radeon_device *rdev, int crtc);
+extern void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc);
void evergreen_disable_interrupt_state(struct radeon_device *rdev);
int evergreen_blit_init(struct radeon_device *rdev);
+int evergreen_mc_wait_for_idle(struct radeon_device *rdev);
/*
* cayman
@@ -451,4 +461,29 @@ void cayman_vm_set_page(struct radeon_device *rdev, struct radeon_vm *vm,
unsigned pfn, uint64_t addr, uint32_t flags);
int evergreen_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
+/* DCE6 - SI */
+void dce6_bandwidth_update(struct radeon_device *rdev);
+
+/*
+ * si
+ */
+void si_fence_ring_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence);
+void si_pcie_gart_tlb_flush(struct radeon_device *rdev);
+int si_init(struct radeon_device *rdev);
+void si_fini(struct radeon_device *rdev);
+int si_suspend(struct radeon_device *rdev);
+int si_resume(struct radeon_device *rdev);
+bool si_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *cp);
+int si_asic_reset(struct radeon_device *rdev);
+void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
+int si_irq_set(struct radeon_device *rdev);
+int si_irq_process(struct radeon_device *rdev);
+int si_vm_init(struct radeon_device *rdev);
+void si_vm_fini(struct radeon_device *rdev);
+int si_vm_bind(struct radeon_device *rdev, struct radeon_vm *vm, int id);
+void si_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm);
+void si_vm_tlb_flush(struct radeon_device *rdev, struct radeon_vm *vm);
+int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
+
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 1f53ae74ada1..f6e69b8c06c6 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -56,6 +56,10 @@ extern void
radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_enum,
uint32_t supported_device);
+/* local */
+static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
+ u16 voltage_id, u16 *voltage);
+
union atom_supported_devices {
struct _ATOM_SUPPORTED_DEVICES_INFO info;
struct _ATOM_SUPPORTED_DEVICES_INFO_2 info_2;
@@ -253,7 +257,9 @@ static struct radeon_hpd radeon_atom_get_hpd_info_from_gpio(struct radeon_device
memset(&hpd, 0, sizeof(struct radeon_hpd));
- if (ASIC_IS_DCE4(rdev))
+ if (ASIC_IS_DCE6(rdev))
+ reg = SI_DC_GPIO_HPD_A;
+ else if (ASIC_IS_DCE4(rdev))
reg = EVERGREEN_DC_GPIO_HPD_A;
else
reg = AVIVO_DC_GPIO_HPD_A;
@@ -442,6 +448,20 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
struct radeon_device *rdev = dev->dev_private;
*i2c_bus = radeon_lookup_i2c_gpio(rdev, 0x93);
}
+
+ /* Fujitsu D3003-S2 board lists DVI-I as DVI-D and VGA */
+ if ((dev->pdev->device == 0x9802) &&
+ (dev->pdev->subsystem_vendor == 0x1734) &&
+ (dev->pdev->subsystem_device == 0x11bd)) {
+ if (*connector_type == DRM_MODE_CONNECTOR_VGA) {
+ *connector_type = DRM_MODE_CONNECTOR_DVII;
+ *line_mux = 0x3103;
+ } else if (*connector_type == DRM_MODE_CONNECTOR_DVID) {
+ *connector_type = DRM_MODE_CONNECTOR_DVII;
+ }
+ }
+
+
return true;
}
@@ -1874,6 +1894,8 @@ static const char *pp_lib_thermal_controller_names[] = {
"emc2103",
"Sumo",
"Northern Islands",
+ "Southern Islands",
+ "lm96163",
};
union power_info {
@@ -1890,6 +1912,7 @@ union pplib_clock_info {
struct _ATOM_PPLIB_RS780_CLOCK_INFO rs780;
struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
+ struct _ATOM_PPLIB_SI_CLOCK_INFO si;
};
union pplib_power_state {
@@ -2147,6 +2170,11 @@ static void radeon_atombios_add_pplib_thermal_controller(struct radeon_device *r
(controller->ucFanParameters &
ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
rdev->pm.int_thermal_type = THERMAL_TYPE_NI;
+ } else if (controller->ucType == ATOM_PP_THERMALCONTROLLER_SISLANDS) {
+ DRM_INFO("Internal thermal controller %s fan control\n",
+ (controller->ucFanParameters &
+ ATOM_PP_FANPARAMETERS_NOFAN) ? "without" : "with");
+ rdev->pm.int_thermal_type = THERMAL_TYPE_SI;
} else if ((controller->ucType ==
ATOM_PP_THERMALCONTROLLER_EXTERNAL_GPIO) ||
(controller->ucType ==
@@ -2267,6 +2295,7 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
union pplib_clock_info *clock_info)
{
u32 sclk, mclk;
+ u16 vddc;
if (rdev->flags & RADEON_IS_IGP) {
if (rdev->family >= CHIP_PALM) {
@@ -2278,6 +2307,19 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
sclk |= clock_info->rs780.ucLowEngineClockHigh << 16;
rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
}
+ } else if (ASIC_IS_DCE6(rdev)) {
+ sclk = le16_to_cpu(clock_info->si.usEngineClockLow);
+ sclk |= clock_info->si.ucEngineClockHigh << 16;
+ mclk = le16_to_cpu(clock_info->si.usMemoryClockLow);
+ mclk |= clock_info->si.ucMemoryClockHigh << 16;
+ rdev->pm.power_state[state_index].clock_info[mode_index].mclk = mclk;
+ rdev->pm.power_state[state_index].clock_info[mode_index].sclk = sclk;
+ rdev->pm.power_state[state_index].clock_info[mode_index].voltage.type =
+ VOLTAGE_SW;
+ rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage =
+ le16_to_cpu(clock_info->si.usVDDC);
+ rdev->pm.power_state[state_index].clock_info[mode_index].voltage.vddci =
+ le16_to_cpu(clock_info->si.usVDDCI);
} else if (ASIC_IS_DCE4(rdev)) {
sclk = le16_to_cpu(clock_info->evergreen.usEngineClockLow);
sclk |= clock_info->evergreen.ucEngineClockHigh << 16;
@@ -2305,11 +2347,18 @@ static bool radeon_atombios_parse_pplib_clock_info(struct radeon_device *rdev,
}
/* patch up vddc if necessary */
- if (rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage == 0xff01) {
- u16 vddc;
-
- if (radeon_atom_get_max_vddc(rdev, &vddc) == 0)
+ switch (rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage) {
+ case ATOM_VIRTUAL_VOLTAGE_ID0:
+ case ATOM_VIRTUAL_VOLTAGE_ID1:
+ case ATOM_VIRTUAL_VOLTAGE_ID2:
+ case ATOM_VIRTUAL_VOLTAGE_ID3:
+ if (radeon_atom_get_max_vddc(rdev, VOLTAGE_TYPE_VDDC,
+ rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage,
+ &vddc) == 0)
rdev->pm.power_state[state_index].clock_info[mode_index].voltage.voltage = vddc;
+ break;
+ default:
+ break;
}
if (rdev->flags & RADEON_IS_IGP) {
@@ -2419,9 +2468,9 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev)
int i, j, non_clock_array_index, clock_array_index;
int state_index = 0, mode_index = 0;
union pplib_clock_info *clock_info;
- struct StateArray *state_array;
- struct ClockInfoArray *clock_info_array;
- struct NonClockInfoArray *non_clock_info_array;
+ struct _StateArray *state_array;
+ struct _ClockInfoArray *clock_info_array;
+ struct _NonClockInfoArray *non_clock_info_array;
bool valid;
union power_info *power_info;
int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
@@ -2434,13 +2483,13 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev)
power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
radeon_atombios_add_pplib_thermal_controller(rdev, &power_info->pplib.sThermalController);
- state_array = (struct StateArray *)
+ state_array = (struct _StateArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset));
- clock_info_array = (struct ClockInfoArray *)
+ clock_info_array = (struct _ClockInfoArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
- non_clock_info_array = (struct NonClockInfoArray *)
+ non_clock_info_array = (struct _NonClockInfoArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
rdev->pm.power_state = kzalloc(sizeof(struct radeon_power_state) *
@@ -2467,7 +2516,7 @@ static int radeon_atombios_parse_power_table_6(struct radeon_device *rdev)
if (clock_array_index >= clock_info_array->ucNumEntries)
continue;
clock_info = (union pplib_clock_info *)
- &clock_info_array->clockInfo[clock_array_index];
+ &clock_info_array->clockInfo[clock_array_index * clock_info_array->ucEntrySize];
valid = radeon_atombios_parse_pplib_clock_info(rdev,
state_index, mode_index,
clock_info);
@@ -2624,6 +2673,7 @@ union set_voltage {
struct _SET_VOLTAGE_PS_ALLOCATION alloc;
struct _SET_VOLTAGE_PARAMETERS v1;
struct _SET_VOLTAGE_PARAMETERS_V2 v2;
+ struct _SET_VOLTAGE_PARAMETERS_V1_3 v3;
};
void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 voltage_type)
@@ -2650,6 +2700,11 @@ void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 v
args.v2.ucVoltageMode = SET_ASIC_VOLTAGE_MODE_SET_VOLTAGE;
args.v2.usVoltageLevel = cpu_to_le16(voltage_level);
break;
+ case 3:
+ args.v3.ucVoltageType = voltage_type;
+ args.v3.ucVoltageMode = ATOM_SET_VOLTAGE;
+ args.v3.usVoltageLevel = cpu_to_le16(voltage_level);
+ break;
default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
return;
@@ -2658,8 +2713,8 @@ void radeon_atom_set_voltage(struct radeon_device *rdev, u16 voltage_level, u8 v
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
}
-int radeon_atom_get_max_vddc(struct radeon_device *rdev,
- u16 *voltage)
+static int radeon_atom_get_max_vddc(struct radeon_device *rdev, u8 voltage_type,
+ u16 voltage_id, u16 *voltage)
{
union set_voltage args;
int index = GetIndexIntoMasterTable(COMMAND, SetVoltage);
@@ -2680,6 +2735,15 @@ int radeon_atom_get_max_vddc(struct radeon_device *rdev,
*voltage = le16_to_cpu(args.v2.usVoltageLevel);
break;
+ case 3:
+ args.v3.ucVoltageType = voltage_type;
+ args.v3.ucVoltageMode = ATOM_GET_VOLTAGE_LEVEL;
+ args.v3.usVoltageLevel = cpu_to_le16(voltage_id);
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+ *voltage = le16_to_cpu(args.v3.usVoltageLevel);
+ break;
default:
DRM_ERROR("Unknown table version %d, %d\n", frev, crev);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c
index 815f2341ab94..fef7b722b05d 100644
--- a/drivers/gpu/drm/radeon/radeon_benchmark.c
+++ b/drivers/gpu/drm/radeon/radeon_benchmark.c
@@ -43,17 +43,19 @@ static int radeon_benchmark_do_move(struct radeon_device *rdev, unsigned size,
start_jiffies = jiffies;
for (i = 0; i < n; i++) {
- r = radeon_fence_create(rdev, &fence, RADEON_RING_TYPE_GFX_INDEX);
- if (r)
- return r;
-
switch (flag) {
case RADEON_BENCHMARK_COPY_DMA:
+ r = radeon_fence_create(rdev, &fence, radeon_copy_dma_ring_index(rdev));
+ if (r)
+ return r;
r = radeon_copy_dma(rdev, saddr, daddr,
size / RADEON_GPU_PAGE_SIZE,
fence);
break;
case RADEON_BENCHMARK_COPY_BLIT:
+ r = radeon_fence_create(rdev, &fence, radeon_copy_blit_ring_index(rdev));
+ if (r)
+ return r;
r = radeon_copy_blit(rdev, saddr, daddr,
size / RADEON_GPU_PAGE_SIZE,
fence);
@@ -129,7 +131,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,
/* r100 doesn't have dma engine so skip the test */
/* also, VRAM-to-VRAM test doesn't make much sense for DMA */
/* skip it as well if domains are the same */
- if ((rdev->asic->copy_dma) && (sdomain != ddomain)) {
+ if ((rdev->asic->copy.dma) && (sdomain != ddomain)) {
time = radeon_benchmark_do_move(rdev, size, saddr, daddr,
RADEON_BENCHMARK_COPY_DMA, n);
if (time < 0)
@@ -208,22 +210,22 @@ void radeon_benchmark(struct radeon_device *rdev, int test_number)
break;
case 3:
/* GTT to VRAM, buffer size sweep, powers of 2 */
- for (i = 1; i <= 65536; i <<= 1)
- radeon_benchmark_move(rdev, i*1024,
+ for (i = 1; i <= 16384; i <<= 1)
+ radeon_benchmark_move(rdev, i * RADEON_GPU_PAGE_SIZE,
RADEON_GEM_DOMAIN_GTT,
RADEON_GEM_DOMAIN_VRAM);
break;
case 4:
/* VRAM to GTT, buffer size sweep, powers of 2 */
- for (i = 1; i <= 65536; i <<= 1)
- radeon_benchmark_move(rdev, i*1024,
+ for (i = 1; i <= 16384; i <<= 1)
+ radeon_benchmark_move(rdev, i * RADEON_GPU_PAGE_SIZE,
RADEON_GEM_DOMAIN_VRAM,
RADEON_GEM_DOMAIN_GTT);
break;
case 5:
/* VRAM to VRAM, buffer size sweep, powers of 2 */
- for (i = 1; i <= 65536; i <<= 1)
- radeon_benchmark_move(rdev, i*1024,
+ for (i = 1; i <= 16384; i <<= 1)
+ radeon_benchmark_move(rdev, i * RADEON_GPU_PAGE_SIZE,
RADEON_GEM_DOMAIN_VRAM,
RADEON_GEM_DOMAIN_VRAM);
break;
diff --git a/drivers/gpu/drm/radeon/radeon_blit_common.h b/drivers/gpu/drm/radeon/radeon_blit_common.h
new file mode 100644
index 000000000000..4ecbe72c9d2d
--- /dev/null
+++ b/drivers/gpu/drm/radeon/radeon_blit_common.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2009 Advanced Micro Devices, Inc.
+ * Copyright 2009 Red Hat Inc.
+ * Copyright 2012 Alcatel-Lucent, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef __RADEON_BLIT_COMMON_H__
+
+#define DI_PT_RECTLIST 0x11
+#define DI_INDEX_SIZE_16_BIT 0x0
+#define DI_SRC_SEL_AUTO_INDEX 0x2
+
+#define FMT_8 0x1
+#define FMT_5_6_5 0x8
+#define FMT_8_8_8_8 0x1a
+#define COLOR_8 0x1
+#define COLOR_5_6_5 0x8
+#define COLOR_8_8_8_8 0x1a
+
+#define RECT_UNIT_H 32
+#define RECT_UNIT_W (RADEON_GPU_PAGE_SIZE / 4 / RECT_UNIT_H)
+
+#define __RADEON_BLIT_COMMON_H__
+#endif
diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
index b6e18c8db9f5..6ae0c75f016a 100644
--- a/drivers/gpu/drm/radeon/radeon_clocks.c
+++ b/drivers/gpu/drm/radeon/radeon_clocks.c
@@ -334,7 +334,7 @@ void radeon_get_clock_info(struct drm_device *dev)
if (!rdev->clock.default_sclk)
rdev->clock.default_sclk = radeon_get_engine_clock(rdev);
- if ((!rdev->clock.default_mclk) && rdev->asic->get_memory_clock)
+ if ((!rdev->clock.default_mclk) && rdev->asic->pm.get_memory_clock)
rdev->clock.default_mclk = radeon_get_memory_clock(rdev);
rdev->pm.current_sclk = rdev->clock.default_sclk;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 8c9a8115b632..bd05156edbdb 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -827,6 +827,27 @@ static int radeon_dvi_get_modes(struct drm_connector *connector)
return ret;
}
+static bool radeon_check_hpd_status_unchanged(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ enum drm_connector_status status;
+
+ /* We only trust HPD on R600 and newer ASICS. */
+ if (rdev->family >= CHIP_R600
+ && radeon_connector->hpd.hpd != RADEON_HPD_NONE) {
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
+ status = connector_status_connected;
+ else
+ status = connector_status_disconnected;
+ if (connector->status == status)
+ return true;
+ }
+
+ return false;
+}
+
/*
* DVI is complicated
* Do a DDC probe, if DDC probe passes, get the full EDID so
@@ -851,6 +872,9 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false;
+ if (!force && radeon_check_hpd_status_unchanged(connector))
+ return connector->status;
+
if (radeon_connector->ddc_bus)
dret = radeon_ddc_probe(radeon_connector);
if (dret) {
@@ -946,6 +970,10 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
encoder = obj_to_encoder(obj);
+ if (encoder->encoder_type != DRM_MODE_ENCODER_DAC ||
+ encoder->encoder_type != DRM_MODE_ENCODER_TVDAC)
+ continue;
+
encoder_funcs = encoder->helper_private;
if (encoder_funcs->detect) {
if (ret != connector_status_connected) {
@@ -1057,7 +1085,7 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector,
(radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B))
return MODE_OK;
else if (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_A) {
- if (0) {
+ if (ASIC_IS_DCE6(rdev)) {
/* HDMI 1.3+ supports max clock of 340 Mhz */
if (mode->clock > 340000)
return MODE_CLOCK_HIGH;
@@ -1250,6 +1278,9 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+ if (!force && radeon_check_hpd_status_unchanged(connector))
+ return connector->status;
+
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
index 72ae8266b8e9..0ebb7d4796fa 100644
--- a/drivers/gpu/drm/radeon/radeon_cp.c
+++ b/drivers/gpu/drm/radeon/radeon_cp.c
@@ -2115,6 +2115,8 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
break;
}
+ pci_set_master(dev->pdev);
+
if (drm_pci_device_is_agp(dev))
dev_priv->flags |= RADEON_IS_AGP;
else if (pci_is_pcie(dev->pdev))
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index e64bec488ed8..5cac83278338 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -85,12 +85,6 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
radeon_bo_list_add_object(&p->relocs[i].lobj,
&p->validated);
- if (p->relocs[i].robj->tbo.sync_obj && !(r->flags & RADEON_RELOC_DONT_SYNC)) {
- struct radeon_fence *fence = p->relocs[i].robj->tbo.sync_obj;
- if (!radeon_fence_signaled(fence)) {
- p->sync_to_ring[fence->ring] = true;
- }
- }
} else
p->relocs[i].handle = 0;
}
@@ -109,8 +103,13 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
p->ring = RADEON_RING_TYPE_GFX_INDEX;
break;
case RADEON_CS_RING_COMPUTE:
- /* for now */
- p->ring = RADEON_RING_TYPE_GFX_INDEX;
+ if (p->rdev->family >= CHIP_TAHITI) {
+ if (p->priority > 0)
+ p->ring = CAYMAN_RING_TYPE_CP1_INDEX;
+ else
+ p->ring = CAYMAN_RING_TYPE_CP2_INDEX;
+ } else
+ p->ring = RADEON_RING_TYPE_GFX_INDEX;
break;
}
return 0;
@@ -118,11 +117,24 @@ static int radeon_cs_get_ring(struct radeon_cs_parser *p, u32 ring, s32 priority
static int radeon_cs_sync_rings(struct radeon_cs_parser *p)
{
+ bool sync_to_ring[RADEON_NUM_RINGS] = { };
int i, r;
+ for (i = 0; i < p->nrelocs; i++) {
+ if (!p->relocs[i].robj || !p->relocs[i].robj->tbo.sync_obj)
+ continue;
+
+ if (!(p->relocs[i].flags & RADEON_RELOC_DONT_SYNC)) {
+ struct radeon_fence *fence = p->relocs[i].robj->tbo.sync_obj;
+ if (!radeon_fence_signaled(fence)) {
+ sync_to_ring[fence->ring] = true;
+ }
+ }
+ }
+
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
/* no need to sync to our own or unused rings */
- if (i == p->ring || !p->sync_to_ring[i] || !p->rdev->ring[i].ready)
+ if (i == p->ring || !sync_to_ring[i] || !p->rdev->ring[i].ready)
continue;
if (!p->ib->fence->semaphore) {
@@ -163,6 +175,7 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
p->chunk_ib_idx = -1;
p->chunk_relocs_idx = -1;
p->chunk_flags_idx = -1;
+ p->chunk_const_ib_idx = -1;
p->chunks_array = kcalloc(cs->num_chunks, sizeof(uint64_t), GFP_KERNEL);
if (p->chunks_array == NULL) {
return -ENOMEM;
@@ -201,6 +214,12 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
if (p->chunks[i].length_dw == 0)
return -EINVAL;
}
+ if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_CONST_IB) {
+ p->chunk_const_ib_idx = i;
+ /* zero length CONST IB isn't useful */
+ if (p->chunks[i].length_dw == 0)
+ return -EINVAL;
+ }
if (p->chunks[i].chunk_id == RADEON_CHUNK_ID_FLAGS) {
p->chunk_flags_idx = i;
/* zero length flags aren't useful */
@@ -236,21 +255,19 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
if ((p->cs_flags & RADEON_CS_USE_VM) &&
!p->rdev->vm_manager.enabled) {
DRM_ERROR("VM not active on asic!\n");
- if (p->chunk_relocs_idx != -1)
- kfree(p->chunks[p->chunk_relocs_idx].kdata);
- if (p->chunk_flags_idx != -1)
- kfree(p->chunks[p->chunk_flags_idx].kdata);
return -EINVAL;
}
- if (radeon_cs_get_ring(p, ring, priority)) {
- if (p->chunk_relocs_idx != -1)
- kfree(p->chunks[p->chunk_relocs_idx].kdata);
- if (p->chunk_flags_idx != -1)
- kfree(p->chunks[p->chunk_flags_idx].kdata);
+ /* we only support VM on SI+ */
+ if ((p->rdev->family >= CHIP_TAHITI) &&
+ ((p->cs_flags & RADEON_CS_USE_VM) == 0)) {
+ DRM_ERROR("VM required on SI+!\n");
return -EINVAL;
}
+ if (radeon_cs_get_ring(p, ring, priority))
+ return -EINVAL;
+
/* deal with non-vm */
if ((p->chunk_ib_idx != -1) &&
@@ -264,11 +281,8 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL ||
- p->chunks[p->chunk_ib_idx].kpage[1] == NULL) {
- kfree(p->chunks[p->chunk_ib_idx].kpage[0]);
- kfree(p->chunks[p->chunk_ib_idx].kpage[1]);
+ p->chunks[p->chunk_ib_idx].kpage[1] == NULL)
return -ENOMEM;
- }
p->chunks[p->chunk_ib_idx].kpage_idx[0] = -1;
p->chunks[p->chunk_ib_idx].kpage_idx[1] = -1;
p->chunks[p->chunk_ib_idx].last_copied_page = -1;
@@ -341,7 +355,7 @@ static int radeon_cs_ib_chunk(struct radeon_device *rdev,
return r;
}
parser->ib->length_dw = ib_chunk->length_dw;
- r = radeon_cs_parse(parser);
+ r = radeon_cs_parse(rdev, parser->ring, parser);
if (r || parser->parser_error) {
DRM_ERROR("Invalid command stream !\n");
return r;
@@ -394,6 +408,32 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
if ((parser->cs_flags & RADEON_CS_USE_VM) == 0)
return 0;
+ if ((rdev->family >= CHIP_TAHITI) &&
+ (parser->chunk_const_ib_idx != -1)) {
+ ib_chunk = &parser->chunks[parser->chunk_const_ib_idx];
+ if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
+ DRM_ERROR("cs IB CONST too big: %d\n", ib_chunk->length_dw);
+ return -EINVAL;
+ }
+ r = radeon_ib_get(rdev, parser->ring, &parser->const_ib,
+ ib_chunk->length_dw * 4);
+ if (r) {
+ DRM_ERROR("Failed to get const ib !\n");
+ return r;
+ }
+ parser->const_ib->is_const_ib = true;
+ parser->const_ib->length_dw = ib_chunk->length_dw;
+ /* Copy the packet into the IB */
+ if (DRM_COPY_FROM_USER(parser->const_ib->ptr, ib_chunk->user_ptr,
+ ib_chunk->length_dw * 4)) {
+ return -EFAULT;
+ }
+ r = radeon_ring_ib_parse(rdev, parser->ring, parser->const_ib);
+ if (r) {
+ return r;
+ }
+ }
+
ib_chunk = &parser->chunks[parser->chunk_ib_idx];
if (ib_chunk->length_dw > RADEON_IB_VM_MAX_SIZE) {
DRM_ERROR("cs IB too big: %d\n", ib_chunk->length_dw);
@@ -429,11 +469,25 @@ static int radeon_cs_ib_vm_chunk(struct radeon_device *rdev,
if (r) {
DRM_ERROR("Failed to synchronize rings !\n");
}
+
+ if ((rdev->family >= CHIP_TAHITI) &&
+ (parser->chunk_const_ib_idx != -1)) {
+ parser->const_ib->vm_id = vm->id;
+ /* ib pool is bind at 0 in virtual address space to gpu_addr is the
+ * offset inside the pool bo
+ */
+ parser->const_ib->gpu_addr = parser->const_ib->sa_bo.offset;
+ r = radeon_ib_schedule(rdev, parser->const_ib);
+ if (r)
+ goto out;
+ }
+
parser->ib->vm_id = vm->id;
/* ib pool is bind at 0 in virtual address space to gpu_addr is the
* offset inside the pool bo
*/
parser->ib->gpu_addr = parser->ib->sa_bo.offset;
+ parser->ib->is_const_ib = false;
r = radeon_ib_schedule(rdev, parser->ib);
out:
if (!r) {
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index fde25c0d65a0..42acc6449dd6 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -151,7 +151,9 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
uint32_t height)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_device *rdev = crtc->dev->dev_private;
struct drm_gem_object *obj;
+ struct radeon_bo *robj;
uint64_t gpu_addr;
int ret;
@@ -173,7 +175,15 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
return -ENOENT;
}
- ret = radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
+ robj = gem_to_radeon_bo(obj);
+ ret = radeon_bo_reserve(robj, false);
+ if (unlikely(ret != 0))
+ goto fail;
+ /* Only 27 bit offset for legacy cursor */
+ ret = radeon_bo_pin_restricted(robj, RADEON_GEM_DOMAIN_VRAM,
+ ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
+ &gpu_addr);
+ radeon_bo_unreserve(robj);
if (ret)
goto fail;
@@ -181,14 +191,18 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
radeon_crtc->cursor_height = height;
radeon_lock_cursor(crtc, true);
- /* XXX only 27 bit offset for legacy cursor */
radeon_set_cursor(crtc, obj, gpu_addr);
radeon_show_cursor(crtc);
radeon_lock_cursor(crtc, false);
unpin:
if (radeon_crtc->cursor_bo) {
- radeon_gem_object_unpin(radeon_crtc->cursor_bo);
+ robj = gem_to_radeon_bo(radeon_crtc->cursor_bo);
+ ret = radeon_bo_reserve(robj, false);
+ if (likely(ret == 0)) {
+ radeon_bo_unpin(robj);
+ radeon_bo_unreserve(robj);
+ }
drm_gem_object_unreference_unlocked(radeon_crtc->cursor_bo);
}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 49f7cb7e226b..ea7df16e2f84 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -89,6 +89,10 @@ static const char radeon_family_name[][16] = {
"TURKS",
"CAICOS",
"CAYMAN",
+ "ARUBA",
+ "TAHITI",
+ "PITCAIRN",
+ "VERDE",
"LAST",
};
@@ -964,7 +968,7 @@ int radeon_resume_kms(struct drm_device *dev)
/* init dig PHYs, disp eng pll */
if (rdev->is_atom_bios) {
radeon_atom_encoder_init(rdev);
- radeon_atom_dcpll_init(rdev);
+ radeon_atom_disp_eng_pll_init(rdev);
}
/* reset hpd state */
radeon_hpd_init(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 3d314338d843..8086c96e0b06 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -303,8 +303,17 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id)
if (update_pending &&
(DRM_SCANOUTPOS_VALID & radeon_get_crtc_scanoutpos(rdev->ddev, crtc_id,
&vpos, &hpos)) &&
- (vpos >=0) &&
- (vpos < (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100)) {
+ ((vpos >= (99 * rdev->mode_info.crtcs[crtc_id]->base.hwmode.crtc_vdisplay)/100) ||
+ (vpos < 0 && !ASIC_IS_AVIVO(rdev)))) {
+ /* crtc didn't flip in this target vblank interval,
+ * but flip is pending in crtc. Based on the current
+ * scanout position we know that the current frame is
+ * (nearly) complete and the flip will (likely)
+ * complete before the start of the next frame.
+ */
+ update_pending = 0;
+ }
+ if (update_pending) {
/* crtc didn't flip in this target vblank interval,
* but flip is pending in crtc. It will complete it
* in next vblank interval, so complete the flip at
@@ -393,7 +402,9 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
DRM_ERROR("failed to reserve new rbo buffer before flip\n");
goto pflip_cleanup;
}
- r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+ /* Only 27 bit offset for legacy CRTC */
+ r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
+ ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base);
if (unlikely(r != 0)) {
radeon_bo_unreserve(rbo);
r = -EINVAL;
@@ -1136,11 +1147,6 @@ static const struct drm_mode_config_funcs radeon_mode_funcs = {
.output_poll_changed = radeon_output_poll_changed
};
-struct drm_prop_enum_list {
- int type;
- char *name;
-};
-
static struct drm_prop_enum_list radeon_tmds_pll_enum_list[] =
{ { 0, "driver" },
{ 1, "bios" },
@@ -1165,86 +1171,53 @@ static struct drm_prop_enum_list radeon_underscan_enum_list[] =
static int radeon_modeset_create_props(struct radeon_device *rdev)
{
- int i, sz;
+ int sz;
if (rdev->is_atom_bios) {
rdev->mode_info.coherent_mode_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_RANGE,
- "coherent", 2);
+ drm_property_create_range(rdev->ddev, 0 , "coherent", 0, 1);
if (!rdev->mode_info.coherent_mode_property)
return -ENOMEM;
-
- rdev->mode_info.coherent_mode_property->values[0] = 0;
- rdev->mode_info.coherent_mode_property->values[1] = 1;
}
if (!ASIC_IS_AVIVO(rdev)) {
sz = ARRAY_SIZE(radeon_tmds_pll_enum_list);
rdev->mode_info.tmds_pll_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_ENUM,
- "tmds_pll", sz);
- for (i = 0; i < sz; i++) {
- drm_property_add_enum(rdev->mode_info.tmds_pll_property,
- i,
- radeon_tmds_pll_enum_list[i].type,
- radeon_tmds_pll_enum_list[i].name);
- }
+ drm_property_create_enum(rdev->ddev, 0,
+ "tmds_pll",
+ radeon_tmds_pll_enum_list, sz);
}
rdev->mode_info.load_detect_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_RANGE,
- "load detection", 2);
+ drm_property_create_range(rdev->ddev, 0, "load detection", 0, 1);
if (!rdev->mode_info.load_detect_property)
return -ENOMEM;
- rdev->mode_info.load_detect_property->values[0] = 0;
- rdev->mode_info.load_detect_property->values[1] = 1;
drm_mode_create_scaling_mode_property(rdev->ddev);
sz = ARRAY_SIZE(radeon_tv_std_enum_list);
rdev->mode_info.tv_std_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_ENUM,
- "tv standard", sz);
- for (i = 0; i < sz; i++) {
- drm_property_add_enum(rdev->mode_info.tv_std_property,
- i,
- radeon_tv_std_enum_list[i].type,
- radeon_tv_std_enum_list[i].name);
- }
+ drm_property_create_enum(rdev->ddev, 0,
+ "tv standard",
+ radeon_tv_std_enum_list, sz);
sz = ARRAY_SIZE(radeon_underscan_enum_list);
rdev->mode_info.underscan_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_ENUM,
- "underscan", sz);
- for (i = 0; i < sz; i++) {
- drm_property_add_enum(rdev->mode_info.underscan_property,
- i,
- radeon_underscan_enum_list[i].type,
- radeon_underscan_enum_list[i].name);
- }
+ drm_property_create_enum(rdev->ddev, 0,
+ "underscan",
+ radeon_underscan_enum_list, sz);
rdev->mode_info.underscan_hborder_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_RANGE,
- "underscan hborder", 2);
+ drm_property_create_range(rdev->ddev, 0,
+ "underscan hborder", 0, 128);
if (!rdev->mode_info.underscan_hborder_property)
return -ENOMEM;
- rdev->mode_info.underscan_hborder_property->values[0] = 0;
- rdev->mode_info.underscan_hborder_property->values[1] = 128;
rdev->mode_info.underscan_vborder_property =
- drm_property_create(rdev->ddev,
- DRM_MODE_PROP_RANGE,
- "underscan vborder", 2);
+ drm_property_create_range(rdev->ddev, 0,
+ "underscan vborder", 0, 128);
if (!rdev->mode_info.underscan_vborder_property)
return -ENOMEM;
- rdev->mode_info.underscan_vborder_property->values[0] = 0;
- rdev->mode_info.underscan_vborder_property->values[1] = 128;
return 0;
}
@@ -1290,6 +1263,9 @@ int radeon_modeset_init(struct radeon_device *rdev)
rdev->ddev->mode_config.max_height = 4096;
}
+ rdev->ddev->mode_config.preferred_depth = 24;
+ rdev->ddev->mode_config.prefer_shadow = 1;
+
rdev->ddev->mode_config.fb_base = rdev->mc.aper_base;
ret = radeon_modeset_create_props(rdev);
@@ -1320,7 +1296,7 @@ int radeon_modeset_init(struct radeon_device *rdev)
/* init dig PHYs, disp eng pll */
if (rdev->is_atom_bios) {
radeon_atom_encoder_init(rdev);
- radeon_atom_dcpll_init(rdev);
+ radeon_atom_disp_eng_pll_init(rdev);
}
/* initialize hpd */
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 8032f1fedb11..ef7bb3f6ecae 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -54,10 +54,12 @@
* 2.10.0 - fusion 2D tiling
* 2.11.0 - backend map, initial compute support for the CS checker
* 2.12.0 - RADEON_CS_KEEP_TILING_FLAGS
- * 2.13.0 - virtual memory support
+ * 2.13.0 - virtual memory support, streamout
+ * 2.14.0 - add evergreen tiling informations
+ * 2.15.0 - add max_pipes query
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 13
+#define KMS_DRIVER_MINOR 15
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index 26e92708d114..74670696277d 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -307,6 +307,8 @@ void radeon_panel_mode_fixup(struct drm_encoder *encoder,
bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
u32 pixel_clock)
{
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *dig_connector;
@@ -324,7 +326,7 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
case DRM_MODE_CONNECTOR_HDMIB:
if (radeon_connector->use_digital) {
/* HDMI 1.3 supports up to 340 Mhz over single link */
- if (0 && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
if (pixel_clock > 340000)
return true;
else
@@ -346,7 +348,7 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
return false;
else {
/* HDMI 1.3 supports up to 340 Mhz over single link */
- if (0 && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
if (pixel_clock > 340000)
return true;
else
diff --git a/drivers/gpu/drm/radeon/radeon_family.h b/drivers/gpu/drm/radeon/radeon_family.h
index ec2f1ea84f81..d1fafeabea09 100644
--- a/drivers/gpu/drm/radeon/radeon_family.h
+++ b/drivers/gpu/drm/radeon/radeon_family.h
@@ -87,6 +87,10 @@ enum radeon_family {
CHIP_TURKS,
CHIP_CAICOS,
CHIP_CAYMAN,
+ CHIP_ARUBA,
+ CHIP_TAHITI,
+ CHIP_PITCAIRN,
+ CHIP_VERDE,
CHIP_LAST,
};
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 195471cf65d3..5906914a78bc 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -164,7 +164,10 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
ret = radeon_bo_reserve(rbo, false);
if (unlikely(ret != 0))
goto out_unref;
- ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, NULL);
+ /* Only 27 bit offset for legacy CRTC */
+ ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
+ ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
+ NULL);
if (ret) {
radeon_bo_unreserve(rbo);
goto out_unref;
@@ -263,11 +266,7 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
info->apertures->ranges[0].base = rdev->ddev->mode_config.fb_base;
info->apertures->ranges[0].size = rdev->mc.aper_size;
- info->pixmap.size = 64*1024;
- info->pixmap.buf_align = 8;
- info->pixmap.access_align = 32;
- info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->pixmap.scan_align = 1;
+ /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
if (info->screen_base == NULL) {
ret = -ENOSPC;
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 7337850af2fa..c7008b5210f7 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -75,32 +75,6 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
return 0;
}
-int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
- uint64_t *gpu_addr)
-{
- struct radeon_bo *robj = gem_to_radeon_bo(obj);
- int r;
-
- r = radeon_bo_reserve(robj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(robj, pin_domain, gpu_addr);
- radeon_bo_unreserve(robj);
- return r;
-}
-
-void radeon_gem_object_unpin(struct drm_gem_object *obj)
-{
- struct radeon_bo *robj = gem_to_radeon_bo(obj);
- int r;
-
- r = radeon_bo_reserve(robj, false);
- if (likely(r == 0)) {
- radeon_bo_unpin(robj);
- radeon_bo_unreserve(robj);
- }
-}
-
int radeon_gem_set_domain(struct drm_gem_object *gobj,
uint32_t rdomain, uint32_t wdomain)
{
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 98a8ad680109..85bcfc8923a7 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -26,10 +26,15 @@
#include <linux/export.h>
#include "drmP.h"
+#include "drm_edid.h"
#include "radeon_drm.h"
#include "radeon.h"
#include "atom.h"
+extern int radeon_atom_hw_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num);
+extern u32 radeon_atom_hw_i2c_func(struct i2c_adapter *adap);
+
/**
* radeon_ddc_probe
*
@@ -41,13 +46,13 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
int ret;
struct i2c_msg msgs[] = {
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = &out,
},
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = 8,
.buf = buf,
@@ -882,6 +887,11 @@ static const struct i2c_algorithm radeon_i2c_algo = {
.functionality = radeon_hw_i2c_func,
};
+static const struct i2c_algorithm radeon_atom_i2c_algo = {
+ .master_xfer = radeon_atom_hw_i2c_xfer,
+ .functionality = radeon_atom_hw_i2c_func,
+};
+
struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
struct radeon_i2c_bus_rec *rec,
const char *name)
@@ -914,6 +924,18 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
DRM_ERROR("Failed to register hw i2c %s\n", name);
goto out_free;
}
+ } else if (rec->hw_capable &&
+ radeon_hw_i2c &&
+ ASIC_IS_DCE3(rdev)) {
+ /* hw i2c using atom */
+ snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
+ "Radeon i2c hw bus %s", name);
+ i2c->adapter.algo = &radeon_atom_i2c_algo;
+ ret = i2c_add_adapter(&i2c->adapter);
+ if (ret) {
+ DRM_ERROR("Failed to register hw i2c %s\n", name);
+ goto out_free;
+ }
} else {
/* set the radeon bit adapter */
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
@@ -925,10 +947,8 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
i2c->algo.bit.setscl = set_clock;
i2c->algo.bit.getsda = get_data;
i2c->algo.bit.getscl = get_clock;
- i2c->algo.bit.udelay = 20;
- /* vesa says 2.2 ms is enough, 1 jiffy doesn't seem to always
- * make this, 2 jiffies is a lot more reliable */
- i2c->algo.bit.timeout = 2;
+ i2c->algo.bit.udelay = 10;
+ i2c->algo.bit.timeout = usecs_to_jiffies(2200); /* from VESA */
i2c->algo.bit.data = i2c;
ret = i2c_bit_add_bus(&i2c->adapter);
if (ret) {
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index d3352889a870..3c2628b14d56 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -57,6 +57,8 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
}
dev->dev_private = (void *)rdev;
+ pci_set_master(dev->pdev);
+
/* update BUS flag */
if (drm_pci_device_is_agp(dev)) {
flags |= RADEON_IS_AGP;
@@ -169,7 +171,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
value = rdev->accel_working;
break;
case RADEON_INFO_TILING_CONFIG:
- if (rdev->family >= CHIP_CAYMAN)
+ if (rdev->family >= CHIP_TAHITI)
+ value = rdev->config.si.tile_config;
+ else if (rdev->family >= CHIP_CAYMAN)
value = rdev->config.cayman.tile_config;
else if (rdev->family >= CHIP_CEDAR)
value = rdev->config.evergreen.tile_config;
@@ -208,7 +212,10 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
value = rdev->clock.spll.reference_freq * 10;
break;
case RADEON_INFO_NUM_BACKENDS:
- if (rdev->family >= CHIP_CAYMAN)
+ if (rdev->family >= CHIP_TAHITI)
+ value = rdev->config.si.max_backends_per_se *
+ rdev->config.si.max_shader_engines;
+ else if (rdev->family >= CHIP_CAYMAN)
value = rdev->config.cayman.max_backends_per_se *
rdev->config.cayman.max_shader_engines;
else if (rdev->family >= CHIP_CEDAR)
@@ -222,7 +229,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
}
break;
case RADEON_INFO_NUM_TILE_PIPES:
- if (rdev->family >= CHIP_CAYMAN)
+ if (rdev->family >= CHIP_TAHITI)
+ value = rdev->config.si.max_tile_pipes;
+ else if (rdev->family >= CHIP_CAYMAN)
value = rdev->config.cayman.max_tile_pipes;
else if (rdev->family >= CHIP_CEDAR)
value = rdev->config.evergreen.max_tile_pipes;
@@ -238,7 +247,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
value = 1;
break;
case RADEON_INFO_BACKEND_MAP:
- if (rdev->family >= CHIP_CAYMAN)
+ if (rdev->family >= CHIP_TAHITI)
+ value = rdev->config.si.backend_map;
+ else if (rdev->family >= CHIP_CAYMAN)
value = rdev->config.cayman.backend_map;
else if (rdev->family >= CHIP_CEDAR)
value = rdev->config.evergreen.backend_map;
@@ -262,6 +273,21 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
return -EINVAL;
value = RADEON_IB_VM_MAX_SIZE;
break;
+ case RADEON_INFO_MAX_PIPES:
+ if (rdev->family >= CHIP_TAHITI)
+ value = rdev->config.si.max_pipes_per_simd;
+ else if (rdev->family >= CHIP_CAYMAN)
+ value = rdev->config.cayman.max_pipes_per_simd;
+ else if (rdev->family >= CHIP_CEDAR)
+ value = rdev->config.evergreen.max_pipes;
+ else if (rdev->family >= CHIP_RV770)
+ value = rdev->config.rv770.max_pipes;
+ else if (rdev->family >= CHIP_R600)
+ value = rdev->config.r600.max_pipes;
+ else {
+ return -EINVAL;
+ }
+ break;
default:
DRM_DEBUG_KMS("Invalid request %d\n", info->request);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 25a19c483075..210317c7045e 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -419,7 +419,9 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc,
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
return r;
- r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+ /* Only 27 bit offset for legacy CRTC */
+ r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27,
+ &base);
if (unlikely(r != 0)) {
radeon_bo_unreserve(rbo);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 8a85598fb242..f7eb5d8b9fd3 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -491,7 +491,7 @@ extern int radeon_dp_get_panel_mode(struct drm_encoder *encoder,
struct drm_connector *connector);
extern void atombios_dig_encoder_setup(struct drm_encoder *encoder, int action, int panel_mode);
extern void radeon_atom_encoder_init(struct radeon_device *rdev);
-extern void radeon_atom_dcpll_init(struct radeon_device *rdev);
+extern void radeon_atom_disp_eng_pll_init(struct radeon_device *rdev);
extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
int action, uint8_t lane_num,
uint8_t lane_set);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index d45df1763598..6f70158d34e4 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -224,7 +224,8 @@ void radeon_bo_unref(struct radeon_bo **bo)
*bo = NULL;
}
-int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
+int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
+ u64 *gpu_addr)
{
int r, i;
@@ -232,6 +233,17 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
bo->pin_count++;
if (gpu_addr)
*gpu_addr = radeon_bo_gpu_offset(bo);
+
+ 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);
@@ -239,6 +251,15 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
/* force to pin into visible video ram */
bo->placement.lpfn = bo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
}
+ if (max_offset) {
+ u64 lpfn = max_offset >> PAGE_SHIFT;
+
+ if (!bo->placement.lpfn)
+ bo->placement.lpfn = bo->rdev->mc.gtt_size >> PAGE_SHIFT;
+
+ if (lpfn < bo->placement.lpfn)
+ bo->placement.lpfn = lpfn;
+ }
for (i = 0; i < bo->placement.num_placement; i++)
bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false, false);
@@ -252,6 +273,11 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
return r;
}
+int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
+{
+ return radeon_bo_pin_restricted(bo, domain, 0, gpu_addr);
+}
+
int radeon_bo_unpin(struct radeon_bo *bo)
{
int r, i;
@@ -445,8 +471,54 @@ static void radeon_bo_clear_surface_reg(struct radeon_bo *bo)
int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
uint32_t tiling_flags, uint32_t pitch)
{
+ struct radeon_device *rdev = bo->rdev;
int r;
+ if (rdev->family >= CHIP_CEDAR) {
+ unsigned bankw, bankh, mtaspect, tilesplit, stilesplit;
+
+ bankw = (tiling_flags >> RADEON_TILING_EG_BANKW_SHIFT) & RADEON_TILING_EG_BANKW_MASK;
+ bankh = (tiling_flags >> RADEON_TILING_EG_BANKH_SHIFT) & RADEON_TILING_EG_BANKH_MASK;
+ mtaspect = (tiling_flags >> RADEON_TILING_EG_MACRO_TILE_ASPECT_SHIFT) & RADEON_TILING_EG_MACRO_TILE_ASPECT_MASK;
+ tilesplit = (tiling_flags >> RADEON_TILING_EG_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_TILE_SPLIT_MASK;
+ stilesplit = (tiling_flags >> RADEON_TILING_EG_STENCIL_TILE_SPLIT_SHIFT) & RADEON_TILING_EG_STENCIL_TILE_SPLIT_MASK;
+ switch (bankw) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (bankh) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -EINVAL;
+ }
+ switch (mtaspect) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (tilesplit > 6) {
+ return -EINVAL;
+ }
+ if (stilesplit > 6) {
+ return -EINVAL;
+ }
+ }
r = radeon_bo_reserve(bo, false);
if (unlikely(r != 0))
return r;
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index cde430308870..f9104be88d7c 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -118,6 +118,8 @@ extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr);
extern void radeon_bo_kunmap(struct radeon_bo *bo);
extern void radeon_bo_unref(struct radeon_bo **bo);
extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr);
+extern int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain,
+ u64 max_offset, u64 *gpu_addr);
extern int radeon_bo_unpin(struct radeon_bo *bo);
extern int radeon_bo_evict_vram(struct radeon_device *rdev);
extern void radeon_bo_force_delete(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
index 095148e29a1f..caa55d68f319 100644
--- a/drivers/gpu/drm/radeon/radeon_pm.c
+++ b/drivers/gpu/drm/radeon/radeon_pm.c
@@ -221,7 +221,7 @@ static void radeon_set_power_state(struct radeon_device *rdev)
}
/* set memory clock */
- if (rdev->asic->set_memory_clock && (mclk != rdev->pm.current_mclk)) {
+ if (rdev->asic->pm.set_memory_clock && (mclk != rdev->pm.current_mclk)) {
radeon_pm_debug_check_in_vbl(rdev, false);
radeon_set_memory_clock(rdev, mclk);
radeon_pm_debug_check_in_vbl(rdev, true);
@@ -474,6 +474,9 @@ static ssize_t radeon_hwmon_show_temp(struct device *dev,
case THERMAL_TYPE_SUMO:
temp = sumo_get_temp(rdev);
break;
+ case THERMAL_TYPE_SI:
+ temp = si_get_temp(rdev);
+ break;
default:
temp = 0;
break;
@@ -514,6 +517,10 @@ static int radeon_hwmon_init(struct radeon_device *rdev)
case THERMAL_TYPE_EVERGREEN:
case THERMAL_TYPE_NI:
case THERMAL_TYPE_SUMO:
+ case THERMAL_TYPE_SI:
+ /* No support for TN yet */
+ if (rdev->family == CHIP_ARUBA)
+ return err;
rdev->pm.int_hwmon_dev = hwmon_device_register(rdev->dev);
if (IS_ERR(rdev->pm.int_hwmon_dev)) {
err = PTR_ERR(rdev->pm.int_hwmon_dev);
@@ -863,11 +870,11 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
seq_printf(m, "default engine clock: %u0 kHz\n", rdev->pm.default_sclk);
seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
seq_printf(m, "default memory clock: %u0 kHz\n", rdev->pm.default_mclk);
- if (rdev->asic->get_memory_clock)
+ if (rdev->asic->pm.get_memory_clock)
seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
if (rdev->pm.current_vddc)
seq_printf(m, "voltage: %u mV\n", rdev->pm.current_vddc);
- if (rdev->asic->get_pcie_lanes)
+ if (rdev->asic->pm.get_pcie_lanes)
seq_printf(m, "PCIE lanes: %d\n", radeon_get_pcie_lanes(rdev));
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
index b4ce86455707..5d8f735d6aaf 100644
--- a/drivers/gpu/drm/radeon/radeon_reg.h
+++ b/drivers/gpu/drm/radeon/radeon_reg.h
@@ -56,6 +56,7 @@
#include "r600_reg.h"
#include "evergreen_reg.h"
#include "ni_reg.h"
+#include "si_reg.h"
#define RADEON_MC_AGP_LOCATION 0x014c
#define RADEON_MC_AGP_START_MASK 0x0000FFFF
@@ -539,9 +540,11 @@
#define RADEON_CRTC2_PITCH 0x032c
#define RADEON_CRTC_STATUS 0x005c
+# define RADEON_CRTC_VBLANK_CUR (1 << 0)
# define RADEON_CRTC_VBLANK_SAVE (1 << 1)
# define RADEON_CRTC_VBLANK_SAVE_CLEAR (1 << 1)
#define RADEON_CRTC2_STATUS 0x03fc
+# define RADEON_CRTC2_VBLANK_CUR (1 << 0)
# define RADEON_CRTC2_VBLANK_SAVE (1 << 1)
# define RADEON_CRTC2_VBLANK_SAVE_CLEAR (1 << 1)
#define RADEON_CRTC_V_SYNC_STRT_WID 0x020c
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 92c9ea4751fb..cc33b3d7c33b 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -133,6 +133,7 @@ retry:
(*ib)->gpu_addr += (*ib)->sa_bo.offset;
(*ib)->fence = fence;
(*ib)->vm_id = 0;
+ (*ib)->is_const_ib = false;
/* ib are most likely to be allocated in a ring fashion
* thus rdev->ib_pool.head_id should be the id of the
* oldest ib
@@ -478,7 +479,9 @@ static struct drm_info_list radeon_debugfs_ring_info_list[] = {
static int radeon_debugfs_ib_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
- struct radeon_ib *ib = node->info_ent->data;
+ struct drm_device *dev = node->minor->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_ib *ib = &rdev->ib_pool.ibs[*((unsigned*)node->info_ent->data)];
unsigned i;
if (ib == NULL) {
@@ -495,6 +498,7 @@ static int radeon_debugfs_ib_info(struct seq_file *m, void *data)
static struct drm_info_list radeon_debugfs_ib_list[RADEON_IB_POOL_SIZE];
static char radeon_debugfs_ib_names[RADEON_IB_POOL_SIZE][32];
+static unsigned radeon_debugfs_ib_idx[RADEON_IB_POOL_SIZE];
#endif
int radeon_debugfs_ring_init(struct radeon_device *rdev)
@@ -517,10 +521,11 @@ int radeon_debugfs_ib_init(struct radeon_device *rdev)
for (i = 0; i < RADEON_IB_POOL_SIZE; i++) {
sprintf(radeon_debugfs_ib_names[i], "radeon_ib_%04u", i);
+ radeon_debugfs_ib_idx[i] = i;
radeon_debugfs_ib_list[i].name = radeon_debugfs_ib_names[i];
radeon_debugfs_ib_list[i].show = &radeon_debugfs_ib_info;
radeon_debugfs_ib_list[i].driver_features = 0;
- radeon_debugfs_ib_list[i].data = &rdev->ib_pool.ibs[i];
+ radeon_debugfs_ib_list[i].data = &radeon_debugfs_ib_idx[i];
}
return radeon_debugfs_add_files(rdev, radeon_debugfs_ib_list,
RADEON_IB_POOL_SIZE);
diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
index c421e77ace71..f493c6403af5 100644
--- a/drivers/gpu/drm/radeon/radeon_ttm.c
+++ b/drivers/gpu/drm/radeon/radeon_ttm.c
@@ -226,7 +226,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
int r, i;
rdev = radeon_get_rdev(bo->bdev);
- r = radeon_fence_create(rdev, &fence, rdev->copy_ring);
+ r = radeon_fence_create(rdev, &fence, radeon_copy_ring_index(rdev));
if (unlikely(r)) {
return r;
}
@@ -255,7 +255,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
DRM_ERROR("Unknown placement %d\n", old_mem->mem_type);
return -EINVAL;
}
- if (!rdev->ring[rdev->copy_ring].ready) {
+ if (!rdev->ring[radeon_copy_ring_index(rdev)].ready) {
DRM_ERROR("Trying to move memory with ring turned off.\n");
return -EINVAL;
}
@@ -266,7 +266,7 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
if (rdev->family >= CHIP_R600) {
for (i = 0; i < RADEON_NUM_RINGS; ++i) {
/* no need to sync to our own or unused rings */
- if (i == rdev->copy_ring || !rdev->ring[i].ready)
+ if (i == radeon_copy_ring_index(rdev) || !rdev->ring[i].ready)
continue;
if (!fence->semaphore) {
@@ -283,12 +283,12 @@ static int radeon_move_blit(struct ttm_buffer_object *bo,
radeon_semaphore_emit_signal(rdev, i, fence->semaphore);
radeon_ring_unlock_commit(rdev, &rdev->ring[i]);
- r = radeon_ring_lock(rdev, &rdev->ring[rdev->copy_ring], 3);
+ r = radeon_ring_lock(rdev, &rdev->ring[radeon_copy_ring_index(rdev)], 3);
/* FIXME: handle ring lock error */
if (r)
continue;
- radeon_semaphore_emit_wait(rdev, rdev->copy_ring, fence->semaphore);
- radeon_ring_unlock_commit(rdev, &rdev->ring[rdev->copy_ring]);
+ radeon_semaphore_emit_wait(rdev, radeon_copy_ring_index(rdev), fence->semaphore);
+ radeon_ring_unlock_commit(rdev, &rdev->ring[radeon_copy_ring_index(rdev)]);
}
}
@@ -410,7 +410,8 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
radeon_move_null(bo, new_mem);
return 0;
}
- if (!rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready || rdev->asic->copy == NULL) {
+ if (!rdev->ring[radeon_copy_ring_index(rdev)].ready ||
+ rdev->asic->copy.copy == NULL) {
/* use memcpy */
goto memcpy;
}
diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman
index 2316977eb924..0f656b111c15 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/cayman
+++ b/drivers/gpu/drm/radeon/reg_srcs/cayman
@@ -1,5 +1,8 @@
cayman 0x9400
0x0000802C GRBM_GFX_INDEX
+0x000084FC CP_STRMOUT_CNTL
+0x000085F0 CP_COHER_CNTL
+0x000085F4 CP_COHER_SIZE
0x000088B0 VGT_VTX_VECT_EJECT_REG
0x000088C4 VGT_CACHE_INVALIDATION
0x000088D4 VGT_GS_VERTEX_REUSE
@@ -77,7 +80,6 @@ cayman 0x9400
0x0002802C DB_DEPTH_CLEAR
0x00028030 PA_SC_SCREEN_SCISSOR_TL
0x00028034 PA_SC_SCREEN_SCISSOR_BR
-0x0002805C DB_DEPTH_SLICE
0x00028140 SQ_ALU_CONST_BUFFER_SIZE_PS_0
0x00028144 SQ_ALU_CONST_BUFFER_SIZE_PS_1
0x00028148 SQ_ALU_CONST_BUFFER_SIZE_PS_2
@@ -206,7 +208,6 @@ cayman 0x9400
0x00028344 PA_SC_VPORT_ZMAX_14
0x00028348 PA_SC_VPORT_ZMIN_15
0x0002834C PA_SC_VPORT_ZMAX_15
-0x00028350 SX_MISC
0x00028354 SX_SURFACE_SYNC
0x0002835C SX_SCATTER_EXPORT_SIZE
0x00028380 SQ_VTX_SEMANTIC_0
@@ -508,10 +509,16 @@ 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
+0x00028AD4 VGT_STRMOUT_VTX_STRIDE_0
+0x00028AE4 VGT_STRMOUT_VTX_STRIDE_1
+0x00028AF4 VGT_STRMOUT_VTX_STRIDE_2
+0x00028B04 VGT_STRMOUT_VTX_STRIDE_3
+0x00028B28 VGT_STRMOUT_DRAW_OPAQUE_OFFSET
+0x00028B2C VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
+0x00028B30 VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
0x00028B38 VGT_GS_MAX_VERT_OUT
0x00028B54 VGT_SHADER_STAGES_EN
0x00028B58 VGT_LS_HS_CONFIG
@@ -551,6 +558,18 @@ cayman 0x9400
0x00028C34 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y1_3
0x00028C38 PA_SC_AA_MASK_X0_Y0_X1_Y0
0x00028C3C PA_SC_AA_MASK_X0_Y1_X1_Y1
+0x00028C78 CB_COLOR0_DIM
+0x00028CB4 CB_COLOR1_DIM
+0x00028CF0 CB_COLOR2_DIM
+0x00028D2C CB_COLOR3_DIM
+0x00028D68 CB_COLOR4_DIM
+0x00028DA4 CB_COLOR5_DIM
+0x00028DE0 CB_COLOR6_DIM
+0x00028E1C CB_COLOR7_DIM
+0x00028E58 CB_COLOR8_DIM
+0x00028E74 CB_COLOR9_DIM
+0x00028E90 CB_COLOR10_DIM
+0x00028EAC CB_COLOR11_DIM
0x00028C8C CB_COLOR0_CLEAR_WORD0
0x00028C90 CB_COLOR0_CLEAR_WORD1
0x00028C94 CB_COLOR0_CLEAR_WORD2
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index 161737a28c23..b912a37689bf 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -4,6 +4,9 @@ evergreen 0x9400
0x00008044 WAIT_UNTIL_POLL_CNTL
0x00008048 WAIT_UNTIL_POLL_MASK
0x0000804c WAIT_UNTIL_POLL_REFDATA
+0x000084FC CP_STRMOUT_CNTL
+0x000085F0 CP_COHER_CNTL
+0x000085F4 CP_COHER_SIZE
0x000088B0 VGT_VTX_VECT_EJECT_REG
0x000088C4 VGT_CACHE_INVALIDATION
0x000088D4 VGT_GS_VERTEX_REUSE
@@ -93,7 +96,6 @@ evergreen 0x9400
0x0002802C DB_DEPTH_CLEAR
0x00028030 PA_SC_SCREEN_SCISSOR_TL
0x00028034 PA_SC_SCREEN_SCISSOR_BR
-0x0002805C DB_DEPTH_SLICE
0x00028140 SQ_ALU_CONST_BUFFER_SIZE_PS_0
0x00028144 SQ_ALU_CONST_BUFFER_SIZE_PS_1
0x00028148 SQ_ALU_CONST_BUFFER_SIZE_PS_2
@@ -222,7 +224,6 @@ evergreen 0x9400
0x00028344 PA_SC_VPORT_ZMAX_14
0x00028348 PA_SC_VPORT_ZMIN_15
0x0002834C PA_SC_VPORT_ZMAX_15
-0x00028350 SX_MISC
0x00028354 SX_SURFACE_SYNC
0x00028380 SQ_VTX_SEMANTIC_0
0x00028384 SQ_VTX_SEMANTIC_1
@@ -518,10 +519,16 @@ 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
+0x00028AD4 VGT_STRMOUT_VTX_STRIDE_0
+0x00028AE4 VGT_STRMOUT_VTX_STRIDE_1
+0x00028AF4 VGT_STRMOUT_VTX_STRIDE_2
+0x00028B04 VGT_STRMOUT_VTX_STRIDE_3
+0x00028B28 VGT_STRMOUT_DRAW_OPAQUE_OFFSET
+0x00028B2C VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
+0x00028B30 VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
0x00028B38 VGT_GS_MAX_VERT_OUT
0x00028B54 VGT_SHADER_STAGES_EN
0x00028B58 VGT_LS_HS_CONFIG
@@ -554,6 +561,18 @@ evergreen 0x9400
0x00028C34 PA_SC_AA_SAMPLE_LOCS_6
0x00028C38 PA_SC_AA_SAMPLE_LOCS_7
0x00028C3C PA_SC_AA_MASK
+0x00028C78 CB_COLOR0_DIM
+0x00028CB4 CB_COLOR1_DIM
+0x00028CF0 CB_COLOR2_DIM
+0x00028D2C CB_COLOR3_DIM
+0x00028D68 CB_COLOR4_DIM
+0x00028DA4 CB_COLOR5_DIM
+0x00028DE0 CB_COLOR6_DIM
+0x00028E1C CB_COLOR7_DIM
+0x00028E58 CB_COLOR8_DIM
+0x00028E74 CB_COLOR9_DIM
+0x00028E90 CB_COLOR10_DIM
+0x00028EAC CB_COLOR11_DIM
0x00028C8C CB_COLOR0_CLEAR_WORD0
0x00028C90 CB_COLOR0_CLEAR_WORD1
0x00028C94 CB_COLOR0_CLEAR_WORD2
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index 0380c5c15f80..5e659b034d9a 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -3,6 +3,9 @@ r600 0x9400
0x00028230 R7xx_PA_SC_EDGERULE
0x000286C8 R7xx_SPI_THREAD_GROUPING
0x00008D8C R7xx_SQ_DYN_GPR_CNTL_PS_FLUSH_REQ
+0x00008490 CP_STRMOUT_CNTL
+0x000085F0 CP_COHER_CNTL
+0x000085F4 CP_COHER_SIZE
0x000088C4 VGT_CACHE_INVALIDATION
0x00028A50 VGT_ENHANCE
0x000088CC VGT_ES_PER_GS
@@ -38,6 +41,13 @@ r600 0x9400
0x00028AB4 VGT_REUSE_OFF
0x00028AB8 VGT_VTX_CNT_EN
0x000088B0 VGT_VTX_VECT_EJECT_REG
+0x00028AD4 VGT_STRMOUT_VTX_STRIDE_0
+0x00028AE4 VGT_STRMOUT_VTX_STRIDE_1
+0x00028AF4 VGT_STRMOUT_VTX_STRIDE_2
+0x00028B04 VGT_STRMOUT_VTX_STRIDE_3
+0x00028B28 VGT_STRMOUT_DRAW_OPAQUE_OFFSET
+0x00028B2C VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
+0x00028B30 VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
0x00028810 PA_CL_CLIP_CNTL
0x00008A14 PA_CL_ENHANCE
0x00028C14 PA_CL_GB_HORZ_CLIP_ADJ
@@ -428,7 +438,7 @@ r600 0x9400
0x00028638 SPI_VS_OUT_ID_9
0x00028438 SX_ALPHA_REF
0x00028410 SX_ALPHA_TEST_CONTROL
-0x00028350 SX_MISC
+0x00028354 SX_SURFACE_SYNC
0x00009014 SX_MEMORY_EXPORT_SIZE
0x00009604 TC_INVALIDATE
0x00009400 TD_FILTER4
@@ -703,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
@@ -743,14 +752,6 @@ r600 0x9400
0x00028114 CB_COLOR5_MASK
0x00028118 CB_COLOR6_MASK
0x0002811C CB_COLOR7_MASK
-0x00028080 CB_COLOR0_VIEW
-0x00028084 CB_COLOR1_VIEW
-0x00028088 CB_COLOR2_VIEW
-0x0002808C CB_COLOR3_VIEW
-0x00028090 CB_COLOR4_VIEW
-0x00028094 CB_COLOR5_VIEW
-0x00028098 CB_COLOR6_VIEW
-0x0002809C CB_COLOR7_VIEW
0x00028808 CB_COLOR_CONTROL
0x0002842C CB_FOG_BLUE
0x00028428 CB_FOG_GREEN
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index 866a05be75f2..4cf381b3a6d8 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -430,7 +430,7 @@ static int rs400_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 4fc700684dcd..d25cf869d08d 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -46,6 +46,25 @@
void rs600_gpu_init(struct radeon_device *rdev);
int rs600_mc_wait_for_idle(struct radeon_device *rdev);
+void avivo_wait_for_vblank(struct radeon_device *rdev, int crtc)
+{
+ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc];
+ int i;
+
+ if (RREG32(AVIVO_D1CRTC_CONTROL + radeon_crtc->crtc_offset) & AVIVO_CRTC_EN) {
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (!(RREG32(AVIVO_D1CRTC_STATUS + radeon_crtc->crtc_offset) & AVIVO_D1CRTC_V_BLANK))
+ break;
+ udelay(1);
+ }
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(AVIVO_D1CRTC_STATUS + radeon_crtc->crtc_offset) & AVIVO_D1CRTC_V_BLANK)
+ break;
+ udelay(1);
+ }
+ }
+}
+
void rs600_pre_page_flip(struct radeon_device *rdev, int crtc)
{
/* enable the pflip int */
@@ -175,7 +194,7 @@ void rs600_pm_misc(struct radeon_device *rdev)
/* set pcie lanes */
if ((rdev->flags & RADEON_IS_PCIE) &&
!(rdev->flags & RADEON_IS_IGP) &&
- rdev->asic->set_pcie_lanes &&
+ rdev->asic->pm.set_pcie_lanes &&
(ps->pcie_lanes !=
rdev->pm.power_state[rdev->pm.current_power_state_index].pcie_lanes)) {
radeon_set_pcie_lanes(rdev,
@@ -864,7 +883,7 @@ static int rs600_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index f68dff2fadcb..f2c3b9d75f18 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -31,7 +31,7 @@
#include "atom.h"
#include "rs690d.h"
-static int rs690_mc_wait_for_idle(struct radeon_device *rdev)
+int rs690_mc_wait_for_idle(struct radeon_device *rdev)
{
unsigned i;
uint32_t tmp;
@@ -647,7 +647,7 @@ static int rs690_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index c520d06a930c..d8d78fe17946 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -53,9 +53,8 @@ void rv515_debugfs(struct radeon_device *rdev)
}
}
-void rv515_ring_start(struct radeon_device *rdev)
+void rv515_ring_start(struct radeon_device *rdev, struct radeon_ring *ring)
{
- struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
int r;
r = radeon_ring_lock(rdev, ring, 64);
@@ -413,7 +412,7 @@ static int rv515_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r100_ib_test(rdev);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "failed testing IB (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index c049c0c51841..c62ae4be3845 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -1074,7 +1074,7 @@ static int rv770_startup(struct radeon_device *rdev)
r = r600_blit_init(rdev);
if (r) {
r600_blit_fini(rdev);
- rdev->asic->copy = NULL;
+ rdev->asic->copy.copy = NULL;
dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
}
@@ -1114,7 +1114,7 @@ static int rv770_startup(struct radeon_device *rdev)
if (r)
return r;
- r = r600_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
if (r) {
dev_err(rdev->dev, "IB test failed (%d).\n", r);
rdev->accel_working = false;
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
new file mode 100644
index 000000000000..ac7a199ffece
--- /dev/null
+++ b/drivers/gpu/drm/radeon/si.c
@@ -0,0 +1,4128 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Alex Deucher
+ */
+#include <linux/firmware.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include "drmP.h"
+#include "radeon.h"
+#include "radeon_asic.h"
+#include "radeon_drm.h"
+#include "sid.h"
+#include "atom.h"
+#include "si_blit_shaders.h"
+
+#define SI_PFP_UCODE_SIZE 2144
+#define SI_PM4_UCODE_SIZE 2144
+#define SI_CE_UCODE_SIZE 2144
+#define SI_RLC_UCODE_SIZE 2048
+#define SI_MC_UCODE_SIZE 7769
+
+MODULE_FIRMWARE("radeon/TAHITI_pfp.bin");
+MODULE_FIRMWARE("radeon/TAHITI_me.bin");
+MODULE_FIRMWARE("radeon/TAHITI_ce.bin");
+MODULE_FIRMWARE("radeon/TAHITI_mc.bin");
+MODULE_FIRMWARE("radeon/TAHITI_rlc.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_pfp.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_me.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_ce.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_mc.bin");
+MODULE_FIRMWARE("radeon/PITCAIRN_rlc.bin");
+MODULE_FIRMWARE("radeon/VERDE_pfp.bin");
+MODULE_FIRMWARE("radeon/VERDE_me.bin");
+MODULE_FIRMWARE("radeon/VERDE_ce.bin");
+MODULE_FIRMWARE("radeon/VERDE_mc.bin");
+MODULE_FIRMWARE("radeon/VERDE_rlc.bin");
+
+extern int r600_ih_ring_alloc(struct radeon_device *rdev);
+extern void r600_ih_ring_fini(struct radeon_device *rdev);
+extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev);
+extern void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save);
+extern void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save);
+extern u32 evergreen_get_number_of_dram_channels(struct radeon_device *rdev);
+
+/* get temperature in millidegrees */
+int si_get_temp(struct radeon_device *rdev)
+{
+ u32 temp;
+ int actual_temp = 0;
+
+ temp = (RREG32(CG_MULT_THERMAL_STATUS) & CTF_TEMP_MASK) >>
+ CTF_TEMP_SHIFT;
+
+ if (temp & 0x200)
+ actual_temp = 255;
+ else
+ actual_temp = temp & 0x1ff;
+
+ actual_temp = (actual_temp * 1000);
+
+ return actual_temp;
+}
+
+#define TAHITI_IO_MC_REGS_SIZE 36
+
+static const u32 tahiti_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = {
+ {0x0000006f, 0x03044000},
+ {0x00000070, 0x0480c018},
+ {0x00000071, 0x00000040},
+ {0x00000072, 0x01000000},
+ {0x00000074, 0x000000ff},
+ {0x00000075, 0x00143400},
+ {0x00000076, 0x08ec0800},
+ {0x00000077, 0x040000cc},
+ {0x00000079, 0x00000000},
+ {0x0000007a, 0x21000409},
+ {0x0000007c, 0x00000000},
+ {0x0000007d, 0xe8000000},
+ {0x0000007e, 0x044408a8},
+ {0x0000007f, 0x00000003},
+ {0x00000080, 0x00000000},
+ {0x00000081, 0x01000000},
+ {0x00000082, 0x02000000},
+ {0x00000083, 0x00000000},
+ {0x00000084, 0xe3f3e4f4},
+ {0x00000085, 0x00052024},
+ {0x00000087, 0x00000000},
+ {0x00000088, 0x66036603},
+ {0x00000089, 0x01000000},
+ {0x0000008b, 0x1c0a0000},
+ {0x0000008c, 0xff010000},
+ {0x0000008e, 0xffffefff},
+ {0x0000008f, 0xfff3efff},
+ {0x00000090, 0xfff3efbf},
+ {0x00000094, 0x00101101},
+ {0x00000095, 0x00000fff},
+ {0x00000096, 0x00116fff},
+ {0x00000097, 0x60010000},
+ {0x00000098, 0x10010000},
+ {0x00000099, 0x00006000},
+ {0x0000009a, 0x00001000},
+ {0x0000009f, 0x00a77400}
+};
+
+static const u32 pitcairn_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = {
+ {0x0000006f, 0x03044000},
+ {0x00000070, 0x0480c018},
+ {0x00000071, 0x00000040},
+ {0x00000072, 0x01000000},
+ {0x00000074, 0x000000ff},
+ {0x00000075, 0x00143400},
+ {0x00000076, 0x08ec0800},
+ {0x00000077, 0x040000cc},
+ {0x00000079, 0x00000000},
+ {0x0000007a, 0x21000409},
+ {0x0000007c, 0x00000000},
+ {0x0000007d, 0xe8000000},
+ {0x0000007e, 0x044408a8},
+ {0x0000007f, 0x00000003},
+ {0x00000080, 0x00000000},
+ {0x00000081, 0x01000000},
+ {0x00000082, 0x02000000},
+ {0x00000083, 0x00000000},
+ {0x00000084, 0xe3f3e4f4},
+ {0x00000085, 0x00052024},
+ {0x00000087, 0x00000000},
+ {0x00000088, 0x66036603},
+ {0x00000089, 0x01000000},
+ {0x0000008b, 0x1c0a0000},
+ {0x0000008c, 0xff010000},
+ {0x0000008e, 0xffffefff},
+ {0x0000008f, 0xfff3efff},
+ {0x00000090, 0xfff3efbf},
+ {0x00000094, 0x00101101},
+ {0x00000095, 0x00000fff},
+ {0x00000096, 0x00116fff},
+ {0x00000097, 0x60010000},
+ {0x00000098, 0x10010000},
+ {0x00000099, 0x00006000},
+ {0x0000009a, 0x00001000},
+ {0x0000009f, 0x00a47400}
+};
+
+static const u32 verde_io_mc_regs[TAHITI_IO_MC_REGS_SIZE][2] = {
+ {0x0000006f, 0x03044000},
+ {0x00000070, 0x0480c018},
+ {0x00000071, 0x00000040},
+ {0x00000072, 0x01000000},
+ {0x00000074, 0x000000ff},
+ {0x00000075, 0x00143400},
+ {0x00000076, 0x08ec0800},
+ {0x00000077, 0x040000cc},
+ {0x00000079, 0x00000000},
+ {0x0000007a, 0x21000409},
+ {0x0000007c, 0x00000000},
+ {0x0000007d, 0xe8000000},
+ {0x0000007e, 0x044408a8},
+ {0x0000007f, 0x00000003},
+ {0x00000080, 0x00000000},
+ {0x00000081, 0x01000000},
+ {0x00000082, 0x02000000},
+ {0x00000083, 0x00000000},
+ {0x00000084, 0xe3f3e4f4},
+ {0x00000085, 0x00052024},
+ {0x00000087, 0x00000000},
+ {0x00000088, 0x66036603},
+ {0x00000089, 0x01000000},
+ {0x0000008b, 0x1c0a0000},
+ {0x0000008c, 0xff010000},
+ {0x0000008e, 0xffffefff},
+ {0x0000008f, 0xfff3efff},
+ {0x00000090, 0xfff3efbf},
+ {0x00000094, 0x00101101},
+ {0x00000095, 0x00000fff},
+ {0x00000096, 0x00116fff},
+ {0x00000097, 0x60010000},
+ {0x00000098, 0x10010000},
+ {0x00000099, 0x00006000},
+ {0x0000009a, 0x00001000},
+ {0x0000009f, 0x00a37400}
+};
+
+/* ucode loading */
+static int si_mc_load_microcode(struct radeon_device *rdev)
+{
+ const __be32 *fw_data;
+ u32 running, blackout = 0;
+ u32 *io_mc_regs;
+ int i, ucode_size, regs_size;
+
+ if (!rdev->mc_fw)
+ return -EINVAL;
+
+ switch (rdev->family) {
+ case CHIP_TAHITI:
+ io_mc_regs = (u32 *)&tahiti_io_mc_regs;
+ ucode_size = SI_MC_UCODE_SIZE;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_PITCAIRN:
+ io_mc_regs = (u32 *)&pitcairn_io_mc_regs;
+ ucode_size = SI_MC_UCODE_SIZE;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ case CHIP_VERDE:
+ default:
+ io_mc_regs = (u32 *)&verde_io_mc_regs;
+ ucode_size = SI_MC_UCODE_SIZE;
+ regs_size = TAHITI_IO_MC_REGS_SIZE;
+ break;
+ }
+
+ running = RREG32(MC_SEQ_SUP_CNTL) & RUN_MASK;
+
+ if (running == 0) {
+ if (running) {
+ blackout = RREG32(MC_SHARED_BLACKOUT_CNTL);
+ WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1);
+ }
+
+ /* reset the engine and set to writable */
+ WREG32(MC_SEQ_SUP_CNTL, 0x00000008);
+ WREG32(MC_SEQ_SUP_CNTL, 0x00000010);
+
+ /* load mc io regs */
+ for (i = 0; i < regs_size; i++) {
+ WREG32(MC_SEQ_IO_DEBUG_INDEX, io_mc_regs[(i << 1)]);
+ WREG32(MC_SEQ_IO_DEBUG_DATA, io_mc_regs[(i << 1) + 1]);
+ }
+ /* load the MC ucode */
+ fw_data = (const __be32 *)rdev->mc_fw->data;
+ for (i = 0; i < ucode_size; i++)
+ WREG32(MC_SEQ_SUP_PGM, be32_to_cpup(fw_data++));
+
+ /* put the engine back into the active state */
+ WREG32(MC_SEQ_SUP_CNTL, 0x00000008);
+ WREG32(MC_SEQ_SUP_CNTL, 0x00000004);
+ WREG32(MC_SEQ_SUP_CNTL, 0x00000001);
+
+ /* wait for training to complete */
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D0)
+ break;
+ udelay(1);
+ }
+ for (i = 0; i < rdev->usec_timeout; i++) {
+ if (RREG32(MC_SEQ_TRAIN_WAKEUP_CNTL) & TRAIN_DONE_D1)
+ break;
+ udelay(1);
+ }
+
+ if (running)
+ WREG32(MC_SHARED_BLACKOUT_CNTL, blackout);
+ }
+
+ return 0;
+}
+
+static int si_init_microcode(struct radeon_device *rdev)
+{
+ struct platform_device *pdev;
+ const char *chip_name;
+ const char *rlc_chip_name;
+ size_t pfp_req_size, me_req_size, ce_req_size, rlc_req_size, mc_req_size;
+ char fw_name[30];
+ int err;
+
+ DRM_DEBUG("\n");
+
+ pdev = platform_device_register_simple("radeon_cp", 0, NULL, 0);
+ err = IS_ERR(pdev);
+ if (err) {
+ printk(KERN_ERR "radeon_cp: Failed to register firmware\n");
+ return -EINVAL;
+ }
+
+ switch (rdev->family) {
+ case CHIP_TAHITI:
+ chip_name = "TAHITI";
+ rlc_chip_name = "TAHITI";
+ pfp_req_size = SI_PFP_UCODE_SIZE * 4;
+ me_req_size = SI_PM4_UCODE_SIZE * 4;
+ ce_req_size = SI_CE_UCODE_SIZE * 4;
+ rlc_req_size = SI_RLC_UCODE_SIZE * 4;
+ mc_req_size = SI_MC_UCODE_SIZE * 4;
+ break;
+ case CHIP_PITCAIRN:
+ chip_name = "PITCAIRN";
+ rlc_chip_name = "PITCAIRN";
+ pfp_req_size = SI_PFP_UCODE_SIZE * 4;
+ me_req_size = SI_PM4_UCODE_SIZE * 4;
+ ce_req_size = SI_CE_UCODE_SIZE * 4;
+ rlc_req_size = SI_RLC_UCODE_SIZE * 4;
+ mc_req_size = SI_MC_UCODE_SIZE * 4;
+ break;
+ case CHIP_VERDE:
+ chip_name = "VERDE";
+ rlc_chip_name = "VERDE";
+ pfp_req_size = SI_PFP_UCODE_SIZE * 4;
+ me_req_size = SI_PM4_UCODE_SIZE * 4;
+ ce_req_size = SI_CE_UCODE_SIZE * 4;
+ rlc_req_size = SI_RLC_UCODE_SIZE * 4;
+ mc_req_size = SI_MC_UCODE_SIZE * 4;
+ break;
+ default: BUG();
+ }
+
+ DRM_INFO("Loading %s Microcode\n", chip_name);
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+ err = request_firmware(&rdev->pfp_fw, fw_name, &pdev->dev);
+ if (err)
+ goto out;
+ if (rdev->pfp_fw->size != pfp_req_size) {
+ printk(KERN_ERR
+ "si_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->pfp_fw->size, fw_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_me.bin", chip_name);
+ err = request_firmware(&rdev->me_fw, fw_name, &pdev->dev);
+ if (err)
+ goto out;
+ if (rdev->me_fw->size != me_req_size) {
+ printk(KERN_ERR
+ "si_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->me_fw->size, fw_name);
+ err = -EINVAL;
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_ce.bin", chip_name);
+ err = request_firmware(&rdev->ce_fw, fw_name, &pdev->dev);
+ if (err)
+ goto out;
+ if (rdev->ce_fw->size != ce_req_size) {
+ printk(KERN_ERR
+ "si_cp: Bogus length %zu in firmware \"%s\"\n",
+ rdev->ce_fw->size, fw_name);
+ err = -EINVAL;
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", rlc_chip_name);
+ err = request_firmware(&rdev->rlc_fw, fw_name, &pdev->dev);
+ if (err)
+ goto out;
+ if (rdev->rlc_fw->size != rlc_req_size) {
+ printk(KERN_ERR
+ "si_rlc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->rlc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
+ err = request_firmware(&rdev->mc_fw, fw_name, &pdev->dev);
+ if (err)
+ goto out;
+ if (rdev->mc_fw->size != mc_req_size) {
+ printk(KERN_ERR
+ "si_mc: Bogus length %zu in firmware \"%s\"\n",
+ rdev->mc_fw->size, fw_name);
+ err = -EINVAL;
+ }
+
+out:
+ platform_device_unregister(pdev);
+
+ if (err) {
+ if (err != -EINVAL)
+ printk(KERN_ERR
+ "si_cp: Failed to load firmware \"%s\"\n",
+ fw_name);
+ release_firmware(rdev->pfp_fw);
+ rdev->pfp_fw = NULL;
+ release_firmware(rdev->me_fw);
+ rdev->me_fw = NULL;
+ release_firmware(rdev->ce_fw);
+ rdev->ce_fw = NULL;
+ release_firmware(rdev->rlc_fw);
+ rdev->rlc_fw = NULL;
+ release_firmware(rdev->mc_fw);
+ rdev->mc_fw = NULL;
+ }
+ return err;
+}
+
+/* watermark setup */
+static u32 dce6_line_buffer_adjust(struct radeon_device *rdev,
+ struct radeon_crtc *radeon_crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *other_mode)
+{
+ u32 tmp;
+ /*
+ * Line Buffer Setup
+ * There are 3 line buffers, each one shared by 2 display controllers.
+ * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between
+ * the display controllers. The paritioning is done via one of four
+ * preset allocations specified in bits 21:20:
+ * 0 - half lb
+ * 2 - whole lb, other crtc must be disabled
+ */
+ /* this can get tricky if we have two large displays on a paired group
+ * of crtcs. Ideally for multiple large displays we'd assign them to
+ * non-linked crtcs for maximum line buffer allocation.
+ */
+ if (radeon_crtc->base.enabled && mode) {
+ if (other_mode)
+ tmp = 0; /* 1/2 */
+ else
+ tmp = 2; /* whole */
+ } else
+ tmp = 0;
+
+ WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset,
+ DC_LB_MEMORY_CONFIG(tmp));
+
+ if (radeon_crtc->base.enabled && mode) {
+ switch (tmp) {
+ case 0:
+ default:
+ return 4096 * 2;
+ case 2:
+ return 8192 * 2;
+ }
+ }
+
+ /* controller not enabled, so no lb used */
+ return 0;
+}
+
+static u32 si_get_number_of_dram_channels(struct radeon_device *rdev)
+{
+ u32 tmp = RREG32(MC_SHARED_CHMAP);
+
+ switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
+ case 0:
+ default:
+ return 1;
+ case 1:
+ return 2;
+ case 2:
+ return 4;
+ case 3:
+ return 8;
+ case 4:
+ return 3;
+ case 5:
+ return 6;
+ case 6:
+ return 10;
+ case 7:
+ return 12;
+ case 8:
+ return 16;
+ }
+}
+
+struct dce6_wm_params {
+ u32 dram_channels; /* number of dram channels */
+ u32 yclk; /* bandwidth per dram data pin in kHz */
+ u32 sclk; /* engine clock in kHz */
+ u32 disp_clk; /* display clock in kHz */
+ u32 src_width; /* viewport width */
+ u32 active_time; /* active display time in ns */
+ u32 blank_time; /* blank time in ns */
+ bool interlaced; /* mode is interlaced */
+ fixed20_12 vsc; /* vertical scale ratio */
+ u32 num_heads; /* number of active crtcs */
+ u32 bytes_per_pixel; /* bytes per pixel display + overlay */
+ u32 lb_size; /* line buffer allocated to pipe */
+ u32 vtaps; /* vertical scaler taps */
+};
+
+static u32 dce6_dram_bandwidth(struct dce6_wm_params *wm)
+{
+ /* Calculate raw DRAM Bandwidth */
+ fixed20_12 dram_efficiency; /* 0.7 */
+ fixed20_12 yclk, dram_channels, bandwidth;
+ fixed20_12 a;
+
+ a.full = dfixed_const(1000);
+ yclk.full = dfixed_const(wm->yclk);
+ yclk.full = dfixed_div(yclk, a);
+ dram_channels.full = dfixed_const(wm->dram_channels * 4);
+ a.full = dfixed_const(10);
+ dram_efficiency.full = dfixed_const(7);
+ dram_efficiency.full = dfixed_div(dram_efficiency, a);
+ bandwidth.full = dfixed_mul(dram_channels, yclk);
+ bandwidth.full = dfixed_mul(bandwidth, dram_efficiency);
+
+ return dfixed_trunc(bandwidth);
+}
+
+static u32 dce6_dram_bandwidth_for_display(struct dce6_wm_params *wm)
+{
+ /* Calculate DRAM Bandwidth and the part allocated to display. */
+ fixed20_12 disp_dram_allocation; /* 0.3 to 0.7 */
+ fixed20_12 yclk, dram_channels, bandwidth;
+ fixed20_12 a;
+
+ a.full = dfixed_const(1000);
+ yclk.full = dfixed_const(wm->yclk);
+ yclk.full = dfixed_div(yclk, a);
+ dram_channels.full = dfixed_const(wm->dram_channels * 4);
+ a.full = dfixed_const(10);
+ disp_dram_allocation.full = dfixed_const(3); /* XXX worse case value 0.3 */
+ disp_dram_allocation.full = dfixed_div(disp_dram_allocation, a);
+ bandwidth.full = dfixed_mul(dram_channels, yclk);
+ bandwidth.full = dfixed_mul(bandwidth, disp_dram_allocation);
+
+ return dfixed_trunc(bandwidth);
+}
+
+static u32 dce6_data_return_bandwidth(struct dce6_wm_params *wm)
+{
+ /* Calculate the display Data return Bandwidth */
+ fixed20_12 return_efficiency; /* 0.8 */
+ fixed20_12 sclk, bandwidth;
+ fixed20_12 a;
+
+ a.full = dfixed_const(1000);
+ sclk.full = dfixed_const(wm->sclk);
+ sclk.full = dfixed_div(sclk, a);
+ a.full = dfixed_const(10);
+ return_efficiency.full = dfixed_const(8);
+ return_efficiency.full = dfixed_div(return_efficiency, a);
+ a.full = dfixed_const(32);
+ bandwidth.full = dfixed_mul(a, sclk);
+ bandwidth.full = dfixed_mul(bandwidth, return_efficiency);
+
+ return dfixed_trunc(bandwidth);
+}
+
+static u32 dce6_get_dmif_bytes_per_request(struct dce6_wm_params *wm)
+{
+ return 32;
+}
+
+static u32 dce6_dmif_request_bandwidth(struct dce6_wm_params *wm)
+{
+ /* Calculate the DMIF Request Bandwidth */
+ fixed20_12 disp_clk_request_efficiency; /* 0.8 */
+ fixed20_12 disp_clk, sclk, bandwidth;
+ fixed20_12 a, b1, b2;
+ u32 min_bandwidth;
+
+ a.full = dfixed_const(1000);
+ disp_clk.full = dfixed_const(wm->disp_clk);
+ disp_clk.full = dfixed_div(disp_clk, a);
+ a.full = dfixed_const(dce6_get_dmif_bytes_per_request(wm) / 2);
+ b1.full = dfixed_mul(a, disp_clk);
+
+ a.full = dfixed_const(1000);
+ sclk.full = dfixed_const(wm->sclk);
+ sclk.full = dfixed_div(sclk, a);
+ a.full = dfixed_const(dce6_get_dmif_bytes_per_request(wm));
+ b2.full = dfixed_mul(a, sclk);
+
+ a.full = dfixed_const(10);
+ disp_clk_request_efficiency.full = dfixed_const(8);
+ disp_clk_request_efficiency.full = dfixed_div(disp_clk_request_efficiency, a);
+
+ min_bandwidth = min(dfixed_trunc(b1), dfixed_trunc(b2));
+
+ a.full = dfixed_const(min_bandwidth);
+ bandwidth.full = dfixed_mul(a, disp_clk_request_efficiency);
+
+ return dfixed_trunc(bandwidth);
+}
+
+static u32 dce6_available_bandwidth(struct dce6_wm_params *wm)
+{
+ /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */
+ u32 dram_bandwidth = dce6_dram_bandwidth(wm);
+ u32 data_return_bandwidth = dce6_data_return_bandwidth(wm);
+ u32 dmif_req_bandwidth = dce6_dmif_request_bandwidth(wm);
+
+ return min(dram_bandwidth, min(data_return_bandwidth, dmif_req_bandwidth));
+}
+
+static u32 dce6_average_bandwidth(struct dce6_wm_params *wm)
+{
+ /* Calculate the display mode Average Bandwidth
+ * DisplayMode should contain the source and destination dimensions,
+ * timing, etc.
+ */
+ fixed20_12 bpp;
+ fixed20_12 line_time;
+ fixed20_12 src_width;
+ fixed20_12 bandwidth;
+ fixed20_12 a;
+
+ a.full = dfixed_const(1000);
+ line_time.full = dfixed_const(wm->active_time + wm->blank_time);
+ line_time.full = dfixed_div(line_time, a);
+ bpp.full = dfixed_const(wm->bytes_per_pixel);
+ src_width.full = dfixed_const(wm->src_width);
+ bandwidth.full = dfixed_mul(src_width, bpp);
+ bandwidth.full = dfixed_mul(bandwidth, wm->vsc);
+ bandwidth.full = dfixed_div(bandwidth, line_time);
+
+ return dfixed_trunc(bandwidth);
+}
+
+static u32 dce6_latency_watermark(struct dce6_wm_params *wm)
+{
+ /* First calcualte the latency in ns */
+ u32 mc_latency = 2000; /* 2000 ns. */
+ u32 available_bandwidth = dce6_available_bandwidth(wm);
+ u32 worst_chunk_return_time = (512 * 8 * 1000) / available_bandwidth;
+ u32 cursor_line_pair_return_time = (128 * 4 * 1000) / available_bandwidth;
+ u32 dc_latency = 40000000 / wm->disp_clk; /* dc pipe latency */
+ u32 other_heads_data_return_time = ((wm->num_heads + 1) * worst_chunk_return_time) +
+ (wm->num_heads * cursor_line_pair_return_time);
+ u32 latency = mc_latency + other_heads_data_return_time + dc_latency;
+ u32 max_src_lines_per_dst_line, lb_fill_bw, line_fill_time;
+ u32 tmp, dmif_size = 12288;
+ fixed20_12 a, b, c;
+
+ if (wm->num_heads == 0)
+ return 0;
+
+ a.full = dfixed_const(2);
+ b.full = dfixed_const(1);
+ if ((wm->vsc.full > a.full) ||
+ ((wm->vsc.full > b.full) && (wm->vtaps >= 3)) ||
+ (wm->vtaps >= 5) ||
+ ((wm->vsc.full >= a.full) && wm->interlaced))
+ max_src_lines_per_dst_line = 4;
+ else
+ max_src_lines_per_dst_line = 2;
+
+ a.full = dfixed_const(available_bandwidth);
+ b.full = dfixed_const(wm->num_heads);
+ a.full = dfixed_div(a, b);
+
+ b.full = dfixed_const(mc_latency + 512);
+ c.full = dfixed_const(wm->disp_clk);
+ b.full = dfixed_div(b, c);
+
+ c.full = dfixed_const(dmif_size);
+ b.full = dfixed_div(c, b);
+
+ tmp = min(dfixed_trunc(a), dfixed_trunc(b));
+
+ b.full = dfixed_const(1000);
+ c.full = dfixed_const(wm->disp_clk);
+ b.full = dfixed_div(c, b);
+ c.full = dfixed_const(wm->bytes_per_pixel);
+ b.full = dfixed_mul(b, c);
+
+ lb_fill_bw = min(tmp, dfixed_trunc(b));
+
+ a.full = dfixed_const(max_src_lines_per_dst_line * wm->src_width * wm->bytes_per_pixel);
+ b.full = dfixed_const(1000);
+ c.full = dfixed_const(lb_fill_bw);
+ b.full = dfixed_div(c, b);
+ a.full = dfixed_div(a, b);
+ line_fill_time = dfixed_trunc(a);
+
+ if (line_fill_time < wm->active_time)
+ return latency;
+ else
+ return latency + (line_fill_time - wm->active_time);
+
+}
+
+static bool dce6_average_bandwidth_vs_dram_bandwidth_for_display(struct dce6_wm_params *wm)
+{
+ if (dce6_average_bandwidth(wm) <=
+ (dce6_dram_bandwidth_for_display(wm) / wm->num_heads))
+ return true;
+ else
+ return false;
+};
+
+static bool dce6_average_bandwidth_vs_available_bandwidth(struct dce6_wm_params *wm)
+{
+ if (dce6_average_bandwidth(wm) <=
+ (dce6_available_bandwidth(wm) / wm->num_heads))
+ return true;
+ else
+ return false;
+};
+
+static bool dce6_check_latency_hiding(struct dce6_wm_params *wm)
+{
+ u32 lb_partitions = wm->lb_size / wm->src_width;
+ u32 line_time = wm->active_time + wm->blank_time;
+ u32 latency_tolerant_lines;
+ u32 latency_hiding;
+ fixed20_12 a;
+
+ a.full = dfixed_const(1);
+ if (wm->vsc.full > a.full)
+ latency_tolerant_lines = 1;
+ else {
+ if (lb_partitions <= (wm->vtaps + 1))
+ latency_tolerant_lines = 1;
+ else
+ latency_tolerant_lines = 2;
+ }
+
+ latency_hiding = (latency_tolerant_lines * line_time + wm->blank_time);
+
+ if (dce6_latency_watermark(wm) <= latency_hiding)
+ return true;
+ else
+ return false;
+}
+
+static void dce6_program_watermarks(struct radeon_device *rdev,
+ struct radeon_crtc *radeon_crtc,
+ u32 lb_size, u32 num_heads)
+{
+ struct drm_display_mode *mode = &radeon_crtc->base.mode;
+ struct dce6_wm_params wm;
+ u32 pixel_period;
+ u32 line_time = 0;
+ u32 latency_watermark_a = 0, latency_watermark_b = 0;
+ u32 priority_a_mark = 0, priority_b_mark = 0;
+ u32 priority_a_cnt = PRIORITY_OFF;
+ u32 priority_b_cnt = PRIORITY_OFF;
+ u32 tmp, arb_control3;
+ fixed20_12 a, b, c;
+
+ if (radeon_crtc->base.enabled && num_heads && mode) {
+ pixel_period = 1000000 / (u32)mode->clock;
+ line_time = min((u32)mode->crtc_htotal * pixel_period, (u32)65535);
+ priority_a_cnt = 0;
+ priority_b_cnt = 0;
+
+ wm.yclk = rdev->pm.current_mclk * 10;
+ wm.sclk = rdev->pm.current_sclk * 10;
+ wm.disp_clk = mode->clock;
+ wm.src_width = mode->crtc_hdisplay;
+ wm.active_time = mode->crtc_hdisplay * pixel_period;
+ wm.blank_time = line_time - wm.active_time;
+ wm.interlaced = false;
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ wm.interlaced = true;
+ wm.vsc = radeon_crtc->vsc;
+ wm.vtaps = 1;
+ if (radeon_crtc->rmx_type != RMX_OFF)
+ wm.vtaps = 2;
+ wm.bytes_per_pixel = 4; /* XXX: get this from fb config */
+ wm.lb_size = lb_size;
+ if (rdev->family == CHIP_ARUBA)
+ wm.dram_channels = evergreen_get_number_of_dram_channels(rdev);
+ else
+ wm.dram_channels = si_get_number_of_dram_channels(rdev);
+ wm.num_heads = num_heads;
+
+ /* set for high clocks */
+ latency_watermark_a = min(dce6_latency_watermark(&wm), (u32)65535);
+ /* set for low clocks */
+ /* wm.yclk = low clk; wm.sclk = low clk */
+ latency_watermark_b = min(dce6_latency_watermark(&wm), (u32)65535);
+
+ /* possibly force display priority to high */
+ /* should really do this at mode validation time... */
+ if (!dce6_average_bandwidth_vs_dram_bandwidth_for_display(&wm) ||
+ !dce6_average_bandwidth_vs_available_bandwidth(&wm) ||
+ !dce6_check_latency_hiding(&wm) ||
+ (rdev->disp_priority == 2)) {
+ DRM_DEBUG_KMS("force priority to high\n");
+ priority_a_cnt |= PRIORITY_ALWAYS_ON;
+ priority_b_cnt |= PRIORITY_ALWAYS_ON;
+ }
+
+ a.full = dfixed_const(1000);
+ b.full = dfixed_const(mode->clock);
+ b.full = dfixed_div(b, a);
+ c.full = dfixed_const(latency_watermark_a);
+ c.full = dfixed_mul(c, b);
+ c.full = dfixed_mul(c, radeon_crtc->hsc);
+ c.full = dfixed_div(c, a);
+ a.full = dfixed_const(16);
+ c.full = dfixed_div(c, a);
+ priority_a_mark = dfixed_trunc(c);
+ priority_a_cnt |= priority_a_mark & PRIORITY_MARK_MASK;
+
+ a.full = dfixed_const(1000);
+ b.full = dfixed_const(mode->clock);
+ b.full = dfixed_div(b, a);
+ c.full = dfixed_const(latency_watermark_b);
+ c.full = dfixed_mul(c, b);
+ c.full = dfixed_mul(c, radeon_crtc->hsc);
+ c.full = dfixed_div(c, a);
+ a.full = dfixed_const(16);
+ c.full = dfixed_div(c, a);
+ priority_b_mark = dfixed_trunc(c);
+ priority_b_cnt |= priority_b_mark & PRIORITY_MARK_MASK;
+ }
+
+ /* select wm A */
+ arb_control3 = RREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset);
+ tmp = arb_control3;
+ tmp &= ~LATENCY_WATERMARK_MASK(3);
+ tmp |= LATENCY_WATERMARK_MASK(1);
+ WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset, tmp);
+ WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset,
+ (LATENCY_LOW_WATERMARK(latency_watermark_a) |
+ LATENCY_HIGH_WATERMARK(line_time)));
+ /* select wm B */
+ tmp = RREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset);
+ tmp &= ~LATENCY_WATERMARK_MASK(3);
+ tmp |= LATENCY_WATERMARK_MASK(2);
+ WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset, tmp);
+ WREG32(DPG_PIPE_LATENCY_CONTROL + radeon_crtc->crtc_offset,
+ (LATENCY_LOW_WATERMARK(latency_watermark_b) |
+ LATENCY_HIGH_WATERMARK(line_time)));
+ /* restore original selection */
+ WREG32(DPG_PIPE_ARBITRATION_CONTROL3 + radeon_crtc->crtc_offset, arb_control3);
+
+ /* write the priority marks */
+ WREG32(PRIORITY_A_CNT + radeon_crtc->crtc_offset, priority_a_cnt);
+ WREG32(PRIORITY_B_CNT + radeon_crtc->crtc_offset, priority_b_cnt);
+
+}
+
+void dce6_bandwidth_update(struct radeon_device *rdev)
+{
+ struct drm_display_mode *mode0 = NULL;
+ struct drm_display_mode *mode1 = NULL;
+ u32 num_heads = 0, lb_size;
+ int i;
+
+ radeon_update_display_priority(rdev);
+
+ for (i = 0; i < rdev->num_crtc; i++) {
+ if (rdev->mode_info.crtcs[i]->base.enabled)
+ num_heads++;
+ }
+ for (i = 0; i < rdev->num_crtc; i += 2) {
+ mode0 = &rdev->mode_info.crtcs[i]->base.mode;
+ mode1 = &rdev->mode_info.crtcs[i+1]->base.mode;
+ lb_size = dce6_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode0, mode1);
+ dce6_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads);
+ lb_size = dce6_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i+1], mode1, mode0);
+ dce6_program_watermarks(rdev, rdev->mode_info.crtcs[i+1], lb_size, num_heads);
+ }
+}
+
+/*
+ * Core functions
+ */
+static u32 si_get_tile_pipe_to_backend_map(struct radeon_device *rdev,
+ u32 num_tile_pipes,
+ u32 num_backends_per_asic,
+ u32 *backend_disable_mask_per_asic,
+ u32 num_shader_engines)
+{
+ u32 backend_map = 0;
+ u32 enabled_backends_mask = 0;
+ u32 enabled_backends_count = 0;
+ u32 num_backends_per_se;
+ u32 cur_pipe;
+ u32 swizzle_pipe[SI_MAX_PIPES];
+ u32 cur_backend = 0;
+ u32 i;
+ bool force_no_swizzle;
+
+ /* force legal values */
+ if (num_tile_pipes < 1)
+ num_tile_pipes = 1;
+ if (num_tile_pipes > rdev->config.si.max_tile_pipes)
+ num_tile_pipes = rdev->config.si.max_tile_pipes;
+ if (num_shader_engines < 1)
+ num_shader_engines = 1;
+ if (num_shader_engines > rdev->config.si.max_shader_engines)
+ num_shader_engines = rdev->config.si.max_shader_engines;
+ if (num_backends_per_asic < num_shader_engines)
+ num_backends_per_asic = num_shader_engines;
+ if (num_backends_per_asic > (rdev->config.si.max_backends_per_se * num_shader_engines))
+ num_backends_per_asic = rdev->config.si.max_backends_per_se * num_shader_engines;
+
+ /* make sure we have the same number of backends per se */
+ num_backends_per_asic = ALIGN(num_backends_per_asic, num_shader_engines);
+ /* set up the number of backends per se */
+ num_backends_per_se = num_backends_per_asic / num_shader_engines;
+ if (num_backends_per_se > rdev->config.si.max_backends_per_se) {
+ num_backends_per_se = rdev->config.si.max_backends_per_se;
+ num_backends_per_asic = num_backends_per_se * num_shader_engines;
+ }
+
+ /* create enable mask and count for enabled backends */
+ for (i = 0; i < SI_MAX_BACKENDS; ++i) {
+ if (((*backend_disable_mask_per_asic >> i) & 1) == 0) {
+ enabled_backends_mask |= (1 << i);
+ ++enabled_backends_count;
+ }
+ if (enabled_backends_count == num_backends_per_asic)
+ break;
+ }
+
+ /* force the backends mask to match the current number of backends */
+ if (enabled_backends_count != num_backends_per_asic) {
+ u32 this_backend_enabled;
+ u32 shader_engine;
+ u32 backend_per_se;
+
+ enabled_backends_mask = 0;
+ enabled_backends_count = 0;
+ *backend_disable_mask_per_asic = SI_MAX_BACKENDS_MASK;
+ for (i = 0; i < SI_MAX_BACKENDS; ++i) {
+ /* calc the current se */
+ shader_engine = i / rdev->config.si.max_backends_per_se;
+ /* calc the backend per se */
+ backend_per_se = i % rdev->config.si.max_backends_per_se;
+ /* default to not enabled */
+ this_backend_enabled = 0;
+ if ((shader_engine < num_shader_engines) &&
+ (backend_per_se < num_backends_per_se))
+ this_backend_enabled = 1;
+ if (this_backend_enabled) {
+ enabled_backends_mask |= (1 << i);
+ *backend_disable_mask_per_asic &= ~(1 << i);
+ ++enabled_backends_count;
+ }
+ }
+ }
+
+
+ memset((uint8_t *)&swizzle_pipe[0], 0, sizeof(u32) * SI_MAX_PIPES);
+ switch (rdev->family) {
+ case CHIP_TAHITI:
+ case CHIP_PITCAIRN:
+ case CHIP_VERDE:
+ force_no_swizzle = true;
+ break;
+ default:
+ force_no_swizzle = false;
+ break;
+ }
+ if (force_no_swizzle) {
+ bool last_backend_enabled = false;
+
+ force_no_swizzle = false;
+ for (i = 0; i < SI_MAX_BACKENDS; ++i) {
+ if (((enabled_backends_mask >> i) & 1) == 1) {
+ if (last_backend_enabled)
+ force_no_swizzle = true;
+ last_backend_enabled = true;
+ } else
+ last_backend_enabled = false;
+ }
+ }
+
+ switch (num_tile_pipes) {
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ DRM_ERROR("odd number of pipes!\n");
+ break;
+ case 2:
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 1;
+ break;
+ case 4:
+ if (force_no_swizzle) {
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 1;
+ swizzle_pipe[2] = 2;
+ swizzle_pipe[3] = 3;
+ } else {
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 2;
+ swizzle_pipe[2] = 1;
+ swizzle_pipe[3] = 3;
+ }
+ break;
+ case 6:
+ if (force_no_swizzle) {
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 1;
+ swizzle_pipe[2] = 2;
+ swizzle_pipe[3] = 3;
+ swizzle_pipe[4] = 4;
+ swizzle_pipe[5] = 5;
+ } else {
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 2;
+ swizzle_pipe[2] = 4;
+ swizzle_pipe[3] = 1;
+ swizzle_pipe[4] = 3;
+ swizzle_pipe[5] = 5;
+ }
+ break;
+ case 8:
+ if (force_no_swizzle) {
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 1;
+ swizzle_pipe[2] = 2;
+ swizzle_pipe[3] = 3;
+ swizzle_pipe[4] = 4;
+ swizzle_pipe[5] = 5;
+ swizzle_pipe[6] = 6;
+ swizzle_pipe[7] = 7;
+ } else {
+ swizzle_pipe[0] = 0;
+ swizzle_pipe[1] = 2;
+ swizzle_pipe[2] = 4;
+ swizzle_pipe[3] = 6;
+ swizzle_pipe[4] = 1;
+ swizzle_pipe[5] = 3;
+ swizzle_pipe[6] = 5;
+ swizzle_pipe[7] = 7;
+ }
+ break;
+ }
+
+ for (cur_pipe = 0; cur_pipe < num_tile_pipes; ++cur_pipe) {
+ while (((1 << cur_backend) & enabled_backends_mask) == 0)
+ cur_backend = (cur_backend + 1) % SI_MAX_BACKENDS;
+
+ backend_map |= (((cur_backend & 0xf) << (swizzle_pipe[cur_pipe] * 4)));
+
+ cur_backend = (cur_backend + 1) % SI_MAX_BACKENDS;
+ }
+
+ return backend_map;
+}
+
+static u32 si_get_disable_mask_per_asic(struct radeon_device *rdev,
+ u32 disable_mask_per_se,
+ u32 max_disable_mask_per_se,
+ u32 num_shader_engines)
+{
+ u32 disable_field_width_per_se = r600_count_pipe_bits(disable_mask_per_se);
+ u32 disable_mask_per_asic = disable_mask_per_se & max_disable_mask_per_se;
+
+ if (num_shader_engines == 1)
+ return disable_mask_per_asic;
+ else if (num_shader_engines == 2)
+ return disable_mask_per_asic | (disable_mask_per_asic << disable_field_width_per_se);
+ else
+ return 0xffffffff;
+}
+
+static void si_tiling_mode_table_init(struct radeon_device *rdev)
+{
+ const u32 num_tile_mode_states = 32;
+ u32 reg_offset, gb_tile_moden, split_equal_to_row_size;
+
+ switch (rdev->config.si.mem_row_size_in_kb) {
+ case 1:
+ split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_1KB;
+ break;
+ case 2:
+ default:
+ split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_2KB;
+ break;
+ case 4:
+ split_equal_to_row_size = ADDR_SURF_TILE_SPLIT_4KB;
+ break;
+ }
+
+ if ((rdev->family == CHIP_TAHITI) ||
+ (rdev->family == CHIP_PITCAIRN)) {
+ for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) {
+ switch (reg_offset) {
+ case 0: /* non-AA compressed depth or any compressed stencil */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 1: /* 2xAA/4xAA compressed depth only */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 2: /* 8xAA compressed depth only */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 3: /* 2xAA/4xAA compressed depth with stencil (for depth buffer) */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 4: /* Maps w/ a dimension less than the 2D macro-tile dimensions (for mipmapped depth textures) */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 5: /* Uncompressed 16bpp depth - and stencil buffer allocated with it */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 6: /* Uncompressed 32bpp depth - and stencil buffer allocated with it */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ case 7: /* Uncompressed 8bpp stencil without depth (drivers typically do not use) */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 8: /* 1D and 1D Array Surfaces */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 9: /* Displayable maps. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 10: /* Display 8bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 11: /* Display 16bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 12: /* Display 32bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ case 13: /* Thin. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 14: /* Thin 8 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ case 15: /* Thin 16 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ case 16: /* Thin 32 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ case 17: /* Thin 64 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ case 21: /* 8 bpp PRT. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 22: /* 16 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 23: /* 32 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 24: /* 64 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 25: /* 128 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_1KB) |
+ NUM_BANKS(ADDR_SURF_8_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ default:
+ gb_tile_moden = 0;
+ break;
+ }
+ WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden);
+ }
+ } else if (rdev->family == CHIP_VERDE) {
+ for (reg_offset = 0; reg_offset < num_tile_mode_states; reg_offset++) {
+ switch (reg_offset) {
+ case 0: /* non-AA compressed depth or any compressed stencil */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 1: /* 2xAA/4xAA compressed depth only */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 2: /* 8xAA compressed depth only */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 3: /* 2xAA/4xAA compressed depth with stencil (for depth buffer) */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_128B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 4: /* Maps w/ a dimension less than the 2D macro-tile dimensions (for mipmapped depth textures) */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 5: /* Uncompressed 16bpp depth - and stencil buffer allocated with it */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 6: /* Uncompressed 32bpp depth - and stencil buffer allocated with it */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 7: /* Uncompressed 8bpp stencil without depth (drivers typically do not use) */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DEPTH_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 8: /* 1D and 1D Array Surfaces */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_LINEAR_ALIGNED) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 9: /* Displayable maps. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 10: /* Display 8bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 11: /* Display 16bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 12: /* Display 32bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_DISPLAY_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 13: /* Thin. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_1D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_64B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 14: /* Thin 8 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 15: /* Thin 16 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 16: /* Thin 32 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 17: /* Thin 64 bpp. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P4_8x16) |
+ TILE_SPLIT(split_equal_to_row_size) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 21: /* 8 bpp PRT. */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_2) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 22: /* 16 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_4) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_4));
+ break;
+ case 23: /* 32 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_256B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_2) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 24: /* 64 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_512B) |
+ NUM_BANKS(ADDR_SURF_16_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_2));
+ break;
+ case 25: /* 128 bpp PRT */
+ gb_tile_moden = (ARRAY_MODE(ARRAY_2D_TILED_THIN1) |
+ MICRO_TILE_MODE(ADDR_SURF_THIN_MICRO_TILING) |
+ PIPE_CONFIG(ADDR_SURF_P8_32x32_8x16) |
+ TILE_SPLIT(ADDR_SURF_TILE_SPLIT_1KB) |
+ NUM_BANKS(ADDR_SURF_8_BANK) |
+ BANK_WIDTH(ADDR_SURF_BANK_WIDTH_1) |
+ BANK_HEIGHT(ADDR_SURF_BANK_HEIGHT_1) |
+ MACRO_TILE_ASPECT(ADDR_SURF_MACRO_ASPECT_1));
+ break;
+ default:
+ gb_tile_moden = 0;
+ break;
+ }
+ WREG32(GB_TILE_MODE0 + (reg_offset * 4), gb_tile_moden);
+ }
+ } else
+ DRM_ERROR("unknown asic: 0x%x\n", rdev->family);
+}
+
+static void si_gpu_init(struct radeon_device *rdev)
+{
+ u32 cc_rb_backend_disable = 0;
+ u32 cc_gc_shader_array_config;
+ u32 gb_addr_config = 0;
+ u32 mc_shared_chmap, mc_arb_ramcfg;
+ u32 gb_backend_map;
+ u32 cgts_tcc_disable;
+ u32 sx_debug_1;
+ u32 gc_user_shader_array_config;
+ u32 gc_user_rb_backend_disable;
+ u32 cgts_user_tcc_disable;
+ u32 hdp_host_path_cntl;
+ u32 tmp;
+ int i, j;
+
+ switch (rdev->family) {
+ case CHIP_TAHITI:
+ rdev->config.si.max_shader_engines = 2;
+ rdev->config.si.max_pipes_per_simd = 4;
+ rdev->config.si.max_tile_pipes = 12;
+ rdev->config.si.max_simds_per_se = 8;
+ rdev->config.si.max_backends_per_se = 4;
+ rdev->config.si.max_texture_channel_caches = 12;
+ rdev->config.si.max_gprs = 256;
+ rdev->config.si.max_gs_threads = 32;
+ rdev->config.si.max_hw_contexts = 8;
+
+ rdev->config.si.sc_prim_fifo_size_frontend = 0x20;
+ rdev->config.si.sc_prim_fifo_size_backend = 0x100;
+ rdev->config.si.sc_hiz_tile_fifo_size = 0x30;
+ rdev->config.si.sc_earlyz_tile_fifo_size = 0x130;
+ break;
+ case CHIP_PITCAIRN:
+ rdev->config.si.max_shader_engines = 2;
+ rdev->config.si.max_pipes_per_simd = 4;
+ rdev->config.si.max_tile_pipes = 8;
+ rdev->config.si.max_simds_per_se = 5;
+ rdev->config.si.max_backends_per_se = 4;
+ rdev->config.si.max_texture_channel_caches = 8;
+ rdev->config.si.max_gprs = 256;
+ rdev->config.si.max_gs_threads = 32;
+ rdev->config.si.max_hw_contexts = 8;
+
+ rdev->config.si.sc_prim_fifo_size_frontend = 0x20;
+ rdev->config.si.sc_prim_fifo_size_backend = 0x100;
+ rdev->config.si.sc_hiz_tile_fifo_size = 0x30;
+ rdev->config.si.sc_earlyz_tile_fifo_size = 0x130;
+ break;
+ case CHIP_VERDE:
+ default:
+ rdev->config.si.max_shader_engines = 1;
+ rdev->config.si.max_pipes_per_simd = 4;
+ rdev->config.si.max_tile_pipes = 4;
+ rdev->config.si.max_simds_per_se = 2;
+ rdev->config.si.max_backends_per_se = 4;
+ rdev->config.si.max_texture_channel_caches = 4;
+ rdev->config.si.max_gprs = 256;
+ rdev->config.si.max_gs_threads = 32;
+ rdev->config.si.max_hw_contexts = 8;
+
+ rdev->config.si.sc_prim_fifo_size_frontend = 0x20;
+ rdev->config.si.sc_prim_fifo_size_backend = 0x40;
+ rdev->config.si.sc_hiz_tile_fifo_size = 0x30;
+ rdev->config.si.sc_earlyz_tile_fifo_size = 0x130;
+ break;
+ }
+
+ /* Initialize HDP */
+ for (i = 0, j = 0; i < 32; i++, j += 0x18) {
+ WREG32((0x2c14 + j), 0x00000000);
+ WREG32((0x2c18 + j), 0x00000000);
+ WREG32((0x2c1c + j), 0x00000000);
+ WREG32((0x2c20 + j), 0x00000000);
+ WREG32((0x2c24 + j), 0x00000000);
+ }
+
+ WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
+
+ evergreen_fix_pci_max_read_req_size(rdev);
+
+ WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN);
+
+ mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
+ mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
+
+ cc_rb_backend_disable = RREG32(CC_RB_BACKEND_DISABLE);
+ cc_gc_shader_array_config = RREG32(CC_GC_SHADER_ARRAY_CONFIG);
+ cgts_tcc_disable = 0xffff0000;
+ for (i = 0; i < rdev->config.si.max_texture_channel_caches; i++)
+ cgts_tcc_disable &= ~(1 << (16 + i));
+ gc_user_rb_backend_disable = RREG32(GC_USER_RB_BACKEND_DISABLE);
+ gc_user_shader_array_config = RREG32(GC_USER_SHADER_ARRAY_CONFIG);
+ cgts_user_tcc_disable = RREG32(CGTS_USER_TCC_DISABLE);
+
+ rdev->config.si.num_shader_engines = rdev->config.si.max_shader_engines;
+ rdev->config.si.num_tile_pipes = rdev->config.si.max_tile_pipes;
+ tmp = ((~gc_user_rb_backend_disable) & BACKEND_DISABLE_MASK) >> BACKEND_DISABLE_SHIFT;
+ rdev->config.si.num_backends_per_se = r600_count_pipe_bits(tmp);
+ tmp = (gc_user_rb_backend_disable & BACKEND_DISABLE_MASK) >> BACKEND_DISABLE_SHIFT;
+ rdev->config.si.backend_disable_mask_per_asic =
+ si_get_disable_mask_per_asic(rdev, tmp, SI_MAX_BACKENDS_PER_SE_MASK,
+ rdev->config.si.num_shader_engines);
+ rdev->config.si.backend_map =
+ si_get_tile_pipe_to_backend_map(rdev, rdev->config.si.num_tile_pipes,
+ rdev->config.si.num_backends_per_se *
+ rdev->config.si.num_shader_engines,
+ &rdev->config.si.backend_disable_mask_per_asic,
+ rdev->config.si.num_shader_engines);
+ tmp = ((~cgts_user_tcc_disable) & TCC_DISABLE_MASK) >> TCC_DISABLE_SHIFT;
+ rdev->config.si.num_texture_channel_caches = r600_count_pipe_bits(tmp);
+ rdev->config.si.mem_max_burst_length_bytes = 256;
+ tmp = (mc_arb_ramcfg & NOOFCOLS_MASK) >> NOOFCOLS_SHIFT;
+ rdev->config.si.mem_row_size_in_kb = (4 * (1 << (8 + tmp))) / 1024;
+ if (rdev->config.si.mem_row_size_in_kb > 4)
+ rdev->config.si.mem_row_size_in_kb = 4;
+ /* XXX use MC settings? */
+ rdev->config.si.shader_engine_tile_size = 32;
+ rdev->config.si.num_gpus = 1;
+ rdev->config.si.multi_gpu_tile_size = 64;
+
+ gb_addr_config = 0;
+ switch (rdev->config.si.num_tile_pipes) {
+ case 1:
+ gb_addr_config |= NUM_PIPES(0);
+ break;
+ case 2:
+ gb_addr_config |= NUM_PIPES(1);
+ break;
+ case 4:
+ gb_addr_config |= NUM_PIPES(2);
+ break;
+ case 8:
+ default:
+ gb_addr_config |= NUM_PIPES(3);
+ break;
+ }
+
+ tmp = (rdev->config.si.mem_max_burst_length_bytes / 256) - 1;
+ gb_addr_config |= PIPE_INTERLEAVE_SIZE(tmp);
+ gb_addr_config |= NUM_SHADER_ENGINES(rdev->config.si.num_shader_engines - 1);
+ tmp = (rdev->config.si.shader_engine_tile_size / 16) - 1;
+ gb_addr_config |= SHADER_ENGINE_TILE_SIZE(tmp);
+ switch (rdev->config.si.num_gpus) {
+ case 1:
+ default:
+ gb_addr_config |= NUM_GPUS(0);
+ break;
+ case 2:
+ gb_addr_config |= NUM_GPUS(1);
+ break;
+ case 4:
+ gb_addr_config |= NUM_GPUS(2);
+ break;
+ }
+ switch (rdev->config.si.multi_gpu_tile_size) {
+ case 16:
+ gb_addr_config |= MULTI_GPU_TILE_SIZE(0);
+ break;
+ case 32:
+ default:
+ gb_addr_config |= MULTI_GPU_TILE_SIZE(1);
+ break;
+ case 64:
+ gb_addr_config |= MULTI_GPU_TILE_SIZE(2);
+ break;
+ case 128:
+ gb_addr_config |= MULTI_GPU_TILE_SIZE(3);
+ break;
+ }
+ switch (rdev->config.si.mem_row_size_in_kb) {
+ case 1:
+ default:
+ gb_addr_config |= ROW_SIZE(0);
+ break;
+ case 2:
+ gb_addr_config |= ROW_SIZE(1);
+ break;
+ case 4:
+ gb_addr_config |= ROW_SIZE(2);
+ break;
+ }
+
+ tmp = (gb_addr_config & NUM_PIPES_MASK) >> NUM_PIPES_SHIFT;
+ rdev->config.si.num_tile_pipes = (1 << tmp);
+ tmp = (gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT;
+ rdev->config.si.mem_max_burst_length_bytes = (tmp + 1) * 256;
+ tmp = (gb_addr_config & NUM_SHADER_ENGINES_MASK) >> NUM_SHADER_ENGINES_SHIFT;
+ rdev->config.si.num_shader_engines = tmp + 1;
+ tmp = (gb_addr_config & NUM_GPUS_MASK) >> NUM_GPUS_SHIFT;
+ rdev->config.si.num_gpus = tmp + 1;
+ tmp = (gb_addr_config & MULTI_GPU_TILE_SIZE_MASK) >> MULTI_GPU_TILE_SIZE_SHIFT;
+ rdev->config.si.multi_gpu_tile_size = 1 << tmp;
+ tmp = (gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT;
+ rdev->config.si.mem_row_size_in_kb = 1 << tmp;
+
+ gb_backend_map =
+ si_get_tile_pipe_to_backend_map(rdev, rdev->config.si.num_tile_pipes,
+ rdev->config.si.num_backends_per_se *
+ rdev->config.si.num_shader_engines,
+ &rdev->config.si.backend_disable_mask_per_asic,
+ rdev->config.si.num_shader_engines);
+
+ /* setup tiling info dword. gb_addr_config is not adequate since it does
+ * not have bank info, so create a custom tiling dword.
+ * bits 3:0 num_pipes
+ * bits 7:4 num_banks
+ * bits 11:8 group_size
+ * bits 15:12 row_size
+ */
+ rdev->config.si.tile_config = 0;
+ switch (rdev->config.si.num_tile_pipes) {
+ case 1:
+ rdev->config.si.tile_config |= (0 << 0);
+ break;
+ case 2:
+ rdev->config.si.tile_config |= (1 << 0);
+ break;
+ case 4:
+ rdev->config.si.tile_config |= (2 << 0);
+ break;
+ case 8:
+ default:
+ /* XXX what about 12? */
+ rdev->config.si.tile_config |= (3 << 0);
+ break;
+ }
+ rdev->config.si.tile_config |=
+ ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) << 4;
+ rdev->config.si.tile_config |=
+ ((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
+ rdev->config.si.tile_config |=
+ ((gb_addr_config & ROW_SIZE_MASK) >> ROW_SIZE_SHIFT) << 12;
+
+ rdev->config.si.backend_map = gb_backend_map;
+ WREG32(GB_ADDR_CONFIG, gb_addr_config);
+ WREG32(DMIF_ADDR_CONFIG, gb_addr_config);
+ WREG32(HDP_ADDR_CONFIG, gb_addr_config);
+
+ /* primary versions */
+ WREG32(CC_RB_BACKEND_DISABLE, cc_rb_backend_disable);
+ WREG32(CC_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable);
+ WREG32(CC_GC_SHADER_ARRAY_CONFIG, cc_gc_shader_array_config);
+
+ WREG32(CGTS_TCC_DISABLE, cgts_tcc_disable);
+
+ /* user versions */
+ WREG32(GC_USER_RB_BACKEND_DISABLE, cc_rb_backend_disable);
+ WREG32(GC_USER_SYS_RB_BACKEND_DISABLE, cc_rb_backend_disable);
+ WREG32(GC_USER_SHADER_ARRAY_CONFIG, cc_gc_shader_array_config);
+
+ WREG32(CGTS_USER_TCC_DISABLE, cgts_tcc_disable);
+
+ si_tiling_mode_table_init(rdev);
+
+ /* set HW defaults for 3D engine */
+ WREG32(CP_QUEUE_THRESHOLDS, (ROQ_IB1_START(0x16) |
+ ROQ_IB2_START(0x2b)));
+ WREG32(CP_MEQ_THRESHOLDS, MEQ1_START(0x30) | MEQ2_START(0x60));
+
+ sx_debug_1 = RREG32(SX_DEBUG_1);
+ WREG32(SX_DEBUG_1, sx_debug_1);
+
+ WREG32(SPI_CONFIG_CNTL_1, VTX_DONE_DELAY(4));
+
+ WREG32(PA_SC_FIFO_SIZE, (SC_FRONTEND_PRIM_FIFO_SIZE(rdev->config.si.sc_prim_fifo_size_frontend) |
+ SC_BACKEND_PRIM_FIFO_SIZE(rdev->config.si.sc_prim_fifo_size_backend) |
+ SC_HIZ_TILE_FIFO_SIZE(rdev->config.si.sc_hiz_tile_fifo_size) |
+ SC_EARLYZ_TILE_FIFO_SIZE(rdev->config.si.sc_earlyz_tile_fifo_size)));
+
+ WREG32(VGT_NUM_INSTANCES, 1);
+
+ WREG32(CP_PERFMON_CNTL, 0);
+
+ WREG32(SQ_CONFIG, 0);
+
+ WREG32(PA_SC_FORCE_EOV_MAX_CNTS, (FORCE_EOV_MAX_CLK_CNT(4095) |
+ FORCE_EOV_MAX_REZ_CNT(255)));
+
+ WREG32(VGT_CACHE_INVALIDATION, CACHE_INVALIDATION(VC_AND_TC) |
+ AUTO_INVLD_EN(ES_AND_GS_AUTO));
+
+ WREG32(VGT_GS_VERTEX_REUSE, 16);
+ WREG32(PA_SC_LINE_STIPPLE_STATE, 0);
+
+ WREG32(CB_PERFCOUNTER0_SELECT0, 0);
+ WREG32(CB_PERFCOUNTER0_SELECT1, 0);
+ WREG32(CB_PERFCOUNTER1_SELECT0, 0);
+ WREG32(CB_PERFCOUNTER1_SELECT1, 0);
+ WREG32(CB_PERFCOUNTER2_SELECT0, 0);
+ WREG32(CB_PERFCOUNTER2_SELECT1, 0);
+ WREG32(CB_PERFCOUNTER3_SELECT0, 0);
+ WREG32(CB_PERFCOUNTER3_SELECT1, 0);
+
+ tmp = RREG32(HDP_MISC_CNTL);
+ tmp |= HDP_FLUSH_INVALIDATE_CACHE;
+ WREG32(HDP_MISC_CNTL, tmp);
+
+ hdp_host_path_cntl = RREG32(HDP_HOST_PATH_CNTL);
+ WREG32(HDP_HOST_PATH_CNTL, hdp_host_path_cntl);
+
+ WREG32(PA_CL_ENHANCE, CLIP_VTX_REORDER_ENA | NUM_CLIP_SEQ(3));
+
+ udelay(50);
+}
+
+/*
+ * GPU scratch registers helpers function.
+ */
+static void si_scratch_init(struct radeon_device *rdev)
+{
+ int i;
+
+ rdev->scratch.num_reg = 7;
+ rdev->scratch.reg_base = SCRATCH_REG0;
+ for (i = 0; i < rdev->scratch.num_reg; i++) {
+ rdev->scratch.free[i] = true;
+ rdev->scratch.reg[i] = rdev->scratch.reg_base + (i * 4);
+ }
+}
+
+void si_fence_ring_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence)
+{
+ struct radeon_ring *ring = &rdev->ring[fence->ring];
+ u64 addr = rdev->fence_drv[fence->ring].gpu_addr;
+
+ /* flush read cache over gart */
+ radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3));
+ radeon_ring_write(ring, PACKET3_TCL1_ACTION_ENA |
+ PACKET3_TC_ACTION_ENA |
+ PACKET3_SH_KCACHE_ACTION_ENA |
+ PACKET3_SH_ICACHE_ACTION_ENA);
+ radeon_ring_write(ring, 0xFFFFFFFF);
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, 10); /* poll interval */
+ /* EVENT_WRITE_EOP - flush caches, send int */
+ radeon_ring_write(ring, PACKET3(PACKET3_EVENT_WRITE_EOP, 4));
+ radeon_ring_write(ring, EVENT_TYPE(CACHE_FLUSH_AND_INV_TS_EVENT) | EVENT_INDEX(5));
+ radeon_ring_write(ring, addr & 0xffffffff);
+ radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | DATA_SEL(1) | INT_SEL(2));
+ radeon_ring_write(ring, fence->seq);
+ radeon_ring_write(ring, 0);
+}
+
+/*
+ * IB stuff
+ */
+void si_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+ struct radeon_ring *ring = &rdev->ring[ib->fence->ring];
+ u32 header;
+
+ if (ib->is_const_ib)
+ header = PACKET3(PACKET3_INDIRECT_BUFFER_CONST, 2);
+ else
+ header = PACKET3(PACKET3_INDIRECT_BUFFER, 2);
+
+ radeon_ring_write(ring, header);
+ radeon_ring_write(ring,
+#ifdef __BIG_ENDIAN
+ (2 << 0) |
+#endif
+ (ib->gpu_addr & 0xFFFFFFFC));
+ radeon_ring_write(ring, upper_32_bits(ib->gpu_addr) & 0xFFFF);
+ radeon_ring_write(ring, ib->length_dw | (ib->vm_id << 24));
+
+ /* flush read cache over gart for this vmid */
+ radeon_ring_write(ring, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(ring, (CP_COHER_CNTL2 - PACKET3_SET_CONFIG_REG_START) >> 2);
+ radeon_ring_write(ring, ib->vm_id);
+ radeon_ring_write(ring, PACKET3(PACKET3_SURFACE_SYNC, 3));
+ radeon_ring_write(ring, PACKET3_TCL1_ACTION_ENA |
+ PACKET3_TC_ACTION_ENA |
+ PACKET3_SH_KCACHE_ACTION_ENA |
+ PACKET3_SH_ICACHE_ACTION_ENA);
+ radeon_ring_write(ring, 0xFFFFFFFF);
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, 10); /* poll interval */
+}
+
+/*
+ * CP.
+ */
+static void si_cp_enable(struct radeon_device *rdev, bool enable)
+{
+ if (enable)
+ WREG32(CP_ME_CNTL, 0);
+ else {
+ radeon_ttm_set_active_vram_size(rdev, rdev->mc.visible_vram_size);
+ WREG32(CP_ME_CNTL, (CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT));
+ WREG32(SCRATCH_UMSK, 0);
+ }
+ udelay(50);
+}
+
+static int si_cp_load_microcode(struct radeon_device *rdev)
+{
+ const __be32 *fw_data;
+ int i;
+
+ if (!rdev->me_fw || !rdev->pfp_fw)
+ return -EINVAL;
+
+ si_cp_enable(rdev, false);
+
+ /* PFP */
+ fw_data = (const __be32 *)rdev->pfp_fw->data;
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+ for (i = 0; i < SI_PFP_UCODE_SIZE; i++)
+ WREG32(CP_PFP_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+
+ /* CE */
+ fw_data = (const __be32 *)rdev->ce_fw->data;
+ WREG32(CP_CE_UCODE_ADDR, 0);
+ for (i = 0; i < SI_CE_UCODE_SIZE; i++)
+ WREG32(CP_CE_UCODE_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_CE_UCODE_ADDR, 0);
+
+ /* ME */
+ fw_data = (const __be32 *)rdev->me_fw->data;
+ WREG32(CP_ME_RAM_WADDR, 0);
+ for (i = 0; i < SI_PM4_UCODE_SIZE; i++)
+ WREG32(CP_ME_RAM_DATA, be32_to_cpup(fw_data++));
+ WREG32(CP_ME_RAM_WADDR, 0);
+
+ WREG32(CP_PFP_UCODE_ADDR, 0);
+ WREG32(CP_CE_UCODE_ADDR, 0);
+ WREG32(CP_ME_RAM_WADDR, 0);
+ WREG32(CP_ME_RAM_RADDR, 0);
+ return 0;
+}
+
+static int si_cp_start(struct radeon_device *rdev)
+{
+ struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ int r, i;
+
+ r = radeon_ring_lock(rdev, ring, 7 + 4);
+ if (r) {
+ DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r);
+ return r;
+ }
+ /* init the CP */
+ radeon_ring_write(ring, PACKET3(PACKET3_ME_INITIALIZE, 5));
+ radeon_ring_write(ring, 0x1);
+ radeon_ring_write(ring, 0x0);
+ radeon_ring_write(ring, rdev->config.si.max_hw_contexts - 1);
+ radeon_ring_write(ring, PACKET3_ME_INITIALIZE_DEVICE_ID(1));
+ radeon_ring_write(ring, 0);
+ radeon_ring_write(ring, 0);
+
+ /* init the CE partitions */
+ radeon_ring_write(ring, PACKET3(PACKET3_SET_BASE, 2));
+ radeon_ring_write(ring, PACKET3_BASE_INDEX(CE_PARTITION_BASE));
+ radeon_ring_write(ring, 0xc000);
+ radeon_ring_write(ring, 0xe000);
+ radeon_ring_unlock_commit(rdev, ring);
+
+ si_cp_enable(rdev, true);
+
+ r = radeon_ring_lock(rdev, ring, si_default_size + 10);
+ if (r) {
+ DRM_ERROR("radeon: cp failed to lock ring (%d).\n", r);
+ return r;
+ }
+
+ /* setup clear context state */
+ radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+ radeon_ring_write(ring, PACKET3_PREAMBLE_BEGIN_CLEAR_STATE);
+
+ for (i = 0; i < si_default_size; i++)
+ radeon_ring_write(ring, si_default_state[i]);
+
+ radeon_ring_write(ring, PACKET3(PACKET3_PREAMBLE_CNTL, 0));
+ radeon_ring_write(ring, PACKET3_PREAMBLE_END_CLEAR_STATE);
+
+ /* set clear context state */
+ radeon_ring_write(ring, PACKET3(PACKET3_CLEAR_STATE, 0));
+ radeon_ring_write(ring, 0);
+
+ radeon_ring_write(ring, PACKET3(PACKET3_SET_CONTEXT_REG, 2));
+ radeon_ring_write(ring, 0x00000316);
+ radeon_ring_write(ring, 0x0000000e); /* VGT_VERTEX_REUSE_BLOCK_CNTL */
+ radeon_ring_write(ring, 0x00000010); /* VGT_OUT_DEALLOC_CNTL */
+
+ radeon_ring_unlock_commit(rdev, ring);
+
+ for (i = RADEON_RING_TYPE_GFX_INDEX; i <= CAYMAN_RING_TYPE_CP2_INDEX; ++i) {
+ ring = &rdev->ring[i];
+ r = radeon_ring_lock(rdev, ring, 2);
+
+ /* clear the compute context state */
+ radeon_ring_write(ring, PACKET3_COMPUTE(PACKET3_CLEAR_STATE, 0));
+ radeon_ring_write(ring, 0);
+
+ radeon_ring_unlock_commit(rdev, ring);
+ }
+
+ return 0;
+}
+
+static void si_cp_fini(struct radeon_device *rdev)
+{
+ si_cp_enable(rdev, false);
+ radeon_ring_fini(rdev, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+ radeon_ring_fini(rdev, &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]);
+ radeon_ring_fini(rdev, &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]);
+}
+
+static int si_cp_resume(struct radeon_device *rdev)
+{
+ struct radeon_ring *ring;
+ u32 tmp;
+ u32 rb_bufsz;
+ int r;
+
+ /* Reset cp; if cp is reset, then PA, SH, VGT also need to be reset */
+ WREG32(GRBM_SOFT_RESET, (SOFT_RESET_CP |
+ SOFT_RESET_PA |
+ SOFT_RESET_VGT |
+ SOFT_RESET_SPI |
+ SOFT_RESET_SX));
+ RREG32(GRBM_SOFT_RESET);
+ mdelay(15);
+ WREG32(GRBM_SOFT_RESET, 0);
+ RREG32(GRBM_SOFT_RESET);
+
+ WREG32(CP_SEM_WAIT_TIMER, 0x0);
+ WREG32(CP_SEM_INCOMPLETE_TIMER_CNTL, 0x0);
+
+ /* Set the write pointer delay */
+ WREG32(CP_RB_WPTR_DELAY, 0);
+
+ WREG32(CP_DEBUG, 0);
+ WREG32(SCRATCH_ADDR, ((rdev->wb.gpu_addr + RADEON_WB_SCRATCH_OFFSET) >> 8) & 0xFFFFFFFF);
+
+ /* ring 0 - compute and gfx */
+ /* Set ring buffer size */
+ ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ rb_bufsz = drm_order(ring->ring_size / 8);
+ tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+#ifdef __BIG_ENDIAN
+ tmp |= BUF_SWAP_32BIT;
+#endif
+ WREG32(CP_RB0_CNTL, tmp);
+
+ /* Initialize the ring buffer's read and write pointers */
+ WREG32(CP_RB0_CNTL, tmp | RB_RPTR_WR_ENA);
+ ring->wptr = 0;
+ WREG32(CP_RB0_WPTR, ring->wptr);
+
+ /* set the wb address wether it's enabled or not */
+ WREG32(CP_RB0_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFFFFFFFC);
+ WREG32(CP_RB0_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP_RPTR_OFFSET) & 0xFF);
+
+ if (rdev->wb.enabled)
+ WREG32(SCRATCH_UMSK, 0xff);
+ else {
+ tmp |= RB_NO_UPDATE;
+ WREG32(SCRATCH_UMSK, 0);
+ }
+
+ mdelay(1);
+ WREG32(CP_RB0_CNTL, tmp);
+
+ WREG32(CP_RB0_BASE, ring->gpu_addr >> 8);
+
+ ring->rptr = RREG32(CP_RB0_RPTR);
+
+ /* ring1 - compute only */
+ /* Set ring buffer size */
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
+ rb_bufsz = drm_order(ring->ring_size / 8);
+ tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+#ifdef __BIG_ENDIAN
+ tmp |= BUF_SWAP_32BIT;
+#endif
+ WREG32(CP_RB1_CNTL, tmp);
+
+ /* Initialize the ring buffer's read and write pointers */
+ WREG32(CP_RB1_CNTL, tmp | RB_RPTR_WR_ENA);
+ ring->wptr = 0;
+ WREG32(CP_RB1_WPTR, ring->wptr);
+
+ /* set the wb address wether it's enabled or not */
+ WREG32(CP_RB1_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFFFFFFFC);
+ WREG32(CP_RB1_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP1_RPTR_OFFSET) & 0xFF);
+
+ mdelay(1);
+ WREG32(CP_RB1_CNTL, tmp);
+
+ WREG32(CP_RB1_BASE, ring->gpu_addr >> 8);
+
+ ring->rptr = RREG32(CP_RB1_RPTR);
+
+ /* ring2 - compute only */
+ /* Set ring buffer size */
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
+ rb_bufsz = drm_order(ring->ring_size / 8);
+ tmp = (drm_order(RADEON_GPU_PAGE_SIZE/8) << 8) | rb_bufsz;
+#ifdef __BIG_ENDIAN
+ tmp |= BUF_SWAP_32BIT;
+#endif
+ WREG32(CP_RB2_CNTL, tmp);
+
+ /* Initialize the ring buffer's read and write pointers */
+ WREG32(CP_RB2_CNTL, tmp | RB_RPTR_WR_ENA);
+ ring->wptr = 0;
+ WREG32(CP_RB2_WPTR, ring->wptr);
+
+ /* set the wb address wether it's enabled or not */
+ WREG32(CP_RB2_RPTR_ADDR, (rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFFFFFFFC);
+ WREG32(CP_RB2_RPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + RADEON_WB_CP2_RPTR_OFFSET) & 0xFF);
+
+ mdelay(1);
+ WREG32(CP_RB2_CNTL, tmp);
+
+ WREG32(CP_RB2_BASE, ring->gpu_addr >> 8);
+
+ ring->rptr = RREG32(CP_RB2_RPTR);
+
+ /* start the rings */
+ si_cp_start(rdev);
+ rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = true;
+ rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = true;
+ rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = true;
+ r = radeon_ring_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+ if (r) {
+ rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false;
+ rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false;
+ rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false;
+ return r;
+ }
+ r = radeon_ring_test(rdev, CAYMAN_RING_TYPE_CP1_INDEX, &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]);
+ if (r) {
+ rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false;
+ }
+ r = radeon_ring_test(rdev, CAYMAN_RING_TYPE_CP2_INDEX, &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]);
+ if (r) {
+ rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false;
+ }
+
+ return 0;
+}
+
+bool si_gpu_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
+{
+ u32 srbm_status;
+ u32 grbm_status, grbm_status2;
+ u32 grbm_status_se0, grbm_status_se1;
+ struct r100_gpu_lockup *lockup = &rdev->config.si.lockup;
+ int r;
+
+ srbm_status = RREG32(SRBM_STATUS);
+ grbm_status = RREG32(GRBM_STATUS);
+ grbm_status2 = RREG32(GRBM_STATUS2);
+ grbm_status_se0 = RREG32(GRBM_STATUS_SE0);
+ grbm_status_se1 = RREG32(GRBM_STATUS_SE1);
+ if (!(grbm_status & GUI_ACTIVE)) {
+ r100_gpu_lockup_update(lockup, ring);
+ return false;
+ }
+ /* force CP activities */
+ r = radeon_ring_lock(rdev, ring, 2);
+ if (!r) {
+ /* PACKET2 NOP */
+ radeon_ring_write(ring, 0x80000000);
+ radeon_ring_write(ring, 0x80000000);
+ radeon_ring_unlock_commit(rdev, ring);
+ }
+ /* XXX deal with CP0,1,2 */
+ ring->rptr = RREG32(ring->rptr_reg);
+ return r100_gpu_cp_is_lockup(rdev, lockup, ring);
+}
+
+static int si_gpu_soft_reset(struct radeon_device *rdev)
+{
+ struct evergreen_mc_save save;
+ u32 grbm_reset = 0;
+
+ if (!(RREG32(GRBM_STATUS) & GUI_ACTIVE))
+ return 0;
+
+ dev_info(rdev->dev, "GPU softreset \n");
+ dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n",
+ RREG32(GRBM_STATUS));
+ dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n",
+ RREG32(GRBM_STATUS2));
+ dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n",
+ RREG32(GRBM_STATUS_SE0));
+ dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n",
+ RREG32(GRBM_STATUS_SE1));
+ dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n",
+ RREG32(SRBM_STATUS));
+ evergreen_mc_stop(rdev, &save);
+ if (radeon_mc_wait_for_idle(rdev)) {
+ dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+ }
+ /* Disable CP parsing/prefetching */
+ WREG32(CP_ME_CNTL, CP_ME_HALT | CP_PFP_HALT | CP_CE_HALT);
+
+ /* reset all the gfx blocks */
+ grbm_reset = (SOFT_RESET_CP |
+ SOFT_RESET_CB |
+ SOFT_RESET_DB |
+ SOFT_RESET_GDS |
+ SOFT_RESET_PA |
+ SOFT_RESET_SC |
+ SOFT_RESET_SPI |
+ SOFT_RESET_SX |
+ SOFT_RESET_TC |
+ SOFT_RESET_TA |
+ SOFT_RESET_VGT |
+ SOFT_RESET_IA);
+
+ dev_info(rdev->dev, " GRBM_SOFT_RESET=0x%08X\n", grbm_reset);
+ WREG32(GRBM_SOFT_RESET, grbm_reset);
+ (void)RREG32(GRBM_SOFT_RESET);
+ udelay(50);
+ WREG32(GRBM_SOFT_RESET, 0);
+ (void)RREG32(GRBM_SOFT_RESET);
+ /* Wait a little for things to settle down */
+ udelay(50);
+ dev_info(rdev->dev, " GRBM_STATUS=0x%08X\n",
+ RREG32(GRBM_STATUS));
+ dev_info(rdev->dev, " GRBM_STATUS2=0x%08X\n",
+ RREG32(GRBM_STATUS2));
+ dev_info(rdev->dev, " GRBM_STATUS_SE0=0x%08X\n",
+ RREG32(GRBM_STATUS_SE0));
+ dev_info(rdev->dev, " GRBM_STATUS_SE1=0x%08X\n",
+ RREG32(GRBM_STATUS_SE1));
+ dev_info(rdev->dev, " SRBM_STATUS=0x%08X\n",
+ RREG32(SRBM_STATUS));
+ evergreen_mc_resume(rdev, &save);
+ return 0;
+}
+
+int si_asic_reset(struct radeon_device *rdev)
+{
+ return si_gpu_soft_reset(rdev);
+}
+
+/* MC */
+static void si_mc_program(struct radeon_device *rdev)
+{
+ struct evergreen_mc_save save;
+ u32 tmp;
+ int i, j;
+
+ /* Initialize HDP */
+ for (i = 0, j = 0; i < 32; i++, j += 0x18) {
+ WREG32((0x2c14 + j), 0x00000000);
+ WREG32((0x2c18 + j), 0x00000000);
+ WREG32((0x2c1c + j), 0x00000000);
+ WREG32((0x2c20 + j), 0x00000000);
+ WREG32((0x2c24 + j), 0x00000000);
+ }
+ WREG32(HDP_REG_COHERENCY_FLUSH_CNTL, 0);
+
+ evergreen_mc_stop(rdev, &save);
+ if (radeon_mc_wait_for_idle(rdev)) {
+ dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+ }
+ /* Lockout access through VGA aperture*/
+ WREG32(VGA_HDP_CONTROL, VGA_MEMORY_DISABLE);
+ /* Update configuration */
+ WREG32(MC_VM_SYSTEM_APERTURE_LOW_ADDR,
+ rdev->mc.vram_start >> 12);
+ WREG32(MC_VM_SYSTEM_APERTURE_HIGH_ADDR,
+ rdev->mc.vram_end >> 12);
+ WREG32(MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR,
+ rdev->vram_scratch.gpu_addr >> 12);
+ tmp = ((rdev->mc.vram_end >> 24) & 0xFFFF) << 16;
+ tmp |= ((rdev->mc.vram_start >> 24) & 0xFFFF);
+ WREG32(MC_VM_FB_LOCATION, tmp);
+ /* XXX double check these! */
+ WREG32(HDP_NONSURFACE_BASE, (rdev->mc.vram_start >> 8));
+ WREG32(HDP_NONSURFACE_INFO, (2 << 7) | (1 << 30));
+ WREG32(HDP_NONSURFACE_SIZE, 0x3FFFFFFF);
+ WREG32(MC_VM_AGP_BASE, 0);
+ WREG32(MC_VM_AGP_TOP, 0x0FFFFFFF);
+ WREG32(MC_VM_AGP_BOT, 0x0FFFFFFF);
+ if (radeon_mc_wait_for_idle(rdev)) {
+ dev_warn(rdev->dev, "Wait for MC idle timedout !\n");
+ }
+ evergreen_mc_resume(rdev, &save);
+ /* we need to own VRAM, so turn off the VGA renderer here
+ * to stop it overwriting our objects */
+ rv515_vga_render_disable(rdev);
+}
+
+/* SI MC address space is 40 bits */
+static void si_vram_location(struct radeon_device *rdev,
+ struct radeon_mc *mc, u64 base)
+{
+ mc->vram_start = base;
+ if (mc->mc_vram_size > (0xFFFFFFFFFFULL - base + 1)) {
+ dev_warn(rdev->dev, "limiting VRAM to PCI aperture size\n");
+ mc->real_vram_size = mc->aper_size;
+ mc->mc_vram_size = mc->aper_size;
+ }
+ mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;
+ dev_info(rdev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n",
+ mc->mc_vram_size >> 20, mc->vram_start,
+ mc->vram_end, mc->real_vram_size >> 20);
+}
+
+static void si_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc)
+{
+ u64 size_af, size_bf;
+
+ size_af = ((0xFFFFFFFFFFULL - mc->vram_end) + mc->gtt_base_align) & ~mc->gtt_base_align;
+ size_bf = mc->vram_start & ~mc->gtt_base_align;
+ if (size_bf > size_af) {
+ if (mc->gtt_size > size_bf) {
+ dev_warn(rdev->dev, "limiting GTT\n");
+ mc->gtt_size = size_bf;
+ }
+ mc->gtt_start = (mc->vram_start & ~mc->gtt_base_align) - mc->gtt_size;
+ } else {
+ if (mc->gtt_size > size_af) {
+ dev_warn(rdev->dev, "limiting GTT\n");
+ mc->gtt_size = size_af;
+ }
+ mc->gtt_start = (mc->vram_end + 1 + mc->gtt_base_align) & ~mc->gtt_base_align;
+ }
+ mc->gtt_end = mc->gtt_start + mc->gtt_size - 1;
+ dev_info(rdev->dev, "GTT: %lluM 0x%016llX - 0x%016llX\n",
+ mc->gtt_size >> 20, mc->gtt_start, mc->gtt_end);
+}
+
+static void si_vram_gtt_location(struct radeon_device *rdev,
+ struct radeon_mc *mc)
+{
+ if (mc->mc_vram_size > 0xFFC0000000ULL) {
+ /* leave room for at least 1024M GTT */
+ dev_warn(rdev->dev, "limiting VRAM\n");
+ mc->real_vram_size = 0xFFC0000000ULL;
+ mc->mc_vram_size = 0xFFC0000000ULL;
+ }
+ si_vram_location(rdev, &rdev->mc, 0);
+ rdev->mc.gtt_base_align = 0;
+ si_gtt_location(rdev, mc);
+}
+
+static int si_mc_init(struct radeon_device *rdev)
+{
+ u32 tmp;
+ int chansize, numchan;
+
+ /* Get VRAM informations */
+ rdev->mc.vram_is_ddr = true;
+ tmp = RREG32(MC_ARB_RAMCFG);
+ if (tmp & CHANSIZE_OVERRIDE) {
+ chansize = 16;
+ } else if (tmp & CHANSIZE_MASK) {
+ chansize = 64;
+ } else {
+ chansize = 32;
+ }
+ tmp = RREG32(MC_SHARED_CHMAP);
+ switch ((tmp & NOOFCHAN_MASK) >> NOOFCHAN_SHIFT) {
+ case 0:
+ default:
+ numchan = 1;
+ break;
+ case 1:
+ numchan = 2;
+ break;
+ case 2:
+ numchan = 4;
+ break;
+ case 3:
+ numchan = 8;
+ break;
+ case 4:
+ numchan = 3;
+ break;
+ case 5:
+ numchan = 6;
+ break;
+ case 6:
+ numchan = 10;
+ break;
+ case 7:
+ numchan = 12;
+ break;
+ case 8:
+ numchan = 16;
+ break;
+ }
+ rdev->mc.vram_width = numchan * chansize;
+ /* Could aper size report 0 ? */
+ rdev->mc.aper_base = pci_resource_start(rdev->pdev, 0);
+ rdev->mc.aper_size = pci_resource_len(rdev->pdev, 0);
+ /* size in MB on si */
+ rdev->mc.mc_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+ rdev->mc.real_vram_size = RREG32(CONFIG_MEMSIZE) * 1024 * 1024;
+ rdev->mc.visible_vram_size = rdev->mc.aper_size;
+ si_vram_gtt_location(rdev, &rdev->mc);
+ radeon_update_bandwidth_info(rdev);
+
+ return 0;
+}
+
+/*
+ * GART
+ */
+void si_pcie_gart_tlb_flush(struct radeon_device *rdev)
+{
+ /* flush hdp cache */
+ WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
+
+ /* bits 0-15 are the VM contexts0-15 */
+ WREG32(VM_INVALIDATE_REQUEST, 1);
+}
+
+int si_pcie_gart_enable(struct radeon_device *rdev)
+{
+ int r, i;
+
+ if (rdev->gart.robj == NULL) {
+ dev_err(rdev->dev, "No VRAM object for PCIE GART.\n");
+ return -EINVAL;
+ }
+ r = radeon_gart_table_vram_pin(rdev);
+ if (r)
+ return r;
+ radeon_gart_restore(rdev);
+ /* Setup TLB control */
+ WREG32(MC_VM_MX_L1_TLB_CNTL,
+ (0xA << 7) |
+ ENABLE_L1_TLB |
+ SYSTEM_ACCESS_MODE_NOT_IN_SYS |
+ ENABLE_ADVANCED_DRIVER_MODEL |
+ SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
+ /* Setup L2 cache */
+ WREG32(VM_L2_CNTL, ENABLE_L2_CACHE |
+ ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
+ ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE |
+ EFFECTIVE_L2_QUEUE_SIZE(7) |
+ CONTEXT1_IDENTITY_ACCESS_MODE(1));
+ WREG32(VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS | INVALIDATE_L2_CACHE);
+ WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
+ L2_CACHE_BIGK_FRAGMENT_SIZE(0));
+ /* setup context0 */
+ WREG32(VM_CONTEXT0_PAGE_TABLE_START_ADDR, rdev->mc.gtt_start >> 12);
+ WREG32(VM_CONTEXT0_PAGE_TABLE_END_ADDR, rdev->mc.gtt_end >> 12);
+ WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR, rdev->gart.table_addr >> 12);
+ WREG32(VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
+ (u32)(rdev->dummy_page.addr >> 12));
+ WREG32(VM_CONTEXT0_CNTL2, 0);
+ WREG32(VM_CONTEXT0_CNTL, (ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) |
+ RANGE_PROTECTION_FAULT_ENABLE_DEFAULT));
+
+ WREG32(0x15D4, 0);
+ WREG32(0x15D8, 0);
+ WREG32(0x15DC, 0);
+
+ /* empty context1-15 */
+ /* FIXME start with 1G, once using 2 level pt switch to full
+ * vm size space
+ */
+ /* set vm size, must be a multiple of 4 */
+ WREG32(VM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
+ WREG32(VM_CONTEXT1_PAGE_TABLE_END_ADDR, (1 << 30) / RADEON_GPU_PAGE_SIZE);
+ for (i = 1; i < 16; i++) {
+ if (i < 8)
+ WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (i << 2),
+ rdev->gart.table_addr >> 12);
+ else
+ WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((i - 8) << 2),
+ rdev->gart.table_addr >> 12);
+ }
+
+ /* enable context1-15 */
+ WREG32(VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR,
+ (u32)(rdev->dummy_page.addr >> 12));
+ WREG32(VM_CONTEXT1_CNTL2, 0);
+ WREG32(VM_CONTEXT1_CNTL, ENABLE_CONTEXT | PAGE_TABLE_DEPTH(0) |
+ RANGE_PROTECTION_FAULT_ENABLE_DEFAULT);
+
+ si_pcie_gart_tlb_flush(rdev);
+ DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
+ (unsigned)(rdev->mc.gtt_size >> 20),
+ (unsigned long long)rdev->gart.table_addr);
+ rdev->gart.ready = true;
+ return 0;
+}
+
+void si_pcie_gart_disable(struct radeon_device *rdev)
+{
+ /* Disable all tables */
+ WREG32(VM_CONTEXT0_CNTL, 0);
+ WREG32(VM_CONTEXT1_CNTL, 0);
+ /* Setup TLB control */
+ WREG32(MC_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE_NOT_IN_SYS |
+ SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU);
+ /* Setup L2 cache */
+ WREG32(VM_L2_CNTL, ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE |
+ ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE |
+ EFFECTIVE_L2_QUEUE_SIZE(7) |
+ CONTEXT1_IDENTITY_ACCESS_MODE(1));
+ WREG32(VM_L2_CNTL2, 0);
+ WREG32(VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY |
+ L2_CACHE_BIGK_FRAGMENT_SIZE(0));
+ radeon_gart_table_vram_unpin(rdev);
+}
+
+void si_pcie_gart_fini(struct radeon_device *rdev)
+{
+ si_pcie_gart_disable(rdev);
+ radeon_gart_table_vram_free(rdev);
+ radeon_gart_fini(rdev);
+}
+
+/* vm parser */
+static bool si_vm_reg_valid(u32 reg)
+{
+ /* context regs are fine */
+ if (reg >= 0x28000)
+ return true;
+
+ /* check config regs */
+ switch (reg) {
+ case GRBM_GFX_INDEX:
+ case VGT_VTX_VECT_EJECT_REG:
+ case VGT_CACHE_INVALIDATION:
+ case VGT_ESGS_RING_SIZE:
+ case VGT_GSVS_RING_SIZE:
+ case VGT_GS_VERTEX_REUSE:
+ case VGT_PRIMITIVE_TYPE:
+ case VGT_INDEX_TYPE:
+ case VGT_NUM_INDICES:
+ case VGT_NUM_INSTANCES:
+ case VGT_TF_RING_SIZE:
+ case VGT_HS_OFFCHIP_PARAM:
+ case VGT_TF_MEMORY_BASE:
+ case PA_CL_ENHANCE:
+ case PA_SU_LINE_STIPPLE_VALUE:
+ case PA_SC_LINE_STIPPLE_STATE:
+ case PA_SC_ENHANCE:
+ case SQC_CACHES:
+ case SPI_STATIC_THREAD_MGMT_1:
+ case SPI_STATIC_THREAD_MGMT_2:
+ case SPI_STATIC_THREAD_MGMT_3:
+ case SPI_PS_MAX_WAVE_ID:
+ case SPI_CONFIG_CNTL:
+ case SPI_CONFIG_CNTL_1:
+ case TA_CNTL_AUX:
+ return true;
+ default:
+ DRM_ERROR("Invalid register 0x%x in CS\n", reg);
+ return false;
+ }
+}
+
+static int si_vm_packet3_ce_check(struct radeon_device *rdev,
+ u32 *ib, struct radeon_cs_packet *pkt)
+{
+ switch (pkt->opcode) {
+ case PACKET3_NOP:
+ case PACKET3_SET_BASE:
+ case PACKET3_SET_CE_DE_COUNTERS:
+ case PACKET3_LOAD_CONST_RAM:
+ case PACKET3_WRITE_CONST_RAM:
+ case PACKET3_WRITE_CONST_RAM_OFFSET:
+ case PACKET3_DUMP_CONST_RAM:
+ case PACKET3_INCREMENT_CE_COUNTER:
+ case PACKET3_WAIT_ON_DE_COUNTER:
+ case PACKET3_CE_WRITE:
+ break;
+ default:
+ DRM_ERROR("Invalid CE packet3: 0x%x\n", pkt->opcode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int si_vm_packet3_gfx_check(struct radeon_device *rdev,
+ u32 *ib, struct radeon_cs_packet *pkt)
+{
+ u32 idx = pkt->idx + 1;
+ u32 idx_value = ib[idx];
+ u32 start_reg, end_reg, reg, i;
+
+ switch (pkt->opcode) {
+ case PACKET3_NOP:
+ case PACKET3_SET_BASE:
+ case PACKET3_CLEAR_STATE:
+ case PACKET3_INDEX_BUFFER_SIZE:
+ case PACKET3_DISPATCH_DIRECT:
+ case PACKET3_DISPATCH_INDIRECT:
+ case PACKET3_ALLOC_GDS:
+ case PACKET3_WRITE_GDS_RAM:
+ case PACKET3_ATOMIC_GDS:
+ case PACKET3_ATOMIC:
+ case PACKET3_OCCLUSION_QUERY:
+ case PACKET3_SET_PREDICATION:
+ case PACKET3_COND_EXEC:
+ case PACKET3_PRED_EXEC:
+ case PACKET3_DRAW_INDIRECT:
+ case PACKET3_DRAW_INDEX_INDIRECT:
+ case PACKET3_INDEX_BASE:
+ case PACKET3_DRAW_INDEX_2:
+ case PACKET3_CONTEXT_CONTROL:
+ case PACKET3_INDEX_TYPE:
+ case PACKET3_DRAW_INDIRECT_MULTI:
+ case PACKET3_DRAW_INDEX_AUTO:
+ case PACKET3_DRAW_INDEX_IMMD:
+ case PACKET3_NUM_INSTANCES:
+ case PACKET3_DRAW_INDEX_MULTI_AUTO:
+ case PACKET3_STRMOUT_BUFFER_UPDATE:
+ case PACKET3_DRAW_INDEX_OFFSET_2:
+ case PACKET3_DRAW_INDEX_MULTI_ELEMENT:
+ case PACKET3_DRAW_INDEX_INDIRECT_MULTI:
+ case PACKET3_MPEG_INDEX:
+ case PACKET3_WAIT_REG_MEM:
+ case PACKET3_MEM_WRITE:
+ case PACKET3_PFP_SYNC_ME:
+ case PACKET3_SURFACE_SYNC:
+ case PACKET3_EVENT_WRITE:
+ case PACKET3_EVENT_WRITE_EOP:
+ case PACKET3_EVENT_WRITE_EOS:
+ case PACKET3_SET_CONTEXT_REG:
+ case PACKET3_SET_CONTEXT_REG_INDIRECT:
+ case PACKET3_SET_SH_REG:
+ case PACKET3_SET_SH_REG_OFFSET:
+ case PACKET3_INCREMENT_DE_COUNTER:
+ case PACKET3_WAIT_ON_CE_COUNTER:
+ case PACKET3_WAIT_ON_AVAIL_BUFFER:
+ case PACKET3_ME_WRITE:
+ break;
+ case PACKET3_COPY_DATA:
+ if ((idx_value & 0xf00) == 0) {
+ reg = ib[idx + 3] * 4;
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ case PACKET3_WRITE_DATA:
+ if ((idx_value & 0xf00) == 0) {
+ start_reg = ib[idx + 1] * 4;
+ if (idx_value & 0x10000) {
+ if (!si_vm_reg_valid(start_reg))
+ return -EINVAL;
+ } else {
+ for (i = 0; i < (pkt->count - 2); i++) {
+ reg = start_reg + (4 * i);
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ }
+ }
+ break;
+ case PACKET3_COND_WRITE:
+ if (idx_value & 0x100) {
+ reg = ib[idx + 5] * 4;
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ case PACKET3_COPY_DW:
+ if (idx_value & 0x2) {
+ reg = ib[idx + 3] * 4;
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ case PACKET3_SET_CONFIG_REG:
+ start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START;
+ end_reg = 4 * pkt->count + start_reg - 4;
+ if ((start_reg < PACKET3_SET_CONFIG_REG_START) ||
+ (start_reg >= PACKET3_SET_CONFIG_REG_END) ||
+ (end_reg >= PACKET3_SET_CONFIG_REG_END)) {
+ DRM_ERROR("bad PACKET3_SET_CONFIG_REG\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < pkt->count; i++) {
+ reg = start_reg + (4 * i);
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ default:
+ DRM_ERROR("Invalid GFX packet3: 0x%x\n", pkt->opcode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int si_vm_packet3_compute_check(struct radeon_device *rdev,
+ u32 *ib, struct radeon_cs_packet *pkt)
+{
+ u32 idx = pkt->idx + 1;
+ u32 idx_value = ib[idx];
+ u32 start_reg, reg, i;
+
+ switch (pkt->opcode) {
+ case PACKET3_NOP:
+ case PACKET3_SET_BASE:
+ case PACKET3_CLEAR_STATE:
+ case PACKET3_DISPATCH_DIRECT:
+ case PACKET3_DISPATCH_INDIRECT:
+ case PACKET3_ALLOC_GDS:
+ case PACKET3_WRITE_GDS_RAM:
+ case PACKET3_ATOMIC_GDS:
+ case PACKET3_ATOMIC:
+ case PACKET3_OCCLUSION_QUERY:
+ case PACKET3_SET_PREDICATION:
+ case PACKET3_COND_EXEC:
+ case PACKET3_PRED_EXEC:
+ case PACKET3_CONTEXT_CONTROL:
+ case PACKET3_STRMOUT_BUFFER_UPDATE:
+ case PACKET3_WAIT_REG_MEM:
+ case PACKET3_MEM_WRITE:
+ case PACKET3_PFP_SYNC_ME:
+ case PACKET3_SURFACE_SYNC:
+ case PACKET3_EVENT_WRITE:
+ case PACKET3_EVENT_WRITE_EOP:
+ case PACKET3_EVENT_WRITE_EOS:
+ case PACKET3_SET_CONTEXT_REG:
+ case PACKET3_SET_CONTEXT_REG_INDIRECT:
+ case PACKET3_SET_SH_REG:
+ case PACKET3_SET_SH_REG_OFFSET:
+ case PACKET3_INCREMENT_DE_COUNTER:
+ case PACKET3_WAIT_ON_CE_COUNTER:
+ case PACKET3_WAIT_ON_AVAIL_BUFFER:
+ case PACKET3_ME_WRITE:
+ break;
+ case PACKET3_COPY_DATA:
+ if ((idx_value & 0xf00) == 0) {
+ reg = ib[idx + 3] * 4;
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ case PACKET3_WRITE_DATA:
+ if ((idx_value & 0xf00) == 0) {
+ start_reg = ib[idx + 1] * 4;
+ if (idx_value & 0x10000) {
+ if (!si_vm_reg_valid(start_reg))
+ return -EINVAL;
+ } else {
+ for (i = 0; i < (pkt->count - 2); i++) {
+ reg = start_reg + (4 * i);
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ }
+ }
+ break;
+ case PACKET3_COND_WRITE:
+ if (idx_value & 0x100) {
+ reg = ib[idx + 5] * 4;
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ case PACKET3_COPY_DW:
+ if (idx_value & 0x2) {
+ reg = ib[idx + 3] * 4;
+ if (!si_vm_reg_valid(reg))
+ return -EINVAL;
+ }
+ break;
+ default:
+ DRM_ERROR("Invalid Compute packet3: 0x%x\n", pkt->opcode);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib)
+{
+ int ret = 0;
+ u32 idx = 0;
+ struct radeon_cs_packet pkt;
+
+ do {
+ pkt.idx = idx;
+ pkt.type = CP_PACKET_GET_TYPE(ib->ptr[idx]);
+ pkt.count = CP_PACKET_GET_COUNT(ib->ptr[idx]);
+ pkt.one_reg_wr = 0;
+ switch (pkt.type) {
+ case PACKET_TYPE0:
+ dev_err(rdev->dev, "Packet0 not allowed!\n");
+ ret = -EINVAL;
+ break;
+ case PACKET_TYPE2:
+ idx += 1;
+ break;
+ case PACKET_TYPE3:
+ pkt.opcode = CP_PACKET3_GET_OPCODE(ib->ptr[idx]);
+ if (ib->is_const_ib)
+ ret = si_vm_packet3_ce_check(rdev, ib->ptr, &pkt);
+ else {
+ switch (ib->fence->ring) {
+ case RADEON_RING_TYPE_GFX_INDEX:
+ ret = si_vm_packet3_gfx_check(rdev, ib->ptr, &pkt);
+ break;
+ case CAYMAN_RING_TYPE_CP1_INDEX:
+ case CAYMAN_RING_TYPE_CP2_INDEX:
+ ret = si_vm_packet3_compute_check(rdev, ib->ptr, &pkt);
+ break;
+ default:
+ dev_err(rdev->dev, "Non-PM4 ring %d !\n", ib->fence->ring);
+ ret = -EINVAL;
+ break;
+ }
+ }
+ idx += pkt.count + 2;
+ break;
+ default:
+ dev_err(rdev->dev, "Unknown packet type %d !\n", pkt.type);
+ ret = -EINVAL;
+ break;
+ }
+ if (ret)
+ break;
+ } while (idx < ib->length_dw);
+
+ return ret;
+}
+
+/*
+ * vm
+ */
+int si_vm_init(struct radeon_device *rdev)
+{
+ /* number of VMs */
+ rdev->vm_manager.nvm = 16;
+ /* base offset of vram pages */
+ rdev->vm_manager.vram_base_offset = 0;
+
+ return 0;
+}
+
+void si_vm_fini(struct radeon_device *rdev)
+{
+}
+
+int si_vm_bind(struct radeon_device *rdev, struct radeon_vm *vm, int id)
+{
+ if (id < 8)
+ WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (id << 2), vm->pt_gpu_addr >> 12);
+ else
+ WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((id - 8) << 2),
+ vm->pt_gpu_addr >> 12);
+ /* flush hdp cache */
+ WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
+ /* bits 0-15 are the VM contexts0-15 */
+ WREG32(VM_INVALIDATE_REQUEST, 1 << id);
+ return 0;
+}
+
+void si_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm)
+{
+ if (vm->id < 8)
+ WREG32(VM_CONTEXT0_PAGE_TABLE_BASE_ADDR + (vm->id << 2), 0);
+ else
+ WREG32(VM_CONTEXT8_PAGE_TABLE_BASE_ADDR + ((vm->id - 8) << 2), 0);
+ /* flush hdp cache */
+ WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
+ /* bits 0-15 are the VM contexts0-15 */
+ WREG32(VM_INVALIDATE_REQUEST, 1 << vm->id);
+}
+
+void si_vm_tlb_flush(struct radeon_device *rdev, struct radeon_vm *vm)
+{
+ if (vm->id == -1)
+ return;
+
+ /* flush hdp cache */
+ WREG32(HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
+ /* bits 0-15 are the VM contexts0-15 */
+ WREG32(VM_INVALIDATE_REQUEST, 1 << vm->id);
+}
+
+/*
+ * RLC
+ */
+void si_rlc_fini(struct radeon_device *rdev)
+{
+ int r;
+
+ /* save restore block */
+ if (rdev->rlc.save_restore_obj) {
+ r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false);
+ if (unlikely(r != 0))
+ dev_warn(rdev->dev, "(%d) reserve RLC sr bo failed\n", r);
+ radeon_bo_unpin(rdev->rlc.save_restore_obj);
+ radeon_bo_unreserve(rdev->rlc.save_restore_obj);
+
+ radeon_bo_unref(&rdev->rlc.save_restore_obj);
+ rdev->rlc.save_restore_obj = NULL;
+ }
+
+ /* clear state block */
+ if (rdev->rlc.clear_state_obj) {
+ r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false);
+ if (unlikely(r != 0))
+ dev_warn(rdev->dev, "(%d) reserve RLC c bo failed\n", r);
+ radeon_bo_unpin(rdev->rlc.clear_state_obj);
+ radeon_bo_unreserve(rdev->rlc.clear_state_obj);
+
+ radeon_bo_unref(&rdev->rlc.clear_state_obj);
+ rdev->rlc.clear_state_obj = NULL;
+ }
+}
+
+int si_rlc_init(struct radeon_device *rdev)
+{
+ int r;
+
+ /* save restore block */
+ if (rdev->rlc.save_restore_obj == NULL) {
+ r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true,
+ RADEON_GEM_DOMAIN_VRAM, &rdev->rlc.save_restore_obj);
+ if (r) {
+ dev_warn(rdev->dev, "(%d) create RLC sr bo failed\n", r);
+ return r;
+ }
+ }
+
+ r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false);
+ if (unlikely(r != 0)) {
+ si_rlc_fini(rdev);
+ return r;
+ }
+ r = radeon_bo_pin(rdev->rlc.save_restore_obj, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->rlc.save_restore_gpu_addr);
+ if (r) {
+ radeon_bo_unreserve(rdev->rlc.save_restore_obj);
+ dev_warn(rdev->dev, "(%d) pin RLC sr bo failed\n", r);
+ si_rlc_fini(rdev);
+ return r;
+ }
+
+ /* clear state block */
+ if (rdev->rlc.clear_state_obj == NULL) {
+ r = radeon_bo_create(rdev, RADEON_GPU_PAGE_SIZE, PAGE_SIZE, true,
+ RADEON_GEM_DOMAIN_VRAM, &rdev->rlc.clear_state_obj);
+ if (r) {
+ dev_warn(rdev->dev, "(%d) create RLC c bo failed\n", r);
+ si_rlc_fini(rdev);
+ return r;
+ }
+ }
+ r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false);
+ if (unlikely(r != 0)) {
+ si_rlc_fini(rdev);
+ return r;
+ }
+ r = radeon_bo_pin(rdev->rlc.clear_state_obj, RADEON_GEM_DOMAIN_VRAM,
+ &rdev->rlc.clear_state_gpu_addr);
+ if (r) {
+
+ radeon_bo_unreserve(rdev->rlc.clear_state_obj);
+ dev_warn(rdev->dev, "(%d) pin RLC c bo failed\n", r);
+ si_rlc_fini(rdev);
+ return r;
+ }
+
+ return 0;
+}
+
+static void si_rlc_stop(struct radeon_device *rdev)
+{
+ WREG32(RLC_CNTL, 0);
+}
+
+static void si_rlc_start(struct radeon_device *rdev)
+{
+ WREG32(RLC_CNTL, RLC_ENABLE);
+}
+
+static int si_rlc_resume(struct radeon_device *rdev)
+{
+ u32 i;
+ const __be32 *fw_data;
+
+ if (!rdev->rlc_fw)
+ return -EINVAL;
+
+ si_rlc_stop(rdev);
+
+ WREG32(RLC_RL_BASE, 0);
+ WREG32(RLC_RL_SIZE, 0);
+ WREG32(RLC_LB_CNTL, 0);
+ WREG32(RLC_LB_CNTR_MAX, 0xffffffff);
+ WREG32(RLC_LB_CNTR_INIT, 0);
+
+ WREG32(RLC_SAVE_AND_RESTORE_BASE, rdev->rlc.save_restore_gpu_addr >> 8);
+ WREG32(RLC_CLEAR_STATE_RESTORE_BASE, rdev->rlc.clear_state_gpu_addr >> 8);
+
+ WREG32(RLC_MC_CNTL, 0);
+ WREG32(RLC_UCODE_CNTL, 0);
+
+ fw_data = (const __be32 *)rdev->rlc_fw->data;
+ for (i = 0; i < SI_RLC_UCODE_SIZE; i++) {
+ WREG32(RLC_UCODE_ADDR, i);
+ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
+ }
+ WREG32(RLC_UCODE_ADDR, 0);
+
+ si_rlc_start(rdev);
+
+ return 0;
+}
+
+static void si_enable_interrupts(struct radeon_device *rdev)
+{
+ u32 ih_cntl = RREG32(IH_CNTL);
+ u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
+
+ ih_cntl |= ENABLE_INTR;
+ ih_rb_cntl |= IH_RB_ENABLE;
+ WREG32(IH_CNTL, ih_cntl);
+ WREG32(IH_RB_CNTL, ih_rb_cntl);
+ rdev->ih.enabled = true;
+}
+
+static void si_disable_interrupts(struct radeon_device *rdev)
+{
+ u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
+ u32 ih_cntl = RREG32(IH_CNTL);
+
+ ih_rb_cntl &= ~IH_RB_ENABLE;
+ ih_cntl &= ~ENABLE_INTR;
+ WREG32(IH_RB_CNTL, ih_rb_cntl);
+ WREG32(IH_CNTL, ih_cntl);
+ /* set rptr, wptr to 0 */
+ WREG32(IH_RB_RPTR, 0);
+ WREG32(IH_RB_WPTR, 0);
+ rdev->ih.enabled = false;
+ rdev->ih.wptr = 0;
+ rdev->ih.rptr = 0;
+}
+
+static void si_disable_interrupt_state(struct radeon_device *rdev)
+{
+ u32 tmp;
+
+ WREG32(CP_INT_CNTL_RING0, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
+ WREG32(CP_INT_CNTL_RING1, 0);
+ WREG32(CP_INT_CNTL_RING2, 0);
+ WREG32(GRBM_INT_CNTL, 0);
+ WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
+ WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
+ if (rdev->num_crtc >= 4) {
+ WREG32(INT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
+ WREG32(INT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
+ }
+ if (rdev->num_crtc >= 6) {
+ WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
+ WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+ }
+
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
+ if (rdev->num_crtc >= 4) {
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
+ }
+ if (rdev->num_crtc >= 6) {
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
+ }
+
+ WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
+
+ tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+ WREG32(DC_HPD1_INT_CONTROL, tmp);
+ tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+ WREG32(DC_HPD2_INT_CONTROL, tmp);
+ tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+ WREG32(DC_HPD3_INT_CONTROL, tmp);
+ tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+ WREG32(DC_HPD4_INT_CONTROL, tmp);
+ tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+ WREG32(DC_HPD5_INT_CONTROL, tmp);
+ tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY;
+ WREG32(DC_HPD6_INT_CONTROL, tmp);
+
+}
+
+static int si_irq_init(struct radeon_device *rdev)
+{
+ int ret = 0;
+ int rb_bufsz;
+ u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
+
+ /* allocate ring */
+ ret = r600_ih_ring_alloc(rdev);
+ if (ret)
+ return ret;
+
+ /* disable irqs */
+ si_disable_interrupts(rdev);
+
+ /* init rlc */
+ ret = si_rlc_resume(rdev);
+ if (ret) {
+ r600_ih_ring_fini(rdev);
+ return ret;
+ }
+
+ /* setup interrupt control */
+ /* set dummy read address to ring address */
+ WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8);
+ interrupt_cntl = RREG32(INTERRUPT_CNTL);
+ /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi
+ * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN
+ */
+ interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
+ /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */
+ interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
+ WREG32(INTERRUPT_CNTL, interrupt_cntl);
+
+ WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8);
+ rb_bufsz = drm_order(rdev->ih.ring_size / 4);
+
+ ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE |
+ IH_WPTR_OVERFLOW_CLEAR |
+ (rb_bufsz << 1));
+
+ if (rdev->wb.enabled)
+ ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE;
+
+ /* set the writeback address whether it's enabled or not */
+ WREG32(IH_RB_WPTR_ADDR_LO, (rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFFFFFFFC);
+ WREG32(IH_RB_WPTR_ADDR_HI, upper_32_bits(rdev->wb.gpu_addr + R600_WB_IH_WPTR_OFFSET) & 0xFF);
+
+ WREG32(IH_RB_CNTL, ih_rb_cntl);
+
+ /* set rptr, wptr to 0 */
+ WREG32(IH_RB_RPTR, 0);
+ WREG32(IH_RB_WPTR, 0);
+
+ /* Default settings for IH_CNTL (disabled at first) */
+ ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10) | MC_VMID(0);
+ /* RPTR_REARM only works if msi's are enabled */
+ if (rdev->msi_enabled)
+ ih_cntl |= RPTR_REARM;
+ WREG32(IH_CNTL, ih_cntl);
+
+ /* force the active interrupt state to all disabled */
+ si_disable_interrupt_state(rdev);
+
+ /* enable irqs */
+ si_enable_interrupts(rdev);
+
+ return ret;
+}
+
+int si_irq_set(struct radeon_device *rdev)
+{
+ u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
+ u32 cp_int_cntl1 = 0, cp_int_cntl2 = 0;
+ u32 crtc1 = 0, crtc2 = 0, crtc3 = 0, crtc4 = 0, crtc5 = 0, crtc6 = 0;
+ u32 hpd1, hpd2, hpd3, hpd4, hpd5, hpd6;
+ u32 grbm_int_cntl = 0;
+ u32 grph1 = 0, grph2 = 0, grph3 = 0, grph4 = 0, grph5 = 0, grph6 = 0;
+
+ if (!rdev->irq.installed) {
+ WARN(1, "Can't enable IRQ/MSI because no handler is installed\n");
+ return -EINVAL;
+ }
+ /* don't enable anything if the ih is disabled */
+ if (!rdev->ih.enabled) {
+ si_disable_interrupts(rdev);
+ /* force the active interrupt state to all disabled */
+ si_disable_interrupt_state(rdev);
+ return 0;
+ }
+
+ hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
+ hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
+ hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
+ hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN;
+ hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN;
+ hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
+
+ /* enable CP interrupts on all rings */
+ if (rdev->irq.sw_int[RADEON_RING_TYPE_GFX_INDEX]) {
+ DRM_DEBUG("si_irq_set: sw int gfx\n");
+ cp_int_cntl |= TIME_STAMP_INT_ENABLE;
+ }
+ if (rdev->irq.sw_int[CAYMAN_RING_TYPE_CP1_INDEX]) {
+ DRM_DEBUG("si_irq_set: sw int cp1\n");
+ cp_int_cntl1 |= TIME_STAMP_INT_ENABLE;
+ }
+ if (rdev->irq.sw_int[CAYMAN_RING_TYPE_CP2_INDEX]) {
+ DRM_DEBUG("si_irq_set: sw int cp2\n");
+ cp_int_cntl2 |= TIME_STAMP_INT_ENABLE;
+ }
+ if (rdev->irq.crtc_vblank_int[0] ||
+ rdev->irq.pflip[0]) {
+ DRM_DEBUG("si_irq_set: vblank 0\n");
+ crtc1 |= VBLANK_INT_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[1] ||
+ rdev->irq.pflip[1]) {
+ DRM_DEBUG("si_irq_set: vblank 1\n");
+ crtc2 |= VBLANK_INT_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[2] ||
+ rdev->irq.pflip[2]) {
+ DRM_DEBUG("si_irq_set: vblank 2\n");
+ crtc3 |= VBLANK_INT_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[3] ||
+ rdev->irq.pflip[3]) {
+ DRM_DEBUG("si_irq_set: vblank 3\n");
+ crtc4 |= VBLANK_INT_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[4] ||
+ rdev->irq.pflip[4]) {
+ DRM_DEBUG("si_irq_set: vblank 4\n");
+ crtc5 |= VBLANK_INT_MASK;
+ }
+ if (rdev->irq.crtc_vblank_int[5] ||
+ rdev->irq.pflip[5]) {
+ DRM_DEBUG("si_irq_set: vblank 5\n");
+ crtc6 |= VBLANK_INT_MASK;
+ }
+ if (rdev->irq.hpd[0]) {
+ DRM_DEBUG("si_irq_set: hpd 1\n");
+ hpd1 |= DC_HPDx_INT_EN;
+ }
+ if (rdev->irq.hpd[1]) {
+ DRM_DEBUG("si_irq_set: hpd 2\n");
+ hpd2 |= DC_HPDx_INT_EN;
+ }
+ if (rdev->irq.hpd[2]) {
+ DRM_DEBUG("si_irq_set: hpd 3\n");
+ hpd3 |= DC_HPDx_INT_EN;
+ }
+ if (rdev->irq.hpd[3]) {
+ DRM_DEBUG("si_irq_set: hpd 4\n");
+ hpd4 |= DC_HPDx_INT_EN;
+ }
+ if (rdev->irq.hpd[4]) {
+ DRM_DEBUG("si_irq_set: hpd 5\n");
+ hpd5 |= DC_HPDx_INT_EN;
+ }
+ if (rdev->irq.hpd[5]) {
+ DRM_DEBUG("si_irq_set: hpd 6\n");
+ hpd6 |= DC_HPDx_INT_EN;
+ }
+ if (rdev->irq.gui_idle) {
+ DRM_DEBUG("gui idle\n");
+ grbm_int_cntl |= GUI_IDLE_INT_ENABLE;
+ }
+
+ WREG32(CP_INT_CNTL_RING0, cp_int_cntl);
+ WREG32(CP_INT_CNTL_RING1, cp_int_cntl1);
+ WREG32(CP_INT_CNTL_RING2, cp_int_cntl2);
+
+ WREG32(GRBM_INT_CNTL, grbm_int_cntl);
+
+ WREG32(INT_MASK + EVERGREEN_CRTC0_REGISTER_OFFSET, crtc1);
+ WREG32(INT_MASK + EVERGREEN_CRTC1_REGISTER_OFFSET, crtc2);
+ if (rdev->num_crtc >= 4) {
+ WREG32(INT_MASK + EVERGREEN_CRTC2_REGISTER_OFFSET, crtc3);
+ WREG32(INT_MASK + EVERGREEN_CRTC3_REGISTER_OFFSET, crtc4);
+ }
+ if (rdev->num_crtc >= 6) {
+ WREG32(INT_MASK + EVERGREEN_CRTC4_REGISTER_OFFSET, crtc5);
+ WREG32(INT_MASK + EVERGREEN_CRTC5_REGISTER_OFFSET, crtc6);
+ }
+
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, grph1);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, grph2);
+ if (rdev->num_crtc >= 4) {
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, grph3);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, grph4);
+ }
+ if (rdev->num_crtc >= 6) {
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, grph5);
+ WREG32(GRPH_INT_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, grph6);
+ }
+
+ WREG32(DC_HPD1_INT_CONTROL, hpd1);
+ WREG32(DC_HPD2_INT_CONTROL, hpd2);
+ WREG32(DC_HPD3_INT_CONTROL, hpd3);
+ WREG32(DC_HPD4_INT_CONTROL, hpd4);
+ WREG32(DC_HPD5_INT_CONTROL, hpd5);
+ WREG32(DC_HPD6_INT_CONTROL, hpd6);
+
+ return 0;
+}
+
+static inline void si_irq_ack(struct radeon_device *rdev)
+{
+ u32 tmp;
+
+ rdev->irq.stat_regs.evergreen.disp_int = RREG32(DISP_INTERRUPT_STATUS);
+ rdev->irq.stat_regs.evergreen.disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE);
+ rdev->irq.stat_regs.evergreen.disp_int_cont2 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE2);
+ rdev->irq.stat_regs.evergreen.disp_int_cont3 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE3);
+ rdev->irq.stat_regs.evergreen.disp_int_cont4 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE4);
+ rdev->irq.stat_regs.evergreen.disp_int_cont5 = RREG32(DISP_INTERRUPT_STATUS_CONTINUE5);
+ rdev->irq.stat_regs.evergreen.d1grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET);
+ rdev->irq.stat_regs.evergreen.d2grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET);
+ if (rdev->num_crtc >= 4) {
+ rdev->irq.stat_regs.evergreen.d3grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET);
+ rdev->irq.stat_regs.evergreen.d4grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET);
+ }
+ if (rdev->num_crtc >= 6) {
+ rdev->irq.stat_regs.evergreen.d5grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET);
+ rdev->irq.stat_regs.evergreen.d6grph_int = RREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET);
+ }
+
+ if (rdev->irq.stat_regs.evergreen.d1grph_int & GRPH_PFLIP_INT_OCCURRED)
+ WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR);
+ if (rdev->irq.stat_regs.evergreen.d2grph_int & GRPH_PFLIP_INT_OCCURRED)
+ WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR);
+ if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT)
+ WREG32(VBLANK_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VBLANK_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT)
+ WREG32(VLINE_STATUS + EVERGREEN_CRTC0_REGISTER_OFFSET, VLINE_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT)
+ WREG32(VBLANK_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VBLANK_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT)
+ WREG32(VLINE_STATUS + EVERGREEN_CRTC1_REGISTER_OFFSET, VLINE_ACK);
+
+ if (rdev->num_crtc >= 4) {
+ if (rdev->irq.stat_regs.evergreen.d3grph_int & GRPH_PFLIP_INT_OCCURRED)
+ WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR);
+ if (rdev->irq.stat_regs.evergreen.d4grph_int & GRPH_PFLIP_INT_OCCURRED)
+ WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT)
+ WREG32(VBLANK_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VBLANK_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT)
+ WREG32(VLINE_STATUS + EVERGREEN_CRTC2_REGISTER_OFFSET, VLINE_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT)
+ WREG32(VBLANK_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VBLANK_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT)
+ WREG32(VLINE_STATUS + EVERGREEN_CRTC3_REGISTER_OFFSET, VLINE_ACK);
+ }
+
+ if (rdev->num_crtc >= 6) {
+ if (rdev->irq.stat_regs.evergreen.d5grph_int & GRPH_PFLIP_INT_OCCURRED)
+ WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR);
+ if (rdev->irq.stat_regs.evergreen.d6grph_int & GRPH_PFLIP_INT_OCCURRED)
+ WREG32(GRPH_INT_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, GRPH_PFLIP_INT_CLEAR);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT)
+ WREG32(VBLANK_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VBLANK_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT)
+ WREG32(VLINE_STATUS + EVERGREEN_CRTC4_REGISTER_OFFSET, VLINE_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT)
+ WREG32(VBLANK_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VBLANK_ACK);
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT)
+ WREG32(VLINE_STATUS + EVERGREEN_CRTC5_REGISTER_OFFSET, VLINE_ACK);
+ }
+
+ if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) {
+ tmp = RREG32(DC_HPD1_INT_CONTROL);
+ tmp |= DC_HPDx_INT_ACK;
+ WREG32(DC_HPD1_INT_CONTROL, tmp);
+ }
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) {
+ tmp = RREG32(DC_HPD2_INT_CONTROL);
+ tmp |= DC_HPDx_INT_ACK;
+ WREG32(DC_HPD2_INT_CONTROL, tmp);
+ }
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) {
+ tmp = RREG32(DC_HPD3_INT_CONTROL);
+ tmp |= DC_HPDx_INT_ACK;
+ WREG32(DC_HPD3_INT_CONTROL, tmp);
+ }
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) {
+ tmp = RREG32(DC_HPD4_INT_CONTROL);
+ tmp |= DC_HPDx_INT_ACK;
+ WREG32(DC_HPD4_INT_CONTROL, tmp);
+ }
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) {
+ tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp |= DC_HPDx_INT_ACK;
+ WREG32(DC_HPD5_INT_CONTROL, tmp);
+ }
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) {
+ tmp = RREG32(DC_HPD5_INT_CONTROL);
+ tmp |= DC_HPDx_INT_ACK;
+ WREG32(DC_HPD6_INT_CONTROL, tmp);
+ }
+}
+
+static void si_irq_disable(struct radeon_device *rdev)
+{
+ si_disable_interrupts(rdev);
+ /* Wait and acknowledge irq */
+ mdelay(1);
+ si_irq_ack(rdev);
+ si_disable_interrupt_state(rdev);
+}
+
+static void si_irq_suspend(struct radeon_device *rdev)
+{
+ si_irq_disable(rdev);
+ si_rlc_stop(rdev);
+}
+
+static void si_irq_fini(struct radeon_device *rdev)
+{
+ si_irq_suspend(rdev);
+ r600_ih_ring_fini(rdev);
+}
+
+static inline u32 si_get_ih_wptr(struct radeon_device *rdev)
+{
+ u32 wptr, tmp;
+
+ if (rdev->wb.enabled)
+ wptr = le32_to_cpu(rdev->wb.wb[R600_WB_IH_WPTR_OFFSET/4]);
+ else
+ wptr = RREG32(IH_RB_WPTR);
+
+ if (wptr & RB_OVERFLOW) {
+ /* When a ring buffer overflow happen start parsing interrupt
+ * from the last not overwritten vector (wptr + 16). Hopefully
+ * this should allow us to catchup.
+ */
+ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
+ wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
+ rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
+ tmp = RREG32(IH_RB_CNTL);
+ tmp |= IH_WPTR_OVERFLOW_CLEAR;
+ WREG32(IH_RB_CNTL, tmp);
+ }
+ return (wptr & rdev->ih.ptr_mask);
+}
+
+/* SI IV Ring
+ * Each IV ring entry is 128 bits:
+ * [7:0] - interrupt source id
+ * [31:8] - reserved
+ * [59:32] - interrupt source data
+ * [63:60] - reserved
+ * [71:64] - RINGID
+ * [79:72] - VMID
+ * [127:80] - reserved
+ */
+int si_irq_process(struct radeon_device *rdev)
+{
+ u32 wptr;
+ u32 rptr;
+ u32 src_id, src_data, ring_id;
+ u32 ring_index;
+ unsigned long flags;
+ bool queue_hotplug = false;
+
+ if (!rdev->ih.enabled || rdev->shutdown)
+ return IRQ_NONE;
+
+ wptr = si_get_ih_wptr(rdev);
+ rptr = rdev->ih.rptr;
+ DRM_DEBUG("si_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
+
+ spin_lock_irqsave(&rdev->ih.lock, flags);
+ if (rptr == wptr) {
+ spin_unlock_irqrestore(&rdev->ih.lock, flags);
+ return IRQ_NONE;
+ }
+restart_ih:
+ /* Order reading of wptr vs. reading of IH ring data */
+ rmb();
+
+ /* display interrupts */
+ si_irq_ack(rdev);
+
+ rdev->ih.wptr = wptr;
+ while (rptr != wptr) {
+ /* wptr/rptr are in bytes! */
+ ring_index = rptr / 4;
+ src_id = le32_to_cpu(rdev->ih.ring[ring_index]) & 0xff;
+ src_data = le32_to_cpu(rdev->ih.ring[ring_index + 1]) & 0xfffffff;
+ ring_id = le32_to_cpu(rdev->ih.ring[ring_index + 2]) & 0xff;
+
+ switch (src_id) {
+ case 1: /* D1 vblank/vline */
+ switch (src_data) {
+ case 0: /* D1 vblank */
+ if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VBLANK_INTERRUPT) {
+ if (rdev->irq.crtc_vblank_int[0]) {
+ drm_handle_vblank(rdev->ddev, 0);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[0])
+ radeon_crtc_handle_flip(rdev, 0);
+ rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VBLANK_INTERRUPT;
+ DRM_DEBUG("IH: D1 vblank\n");
+ }
+ break;
+ case 1: /* D1 vline */
+ if (rdev->irq.stat_regs.evergreen.disp_int & LB_D1_VLINE_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int &= ~LB_D1_VLINE_INTERRUPT;
+ DRM_DEBUG("IH: D1 vline\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 2: /* D2 vblank/vline */
+ switch (src_data) {
+ case 0: /* D2 vblank */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VBLANK_INTERRUPT) {
+ if (rdev->irq.crtc_vblank_int[1]) {
+ drm_handle_vblank(rdev->ddev, 1);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[1])
+ radeon_crtc_handle_flip(rdev, 1);
+ rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VBLANK_INTERRUPT;
+ DRM_DEBUG("IH: D2 vblank\n");
+ }
+ break;
+ case 1: /* D2 vline */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont & LB_D2_VLINE_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont &= ~LB_D2_VLINE_INTERRUPT;
+ DRM_DEBUG("IH: D2 vline\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 3: /* D3 vblank/vline */
+ switch (src_data) {
+ case 0: /* D3 vblank */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VBLANK_INTERRUPT) {
+ if (rdev->irq.crtc_vblank_int[2]) {
+ drm_handle_vblank(rdev->ddev, 2);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[2])
+ radeon_crtc_handle_flip(rdev, 2);
+ rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VBLANK_INTERRUPT;
+ DRM_DEBUG("IH: D3 vblank\n");
+ }
+ break;
+ case 1: /* D3 vline */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & LB_D3_VLINE_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~LB_D3_VLINE_INTERRUPT;
+ DRM_DEBUG("IH: D3 vline\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 4: /* D4 vblank/vline */
+ switch (src_data) {
+ case 0: /* D4 vblank */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VBLANK_INTERRUPT) {
+ if (rdev->irq.crtc_vblank_int[3]) {
+ drm_handle_vblank(rdev->ddev, 3);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[3])
+ radeon_crtc_handle_flip(rdev, 3);
+ rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VBLANK_INTERRUPT;
+ DRM_DEBUG("IH: D4 vblank\n");
+ }
+ break;
+ case 1: /* D4 vline */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & LB_D4_VLINE_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~LB_D4_VLINE_INTERRUPT;
+ DRM_DEBUG("IH: D4 vline\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 5: /* D5 vblank/vline */
+ switch (src_data) {
+ case 0: /* D5 vblank */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VBLANK_INTERRUPT) {
+ if (rdev->irq.crtc_vblank_int[4]) {
+ drm_handle_vblank(rdev->ddev, 4);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[4])
+ radeon_crtc_handle_flip(rdev, 4);
+ rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VBLANK_INTERRUPT;
+ DRM_DEBUG("IH: D5 vblank\n");
+ }
+ break;
+ case 1: /* D5 vline */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & LB_D5_VLINE_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~LB_D5_VLINE_INTERRUPT;
+ DRM_DEBUG("IH: D5 vline\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 6: /* D6 vblank/vline */
+ switch (src_data) {
+ case 0: /* D6 vblank */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VBLANK_INTERRUPT) {
+ if (rdev->irq.crtc_vblank_int[5]) {
+ drm_handle_vblank(rdev->ddev, 5);
+ rdev->pm.vblank_sync = true;
+ wake_up(&rdev->irq.vblank_queue);
+ }
+ if (rdev->irq.pflip[5])
+ radeon_crtc_handle_flip(rdev, 5);
+ rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VBLANK_INTERRUPT;
+ DRM_DEBUG("IH: D6 vblank\n");
+ }
+ break;
+ case 1: /* D6 vline */
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & LB_D6_VLINE_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~LB_D6_VLINE_INTERRUPT;
+ DRM_DEBUG("IH: D6 vline\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 42: /* HPD hotplug */
+ switch (src_data) {
+ case 0:
+ if (rdev->irq.stat_regs.evergreen.disp_int & DC_HPD1_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int &= ~DC_HPD1_INTERRUPT;
+ queue_hotplug = true;
+ DRM_DEBUG("IH: HPD1\n");
+ }
+ break;
+ case 1:
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont & DC_HPD2_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont &= ~DC_HPD2_INTERRUPT;
+ queue_hotplug = true;
+ DRM_DEBUG("IH: HPD2\n");
+ }
+ break;
+ case 2:
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont2 & DC_HPD3_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont2 &= ~DC_HPD3_INTERRUPT;
+ queue_hotplug = true;
+ DRM_DEBUG("IH: HPD3\n");
+ }
+ break;
+ case 3:
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont3 & DC_HPD4_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont3 &= ~DC_HPD4_INTERRUPT;
+ queue_hotplug = true;
+ DRM_DEBUG("IH: HPD4\n");
+ }
+ break;
+ case 4:
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont4 & DC_HPD5_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont4 &= ~DC_HPD5_INTERRUPT;
+ queue_hotplug = true;
+ DRM_DEBUG("IH: HPD5\n");
+ }
+ break;
+ case 5:
+ if (rdev->irq.stat_regs.evergreen.disp_int_cont5 & DC_HPD6_INTERRUPT) {
+ rdev->irq.stat_regs.evergreen.disp_int_cont5 &= ~DC_HPD6_INTERRUPT;
+ queue_hotplug = true;
+ DRM_DEBUG("IH: HPD6\n");
+ }
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+ break;
+ case 176: /* RINGID0 CP_INT */
+ radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ break;
+ case 177: /* RINGID1 CP_INT */
+ radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX);
+ break;
+ case 178: /* RINGID2 CP_INT */
+ radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX);
+ break;
+ case 181: /* CP EOP event */
+ DRM_DEBUG("IH: CP EOP\n");
+ switch (ring_id) {
+ case 0:
+ radeon_fence_process(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ break;
+ case 1:
+ radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP1_INDEX);
+ break;
+ case 2:
+ radeon_fence_process(rdev, CAYMAN_RING_TYPE_CP2_INDEX);
+ break;
+ }
+ break;
+ case 233: /* GUI IDLE */
+ DRM_DEBUG("IH: GUI idle\n");
+ rdev->pm.gui_idle = true;
+ wake_up(&rdev->irq.idle_queue);
+ break;
+ default:
+ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
+ break;
+ }
+
+ /* wptr/rptr are in bytes! */
+ rptr += 16;
+ rptr &= rdev->ih.ptr_mask;
+ }
+ /* make sure wptr hasn't changed while processing */
+ wptr = si_get_ih_wptr(rdev);
+ if (wptr != rdev->ih.wptr)
+ goto restart_ih;
+ if (queue_hotplug)
+ schedule_work(&rdev->hotplug_work);
+ rdev->ih.rptr = rptr;
+ WREG32(IH_RB_RPTR, rdev->ih.rptr);
+ spin_unlock_irqrestore(&rdev->ih.lock, flags);
+ return IRQ_HANDLED;
+}
+
+/*
+ * startup/shutdown callbacks
+ */
+static int si_startup(struct radeon_device *rdev)
+{
+ struct radeon_ring *ring;
+ int r;
+
+ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->ce_fw ||
+ !rdev->rlc_fw || !rdev->mc_fw) {
+ r = si_init_microcode(rdev);
+ if (r) {
+ DRM_ERROR("Failed to load firmware!\n");
+ return r;
+ }
+ }
+
+ r = si_mc_load_microcode(rdev);
+ if (r) {
+ DRM_ERROR("Failed to load MC firmware!\n");
+ return r;
+ }
+
+ r = r600_vram_scratch_init(rdev);
+ if (r)
+ return r;
+
+ si_mc_program(rdev);
+ r = si_pcie_gart_enable(rdev);
+ if (r)
+ return r;
+ si_gpu_init(rdev);
+
+#if 0
+ r = evergreen_blit_init(rdev);
+ if (r) {
+ r600_blit_fini(rdev);
+ rdev->asic->copy = NULL;
+ dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
+ }
+#endif
+ /* allocate rlc buffers */
+ r = si_rlc_init(rdev);
+ if (r) {
+ DRM_ERROR("Failed to init rlc BOs!\n");
+ return r;
+ }
+
+ /* allocate wb buffer */
+ r = radeon_wb_init(rdev);
+ if (r)
+ return r;
+
+ r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX);
+ if (r) {
+ dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
+ return r;
+ }
+
+ r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP1_INDEX);
+ if (r) {
+ dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
+ return r;
+ }
+
+ r = radeon_fence_driver_start_ring(rdev, CAYMAN_RING_TYPE_CP2_INDEX);
+ if (r) {
+ dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r);
+ return r;
+ }
+
+ /* Enable IRQ */
+ r = si_irq_init(rdev);
+ if (r) {
+ DRM_ERROR("radeon: IH init failed (%d).\n", r);
+ radeon_irq_kms_fini(rdev);
+ return r;
+ }
+ si_irq_set(rdev);
+
+ ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET,
+ CP_RB0_RPTR, CP_RB0_WPTR,
+ 0, 0xfffff, RADEON_CP_PACKET2);
+ if (r)
+ return r;
+
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
+ r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP1_RPTR_OFFSET,
+ CP_RB1_RPTR, CP_RB1_WPTR,
+ 0, 0xfffff, RADEON_CP_PACKET2);
+ if (r)
+ return r;
+
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
+ r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP2_RPTR_OFFSET,
+ CP_RB2_RPTR, CP_RB2_WPTR,
+ 0, 0xfffff, RADEON_CP_PACKET2);
+ if (r)
+ return r;
+
+ r = si_cp_load_microcode(rdev);
+ if (r)
+ return r;
+ r = si_cp_resume(rdev);
+ if (r)
+ return r;
+
+ r = radeon_ib_pool_start(rdev);
+ if (r)
+ return r;
+
+ r = radeon_ib_test(rdev, RADEON_RING_TYPE_GFX_INDEX, &rdev->ring[RADEON_RING_TYPE_GFX_INDEX]);
+ if (r) {
+ DRM_ERROR("radeon: failed testing IB (%d) on CP ring 0\n", r);
+ rdev->accel_working = false;
+ return r;
+ }
+
+ r = radeon_ib_test(rdev, CAYMAN_RING_TYPE_CP1_INDEX, &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX]);
+ if (r) {
+ DRM_ERROR("radeon: failed testing IB (%d) on CP ring 1\n", r);
+ rdev->accel_working = false;
+ return r;
+ }
+
+ r = radeon_ib_test(rdev, CAYMAN_RING_TYPE_CP2_INDEX, &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX]);
+ if (r) {
+ DRM_ERROR("radeon: failed testing IB (%d) on CP ring 2\n", r);
+ rdev->accel_working = false;
+ return r;
+ }
+
+ r = radeon_vm_manager_start(rdev);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+int si_resume(struct radeon_device *rdev)
+{
+ int r;
+
+ /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw,
+ * posting will perform necessary task to bring back GPU into good
+ * shape.
+ */
+ /* post card */
+ atom_asic_init(rdev->mode_info.atom_context);
+
+ rdev->accel_working = true;
+ r = si_startup(rdev);
+ if (r) {
+ DRM_ERROR("si startup failed on resume\n");
+ rdev->accel_working = false;
+ return r;
+ }
+
+ return r;
+
+}
+
+int si_suspend(struct radeon_device *rdev)
+{
+ /* FIXME: we should wait for ring to be empty */
+ radeon_ib_pool_suspend(rdev);
+ radeon_vm_manager_suspend(rdev);
+#if 0
+ r600_blit_suspend(rdev);
+#endif
+ si_cp_enable(rdev, false);
+ rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready = false;
+ rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX].ready = false;
+ rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX].ready = false;
+ si_irq_suspend(rdev);
+ radeon_wb_disable(rdev);
+ si_pcie_gart_disable(rdev);
+ return 0;
+}
+
+/* Plan is to move initialization in that function and use
+ * helper function so that radeon_device_init pretty much
+ * do nothing more than calling asic specific function. This
+ * should also allow to remove a bunch of callback function
+ * like vram_info.
+ */
+int si_init(struct radeon_device *rdev)
+{
+ struct radeon_ring *ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ int r;
+
+ /* This don't do much */
+ r = radeon_gem_init(rdev);
+ if (r)
+ return r;
+ /* Read BIOS */
+ if (!radeon_get_bios(rdev)) {
+ if (ASIC_IS_AVIVO(rdev))
+ return -EINVAL;
+ }
+ /* Must be an ATOMBIOS */
+ if (!rdev->is_atom_bios) {
+ dev_err(rdev->dev, "Expecting atombios for cayman GPU\n");
+ return -EINVAL;
+ }
+ r = radeon_atombios_init(rdev);
+ if (r)
+ return r;
+
+ /* Post card if necessary */
+ if (!radeon_card_posted(rdev)) {
+ if (!rdev->bios) {
+ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
+ return -EINVAL;
+ }
+ DRM_INFO("GPU not posted. posting now...\n");
+ atom_asic_init(rdev->mode_info.atom_context);
+ }
+ /* Initialize scratch registers */
+ si_scratch_init(rdev);
+ /* Initialize surface registers */
+ radeon_surface_init(rdev);
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
+
+ /* Fence driver */
+ r = radeon_fence_driver_init(rdev);
+ if (r)
+ return r;
+
+ /* initialize memory controller */
+ r = si_mc_init(rdev);
+ if (r)
+ return r;
+ /* Memory manager */
+ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+
+ r = radeon_irq_kms_init(rdev);
+ if (r)
+ return r;
+
+ ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
+ ring->ring_obj = NULL;
+ r600_ring_init(rdev, ring, 1024 * 1024);
+
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP1_INDEX];
+ ring->ring_obj = NULL;
+ r600_ring_init(rdev, ring, 1024 * 1024);
+
+ ring = &rdev->ring[CAYMAN_RING_TYPE_CP2_INDEX];
+ ring->ring_obj = NULL;
+ r600_ring_init(rdev, ring, 1024 * 1024);
+
+ rdev->ih.ring_obj = NULL;
+ r600_ih_ring_init(rdev, 64 * 1024);
+
+ r = r600_pcie_gart_init(rdev);
+ if (r)
+ return r;
+
+ r = radeon_ib_pool_init(rdev);
+ rdev->accel_working = true;
+ if (r) {
+ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+ rdev->accel_working = false;
+ }
+ r = radeon_vm_manager_init(rdev);
+ if (r) {
+ dev_err(rdev->dev, "vm manager initialization failed (%d).\n", r);
+ }
+
+ r = si_startup(rdev);
+ if (r) {
+ dev_err(rdev->dev, "disabling GPU acceleration\n");
+ si_cp_fini(rdev);
+ si_irq_fini(rdev);
+ si_rlc_fini(rdev);
+ radeon_wb_fini(rdev);
+ r100_ib_fini(rdev);
+ radeon_vm_manager_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ si_pcie_gart_fini(rdev);
+ rdev->accel_working = false;
+ }
+
+ /* Don't start up if the MC ucode is missing.
+ * The default clocks and voltages before the MC ucode
+ * is loaded are not suffient for advanced operations.
+ */
+ if (!rdev->mc_fw) {
+ DRM_ERROR("radeon: MC ucode required for NI+.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void si_fini(struct radeon_device *rdev)
+{
+#if 0
+ r600_blit_fini(rdev);
+#endif
+ si_cp_fini(rdev);
+ si_irq_fini(rdev);
+ si_rlc_fini(rdev);
+ radeon_wb_fini(rdev);
+ radeon_vm_manager_fini(rdev);
+ r100_ib_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ si_pcie_gart_fini(rdev);
+ r600_vram_scratch_fini(rdev);
+ radeon_gem_fini(rdev);
+ radeon_semaphore_driver_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+}
+
diff --git a/drivers/gpu/drm/radeon/si_blit_shaders.c b/drivers/gpu/drm/radeon/si_blit_shaders.c
new file mode 100644
index 000000000000..ec415e7dfa4b
--- /dev/null
+++ b/drivers/gpu/drm/radeon/si_blit_shaders.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ * Alex Deucher <alexander.deucher@amd.com>
+ */
+
+#include <linux/types.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+const u32 si_default_state[] =
+{
+ 0xc0066900,
+ 0x00000000,
+ 0x00000060, /* DB_RENDER_CONTROL */
+ 0x00000000, /* DB_COUNT_CONTROL */
+ 0x00000000, /* DB_DEPTH_VIEW */
+ 0x0000002a, /* DB_RENDER_OVERRIDE */
+ 0x00000000, /* DB_RENDER_OVERRIDE2 */
+ 0x00000000, /* DB_HTILE_DATA_BASE */
+
+ 0xc0046900,
+ 0x00000008,
+ 0x00000000, /* DB_DEPTH_BOUNDS_MIN */
+ 0x00000000, /* DB_DEPTH_BOUNDS_MAX */
+ 0x00000000, /* DB_STENCIL_CLEAR */
+ 0x00000000, /* DB_DEPTH_CLEAR */
+
+ 0xc0036900,
+ 0x0000000f,
+ 0x00000000, /* DB_DEPTH_INFO */
+ 0x00000000, /* DB_Z_INFO */
+ 0x00000000, /* DB_STENCIL_INFO */
+
+ 0xc0016900,
+ 0x00000080,
+ 0x00000000, /* PA_SC_WINDOW_OFFSET */
+
+ 0xc00d6900,
+ 0x00000083,
+ 0x0000ffff, /* PA_SC_CLIPRECT_RULE */
+ 0x00000000, /* PA_SC_CLIPRECT_0_TL */
+ 0x20002000, /* PA_SC_CLIPRECT_0_BR */
+ 0x00000000,
+ 0x20002000,
+ 0x00000000,
+ 0x20002000,
+ 0x00000000,
+ 0x20002000,
+ 0xaaaaaaaa, /* PA_SC_EDGERULE */
+ 0x00000000, /* PA_SU_HARDWARE_SCREEN_OFFSET */
+ 0x0000000f, /* CB_TARGET_MASK */
+ 0x0000000f, /* CB_SHADER_MASK */
+
+ 0xc0226900,
+ 0x00000094,
+ 0x80000000, /* PA_SC_VPORT_SCISSOR_0_TL */
+ 0x20002000, /* PA_SC_VPORT_SCISSOR_0_BR */
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x80000000,
+ 0x20002000,
+ 0x00000000, /* PA_SC_VPORT_ZMIN_0 */
+ 0x3f800000, /* PA_SC_VPORT_ZMAX_0 */
+
+ 0xc0026900,
+ 0x000000d9,
+ 0x00000000, /* CP_RINGID */
+ 0x00000000, /* CP_VMID */
+
+ 0xc0046900,
+ 0x00000100,
+ 0xffffffff, /* VGT_MAX_VTX_INDX */
+ 0x00000000, /* VGT_MIN_VTX_INDX */
+ 0x00000000, /* VGT_INDX_OFFSET */
+ 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_INDX */
+
+ 0xc0046900,
+ 0x00000105,
+ 0x00000000, /* CB_BLEND_RED */
+ 0x00000000, /* CB_BLEND_GREEN */
+ 0x00000000, /* CB_BLEND_BLUE */
+ 0x00000000, /* CB_BLEND_ALPHA */
+
+ 0xc0016900,
+ 0x000001e0,
+ 0x00000000, /* CB_BLEND0_CONTROL */
+
+ 0xc00e6900,
+ 0x00000200,
+ 0x00000000, /* DB_DEPTH_CONTROL */
+ 0x00000000, /* DB_EQAA */
+ 0x00cc0010, /* CB_COLOR_CONTROL */
+ 0x00000210, /* DB_SHADER_CONTROL */
+ 0x00010000, /* PA_CL_CLIP_CNTL */
+ 0x00000004, /* PA_SU_SC_MODE_CNTL */
+ 0x00000100, /* PA_CL_VTE_CNTL */
+ 0x00000000, /* PA_CL_VS_OUT_CNTL */
+ 0x00000000, /* PA_CL_NANINF_CNTL */
+ 0x00000000, /* PA_SU_LINE_STIPPLE_CNTL */
+ 0x00000000, /* PA_SU_LINE_STIPPLE_SCALE */
+ 0x00000000, /* PA_SU_PRIM_FILTER_CNTL */
+ 0x00000000, /* */
+ 0x00000000, /* */
+
+ 0xc0116900,
+ 0x00000280,
+ 0x00000000, /* PA_SU_POINT_SIZE */
+ 0x00000000, /* PA_SU_POINT_MINMAX */
+ 0x00000008, /* PA_SU_LINE_CNTL */
+ 0x00000000, /* PA_SC_LINE_STIPPLE */
+ 0x00000000, /* VGT_OUTPUT_PATH_CNTL */
+ 0x00000000, /* VGT_HOS_CNTL */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000, /* VGT_GS_MODE */
+
+ 0xc0026900,
+ 0x00000292,
+ 0x00000000, /* PA_SC_MODE_CNTL_0 */
+ 0x00000000, /* PA_SC_MODE_CNTL_1 */
+
+ 0xc0016900,
+ 0x000002a1,
+ 0x00000000, /* VGT_PRIMITIVEID_EN */
+
+ 0xc0016900,
+ 0x000002a5,
+ 0x00000000, /* VGT_MULTI_PRIM_IB_RESET_EN */
+
+ 0xc0026900,
+ 0x000002a8,
+ 0x00000000, /* VGT_INSTANCE_STEP_RATE_0 */
+ 0x00000000,
+
+ 0xc0026900,
+ 0x000002ad,
+ 0x00000000, /* VGT_REUSE_OFF */
+ 0x00000000,
+
+ 0xc0016900,
+ 0x000002d5,
+ 0x00000000, /* VGT_SHADER_STAGES_EN */
+
+ 0xc0016900,
+ 0x000002dc,
+ 0x0000aa00, /* DB_ALPHA_TO_MASK */
+
+ 0xc0066900,
+ 0x000002de,
+ 0x00000000, /* PA_SU_POLY_OFFSET_DB_FMT_CNTL */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+
+ 0xc0026900,
+ 0x000002e5,
+ 0x00000000, /* VGT_STRMOUT_CONFIG */
+ 0x00000000,
+
+ 0xc01b6900,
+ 0x000002f5,
+ 0x76543210, /* PA_SC_CENTROID_PRIORITY_0 */
+ 0xfedcba98, /* PA_SC_CENTROID_PRIORITY_1 */
+ 0x00000000, /* PA_SC_LINE_CNTL */
+ 0x00000000, /* PA_SC_AA_CONFIG */
+ 0x00000005, /* PA_SU_VTX_CNTL */
+ 0x3f800000, /* PA_CL_GB_VERT_CLIP_ADJ */
+ 0x3f800000, /* PA_CL_GB_VERT_DISC_ADJ */
+ 0x3f800000, /* PA_CL_GB_HORZ_CLIP_ADJ */
+ 0x3f800000, /* PA_CL_GB_HORZ_DISC_ADJ */
+ 0x00000000, /* PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0 */
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0x00000000,
+ 0xffffffff, /* PA_SC_AA_MASK_X0Y0_X1Y0 */
+ 0xffffffff,
+
+ 0xc0026900,
+ 0x00000316,
+ 0x0000000e, /* VGT_VERTEX_REUSE_BLOCK_CNTL */
+ 0x00000010, /* */
+};
+
+const u32 si_default_size = ARRAY_SIZE(si_default_state);
diff --git a/drivers/gpu/drm/radeon/si_blit_shaders.h b/drivers/gpu/drm/radeon/si_blit_shaders.h
new file mode 100644
index 000000000000..c739e51e3961
--- /dev/null
+++ b/drivers/gpu/drm/radeon/si_blit_shaders.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#ifndef SI_BLIT_SHADERS_H
+#define SI_BLIT_SHADERS_H
+
+extern const u32 si_default_state[];
+
+extern const u32 si_default_size;
+
+#endif
diff --git a/drivers/gpu/drm/radeon/si_reg.h b/drivers/gpu/drm/radeon/si_reg.h
new file mode 100644
index 000000000000..eda938a7cb6e
--- /dev/null
+++ b/drivers/gpu/drm/radeon/si_reg.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Alex Deucher
+ */
+#ifndef __SI_REG_H__
+#define __SI_REG_H__
+
+/* SI */
+#define SI_DC_GPIO_HPD_MASK 0x65b0
+#define SI_DC_GPIO_HPD_A 0x65b4
+#define SI_DC_GPIO_HPD_EN 0x65b8
+#define SI_DC_GPIO_HPD_Y 0x65bc
+
+#endif
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
new file mode 100644
index 000000000000..53ea2c42dbd6
--- /dev/null
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -0,0 +1,886 @@
+/*
+ * Copyright 2011 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Alex Deucher
+ */
+#ifndef SI_H
+#define SI_H
+
+#define CG_MULT_THERMAL_STATUS 0x714
+#define ASIC_MAX_TEMP(x) ((x) << 0)
+#define ASIC_MAX_TEMP_MASK 0x000001ff
+#define ASIC_MAX_TEMP_SHIFT 0
+#define CTF_TEMP(x) ((x) << 9)
+#define CTF_TEMP_MASK 0x0003fe00
+#define CTF_TEMP_SHIFT 9
+
+#define SI_MAX_SH_GPRS 256
+#define SI_MAX_TEMP_GPRS 16
+#define SI_MAX_SH_THREADS 256
+#define SI_MAX_SH_STACK_ENTRIES 4096
+#define SI_MAX_FRC_EOV_CNT 16384
+#define SI_MAX_BACKENDS 8
+#define SI_MAX_BACKENDS_MASK 0xFF
+#define SI_MAX_BACKENDS_PER_SE_MASK 0x0F
+#define SI_MAX_SIMDS 12
+#define SI_MAX_SIMDS_MASK 0x0FFF
+#define SI_MAX_SIMDS_PER_SE_MASK 0x00FF
+#define SI_MAX_PIPES 8
+#define SI_MAX_PIPES_MASK 0xFF
+#define SI_MAX_PIPES_PER_SIMD_MASK 0x3F
+#define SI_MAX_LDS_NUM 0xFFFF
+#define SI_MAX_TCC 16
+#define SI_MAX_TCC_MASK 0xFFFF
+
+#define VGA_HDP_CONTROL 0x328
+#define VGA_MEMORY_DISABLE (1 << 4)
+
+#define DMIF_ADDR_CONFIG 0xBD4
+
+#define SRBM_STATUS 0xE50
+
+#define CC_SYS_RB_BACKEND_DISABLE 0xe80
+#define GC_USER_SYS_RB_BACKEND_DISABLE 0xe84
+
+#define VM_L2_CNTL 0x1400
+#define ENABLE_L2_CACHE (1 << 0)
+#define ENABLE_L2_FRAGMENT_PROCESSING (1 << 1)
+#define L2_CACHE_PTE_ENDIAN_SWAP_MODE(x) ((x) << 2)
+#define L2_CACHE_PDE_ENDIAN_SWAP_MODE(x) ((x) << 4)
+#define ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE (1 << 9)
+#define ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE (1 << 10)
+#define EFFECTIVE_L2_QUEUE_SIZE(x) (((x) & 7) << 15)
+#define CONTEXT1_IDENTITY_ACCESS_MODE(x) (((x) & 3) << 19)
+#define VM_L2_CNTL2 0x1404
+#define INVALIDATE_ALL_L1_TLBS (1 << 0)
+#define INVALIDATE_L2_CACHE (1 << 1)
+#define INVALIDATE_CACHE_MODE(x) ((x) << 26)
+#define INVALIDATE_PTE_AND_PDE_CACHES 0
+#define INVALIDATE_ONLY_PTE_CACHES 1
+#define INVALIDATE_ONLY_PDE_CACHES 2
+#define VM_L2_CNTL3 0x1408
+#define BANK_SELECT(x) ((x) << 0)
+#define L2_CACHE_UPDATE_MODE(x) ((x) << 6)
+#define L2_CACHE_BIGK_FRAGMENT_SIZE(x) ((x) << 15)
+#define L2_CACHE_BIGK_ASSOCIATIVITY (1 << 20)
+#define VM_L2_STATUS 0x140C
+#define L2_BUSY (1 << 0)
+#define VM_CONTEXT0_CNTL 0x1410
+#define ENABLE_CONTEXT (1 << 0)
+#define PAGE_TABLE_DEPTH(x) (((x) & 3) << 1)
+#define RANGE_PROTECTION_FAULT_ENABLE_DEFAULT (1 << 4)
+#define VM_CONTEXT1_CNTL 0x1414
+#define VM_CONTEXT0_CNTL2 0x1430
+#define VM_CONTEXT1_CNTL2 0x1434
+#define VM_CONTEXT8_PAGE_TABLE_BASE_ADDR 0x1438
+#define VM_CONTEXT9_PAGE_TABLE_BASE_ADDR 0x143c
+#define VM_CONTEXT10_PAGE_TABLE_BASE_ADDR 0x1440
+#define VM_CONTEXT11_PAGE_TABLE_BASE_ADDR 0x1444
+#define VM_CONTEXT12_PAGE_TABLE_BASE_ADDR 0x1448
+#define VM_CONTEXT13_PAGE_TABLE_BASE_ADDR 0x144c
+#define VM_CONTEXT14_PAGE_TABLE_BASE_ADDR 0x1450
+#define VM_CONTEXT15_PAGE_TABLE_BASE_ADDR 0x1454
+
+#define VM_INVALIDATE_REQUEST 0x1478
+#define VM_INVALIDATE_RESPONSE 0x147c
+
+#define VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR 0x1518
+#define VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR 0x151c
+
+#define VM_CONTEXT0_PAGE_TABLE_BASE_ADDR 0x153c
+#define VM_CONTEXT1_PAGE_TABLE_BASE_ADDR 0x1540
+#define VM_CONTEXT2_PAGE_TABLE_BASE_ADDR 0x1544
+#define VM_CONTEXT3_PAGE_TABLE_BASE_ADDR 0x1548
+#define VM_CONTEXT4_PAGE_TABLE_BASE_ADDR 0x154c
+#define VM_CONTEXT5_PAGE_TABLE_BASE_ADDR 0x1550
+#define VM_CONTEXT6_PAGE_TABLE_BASE_ADDR 0x1554
+#define VM_CONTEXT7_PAGE_TABLE_BASE_ADDR 0x1558
+#define VM_CONTEXT0_PAGE_TABLE_START_ADDR 0x155c
+#define VM_CONTEXT1_PAGE_TABLE_START_ADDR 0x1560
+
+#define VM_CONTEXT0_PAGE_TABLE_END_ADDR 0x157C
+#define VM_CONTEXT1_PAGE_TABLE_END_ADDR 0x1580
+
+#define MC_SHARED_CHMAP 0x2004
+#define NOOFCHAN_SHIFT 12
+#define NOOFCHAN_MASK 0x0000f000
+#define MC_SHARED_CHREMAP 0x2008
+
+#define MC_VM_FB_LOCATION 0x2024
+#define MC_VM_AGP_TOP 0x2028
+#define MC_VM_AGP_BOT 0x202C
+#define MC_VM_AGP_BASE 0x2030
+#define MC_VM_SYSTEM_APERTURE_LOW_ADDR 0x2034
+#define MC_VM_SYSTEM_APERTURE_HIGH_ADDR 0x2038
+#define MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR 0x203C
+
+#define MC_VM_MX_L1_TLB_CNTL 0x2064
+#define ENABLE_L1_TLB (1 << 0)
+#define ENABLE_L1_FRAGMENT_PROCESSING (1 << 1)
+#define SYSTEM_ACCESS_MODE_PA_ONLY (0 << 3)
+#define SYSTEM_ACCESS_MODE_USE_SYS_MAP (1 << 3)
+#define SYSTEM_ACCESS_MODE_IN_SYS (2 << 3)
+#define SYSTEM_ACCESS_MODE_NOT_IN_SYS (3 << 3)
+#define SYSTEM_APERTURE_UNMAPPED_ACCESS_PASS_THRU (0 << 5)
+#define ENABLE_ADVANCED_DRIVER_MODEL (1 << 6)
+
+#define MC_SHARED_BLACKOUT_CNTL 0x20ac
+
+#define MC_ARB_RAMCFG 0x2760
+#define NOOFBANK_SHIFT 0
+#define NOOFBANK_MASK 0x00000003
+#define NOOFRANK_SHIFT 2
+#define NOOFRANK_MASK 0x00000004
+#define NOOFROWS_SHIFT 3
+#define NOOFROWS_MASK 0x00000038
+#define NOOFCOLS_SHIFT 6
+#define NOOFCOLS_MASK 0x000000C0
+#define CHANSIZE_SHIFT 8
+#define CHANSIZE_MASK 0x00000100
+#define CHANSIZE_OVERRIDE (1 << 11)
+#define NOOFGROUPS_SHIFT 12
+#define NOOFGROUPS_MASK 0x00001000
+
+#define MC_SEQ_TRAIN_WAKEUP_CNTL 0x2808
+#define TRAIN_DONE_D0 (1 << 30)
+#define TRAIN_DONE_D1 (1 << 31)
+
+#define MC_SEQ_SUP_CNTL 0x28c8
+#define RUN_MASK (1 << 0)
+#define MC_SEQ_SUP_PGM 0x28cc
+
+#define MC_IO_PAD_CNTL_D0 0x29d0
+#define MEM_FALL_OUT_CMD (1 << 8)
+
+#define MC_SEQ_IO_DEBUG_INDEX 0x2a44
+#define MC_SEQ_IO_DEBUG_DATA 0x2a48
+
+#define HDP_HOST_PATH_CNTL 0x2C00
+#define HDP_NONSURFACE_BASE 0x2C04
+#define HDP_NONSURFACE_INFO 0x2C08
+#define HDP_NONSURFACE_SIZE 0x2C0C
+
+#define HDP_ADDR_CONFIG 0x2F48
+#define HDP_MISC_CNTL 0x2F4C
+#define HDP_FLUSH_INVALIDATE_CACHE (1 << 0)
+
+#define IH_RB_CNTL 0x3e00
+# define IH_RB_ENABLE (1 << 0)
+# define IH_IB_SIZE(x) ((x) << 1) /* log2 */
+# define IH_RB_FULL_DRAIN_ENABLE (1 << 6)
+# define IH_WPTR_WRITEBACK_ENABLE (1 << 8)
+# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */
+# define IH_WPTR_OVERFLOW_ENABLE (1 << 16)
+# define IH_WPTR_OVERFLOW_CLEAR (1 << 31)
+#define IH_RB_BASE 0x3e04
+#define IH_RB_RPTR 0x3e08
+#define IH_RB_WPTR 0x3e0c
+# define RB_OVERFLOW (1 << 0)
+# define WPTR_OFFSET_MASK 0x3fffc
+#define IH_RB_WPTR_ADDR_HI 0x3e10
+#define IH_RB_WPTR_ADDR_LO 0x3e14
+#define IH_CNTL 0x3e18
+# define ENABLE_INTR (1 << 0)
+# define IH_MC_SWAP(x) ((x) << 1)
+# define IH_MC_SWAP_NONE 0
+# define IH_MC_SWAP_16BIT 1
+# define IH_MC_SWAP_32BIT 2
+# define IH_MC_SWAP_64BIT 3
+# define RPTR_REARM (1 << 4)
+# define MC_WRREQ_CREDIT(x) ((x) << 15)
+# define MC_WR_CLEAN_CNT(x) ((x) << 20)
+# define MC_VMID(x) ((x) << 25)
+
+#define CONFIG_MEMSIZE 0x5428
+
+#define INTERRUPT_CNTL 0x5468
+# define IH_DUMMY_RD_OVERRIDE (1 << 0)
+# define IH_DUMMY_RD_EN (1 << 1)
+# define IH_REQ_NONSNOOP_EN (1 << 3)
+# define GEN_IH_INT_EN (1 << 8)
+#define INTERRUPT_CNTL2 0x546c
+
+#define HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480
+
+#define BIF_FB_EN 0x5490
+#define FB_READ_EN (1 << 0)
+#define FB_WRITE_EN (1 << 1)
+
+#define HDP_REG_COHERENCY_FLUSH_CNTL 0x54A0
+
+#define DC_LB_MEMORY_SPLIT 0x6b0c
+#define DC_LB_MEMORY_CONFIG(x) ((x) << 20)
+
+#define PRIORITY_A_CNT 0x6b18
+#define PRIORITY_MARK_MASK 0x7fff
+#define PRIORITY_OFF (1 << 16)
+#define PRIORITY_ALWAYS_ON (1 << 20)
+#define PRIORITY_B_CNT 0x6b1c
+
+#define DPG_PIPE_ARBITRATION_CONTROL3 0x6cc8
+# define LATENCY_WATERMARK_MASK(x) ((x) << 16)
+#define DPG_PIPE_LATENCY_CONTROL 0x6ccc
+# define LATENCY_LOW_WATERMARK(x) ((x) << 0)
+# define LATENCY_HIGH_WATERMARK(x) ((x) << 16)
+
+/* 0x6bb8, 0x77b8, 0x103b8, 0x10fb8, 0x11bb8, 0x127b8 */
+#define VLINE_STATUS 0x6bb8
+# define VLINE_OCCURRED (1 << 0)
+# define VLINE_ACK (1 << 4)
+# define VLINE_STAT (1 << 12)
+# define VLINE_INTERRUPT (1 << 16)
+# define VLINE_INTERRUPT_TYPE (1 << 17)
+/* 0x6bbc, 0x77bc, 0x103bc, 0x10fbc, 0x11bbc, 0x127bc */
+#define VBLANK_STATUS 0x6bbc
+# define VBLANK_OCCURRED (1 << 0)
+# define VBLANK_ACK (1 << 4)
+# define VBLANK_STAT (1 << 12)
+# define VBLANK_INTERRUPT (1 << 16)
+# define VBLANK_INTERRUPT_TYPE (1 << 17)
+
+/* 0x6b40, 0x7740, 0x10340, 0x10f40, 0x11b40, 0x12740 */
+#define INT_MASK 0x6b40
+# define VBLANK_INT_MASK (1 << 0)
+# define VLINE_INT_MASK (1 << 4)
+
+#define DISP_INTERRUPT_STATUS 0x60f4
+# define LB_D1_VLINE_INTERRUPT (1 << 2)
+# define LB_D1_VBLANK_INTERRUPT (1 << 3)
+# define DC_HPD1_INTERRUPT (1 << 17)
+# define DC_HPD1_RX_INTERRUPT (1 << 18)
+# define DACA_AUTODETECT_INTERRUPT (1 << 22)
+# define DACB_AUTODETECT_INTERRUPT (1 << 23)
+# define DC_I2C_SW_DONE_INTERRUPT (1 << 24)
+# define DC_I2C_HW_DONE_INTERRUPT (1 << 25)
+#define DISP_INTERRUPT_STATUS_CONTINUE 0x60f8
+# define LB_D2_VLINE_INTERRUPT (1 << 2)
+# define LB_D2_VBLANK_INTERRUPT (1 << 3)
+# define DC_HPD2_INTERRUPT (1 << 17)
+# define DC_HPD2_RX_INTERRUPT (1 << 18)
+# define DISP_TIMER_INTERRUPT (1 << 24)
+#define DISP_INTERRUPT_STATUS_CONTINUE2 0x60fc
+# define LB_D3_VLINE_INTERRUPT (1 << 2)
+# define LB_D3_VBLANK_INTERRUPT (1 << 3)
+# define DC_HPD3_INTERRUPT (1 << 17)
+# define DC_HPD3_RX_INTERRUPT (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE3 0x6100
+# define LB_D4_VLINE_INTERRUPT (1 << 2)
+# define LB_D4_VBLANK_INTERRUPT (1 << 3)
+# define DC_HPD4_INTERRUPT (1 << 17)
+# define DC_HPD4_RX_INTERRUPT (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE4 0x614c
+# define LB_D5_VLINE_INTERRUPT (1 << 2)
+# define LB_D5_VBLANK_INTERRUPT (1 << 3)
+# define DC_HPD5_INTERRUPT (1 << 17)
+# define DC_HPD5_RX_INTERRUPT (1 << 18)
+#define DISP_INTERRUPT_STATUS_CONTINUE5 0x6150
+# define LB_D6_VLINE_INTERRUPT (1 << 2)
+# define LB_D6_VBLANK_INTERRUPT (1 << 3)
+# define DC_HPD6_INTERRUPT (1 << 17)
+# define DC_HPD6_RX_INTERRUPT (1 << 18)
+
+/* 0x6858, 0x7458, 0x10058, 0x10c58, 0x11858, 0x12458 */
+#define GRPH_INT_STATUS 0x6858
+# define GRPH_PFLIP_INT_OCCURRED (1 << 0)
+# define GRPH_PFLIP_INT_CLEAR (1 << 8)
+/* 0x685c, 0x745c, 0x1005c, 0x10c5c, 0x1185c, 0x1245c */
+#define GRPH_INT_CONTROL 0x685c
+# define GRPH_PFLIP_INT_MASK (1 << 0)
+# define GRPH_PFLIP_INT_TYPE (1 << 8)
+
+#define DACA_AUTODETECT_INT_CONTROL 0x66c8
+
+#define DC_HPD1_INT_STATUS 0x601c
+#define DC_HPD2_INT_STATUS 0x6028
+#define DC_HPD3_INT_STATUS 0x6034
+#define DC_HPD4_INT_STATUS 0x6040
+#define DC_HPD5_INT_STATUS 0x604c
+#define DC_HPD6_INT_STATUS 0x6058
+# define DC_HPDx_INT_STATUS (1 << 0)
+# define DC_HPDx_SENSE (1 << 1)
+# define DC_HPDx_RX_INT_STATUS (1 << 8)
+
+#define DC_HPD1_INT_CONTROL 0x6020
+#define DC_HPD2_INT_CONTROL 0x602c
+#define DC_HPD3_INT_CONTROL 0x6038
+#define DC_HPD4_INT_CONTROL 0x6044
+#define DC_HPD5_INT_CONTROL 0x6050
+#define DC_HPD6_INT_CONTROL 0x605c
+# define DC_HPDx_INT_ACK (1 << 0)
+# define DC_HPDx_INT_POLARITY (1 << 8)
+# define DC_HPDx_INT_EN (1 << 16)
+# define DC_HPDx_RX_INT_ACK (1 << 20)
+# define DC_HPDx_RX_INT_EN (1 << 24)
+
+#define DC_HPD1_CONTROL 0x6024
+#define DC_HPD2_CONTROL 0x6030
+#define DC_HPD3_CONTROL 0x603c
+#define DC_HPD4_CONTROL 0x6048
+#define DC_HPD5_CONTROL 0x6054
+#define DC_HPD6_CONTROL 0x6060
+# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0)
+# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16)
+# define DC_HPDx_EN (1 << 28)
+
+/* 0x6e98, 0x7a98, 0x10698, 0x11298, 0x11e98, 0x12a98 */
+#define CRTC_STATUS_FRAME_COUNT 0x6e98
+
+#define GRBM_CNTL 0x8000
+#define GRBM_READ_TIMEOUT(x) ((x) << 0)
+
+#define GRBM_STATUS2 0x8008
+#define RLC_RQ_PENDING (1 << 0)
+#define RLC_BUSY (1 << 8)
+#define TC_BUSY (1 << 9)
+
+#define GRBM_STATUS 0x8010
+#define CMDFIFO_AVAIL_MASK 0x0000000F
+#define RING2_RQ_PENDING (1 << 4)
+#define SRBM_RQ_PENDING (1 << 5)
+#define RING1_RQ_PENDING (1 << 6)
+#define CF_RQ_PENDING (1 << 7)
+#define PF_RQ_PENDING (1 << 8)
+#define GDS_DMA_RQ_PENDING (1 << 9)
+#define GRBM_EE_BUSY (1 << 10)
+#define DB_CLEAN (1 << 12)
+#define CB_CLEAN (1 << 13)
+#define TA_BUSY (1 << 14)
+#define GDS_BUSY (1 << 15)
+#define VGT_BUSY (1 << 17)
+#define IA_BUSY_NO_DMA (1 << 18)
+#define IA_BUSY (1 << 19)
+#define SX_BUSY (1 << 20)
+#define SPI_BUSY (1 << 22)
+#define BCI_BUSY (1 << 23)
+#define SC_BUSY (1 << 24)
+#define PA_BUSY (1 << 25)
+#define DB_BUSY (1 << 26)
+#define CP_COHERENCY_BUSY (1 << 28)
+#define CP_BUSY (1 << 29)
+#define CB_BUSY (1 << 30)
+#define GUI_ACTIVE (1 << 31)
+#define GRBM_STATUS_SE0 0x8014
+#define GRBM_STATUS_SE1 0x8018
+#define SE_DB_CLEAN (1 << 1)
+#define SE_CB_CLEAN (1 << 2)
+#define SE_BCI_BUSY (1 << 22)
+#define SE_VGT_BUSY (1 << 23)
+#define SE_PA_BUSY (1 << 24)
+#define SE_TA_BUSY (1 << 25)
+#define SE_SX_BUSY (1 << 26)
+#define SE_SPI_BUSY (1 << 27)
+#define SE_SC_BUSY (1 << 29)
+#define SE_DB_BUSY (1 << 30)
+#define SE_CB_BUSY (1 << 31)
+
+#define GRBM_SOFT_RESET 0x8020
+#define SOFT_RESET_CP (1 << 0)
+#define SOFT_RESET_CB (1 << 1)
+#define SOFT_RESET_RLC (1 << 2)
+#define SOFT_RESET_DB (1 << 3)
+#define SOFT_RESET_GDS (1 << 4)
+#define SOFT_RESET_PA (1 << 5)
+#define SOFT_RESET_SC (1 << 6)
+#define SOFT_RESET_BCI (1 << 7)
+#define SOFT_RESET_SPI (1 << 8)
+#define SOFT_RESET_SX (1 << 10)
+#define SOFT_RESET_TC (1 << 11)
+#define SOFT_RESET_TA (1 << 12)
+#define SOFT_RESET_VGT (1 << 14)
+#define SOFT_RESET_IA (1 << 15)
+
+#define GRBM_GFX_INDEX 0x802C
+
+#define GRBM_INT_CNTL 0x8060
+# define RDERR_INT_ENABLE (1 << 0)
+# define GUI_IDLE_INT_ENABLE (1 << 19)
+
+#define SCRATCH_REG0 0x8500
+#define SCRATCH_REG1 0x8504
+#define SCRATCH_REG2 0x8508
+#define SCRATCH_REG3 0x850C
+#define SCRATCH_REG4 0x8510
+#define SCRATCH_REG5 0x8514
+#define SCRATCH_REG6 0x8518
+#define SCRATCH_REG7 0x851C
+
+#define SCRATCH_UMSK 0x8540
+#define SCRATCH_ADDR 0x8544
+
+#define CP_SEM_WAIT_TIMER 0x85BC
+
+#define CP_SEM_INCOMPLETE_TIMER_CNTL 0x85C8
+
+#define CP_ME_CNTL 0x86D8
+#define CP_CE_HALT (1 << 24)
+#define CP_PFP_HALT (1 << 26)
+#define CP_ME_HALT (1 << 28)
+
+#define CP_COHER_CNTL2 0x85E8
+
+#define CP_RB2_RPTR 0x86f8
+#define CP_RB1_RPTR 0x86fc
+#define CP_RB0_RPTR 0x8700
+#define CP_RB_WPTR_DELAY 0x8704
+
+#define CP_QUEUE_THRESHOLDS 0x8760
+#define ROQ_IB1_START(x) ((x) << 0)
+#define ROQ_IB2_START(x) ((x) << 8)
+#define CP_MEQ_THRESHOLDS 0x8764
+#define MEQ1_START(x) ((x) << 0)
+#define MEQ2_START(x) ((x) << 8)
+
+#define CP_PERFMON_CNTL 0x87FC
+
+#define VGT_VTX_VECT_EJECT_REG 0x88B0
+
+#define VGT_CACHE_INVALIDATION 0x88C4
+#define CACHE_INVALIDATION(x) ((x) << 0)
+#define VC_ONLY 0
+#define TC_ONLY 1
+#define VC_AND_TC 2
+#define AUTO_INVLD_EN(x) ((x) << 6)
+#define NO_AUTO 0
+#define ES_AUTO 1
+#define GS_AUTO 2
+#define ES_AND_GS_AUTO 3
+#define VGT_ESGS_RING_SIZE 0x88C8
+#define VGT_GSVS_RING_SIZE 0x88CC
+
+#define VGT_GS_VERTEX_REUSE 0x88D4
+
+#define VGT_PRIMITIVE_TYPE 0x8958
+#define VGT_INDEX_TYPE 0x895C
+
+#define VGT_NUM_INDICES 0x8970
+#define VGT_NUM_INSTANCES 0x8974
+
+#define VGT_TF_RING_SIZE 0x8988
+
+#define VGT_HS_OFFCHIP_PARAM 0x89B0
+
+#define VGT_TF_MEMORY_BASE 0x89B8
+
+#define CC_GC_SHADER_ARRAY_CONFIG 0x89bc
+#define GC_USER_SHADER_ARRAY_CONFIG 0x89c0
+
+#define PA_CL_ENHANCE 0x8A14
+#define CLIP_VTX_REORDER_ENA (1 << 0)
+#define NUM_CLIP_SEQ(x) ((x) << 1)
+
+#define PA_SU_LINE_STIPPLE_VALUE 0x8A60
+
+#define PA_SC_LINE_STIPPLE_STATE 0x8B10
+
+#define PA_SC_FORCE_EOV_MAX_CNTS 0x8B24
+#define FORCE_EOV_MAX_CLK_CNT(x) ((x) << 0)
+#define FORCE_EOV_MAX_REZ_CNT(x) ((x) << 16)
+
+#define PA_SC_FIFO_SIZE 0x8BCC
+#define SC_FRONTEND_PRIM_FIFO_SIZE(x) ((x) << 0)
+#define SC_BACKEND_PRIM_FIFO_SIZE(x) ((x) << 6)
+#define SC_HIZ_TILE_FIFO_SIZE(x) ((x) << 15)
+#define SC_EARLYZ_TILE_FIFO_SIZE(x) ((x) << 23)
+
+#define PA_SC_ENHANCE 0x8BF0
+
+#define SQ_CONFIG 0x8C00
+
+#define SQC_CACHES 0x8C08
+
+#define SX_DEBUG_1 0x9060
+
+#define SPI_STATIC_THREAD_MGMT_1 0x90E0
+#define SPI_STATIC_THREAD_MGMT_2 0x90E4
+#define SPI_STATIC_THREAD_MGMT_3 0x90E8
+#define SPI_PS_MAX_WAVE_ID 0x90EC
+
+#define SPI_CONFIG_CNTL 0x9100
+
+#define SPI_CONFIG_CNTL_1 0x913C
+#define VTX_DONE_DELAY(x) ((x) << 0)
+#define INTERP_ONE_PRIM_PER_ROW (1 << 4)
+
+#define CGTS_TCC_DISABLE 0x9148
+#define CGTS_USER_TCC_DISABLE 0x914C
+#define TCC_DISABLE_MASK 0xFFFF0000
+#define TCC_DISABLE_SHIFT 16
+
+#define TA_CNTL_AUX 0x9508
+
+#define CC_RB_BACKEND_DISABLE 0x98F4
+#define BACKEND_DISABLE(x) ((x) << 16)
+#define GB_ADDR_CONFIG 0x98F8
+#define NUM_PIPES(x) ((x) << 0)
+#define NUM_PIPES_MASK 0x00000007
+#define NUM_PIPES_SHIFT 0
+#define PIPE_INTERLEAVE_SIZE(x) ((x) << 4)
+#define PIPE_INTERLEAVE_SIZE_MASK 0x00000070
+#define PIPE_INTERLEAVE_SIZE_SHIFT 4
+#define NUM_SHADER_ENGINES(x) ((x) << 12)
+#define NUM_SHADER_ENGINES_MASK 0x00003000
+#define NUM_SHADER_ENGINES_SHIFT 12
+#define SHADER_ENGINE_TILE_SIZE(x) ((x) << 16)
+#define SHADER_ENGINE_TILE_SIZE_MASK 0x00070000
+#define SHADER_ENGINE_TILE_SIZE_SHIFT 16
+#define NUM_GPUS(x) ((x) << 20)
+#define NUM_GPUS_MASK 0x00700000
+#define NUM_GPUS_SHIFT 20
+#define MULTI_GPU_TILE_SIZE(x) ((x) << 24)
+#define MULTI_GPU_TILE_SIZE_MASK 0x03000000
+#define MULTI_GPU_TILE_SIZE_SHIFT 24
+#define ROW_SIZE(x) ((x) << 28)
+#define ROW_SIZE_MASK 0x30000000
+#define ROW_SIZE_SHIFT 28
+
+#define GB_TILE_MODE0 0x9910
+# define MICRO_TILE_MODE(x) ((x) << 0)
+# define ADDR_SURF_DISPLAY_MICRO_TILING 0
+# define ADDR_SURF_THIN_MICRO_TILING 1
+# define ADDR_SURF_DEPTH_MICRO_TILING 2
+# define ARRAY_MODE(x) ((x) << 2)
+# define ARRAY_LINEAR_GENERAL 0
+# define ARRAY_LINEAR_ALIGNED 1
+# define ARRAY_1D_TILED_THIN1 2
+# define ARRAY_2D_TILED_THIN1 4
+# define PIPE_CONFIG(x) ((x) << 6)
+# define ADDR_SURF_P2 0
+# define ADDR_SURF_P4_8x16 4
+# define ADDR_SURF_P4_16x16 5
+# define ADDR_SURF_P4_16x32 6
+# define ADDR_SURF_P4_32x32 7
+# define ADDR_SURF_P8_16x16_8x16 8
+# define ADDR_SURF_P8_16x32_8x16 9
+# define ADDR_SURF_P8_32x32_8x16 10
+# define ADDR_SURF_P8_16x32_16x16 11
+# define ADDR_SURF_P8_32x32_16x16 12
+# define ADDR_SURF_P8_32x32_16x32 13
+# define ADDR_SURF_P8_32x64_32x32 14
+# define TILE_SPLIT(x) ((x) << 11)
+# define ADDR_SURF_TILE_SPLIT_64B 0
+# define ADDR_SURF_TILE_SPLIT_128B 1
+# define ADDR_SURF_TILE_SPLIT_256B 2
+# define ADDR_SURF_TILE_SPLIT_512B 3
+# define ADDR_SURF_TILE_SPLIT_1KB 4
+# define ADDR_SURF_TILE_SPLIT_2KB 5
+# define ADDR_SURF_TILE_SPLIT_4KB 6
+# define BANK_WIDTH(x) ((x) << 14)
+# define ADDR_SURF_BANK_WIDTH_1 0
+# define ADDR_SURF_BANK_WIDTH_2 1
+# define ADDR_SURF_BANK_WIDTH_4 2
+# define ADDR_SURF_BANK_WIDTH_8 3
+# define BANK_HEIGHT(x) ((x) << 16)
+# define ADDR_SURF_BANK_HEIGHT_1 0
+# define ADDR_SURF_BANK_HEIGHT_2 1
+# define ADDR_SURF_BANK_HEIGHT_4 2
+# define ADDR_SURF_BANK_HEIGHT_8 3
+# define MACRO_TILE_ASPECT(x) ((x) << 18)
+# define ADDR_SURF_MACRO_ASPECT_1 0
+# define ADDR_SURF_MACRO_ASPECT_2 1
+# define ADDR_SURF_MACRO_ASPECT_4 2
+# define ADDR_SURF_MACRO_ASPECT_8 3
+# define NUM_BANKS(x) ((x) << 20)
+# define ADDR_SURF_2_BANK 0
+# define ADDR_SURF_4_BANK 1
+# define ADDR_SURF_8_BANK 2
+# define ADDR_SURF_16_BANK 3
+
+#define CB_PERFCOUNTER0_SELECT0 0x9a20
+#define CB_PERFCOUNTER0_SELECT1 0x9a24
+#define CB_PERFCOUNTER1_SELECT0 0x9a28
+#define CB_PERFCOUNTER1_SELECT1 0x9a2c
+#define CB_PERFCOUNTER2_SELECT0 0x9a30
+#define CB_PERFCOUNTER2_SELECT1 0x9a34
+#define CB_PERFCOUNTER3_SELECT0 0x9a38
+#define CB_PERFCOUNTER3_SELECT1 0x9a3c
+
+#define GC_USER_RB_BACKEND_DISABLE 0x9B7C
+#define BACKEND_DISABLE_MASK 0x00FF0000
+#define BACKEND_DISABLE_SHIFT 16
+
+#define TCP_CHAN_STEER_LO 0xac0c
+#define TCP_CHAN_STEER_HI 0xac10
+
+#define CP_RB0_BASE 0xC100
+#define CP_RB0_CNTL 0xC104
+#define RB_BUFSZ(x) ((x) << 0)
+#define RB_BLKSZ(x) ((x) << 8)
+#define BUF_SWAP_32BIT (2 << 16)
+#define RB_NO_UPDATE (1 << 27)
+#define RB_RPTR_WR_ENA (1 << 31)
+
+#define CP_RB0_RPTR_ADDR 0xC10C
+#define CP_RB0_RPTR_ADDR_HI 0xC110
+#define CP_RB0_WPTR 0xC114
+
+#define CP_PFP_UCODE_ADDR 0xC150
+#define CP_PFP_UCODE_DATA 0xC154
+#define CP_ME_RAM_RADDR 0xC158
+#define CP_ME_RAM_WADDR 0xC15C
+#define CP_ME_RAM_DATA 0xC160
+
+#define CP_CE_UCODE_ADDR 0xC168
+#define CP_CE_UCODE_DATA 0xC16C
+
+#define CP_RB1_BASE 0xC180
+#define CP_RB1_CNTL 0xC184
+#define CP_RB1_RPTR_ADDR 0xC188
+#define CP_RB1_RPTR_ADDR_HI 0xC18C
+#define CP_RB1_WPTR 0xC190
+#define CP_RB2_BASE 0xC194
+#define CP_RB2_CNTL 0xC198
+#define CP_RB2_RPTR_ADDR 0xC19C
+#define CP_RB2_RPTR_ADDR_HI 0xC1A0
+#define CP_RB2_WPTR 0xC1A4
+#define CP_INT_CNTL_RING0 0xC1A8
+#define CP_INT_CNTL_RING1 0xC1AC
+#define CP_INT_CNTL_RING2 0xC1B0
+# define CNTX_BUSY_INT_ENABLE (1 << 19)
+# define CNTX_EMPTY_INT_ENABLE (1 << 20)
+# define WAIT_MEM_SEM_INT_ENABLE (1 << 21)
+# define TIME_STAMP_INT_ENABLE (1 << 26)
+# define CP_RINGID2_INT_ENABLE (1 << 29)
+# define CP_RINGID1_INT_ENABLE (1 << 30)
+# define CP_RINGID0_INT_ENABLE (1 << 31)
+#define CP_INT_STATUS_RING0 0xC1B4
+#define CP_INT_STATUS_RING1 0xC1B8
+#define CP_INT_STATUS_RING2 0xC1BC
+# define WAIT_MEM_SEM_INT_STAT (1 << 21)
+# define TIME_STAMP_INT_STAT (1 << 26)
+# define CP_RINGID2_INT_STAT (1 << 29)
+# define CP_RINGID1_INT_STAT (1 << 30)
+# define CP_RINGID0_INT_STAT (1 << 31)
+
+#define CP_DEBUG 0xC1FC
+
+#define RLC_CNTL 0xC300
+# define RLC_ENABLE (1 << 0)
+#define RLC_RL_BASE 0xC304
+#define RLC_RL_SIZE 0xC308
+#define RLC_LB_CNTL 0xC30C
+#define RLC_SAVE_AND_RESTORE_BASE 0xC310
+#define RLC_LB_CNTR_MAX 0xC314
+#define RLC_LB_CNTR_INIT 0xC318
+
+#define RLC_CLEAR_STATE_RESTORE_BASE 0xC320
+
+#define RLC_UCODE_ADDR 0xC32C
+#define RLC_UCODE_DATA 0xC330
+
+#define RLC_MC_CNTL 0xC344
+#define RLC_UCODE_CNTL 0xC348
+
+#define VGT_EVENT_INITIATOR 0x28a90
+# define SAMPLE_STREAMOUTSTATS1 (1 << 0)
+# define SAMPLE_STREAMOUTSTATS2 (2 << 0)
+# define SAMPLE_STREAMOUTSTATS3 (3 << 0)
+# define CACHE_FLUSH_TS (4 << 0)
+# define CACHE_FLUSH (6 << 0)
+# define CS_PARTIAL_FLUSH (7 << 0)
+# define VGT_STREAMOUT_RESET (10 << 0)
+# define END_OF_PIPE_INCR_DE (11 << 0)
+# define END_OF_PIPE_IB_END (12 << 0)
+# define RST_PIX_CNT (13 << 0)
+# define VS_PARTIAL_FLUSH (15 << 0)
+# define PS_PARTIAL_FLUSH (16 << 0)
+# define CACHE_FLUSH_AND_INV_TS_EVENT (20 << 0)
+# define ZPASS_DONE (21 << 0)
+# define CACHE_FLUSH_AND_INV_EVENT (22 << 0)
+# define PERFCOUNTER_START (23 << 0)
+# define PERFCOUNTER_STOP (24 << 0)
+# define PIPELINESTAT_START (25 << 0)
+# define PIPELINESTAT_STOP (26 << 0)
+# define PERFCOUNTER_SAMPLE (27 << 0)
+# define SAMPLE_PIPELINESTAT (30 << 0)
+# define SAMPLE_STREAMOUTSTATS (32 << 0)
+# define RESET_VTX_CNT (33 << 0)
+# define VGT_FLUSH (36 << 0)
+# define BOTTOM_OF_PIPE_TS (40 << 0)
+# define DB_CACHE_FLUSH_AND_INV (42 << 0)
+# define FLUSH_AND_INV_DB_DATA_TS (43 << 0)
+# define FLUSH_AND_INV_DB_META (44 << 0)
+# define FLUSH_AND_INV_CB_DATA_TS (45 << 0)
+# define FLUSH_AND_INV_CB_META (46 << 0)
+# define CS_DONE (47 << 0)
+# define PS_DONE (48 << 0)
+# define FLUSH_AND_INV_CB_PIXEL_DATA (49 << 0)
+# define THREAD_TRACE_START (51 << 0)
+# define THREAD_TRACE_STOP (52 << 0)
+# define THREAD_TRACE_FLUSH (54 << 0)
+# define THREAD_TRACE_FINISH (55 << 0)
+
+/*
+ * PM4
+ */
+#define PACKET_TYPE0 0
+#define PACKET_TYPE1 1
+#define PACKET_TYPE2 2
+#define PACKET_TYPE3 3
+
+#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3)
+#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF)
+#define CP_PACKET0_GET_REG(h) (((h) & 0xFFFF) << 2)
+#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF)
+#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \
+ (((reg) >> 2) & 0xFFFF) | \
+ ((n) & 0x3FFF) << 16)
+#define CP_PACKET2 0x80000000
+#define PACKET2_PAD_SHIFT 0
+#define PACKET2_PAD_MASK (0x3fffffff << 0)
+
+#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v)))
+
+#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \
+ (((op) & 0xFF) << 8) | \
+ ((n) & 0x3FFF) << 16)
+
+#define PACKET3_COMPUTE(op, n) (PACKET3(op, n) | 1 << 1)
+
+/* Packet 3 types */
+#define PACKET3_NOP 0x10
+#define PACKET3_SET_BASE 0x11
+#define PACKET3_BASE_INDEX(x) ((x) << 0)
+#define GDS_PARTITION_BASE 2
+#define CE_PARTITION_BASE 3
+#define PACKET3_CLEAR_STATE 0x12
+#define PACKET3_INDEX_BUFFER_SIZE 0x13
+#define PACKET3_DISPATCH_DIRECT 0x15
+#define PACKET3_DISPATCH_INDIRECT 0x16
+#define PACKET3_ALLOC_GDS 0x1B
+#define PACKET3_WRITE_GDS_RAM 0x1C
+#define PACKET3_ATOMIC_GDS 0x1D
+#define PACKET3_ATOMIC 0x1E
+#define PACKET3_OCCLUSION_QUERY 0x1F
+#define PACKET3_SET_PREDICATION 0x20
+#define PACKET3_REG_RMW 0x21
+#define PACKET3_COND_EXEC 0x22
+#define PACKET3_PRED_EXEC 0x23
+#define PACKET3_DRAW_INDIRECT 0x24
+#define PACKET3_DRAW_INDEX_INDIRECT 0x25
+#define PACKET3_INDEX_BASE 0x26
+#define PACKET3_DRAW_INDEX_2 0x27
+#define PACKET3_CONTEXT_CONTROL 0x28
+#define PACKET3_INDEX_TYPE 0x2A
+#define PACKET3_DRAW_INDIRECT_MULTI 0x2C
+#define PACKET3_DRAW_INDEX_AUTO 0x2D
+#define PACKET3_DRAW_INDEX_IMMD 0x2E
+#define PACKET3_NUM_INSTANCES 0x2F
+#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30
+#define PACKET3_INDIRECT_BUFFER_CONST 0x31
+#define PACKET3_INDIRECT_BUFFER 0x32
+#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34
+#define PACKET3_DRAW_INDEX_OFFSET_2 0x35
+#define PACKET3_DRAW_INDEX_MULTI_ELEMENT 0x36
+#define PACKET3_WRITE_DATA 0x37
+#define PACKET3_DRAW_INDEX_INDIRECT_MULTI 0x38
+#define PACKET3_MEM_SEMAPHORE 0x39
+#define PACKET3_MPEG_INDEX 0x3A
+#define PACKET3_COPY_DW 0x3B
+#define PACKET3_WAIT_REG_MEM 0x3C
+#define PACKET3_MEM_WRITE 0x3D
+#define PACKET3_COPY_DATA 0x40
+#define PACKET3_PFP_SYNC_ME 0x42
+#define PACKET3_SURFACE_SYNC 0x43
+# define PACKET3_DEST_BASE_0_ENA (1 << 0)
+# define PACKET3_DEST_BASE_1_ENA (1 << 1)
+# define PACKET3_CB0_DEST_BASE_ENA (1 << 6)
+# define PACKET3_CB1_DEST_BASE_ENA (1 << 7)
+# define PACKET3_CB2_DEST_BASE_ENA (1 << 8)
+# define PACKET3_CB3_DEST_BASE_ENA (1 << 9)
+# define PACKET3_CB4_DEST_BASE_ENA (1 << 10)
+# define PACKET3_CB5_DEST_BASE_ENA (1 << 11)
+# define PACKET3_CB6_DEST_BASE_ENA (1 << 12)
+# define PACKET3_CB7_DEST_BASE_ENA (1 << 13)
+# define PACKET3_DB_DEST_BASE_ENA (1 << 14)
+# define PACKET3_DEST_BASE_2_ENA (1 << 19)
+# define PACKET3_DEST_BASE_3_ENA (1 << 21)
+# define PACKET3_TCL1_ACTION_ENA (1 << 22)
+# define PACKET3_TC_ACTION_ENA (1 << 23)
+# define PACKET3_CB_ACTION_ENA (1 << 25)
+# define PACKET3_DB_ACTION_ENA (1 << 26)
+# define PACKET3_SH_KCACHE_ACTION_ENA (1 << 27)
+# define PACKET3_SH_ICACHE_ACTION_ENA (1 << 29)
+#define PACKET3_ME_INITIALIZE 0x44
+#define PACKET3_ME_INITIALIZE_DEVICE_ID(x) ((x) << 16)
+#define PACKET3_COND_WRITE 0x45
+#define PACKET3_EVENT_WRITE 0x46
+#define EVENT_TYPE(x) ((x) << 0)
+#define EVENT_INDEX(x) ((x) << 8)
+ /* 0 - any non-TS event
+ * 1 - ZPASS_DONE
+ * 2 - SAMPLE_PIPELINESTAT
+ * 3 - SAMPLE_STREAMOUTSTAT*
+ * 4 - *S_PARTIAL_FLUSH
+ * 5 - EOP events
+ * 6 - EOS events
+ * 7 - CACHE_FLUSH, CACHE_FLUSH_AND_INV_EVENT
+ */
+#define INV_L2 (1 << 20)
+ /* INV TC L2 cache when EVENT_INDEX = 7 */
+#define PACKET3_EVENT_WRITE_EOP 0x47
+#define DATA_SEL(x) ((x) << 29)
+ /* 0 - discard
+ * 1 - send low 32bit data
+ * 2 - send 64bit data
+ * 3 - send 64bit counter value
+ */
+#define INT_SEL(x) ((x) << 24)
+ /* 0 - none
+ * 1 - interrupt only (DATA_SEL = 0)
+ * 2 - interrupt when data write is confirmed
+ */
+#define PACKET3_EVENT_WRITE_EOS 0x48
+#define PACKET3_PREAMBLE_CNTL 0x4A
+# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28)
+# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28)
+#define PACKET3_ONE_REG_WRITE 0x57
+#define PACKET3_LOAD_CONFIG_REG 0x5F
+#define PACKET3_LOAD_CONTEXT_REG 0x60
+#define PACKET3_LOAD_SH_REG 0x61
+#define PACKET3_SET_CONFIG_REG 0x68
+#define PACKET3_SET_CONFIG_REG_START 0x00008000
+#define PACKET3_SET_CONFIG_REG_END 0x0000b000
+#define PACKET3_SET_CONTEXT_REG 0x69
+#define PACKET3_SET_CONTEXT_REG_START 0x00028000
+#define PACKET3_SET_CONTEXT_REG_END 0x00029000
+#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73
+#define PACKET3_SET_RESOURCE_INDIRECT 0x74
+#define PACKET3_SET_SH_REG 0x76
+#define PACKET3_SET_SH_REG_START 0x0000b000
+#define PACKET3_SET_SH_REG_END 0x0000c000
+#define PACKET3_SET_SH_REG_OFFSET 0x77
+#define PACKET3_ME_WRITE 0x7A
+#define PACKET3_SCRATCH_RAM_WRITE 0x7D
+#define PACKET3_SCRATCH_RAM_READ 0x7E
+#define PACKET3_CE_WRITE 0x7F
+#define PACKET3_LOAD_CONST_RAM 0x80
+#define PACKET3_WRITE_CONST_RAM 0x81
+#define PACKET3_WRITE_CONST_RAM_OFFSET 0x82
+#define PACKET3_DUMP_CONST_RAM 0x83
+#define PACKET3_INCREMENT_CE_COUNTER 0x84
+#define PACKET3_INCREMENT_DE_COUNTER 0x85
+#define PACKET3_WAIT_ON_CE_COUNTER 0x86
+#define PACKET3_WAIT_ON_DE_COUNTER 0x87
+#define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88
+#define PACKET3_SET_CE_DE_COUNTERS 0x89
+#define PACKET3_WAIT_ON_AVAIL_BUFFER 0x8A
+
+#endif
diff --git a/drivers/gpu/drm/savage/savage_state.c b/drivers/gpu/drm/savage/savage_state.c
index 8a3e31599c94..031aaaf79ac2 100644
--- a/drivers/gpu/drm/savage/savage_state.c
+++ b/drivers/gpu/drm/savage/savage_state.c
@@ -1057,7 +1057,8 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_
DRM_ERROR("indexed drawing command extends "
"beyond end of command buffer\n");
DMA_FLUSH();
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
/* fall through */
case SAVAGE_CMD_DMA_PRIM:
@@ -1076,7 +1077,7 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_
cmdbuf->vb_stride,
cmdbuf->nbox, cmdbuf->box_addr);
if (ret != 0)
- return ret;
+ goto done;
first_draw_cmd = NULL;
}
}
diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
index 573220cc5269..30d98d14b5c5 100644
--- a/drivers/gpu/drm/sis/sis_drv.c
+++ b/drivers/gpu/drm/sis/sis_drv.c
@@ -41,6 +41,8 @@ static int sis_driver_load(struct drm_device *dev, unsigned long chipset)
{
drm_sis_private_t *dev_priv;
+ pci_set_master(dev->pdev);
+
dev_priv = kzalloc(sizeof(drm_sis_private_t), GFP_KERNEL);
if (dev_priv == NULL)
return -ENOMEM;
diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c
index 747c1413fc95..4a8728291361 100644
--- a/drivers/gpu/drm/ttm/ttm_agp_backend.c
+++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c
@@ -29,6 +29,8 @@
* Keith Packard.
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_module.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_page_alloc.h"
@@ -74,7 +76,7 @@ static int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem)
ret = agp_bind_memory(mem, node->start);
if (ret)
- printk(KERN_ERR TTM_PFX "AGP Bind memory failed.\n");
+ pr_err("AGP Bind memory failed\n");
return ret;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 7c3a57de8187..1f5c67c579cf 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -28,6 +28,8 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_module.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_placement.h"
@@ -68,15 +70,13 @@ static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type)
{
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
- printk(KERN_ERR TTM_PFX " has_type: %d\n", man->has_type);
- printk(KERN_ERR TTM_PFX " use_type: %d\n", man->use_type);
- printk(KERN_ERR TTM_PFX " flags: 0x%08X\n", man->flags);
- printk(KERN_ERR TTM_PFX " gpu_offset: 0x%08lX\n", man->gpu_offset);
- printk(KERN_ERR TTM_PFX " size: %llu\n", man->size);
- printk(KERN_ERR TTM_PFX " available_caching: 0x%08X\n",
- man->available_caching);
- printk(KERN_ERR TTM_PFX " default_caching: 0x%08X\n",
- man->default_caching);
+ pr_err(" has_type: %d\n", man->has_type);
+ pr_err(" use_type: %d\n", man->use_type);
+ pr_err(" flags: 0x%08X\n", man->flags);
+ pr_err(" gpu_offset: 0x%08lX\n", man->gpu_offset);
+ pr_err(" size: %llu\n", man->size);
+ pr_err(" available_caching: 0x%08X\n", man->available_caching);
+ pr_err(" default_caching: 0x%08X\n", man->default_caching);
if (mem_type != TTM_PL_SYSTEM)
(*man->func->debug)(man, TTM_PFX);
}
@@ -86,16 +86,16 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
{
int i, ret, mem_type;
- printk(KERN_ERR TTM_PFX "No space for %p (%lu pages, %luK, %luM)\n",
- bo, bo->mem.num_pages, bo->mem.size >> 10,
- bo->mem.size >> 20);
+ pr_err("No space for %p (%lu pages, %luK, %luM)\n",
+ bo, bo->mem.num_pages, bo->mem.size >> 10,
+ bo->mem.size >> 20);
for (i = 0; i < placement->num_placement; i++) {
ret = ttm_mem_type_from_flags(placement->placement[i],
&mem_type);
if (ret)
return;
- printk(KERN_ERR TTM_PFX " placement[%d]=0x%08X (%d)\n",
- i, placement->placement[i], mem_type);
+ pr_err(" placement[%d]=0x%08X (%d)\n",
+ i, placement->placement[i], mem_type);
ttm_mem_type_debug(bo->bdev, mem_type);
}
}
@@ -344,7 +344,7 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
ret = -ENOMEM;
break;
default:
- printk(KERN_ERR TTM_PFX "Illegal buffer object type\n");
+ pr_err("Illegal buffer object type\n");
ret = -EINVAL;
break;
}
@@ -432,7 +432,7 @@ moved:
if (bo->evicted) {
ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement);
if (ret)
- printk(KERN_ERR TTM_PFX "Can not flush read caches\n");
+ pr_err("Can not flush read caches\n");
bo->evicted = false;
}
@@ -734,9 +734,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS) {
- printk(KERN_ERR TTM_PFX
- "Failed to expire sync object before "
- "buffer eviction.\n");
+ pr_err("Failed to expire sync object before buffer eviction\n");
}
goto out;
}
@@ -757,9 +755,8 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
no_wait_reserve, no_wait_gpu);
if (ret) {
if (ret != -ERESTARTSYS) {
- printk(KERN_ERR TTM_PFX
- "Failed to find memory space for "
- "buffer 0x%p eviction.\n", bo);
+ pr_err("Failed to find memory space for buffer 0x%p eviction\n",
+ bo);
ttm_bo_mem_space_debug(bo, &placement);
}
goto out;
@@ -769,7 +766,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
no_wait_reserve, no_wait_gpu);
if (ret) {
if (ret != -ERESTARTSYS)
- printk(KERN_ERR TTM_PFX "Buffer eviction failed\n");
+ pr_err("Buffer eviction failed\n");
ttm_bo_mem_put(bo, &evict_mem);
goto out;
}
@@ -1180,7 +1177,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false);
if (ret) {
- printk(KERN_ERR TTM_PFX "Out of kernel memory.\n");
+ pr_err("Out of kernel memory\n");
if (destroy)
(*destroy)(bo);
else
@@ -1191,7 +1188,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
size += buffer_start & ~PAGE_MASK;
num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (num_pages == 0) {
- printk(KERN_ERR TTM_PFX "Illegal buffer object size.\n");
+ pr_err("Illegal buffer object size\n");
if (destroy)
(*destroy)(bo);
else
@@ -1342,8 +1339,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
if (allow_errors) {
return ret;
} else {
- printk(KERN_ERR TTM_PFX
- "Cleanup eviction failed\n");
+ pr_err("Cleanup eviction failed\n");
}
}
spin_lock(&glob->lru_lock);
@@ -1358,14 +1354,14 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
int ret = -EINVAL;
if (mem_type >= TTM_NUM_MEM_TYPES) {
- printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", mem_type);
+ pr_err("Illegal memory type %d\n", mem_type);
return ret;
}
man = &bdev->man[mem_type];
if (!man->has_type) {
- printk(KERN_ERR TTM_PFX "Trying to take down uninitialized "
- "memory manager type %u\n", mem_type);
+ pr_err("Trying to take down uninitialized memory manager type %u\n",
+ mem_type);
return ret;
}
@@ -1388,16 +1384,12 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type)
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
if (mem_type == 0 || mem_type >= TTM_NUM_MEM_TYPES) {
- printk(KERN_ERR TTM_PFX
- "Illegal memory manager memory type %u.\n",
- mem_type);
+ pr_err("Illegal memory manager memory type %u\n", mem_type);
return -EINVAL;
}
if (!man->has_type) {
- printk(KERN_ERR TTM_PFX
- "Memory type %u has not been initialized.\n",
- mem_type);
+ pr_err("Memory type %u has not been initialized\n", mem_type);
return 0;
}
@@ -1482,8 +1474,7 @@ int ttm_bo_global_init(struct drm_global_reference *ref)
ttm_mem_init_shrink(&glob->shrink, ttm_bo_swapout);
ret = ttm_mem_register_shrink(glob->mem_glob, &glob->shrink);
if (unlikely(ret != 0)) {
- printk(KERN_ERR TTM_PFX
- "Could not register buffer object swapout.\n");
+ pr_err("Could not register buffer object swapout\n");
goto out_no_shrink;
}
@@ -1516,9 +1507,8 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
man->use_type = false;
if ((i != TTM_PL_SYSTEM) && ttm_bo_clean_mm(bdev, i)) {
ret = -EBUSY;
- printk(KERN_ERR TTM_PFX
- "DRM memory manager type %d "
- "is not clean.\n", i);
+ pr_err("DRM memory manager type %d is not clean\n",
+ i);
}
man->has_type = false;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 54412848de88..a877813571a4 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -28,6 +28,8 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <ttm/ttm_module.h>
#include <ttm/ttm_bo_driver.h>
#include <ttm/ttm_placement.h>
@@ -262,8 +264,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
read_unlock(&bdev->vm_lock);
if (unlikely(bo == NULL)) {
- printk(KERN_ERR TTM_PFX
- "Could not find buffer object to map.\n");
+ pr_err("Could not find buffer object to map\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c
index 9eba8e9a4e9c..23d2ecbaed59 100644
--- a/drivers/gpu/drm/ttm/ttm_memory.c
+++ b/drivers/gpu/drm/ttm/ttm_memory.c
@@ -25,6 +25,8 @@
*
**************************************************************************/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_memory.h"
#include "ttm/ttm_module.h"
#include "ttm/ttm_page_alloc.h"
@@ -74,9 +76,8 @@ static void ttm_mem_zone_kobj_release(struct kobject *kobj)
struct ttm_mem_zone *zone =
container_of(kobj, struct ttm_mem_zone, kobj);
- printk(KERN_INFO TTM_PFX
- "Zone %7s: Used memory at exit: %llu kiB.\n",
- zone->name, (unsigned long long) zone->used_mem >> 10);
+ pr_info("Zone %7s: Used memory at exit: %llu kiB\n",
+ zone->name, (unsigned long long)zone->used_mem >> 10);
kfree(zone);
}
@@ -390,9 +391,8 @@ int ttm_mem_global_init(struct ttm_mem_global *glob)
#endif
for (i = 0; i < glob->num_zones; ++i) {
zone = glob->zones[i];
- printk(KERN_INFO TTM_PFX
- "Zone %7s: Available graphics memory: %llu kiB.\n",
- zone->name, (unsigned long long) zone->max_mem >> 10);
+ pr_info("Zone %7s: Available graphics memory: %llu kiB\n",
+ zone->name, (unsigned long long)zone->max_mem >> 10);
}
ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index 93577f2e2954..68daca412cbd 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -49,6 +49,8 @@
* for fast lookup of ref objects given a base object.
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_object.h"
#include "ttm/ttm_module.h"
#include <linux/list.h>
@@ -232,8 +234,7 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
return NULL;
if (tfile != base->tfile && !base->shareable) {
- printk(KERN_ERR TTM_PFX
- "Attempted access of non-shareable object.\n");
+ pr_err("Attempted access of non-shareable object\n");
ttm_base_object_unref(&base);
return NULL;
}
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index 499debda791e..ebc6fac96e36 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -30,6 +30,9 @@
* - Use page->lru to keep a free list
* - doesn't track currently in use pages
*/
+
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/highmem.h>
@@ -167,18 +170,13 @@ static ssize_t ttm_pool_store(struct kobject *kobj,
m->options.small = val;
else if (attr == &ttm_page_pool_alloc_size) {
if (val > NUM_PAGES_TO_ALLOC*8) {
- printk(KERN_ERR TTM_PFX
- "Setting allocation size to %lu "
- "is not allowed. Recommended size is "
- "%lu\n",
+ pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n",
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7),
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
return size;
} else if (val > NUM_PAGES_TO_ALLOC) {
- printk(KERN_WARNING TTM_PFX
- "Setting allocation size to "
- "larger than %lu is not recommended.\n",
- NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
+ pr_warn("Setting allocation size to larger than %lu is not recommended\n",
+ NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
}
m->options.alloc_size = val;
}
@@ -279,8 +277,7 @@ static void ttm_pages_put(struct page *pages[], unsigned npages)
{
unsigned i;
if (set_pages_array_wb(pages, npages))
- printk(KERN_ERR TTM_PFX "Failed to set %d pages to wb!\n",
- npages);
+ pr_err("Failed to set %d pages to wb!\n", npages);
for (i = 0; i < npages; ++i)
__free_page(pages[i]);
}
@@ -315,8 +312,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free)
pages_to_free = kmalloc(npages_to_free * sizeof(struct page *),
GFP_KERNEL);
if (!pages_to_free) {
- printk(KERN_ERR TTM_PFX
- "Failed to allocate memory for pool free operation.\n");
+ pr_err("Failed to allocate memory for pool free operation\n");
return 0;
}
@@ -438,16 +434,12 @@ static int ttm_set_pages_caching(struct page **pages,
case tt_uncached:
r = set_pages_array_uc(pages, cpages);
if (r)
- printk(KERN_ERR TTM_PFX
- "Failed to set %d pages to uc!\n",
- cpages);
+ pr_err("Failed to set %d pages to uc!\n", cpages);
break;
case tt_wc:
r = set_pages_array_wc(pages, cpages);
if (r)
- printk(KERN_ERR TTM_PFX
- "Failed to set %d pages to wc!\n",
- cpages);
+ pr_err("Failed to set %d pages to wc!\n", cpages);
break;
default:
break;
@@ -492,8 +484,7 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags,
caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL);
if (!caching_array) {
- printk(KERN_ERR TTM_PFX
- "Unable to allocate table for new pages.");
+ pr_err("Unable to allocate table for new pages\n");
return -ENOMEM;
}
@@ -501,7 +492,7 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags,
p = alloc_page(gfp_flags);
if (!p) {
- printk(KERN_ERR TTM_PFX "Unable to get page %u.\n", i);
+ pr_err("Unable to get page %u\n", i);
/* store already allocated pages in the pool after
* setting the caching state */
@@ -599,8 +590,7 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool,
++pool->nrefills;
pool->npages += alloc_size;
} else {
- printk(KERN_ERR TTM_PFX
- "Failed to fill pool (%p).", pool);
+ pr_err("Failed to fill pool (%p)\n", pool);
/* If we have any pages left put them to the pool. */
list_for_each_entry(p, &pool->list, lru) {
++cpages;
@@ -675,9 +665,7 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags,
for (i = 0; i < npages; i++) {
if (pages[i]) {
if (page_count(pages[i]) != 1)
- printk(KERN_ERR TTM_PFX
- "Erroneous page count. "
- "Leaking pages.\n");
+ pr_err("Erroneous page count. Leaking pages.\n");
__free_page(pages[i]);
pages[i] = NULL;
}
@@ -689,9 +677,7 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags,
for (i = 0; i < npages; i++) {
if (pages[i]) {
if (page_count(pages[i]) != 1)
- printk(KERN_ERR TTM_PFX
- "Erroneous page count. "
- "Leaking pages.\n");
+ pr_err("Erroneous page count. Leaking pages.\n");
list_add_tail(&pages[i]->lru, &pool->list);
pages[i] = NULL;
pool->npages++;
@@ -740,8 +726,7 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags,
p = alloc_page(gfp_flags);
if (!p) {
- printk(KERN_ERR TTM_PFX
- "Unable to allocate page.");
+ pr_err("Unable to allocate page\n");
return -ENOMEM;
}
@@ -781,9 +766,7 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags,
if (r) {
/* If there is any pages in the list put them back to
* the pool. */
- printk(KERN_ERR TTM_PFX
- "Failed to allocate extra pages "
- "for large request.");
+ pr_err("Failed to allocate extra pages for large request\n");
ttm_put_pages(pages, count, flags, cstate);
return r;
}
@@ -809,7 +792,7 @@ int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages)
WARN_ON(_manager);
- printk(KERN_INFO TTM_PFX "Initializing pool allocator.\n");
+ pr_info("Initializing pool allocator\n");
_manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
@@ -844,7 +827,7 @@ void ttm_page_alloc_fini(void)
{
int i;
- printk(KERN_INFO TTM_PFX "Finalizing pool allocator.\n");
+ pr_info("Finalizing pool allocator\n");
ttm_pool_mm_shrink_fini(_manager);
for (i = 0; i < NUM_POOLS; ++i)
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
index 0c46d8cdc6ea..4f9e548b2eec 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
@@ -33,6 +33,8 @@
* when freed).
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <linux/dma-mapping.h>
#include <linux/list.h>
#include <linux/seq_file.h> /* for seq_printf */
@@ -221,18 +223,13 @@ static ssize_t ttm_pool_store(struct kobject *kobj, struct attribute *attr,
m->options.small = val;
else if (attr == &ttm_page_pool_alloc_size) {
if (val > NUM_PAGES_TO_ALLOC*8) {
- printk(KERN_ERR TTM_PFX
- "Setting allocation size to %lu "
- "is not allowed. Recommended size is "
- "%lu\n",
+ pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n",
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7),
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
return size;
} else if (val > NUM_PAGES_TO_ALLOC) {
- printk(KERN_WARNING TTM_PFX
- "Setting allocation size to "
- "larger than %lu is not recommended.\n",
- NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
+ pr_warn("Setting allocation size to larger than %lu is not recommended\n",
+ NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
}
m->options.alloc_size = val;
}
@@ -313,15 +310,13 @@ static int ttm_set_pages_caching(struct dma_pool *pool,
if (pool->type & IS_UC) {
r = set_pages_array_uc(pages, cpages);
if (r)
- pr_err(TTM_PFX
- "%s: Failed to set %d pages to uc!\n",
+ pr_err("%s: Failed to set %d pages to uc!\n",
pool->dev_name, cpages);
}
if (pool->type & IS_WC) {
r = set_pages_array_wc(pages, cpages);
if (r)
- pr_err(TTM_PFX
- "%s: Failed to set %d pages to wc!\n",
+ pr_err("%s: Failed to set %d pages to wc!\n",
pool->dev_name, cpages);
}
return r;
@@ -387,8 +382,8 @@ static void ttm_dma_pages_put(struct dma_pool *pool, struct list_head *d_pages,
/* Don't set WB on WB page pool. */
if (npages && !(pool->type & IS_CACHED) &&
set_pages_array_wb(pages, npages))
- pr_err(TTM_PFX "%s: Failed to set %d pages to wb!\n",
- pool->dev_name, npages);
+ pr_err("%s: Failed to set %d pages to wb!\n",
+ pool->dev_name, npages);
list_for_each_entry_safe(d_page, tmp, d_pages, page_list) {
list_del(&d_page->page_list);
@@ -400,8 +395,8 @@ static void ttm_dma_page_put(struct dma_pool *pool, struct dma_page *d_page)
{
/* Don't set WB on WB page pool. */
if (!(pool->type & IS_CACHED) && set_pages_array_wb(&d_page->p, 1))
- pr_err(TTM_PFX "%s: Failed to set %d pages to wb!\n",
- pool->dev_name, 1);
+ pr_err("%s: Failed to set %d pages to wb!\n",
+ pool->dev_name, 1);
list_del(&d_page->page_list);
__ttm_dma_free_page(pool, d_page);
@@ -430,17 +425,16 @@ static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free)
#if 0
if (nr_free > 1) {
pr_debug("%s: (%s:%d) Attempting to free %d (%d) pages\n",
- pool->dev_name, pool->name, current->pid,
- npages_to_free, nr_free);
+ pool->dev_name, pool->name, current->pid,
+ npages_to_free, nr_free);
}
#endif
pages_to_free = kmalloc(npages_to_free * sizeof(struct page *),
GFP_KERNEL);
if (!pages_to_free) {
- pr_err(TTM_PFX
- "%s: Failed to allocate memory for pool free operation.\n",
- pool->dev_name);
+ pr_err("%s: Failed to allocate memory for pool free operation\n",
+ pool->dev_name);
return 0;
}
INIT_LIST_HEAD(&d_pages);
@@ -723,23 +717,21 @@ static int ttm_dma_pool_alloc_new_pages(struct dma_pool *pool,
caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL);
if (!caching_array) {
- pr_err(TTM_PFX
- "%s: Unable to allocate table for new pages.",
- pool->dev_name);
+ pr_err("%s: Unable to allocate table for new pages\n",
+ pool->dev_name);
return -ENOMEM;
}
if (count > 1) {
pr_debug("%s: (%s:%d) Getting %d pages\n",
- pool->dev_name, pool->name, current->pid,
- count);
+ pool->dev_name, pool->name, current->pid, count);
}
for (i = 0, cpages = 0; i < count; ++i) {
dma_p = __ttm_dma_alloc_page(pool);
if (!dma_p) {
- pr_err(TTM_PFX "%s: Unable to get page %u.\n",
- pool->dev_name, i);
+ pr_err("%s: Unable to get page %u\n",
+ pool->dev_name, i);
/* store already allocated pages in the pool after
* setting the caching state */
@@ -821,8 +813,8 @@ static int ttm_dma_page_pool_fill_locked(struct dma_pool *pool,
struct dma_page *d_page;
unsigned cpages = 0;
- pr_err(TTM_PFX "%s: Failed to fill %s pool (r:%d)!\n",
- pool->dev_name, pool->name, r);
+ pr_err("%s: Failed to fill %s pool (r:%d)!\n",
+ pool->dev_name, pool->name, r);
list_for_each_entry(d_page, &d_pages, page_list) {
cpages++;
@@ -1038,8 +1030,8 @@ static int ttm_dma_pool_mm_shrink(struct shrinker *shrink,
nr_free = shrink_pages;
shrink_pages = ttm_dma_page_pool_free(p->pool, nr_free);
pr_debug("%s: (%s:%d) Asked to shrink %d, have %d more to go\n",
- p->pool->dev_name, p->pool->name, current->pid, nr_free,
- shrink_pages);
+ p->pool->dev_name, p->pool->name, current->pid,
+ nr_free, shrink_pages);
}
mutex_unlock(&_manager->lock);
/* return estimated number of unused pages in pool */
@@ -1064,7 +1056,7 @@ int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages)
WARN_ON(_manager);
- printk(KERN_INFO TTM_PFX "Initializing DMA pool allocator.\n");
+ pr_info("Initializing DMA pool allocator\n");
_manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
if (!_manager)
@@ -1097,7 +1089,7 @@ void ttm_dma_page_alloc_fini(void)
{
struct device_pools *p, *t;
- printk(KERN_INFO TTM_PFX "Finalizing DMA pool allocator.\n");
+ pr_info("Finalizing DMA pool allocator\n");
ttm_dma_pool_mm_shrink_fini(_manager);
list_for_each_entry_safe_reverse(p, t, &_manager->pools, pools) {
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index c10cf5e2443a..fa09daf9a50c 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -28,6 +28,8 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <linux/sched.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
@@ -196,7 +198,7 @@ int ttm_tt_init(struct ttm_tt *ttm, struct ttm_bo_device *bdev,
ttm_tt_alloc_page_directory(ttm);
if (!ttm->pages) {
ttm_tt_destroy(ttm);
- printk(KERN_ERR TTM_PFX "Failed allocating page table\n");
+ pr_err("Failed allocating page table\n");
return -ENOMEM;
}
return 0;
@@ -229,7 +231,7 @@ int ttm_dma_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_bo_device *bdev,
ttm_dma_tt_alloc_page_directory(ttm_dma);
if (!ttm->pages || !ttm_dma->dma_address) {
ttm_tt_destroy(ttm);
- printk(KERN_ERR TTM_PFX "Failed allocating page table\n");
+ pr_err("Failed allocating page table\n");
return -ENOMEM;
}
return 0;
@@ -347,7 +349,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
ttm->num_pages << PAGE_SHIFT,
0);
if (unlikely(IS_ERR(swap_storage))) {
- printk(KERN_ERR "Failed allocating swap storage.\n");
+ pr_err("Failed allocating swap storage\n");
return PTR_ERR(swap_storage);
}
} else
diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig
new file mode 100644
index 000000000000..0b5e096d39a6
--- /dev/null
+++ b/drivers/gpu/drm/udl/Kconfig
@@ -0,0 +1,12 @@
+config DRM_UDL
+ tristate "DisplayLink"
+ depends on DRM && EXPERIMENTAL
+ select DRM_USB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_DEFERRED_IO
+ select DRM_KMS_HELPER
+ help
+ This is a KMS driver for the USB displaylink video adapters.
+ Say M/Y to add support for these devices via drm/kms interfaces.
diff --git a/drivers/gpu/drm/udl/Makefile b/drivers/gpu/drm/udl/Makefile
new file mode 100644
index 000000000000..05c7481bfd40
--- /dev/null
+++ b/drivers/gpu/drm/udl/Makefile
@@ -0,0 +1,6 @@
+
+ccflags-y := -Iinclude/drm
+
+udl-y := udl_drv.o udl_modeset.o udl_connector.o udl_encoder.o udl_main.o udl_fb.o udl_transfer.o udl_gem.o
+
+obj-$(CONFIG_DRM_UDL) := udl.o
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
new file mode 100644
index 000000000000..ba055e9ca007
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+/* dummy connector to just get EDID,
+ all UDL appear to have a DVI-D */
+
+static u8 *udl_get_edid(struct udl_device *udl)
+{
+ u8 *block;
+ char rbuf[3];
+ int ret, i;
+
+ block = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (block == NULL)
+ return NULL;
+
+ for (i = 0; i < EDID_LENGTH; i++) {
+ ret = usb_control_msg(udl->ddev->usbdev,
+ usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02),
+ (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
+ HZ);
+ if (ret < 1) {
+ DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret);
+ i--;
+ goto error;
+ }
+ block[i] = rbuf[1];
+ }
+
+ return block;
+
+error:
+ kfree(block);
+ return NULL;
+}
+
+static int udl_get_modes(struct drm_connector *connector)
+{
+ struct udl_device *udl = connector->dev->dev_private;
+ struct edid *edid;
+ int ret;
+
+ edid = (struct edid *)udl_get_edid(udl);
+
+ connector->display_info.raw_edid = (char *)edid;
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+ return ret;
+}
+
+static int udl_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return 0;
+}
+
+static enum drm_connector_status
+udl_detect(struct drm_connector *connector, bool force)
+{
+ if (drm_device_is_unplugged(connector->dev))
+ return connector_status_disconnected;
+ return connector_status_connected;
+}
+
+struct drm_encoder *udl_best_single_encoder(struct drm_connector *connector)
+{
+ int enc_id = connector->encoder_ids[0];
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+
+ obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
+ if (!obj)
+ return NULL;
+ encoder = obj_to_encoder(obj);
+ return encoder;
+}
+
+int udl_connector_set_property(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val)
+{
+ return 0;
+}
+
+static void udl_connector_destroy(struct drm_connector *connector)
+{
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+struct drm_connector_helper_funcs udl_connector_helper_funcs = {
+ .get_modes = udl_get_modes,
+ .mode_valid = udl_mode_valid,
+ .best_encoder = udl_best_single_encoder,
+};
+
+struct drm_connector_funcs udl_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = udl_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = udl_connector_destroy,
+ .set_property = udl_connector_set_property,
+};
+
+int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder)
+{
+ struct drm_connector *connector;
+
+ connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL);
+ if (!connector)
+ return -ENOMEM;
+
+ drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII);
+ drm_connector_helper_add(connector, &udl_connector_helper_funcs);
+
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.dirty_info_property,
+ 1);
+ return 0;
+}
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
new file mode 100644
index 000000000000..5340c5f3987b
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include "drm_usb.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+static struct drm_driver driver;
+
+static struct usb_device_id id_table[] = {
+ {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
+ {},
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+MODULE_LICENSE("GPL");
+
+static int udl_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return drm_get_usb_dev(interface, id, &driver);
+}
+
+static void udl_usb_disconnect(struct usb_interface *interface)
+{
+ struct drm_device *dev = usb_get_intfdata(interface);
+
+ drm_kms_helper_poll_disable(dev);
+ drm_connector_unplug_all(dev);
+ udl_fbdev_unplug(dev);
+ udl_drop_usb(dev);
+ drm_unplug_dev(dev);
+}
+
+static struct vm_operations_struct udl_gem_vm_ops = {
+ .fault = udl_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static const struct file_operations udl_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ .fasync = drm_fasync,
+ .llseek = noop_llseek,
+};
+
+static struct drm_driver driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = udl_driver_load,
+ .unload = udl_driver_unload,
+
+ /* gem hooks */
+ .gem_init_object = udl_gem_init_object,
+ .gem_free_object = udl_gem_free_object,
+ .gem_vm_ops = &udl_gem_vm_ops,
+
+ .dumb_create = udl_dumb_create,
+ .dumb_map_offset = udl_gem_mmap,
+ .dumb_destroy = udl_dumb_destroy,
+ .fops = &udl_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static struct usb_driver udl_driver = {
+ .name = "udl",
+ .probe = udl_usb_probe,
+ .disconnect = udl_usb_disconnect,
+ .id_table = id_table,
+};
+
+static int __init udl_init(void)
+{
+ return drm_usb_init(&driver, &udl_driver);
+}
+
+static void __exit udl_exit(void)
+{
+ drm_usb_exit(&driver, &udl_driver);
+}
+
+module_init(udl_init);
+module_exit(udl_exit);
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
new file mode 100644
index 000000000000..1612954a5bc4
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef UDL_DRV_H
+#define UDL_DRV_H
+
+#include <linux/usb.h>
+
+#define DRIVER_NAME "udl"
+#define DRIVER_DESC "DisplayLink"
+#define DRIVER_DATE "20120220"
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 1
+
+struct udl_device;
+
+struct urb_node {
+ struct list_head entry;
+ struct udl_device *dev;
+ struct delayed_work release_urb_work;
+ struct urb *urb;
+};
+
+struct urb_list {
+ struct list_head list;
+ spinlock_t lock;
+ struct semaphore limit_sem;
+ int available;
+ int count;
+ size_t size;
+};
+
+struct udl_fbdev;
+
+struct udl_device {
+ struct device *dev;
+ struct drm_device *ddev;
+
+ int sku_pixel_limit;
+
+ struct urb_list urbs;
+ atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
+
+ struct udl_fbdev *fbdev;
+ char mode_buf[1024];
+ uint32_t mode_buf_len;
+ atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */
+ atomic_t bytes_identical; /* saved effort with backbuffer comparison */
+ atomic_t bytes_sent; /* to usb, after compression including overhead */
+ atomic_t cpu_kcycles_used; /* transpired during pixel processing */
+};
+
+struct udl_gem_object {
+ struct drm_gem_object base;
+ struct page **pages;
+ void *vmapping;
+};
+
+#define to_udl_bo(x) container_of(x, struct udl_gem_object, base)
+
+struct udl_framebuffer {
+ struct drm_framebuffer base;
+ struct udl_gem_object *obj;
+ bool active_16; /* active on the 16-bit channel */
+};
+
+#define to_udl_fb(x) container_of(x, struct udl_framebuffer, base)
+
+/* modeset */
+int udl_modeset_init(struct drm_device *dev);
+void udl_modeset_cleanup(struct drm_device *dev);
+int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder);
+
+struct drm_encoder *udl_encoder_init(struct drm_device *dev);
+
+struct urb *udl_get_urb(struct drm_device *dev);
+
+int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len);
+void udl_urb_completion(struct urb *urb);
+
+int udl_driver_load(struct drm_device *dev, unsigned long flags);
+int udl_driver_unload(struct drm_device *dev);
+
+int udl_fbdev_init(struct drm_device *dev);
+void udl_fbdev_cleanup(struct drm_device *dev);
+void udl_fbdev_unplug(struct drm_device *dev);
+struct drm_framebuffer *
+udl_fb_user_fb_create(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_mode_fb_cmd2 *mode_cmd);
+
+int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr,
+ const char *front, char **urb_buf_ptr,
+ u32 byte_offset, u32 byte_width,
+ int *ident_ptr, int *sent_ptr);
+
+int udl_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int udl_gem_mmap(struct drm_file *file_priv, struct drm_device *dev,
+ uint32_t handle, uint64_t *offset);
+int udl_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev,
+ uint32_t handle);
+
+int udl_gem_init_object(struct drm_gem_object *obj);
+void udl_gem_free_object(struct drm_gem_object *gem_obj);
+struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev,
+ size_t size);
+
+int udl_gem_vmap(struct udl_gem_object *obj);
+void udl_gem_vunmap(struct udl_gem_object *obj);
+int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+
+int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
+ int width, int height);
+
+int udl_drop_usb(struct drm_device *dev);
+
+#define CMD_WRITE_RAW8 "\xAF\x60" /**< 8 bit raw write command. */
+#define CMD_WRITE_RL8 "\xAF\x61" /**< 8 bit run length command. */
+#define CMD_WRITE_COPY8 "\xAF\x62" /**< 8 bit copy command. */
+#define CMD_WRITE_RLX8 "\xAF\x63" /**< 8 bit extended run length command. */
+
+#define CMD_WRITE_RAW16 "\xAF\x68" /**< 16 bit raw write command. */
+#define CMD_WRITE_RL16 "\xAF\x69" /**< 16 bit run length command. */
+#define CMD_WRITE_COPY16 "\xAF\x6A" /**< 16 bit copy command. */
+#define CMD_WRITE_RLX16 "\xAF\x6B" /**< 16 bit extended run length command. */
+
+#endif
diff --git a/drivers/gpu/drm/udl/udl_encoder.c b/drivers/gpu/drm/udl/udl_encoder.c
new file mode 100644
index 000000000000..56e75f0f1df5
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_encoder.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+/* dummy encoder */
+void udl_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+static void udl_encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static bool udl_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void udl_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void udl_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void udl_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void
+udl_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static const struct drm_encoder_helper_funcs udl_helper_funcs = {
+ .dpms = udl_encoder_dpms,
+ .mode_fixup = udl_mode_fixup,
+ .prepare = udl_encoder_prepare,
+ .mode_set = udl_encoder_mode_set,
+ .commit = udl_encoder_commit,
+ .disable = udl_encoder_disable,
+};
+
+static const struct drm_encoder_funcs udl_enc_funcs = {
+ .destroy = udl_enc_destroy,
+};
+
+struct drm_encoder *udl_encoder_init(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ encoder = kzalloc(sizeof(struct drm_encoder), GFP_KERNEL);
+ if (!encoder)
+ return NULL;
+
+ drm_encoder_init(dev, encoder, &udl_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &udl_helper_funcs);
+ encoder->possible_crtcs = 1;
+ return encoder;
+}
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
new file mode 100644
index 000000000000..4d9c3a5d8a45
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+#include "drm_fb_helper.h"
+
+#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */
+
+static int fb_defio = 1; /* Optionally enable experimental fb_defio mmap support */
+static int fb_bpp = 16;
+
+module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+module_param(fb_defio, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+
+struct udl_fbdev {
+ struct drm_fb_helper helper;
+ struct udl_framebuffer ufb;
+ struct list_head fbdev_list;
+ int fb_count;
+};
+
+#define DL_ALIGN_UP(x, a) ALIGN(x, a)
+#define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a)
+
+/** Read the red component (0..255) of a 32 bpp colour. */
+#define DLO_RGB_GETRED(col) (uint8_t)((col) & 0xFF)
+
+/** Read the green component (0..255) of a 32 bpp colour. */
+#define DLO_RGB_GETGRN(col) (uint8_t)(((col) >> 8) & 0xFF)
+
+/** Read the blue component (0..255) of a 32 bpp colour. */
+#define DLO_RGB_GETBLU(col) (uint8_t)(((col) >> 16) & 0xFF)
+
+/** Return red/green component of a 16 bpp colour number. */
+#define DLO_RG16(red, grn) (uint8_t)((((red) & 0xF8) | ((grn) >> 5)) & 0xFF)
+
+/** Return green/blue component of a 16 bpp colour number. */
+#define DLO_GB16(grn, blu) (uint8_t)(((((grn) & 0x1C) << 3) | ((blu) >> 3)) & 0xFF)
+
+/** Return 8 bpp colour number from red, green and blue components. */
+#define DLO_RGB8(red, grn, blu) ((((red) << 5) | (((grn) & 3) << 3) | ((blu) & 7)) & 0xFF)
+
+#if 0
+static uint8_t rgb8(uint32_t col)
+{
+ uint8_t red = DLO_RGB_GETRED(col);
+ uint8_t grn = DLO_RGB_GETGRN(col);
+ uint8_t blu = DLO_RGB_GETBLU(col);
+
+ return DLO_RGB8(red, grn, blu);
+}
+
+static uint16_t rgb16(uint32_t col)
+{
+ uint8_t red = DLO_RGB_GETRED(col);
+ uint8_t grn = DLO_RGB_GETGRN(col);
+ uint8_t blu = DLO_RGB_GETBLU(col);
+
+ return (DLO_RG16(red, grn) << 8) + DLO_GB16(grn, blu);
+}
+#endif
+
+/*
+ * NOTE: fb_defio.c is holding info->fbdefio.mutex
+ * Touching ANY framebuffer memory that triggers a page fault
+ * in fb_defio will cause a deadlock, when it also tries to
+ * grab the same mutex.
+ */
+static void udlfb_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct page *cur;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct udl_fbdev *ufbdev = info->par;
+ struct drm_device *dev = ufbdev->ufb.base.dev;
+ struct udl_device *udl = dev->dev_private;
+ struct urb *urb;
+ char *cmd;
+ cycles_t start_cycles, end_cycles;
+ int bytes_sent = 0;
+ int bytes_identical = 0;
+ int bytes_rendered = 0;
+
+ if (!fb_defio)
+ return;
+
+ start_cycles = get_cycles();
+
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return;
+
+ cmd = urb->transfer_buffer;
+
+ /* walk the written page list and render each to device */
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+
+ if (udl_render_hline(dev, (ufbdev->ufb.base.bits_per_pixel / 8),
+ &urb, (char *) info->fix.smem_start,
+ &cmd, cur->index << PAGE_SHIFT,
+ PAGE_SIZE, &bytes_identical, &bytes_sent))
+ goto error;
+ bytes_rendered += PAGE_SIZE;
+ }
+
+ if (cmd > (char *) urb->transfer_buffer) {
+ /* Send partial buffer remaining before exiting */
+ int len = cmd - (char *) urb->transfer_buffer;
+ udl_submit_urb(dev, urb, len);
+ bytes_sent += len;
+ } else
+ udl_urb_completion(urb);
+
+error:
+ atomic_add(bytes_sent, &udl->bytes_sent);
+ atomic_add(bytes_identical, &udl->bytes_identical);
+ atomic_add(bytes_rendered, &udl->bytes_rendered);
+ end_cycles = get_cycles();
+ atomic_add(((unsigned int) ((end_cycles - start_cycles)
+ >> 10)), /* Kcycles */
+ &udl->cpu_kcycles_used);
+}
+
+int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
+ int width, int height)
+{
+ struct drm_device *dev = fb->base.dev;
+ struct udl_device *udl = dev->dev_private;
+ int i, ret;
+ char *cmd;
+ cycles_t start_cycles, end_cycles;
+ int bytes_sent = 0;
+ int bytes_identical = 0;
+ struct urb *urb;
+ int aligned_x;
+ int bpp = (fb->base.bits_per_pixel / 8);
+
+ if (!fb->active_16)
+ return 0;
+
+ if (!fb->obj->vmapping)
+ udl_gem_vmap(fb->obj);
+
+ start_cycles = get_cycles();
+
+ aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
+ width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
+ x = aligned_x;
+
+ if ((width <= 0) ||
+ (x + width > fb->base.width) ||
+ (y + height > fb->base.height))
+ return -EINVAL;
+
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return 0;
+ cmd = urb->transfer_buffer;
+
+ for (i = y; i < y + height ; i++) {
+ const int line_offset = fb->base.pitches[0] * i;
+ const int byte_offset = line_offset + (x * bpp);
+
+ if (udl_render_hline(dev, bpp, &urb,
+ (char *) fb->obj->vmapping,
+ &cmd, byte_offset, width * bpp,
+ &bytes_identical, &bytes_sent))
+ goto error;
+ }
+
+ if (cmd > (char *) urb->transfer_buffer) {
+ /* Send partial buffer remaining before exiting */
+ int len = cmd - (char *) urb->transfer_buffer;
+ ret = udl_submit_urb(dev, urb, len);
+ bytes_sent += len;
+ } else
+ udl_urb_completion(urb);
+
+error:
+ atomic_add(bytes_sent, &udl->bytes_sent);
+ atomic_add(bytes_identical, &udl->bytes_identical);
+ atomic_add(width*height*bpp, &udl->bytes_rendered);
+ end_cycles = get_cycles();
+ atomic_add(((unsigned int) ((end_cycles - start_cycles)
+ >> 10)), /* Kcycles */
+ &udl->cpu_kcycles_used);
+
+ return 0;
+}
+
+static int udl_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ pos = (unsigned long)info->fix.smem_start + offset;
+
+ pr_notice("mmap() framebuffer addr:%lu size:%lu\n",
+ pos, size);
+
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ return 0;
+}
+
+static void udl_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ sys_fillrect(info, rect);
+
+ udl_handle_damage(&ufbdev->ufb, rect->dx, rect->dy, rect->width,
+ rect->height);
+}
+
+static void udl_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ sys_copyarea(info, region);
+
+ udl_handle_damage(&ufbdev->ufb, region->dx, region->dy, region->width,
+ region->height);
+}
+
+static void udl_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ sys_imageblit(info, image);
+
+ udl_handle_damage(&ufbdev->ufb, image->dx, image->dy, image->width,
+ image->height);
+}
+
+/*
+ * It's common for several clients to have framebuffer open simultaneously.
+ * e.g. both fbcon and X. Makes things interesting.
+ * Assumes caller is holding info->lock (for open and release at least)
+ */
+static int udl_fb_open(struct fb_info *info, int user)
+{
+ struct udl_fbdev *ufbdev = info->par;
+ struct drm_device *dev = ufbdev->ufb.base.dev;
+ struct udl_device *udl = dev->dev_private;
+
+ /* If the USB device is gone, we don't accept new opens */
+ if (drm_device_is_unplugged(udl->ddev))
+ return -ENODEV;
+
+ ufbdev->fb_count++;
+
+ if (fb_defio && (info->fbdefio == NULL)) {
+ /* enable defio at last moment if not disabled by client */
+
+ struct fb_deferred_io *fbdefio;
+
+ fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL);
+
+ if (fbdefio) {
+ fbdefio->delay = DL_DEFIO_WRITE_DELAY;
+ fbdefio->deferred_io = udlfb_dpy_deferred_io;
+ }
+
+ info->fbdefio = fbdefio;
+ fb_deferred_io_init(info);
+ }
+
+ pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n",
+ info->node, user, info, ufbdev->fb_count);
+
+ return 0;
+}
+
+
+/*
+ * Assumes caller is holding info->lock mutex (for open and release at least)
+ */
+static int udl_fb_release(struct fb_info *info, int user)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ ufbdev->fb_count--;
+
+ if ((ufbdev->fb_count == 0) && (info->fbdefio)) {
+ fb_deferred_io_cleanup(info);
+ kfree(info->fbdefio);
+ info->fbdefio = NULL;
+ info->fbops->fb_mmap = udl_fb_mmap;
+ }
+
+ pr_warn("released /dev/fb%d user=%d count=%d\n",
+ info->node, user, ufbdev->fb_count);
+
+ return 0;
+}
+
+static struct fb_ops udlfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_fillrect = udl_fb_fillrect,
+ .fb_copyarea = udl_fb_copyarea,
+ .fb_imageblit = udl_fb_imageblit,
+ .fb_pan_display = drm_fb_helper_pan_display,
+ .fb_blank = drm_fb_helper_blank,
+ .fb_setcmap = drm_fb_helper_setcmap,
+ .fb_debug_enter = drm_fb_helper_debug_enter,
+ .fb_debug_leave = drm_fb_helper_debug_leave,
+ .fb_mmap = udl_fb_mmap,
+ .fb_open = udl_fb_open,
+ .fb_release = udl_fb_release,
+};
+
+void udl_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno)
+{
+}
+
+void udl_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, int regno)
+{
+ *red = 0;
+ *green = 0;
+ *blue = 0;
+}
+
+static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct udl_framebuffer *ufb = to_udl_fb(fb);
+ int i;
+
+ if (!ufb->active_16)
+ return 0;
+
+ for (i = 0; i < num_clips; i++) {
+ udl_handle_damage(ufb, clips[i].x1, clips[i].y1,
+ clips[i].x2 - clips[i].x1,
+ clips[i].y2 - clips[i].y1);
+ }
+ return 0;
+}
+
+static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct udl_framebuffer *ufb = to_udl_fb(fb);
+
+ if (ufb->obj)
+ drm_gem_object_unreference_unlocked(&ufb->obj->base);
+
+ drm_framebuffer_cleanup(fb);
+ kfree(ufb);
+}
+
+static const struct drm_framebuffer_funcs udlfb_funcs = {
+ .destroy = udl_user_framebuffer_destroy,
+ .dirty = udl_user_framebuffer_dirty,
+ .create_handle = NULL,
+};
+
+
+static int
+udl_framebuffer_init(struct drm_device *dev,
+ struct udl_framebuffer *ufb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct udl_gem_object *obj)
+{
+ int ret;
+
+ ufb->obj = obj;
+ ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);
+ drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd);
+ return ret;
+}
+
+
+static int udlfb_create(struct udl_fbdev *ufbdev,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_device *dev = ufbdev->helper.dev;
+ struct fb_info *info;
+ struct device *device = &dev->usbdev->dev;
+ struct drm_framebuffer *fb;
+ struct drm_mode_fb_cmd2 mode_cmd;
+ struct udl_gem_object *obj;
+ uint32_t size;
+ int ret = 0;
+
+ if (sizes->surface_bpp == 24)
+ sizes->surface_bpp = 32;
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
+
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+ size = ALIGN(size, PAGE_SIZE);
+
+ obj = udl_gem_alloc_object(dev, size);
+ if (!obj)
+ goto out;
+
+ ret = udl_gem_vmap(obj);
+ if (ret) {
+ DRM_ERROR("failed to vmap fb\n");
+ goto out_gfree;
+ }
+
+ info = framebuffer_alloc(0, device);
+ if (!info) {
+ ret = -ENOMEM;
+ goto out_gfree;
+ }
+ info->par = ufbdev;
+
+ ret = udl_framebuffer_init(dev, &ufbdev->ufb, &mode_cmd, obj);
+ if (ret)
+ goto out_gfree;
+
+ fb = &ufbdev->ufb.base;
+
+ ufbdev->helper.fb = fb;
+ ufbdev->helper.fbdev = info;
+
+ strcpy(info->fix.id, "udldrmfb");
+
+ info->screen_base = ufbdev->ufb.obj->vmapping;
+ info->fix.smem_len = size;
+ info->fix.smem_start = (unsigned long)ufbdev->ufb.obj->vmapping;
+
+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+ info->fbops = &udlfb_ops;
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(info, &ufbdev->helper, sizes->fb_width, sizes->fb_height);
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out_gfree;
+ }
+
+
+ DRM_DEBUG_KMS("allocated %dx%d vmal %p\n",
+ fb->width, fb->height,
+ ufbdev->ufb.obj->vmapping);
+
+ return ret;
+out_gfree:
+ drm_gem_object_unreference(&ufbdev->ufb.obj->base);
+out:
+ return ret;
+}
+
+static int udl_fb_find_or_create_single(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper;
+ int new_fb = 0;
+ int ret;
+
+ if (!helper->fb) {
+ ret = udlfb_create(ufbdev, sizes);
+ if (ret)
+ return ret;
+
+ new_fb = 1;
+ }
+ return new_fb;
+}
+
+static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
+ .gamma_set = udl_crtc_fb_gamma_set,
+ .gamma_get = udl_crtc_fb_gamma_get,
+ .fb_probe = udl_fb_find_or_create_single,
+};
+
+static void udl_fbdev_destroy(struct drm_device *dev,
+ struct udl_fbdev *ufbdev)
+{
+ struct fb_info *info;
+ if (ufbdev->helper.fbdev) {
+ info = ufbdev->helper.fbdev;
+ unregister_framebuffer(info);
+ if (info->cmap.len)
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+ }
+ drm_fb_helper_fini(&ufbdev->helper);
+ drm_framebuffer_cleanup(&ufbdev->ufb.base);
+ drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base);
+}
+
+int udl_fbdev_init(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ int bpp_sel = fb_bpp;
+ struct udl_fbdev *ufbdev;
+ int ret;
+
+ ufbdev = kzalloc(sizeof(struct udl_fbdev), GFP_KERNEL);
+ if (!ufbdev)
+ return -ENOMEM;
+
+ udl->fbdev = ufbdev;
+ ufbdev->helper.funcs = &udl_fb_helper_funcs;
+
+ ret = drm_fb_helper_init(dev, &ufbdev->helper,
+ 1, 1);
+ if (ret) {
+ kfree(ufbdev);
+ return ret;
+
+ }
+
+ drm_fb_helper_single_add_all_connectors(&ufbdev->helper);
+ drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel);
+ return 0;
+}
+
+void udl_fbdev_cleanup(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ if (!udl->fbdev)
+ return;
+
+ udl_fbdev_destroy(dev, udl->fbdev);
+ kfree(udl->fbdev);
+ udl->fbdev = NULL;
+}
+
+void udl_fbdev_unplug(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ struct udl_fbdev *ufbdev;
+ if (!udl->fbdev)
+ return;
+
+ ufbdev = udl->fbdev;
+ if (ufbdev->helper.fbdev) {
+ struct fb_info *info;
+ info = ufbdev->helper.fbdev;
+ unlink_framebuffer(info);
+ }
+}
+
+struct drm_framebuffer *
+udl_fb_user_fb_create(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_gem_object *obj;
+ struct udl_framebuffer *ufb;
+ int ret;
+
+ obj = drm_gem_object_lookup(dev, file, mode_cmd->handles[0]);
+ if (obj == NULL)
+ return ERR_PTR(-ENOENT);
+
+ ufb = kzalloc(sizeof(*ufb), GFP_KERNEL);
+ if (ufb == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ret = udl_framebuffer_init(dev, ufb, mode_cmd, to_udl_bo(obj));
+ if (ret) {
+ kfree(ufb);
+ return ERR_PTR(-EINVAL);
+ }
+ return &ufb->base;
+}
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
new file mode 100644
index 000000000000..852642dc1187
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "udl_drv.h"
+#include <linux/shmem_fs.h>
+
+struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev,
+ size_t size)
+{
+ struct udl_gem_object *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (obj == NULL)
+ return NULL;
+
+ if (drm_gem_object_init(dev, &obj->base, size) != 0) {
+ kfree(obj);
+ return NULL;
+ }
+
+ return obj;
+}
+
+static int
+udl_gem_create(struct drm_file *file,
+ struct drm_device *dev,
+ uint64_t size,
+ uint32_t *handle_p)
+{
+ struct udl_gem_object *obj;
+ int ret;
+ u32 handle;
+
+ size = roundup(size, PAGE_SIZE);
+
+ obj = udl_gem_alloc_object(dev, size);
+ if (obj == NULL)
+ return -ENOMEM;
+
+ ret = drm_gem_handle_create(file, &obj->base, &handle);
+ if (ret) {
+ drm_gem_object_release(&obj->base);
+ kfree(obj);
+ return ret;
+ }
+
+ drm_gem_object_unreference(&obj->base);
+ *handle_p = handle;
+ return 0;
+}
+
+int udl_dumb_create(struct drm_file *file,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ args->pitch = args->width * ((args->bpp + 1) / 8);
+ args->size = args->pitch * args->height;
+ return udl_gem_create(file, dev,
+ args->size, &args->handle);
+}
+
+int udl_dumb_destroy(struct drm_file *file, struct drm_device *dev,
+ uint32_t handle)
+{
+ return drm_gem_handle_delete(file, handle);
+}
+
+int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct udl_gem_object *obj = to_udl_bo(vma->vm_private_data);
+ struct page *page;
+ unsigned int page_offset;
+ int ret = 0;
+
+ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
+ PAGE_SHIFT;
+
+ if (!obj->pages)
+ return VM_FAULT_SIGBUS;
+
+ page = obj->pages[page_offset];
+ ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
+ switch (ret) {
+ case -EAGAIN:
+ set_need_resched();
+ case 0:
+ case -ERESTARTSYS:
+ return VM_FAULT_NOPAGE;
+ case -ENOMEM:
+ return VM_FAULT_OOM;
+ default:
+ return VM_FAULT_SIGBUS;
+ }
+}
+
+int udl_gem_init_object(struct drm_gem_object *obj)
+{
+ BUG();
+
+ return 0;
+}
+
+static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask)
+{
+ int page_count, i;
+ struct page *page;
+ struct inode *inode;
+ struct address_space *mapping;
+
+ if (obj->pages)
+ return 0;
+
+ page_count = obj->base.size / PAGE_SIZE;
+ BUG_ON(obj->pages != NULL);
+ obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
+ if (obj->pages == NULL)
+ return -ENOMEM;
+
+ inode = obj->base.filp->f_path.dentry->d_inode;
+ mapping = inode->i_mapping;
+ gfpmask |= mapping_gfp_mask(mapping);
+
+ for (i = 0; i < page_count; i++) {
+ page = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+ if (IS_ERR(page))
+ goto err_pages;
+ obj->pages[i] = page;
+ }
+
+ return 0;
+err_pages:
+ while (i--)
+ page_cache_release(obj->pages[i]);
+ drm_free_large(obj->pages);
+ obj->pages = NULL;
+ return PTR_ERR(page);
+}
+
+static void udl_gem_put_pages(struct udl_gem_object *obj)
+{
+ int page_count = obj->base.size / PAGE_SIZE;
+ int i;
+
+ for (i = 0; i < page_count; i++)
+ page_cache_release(obj->pages[i]);
+
+ drm_free_large(obj->pages);
+ obj->pages = NULL;
+}
+
+int udl_gem_vmap(struct udl_gem_object *obj)
+{
+ int page_count = obj->base.size / PAGE_SIZE;
+ int ret;
+
+ ret = udl_gem_get_pages(obj, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ obj->vmapping = vmap(obj->pages, page_count, 0, PAGE_KERNEL);
+ if (!obj->vmapping)
+ return -ENOMEM;
+ return 0;
+}
+
+void udl_gem_vunmap(struct udl_gem_object *obj)
+{
+ if (obj->vmapping)
+ vunmap(obj->vmapping);
+
+ udl_gem_put_pages(obj);
+}
+
+void udl_gem_free_object(struct drm_gem_object *gem_obj)
+{
+ struct udl_gem_object *obj = to_udl_bo(gem_obj);
+
+ if (obj->vmapping)
+ udl_gem_vunmap(obj);
+
+ if (obj->pages)
+ udl_gem_put_pages(obj);
+
+ if (gem_obj->map_list.map)
+ drm_gem_free_mmap_offset(gem_obj);
+}
+
+/* the dumb interface doesn't work with the GEM straight MMAP
+ interface, it expects to do MMAP on the drm fd, like normal */
+int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
+ uint32_t handle, uint64_t *offset)
+{
+ struct udl_gem_object *gobj;
+ struct drm_gem_object *obj;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+ obj = drm_gem_object_lookup(dev, file, handle);
+ if (obj == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+ gobj = to_udl_bo(obj);
+
+ ret = udl_gem_get_pages(gobj, GFP_KERNEL);
+ if (ret)
+ return ret;
+ if (!gobj->base.map_list.map) {
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto out;
+ }
+
+ *offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
+
+out:
+ drm_gem_object_unreference(&gobj->base);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
new file mode 100644
index 000000000000..a8d5f09428c7
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+#include "drmP.h"
+#include "udl_drv.h"
+
+/* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */
+#define BULK_SIZE 512
+
+#define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE)
+#define WRITES_IN_FLIGHT (4)
+#define MAX_VENDOR_DESCRIPTOR_SIZE 256
+
+#define GET_URB_TIMEOUT HZ
+#define FREE_URB_TIMEOUT (HZ*2)
+
+static int udl_parse_vendor_descriptor(struct drm_device *dev,
+ struct usb_device *usbdev)
+{
+ struct udl_device *udl = dev->dev_private;
+ char *desc;
+ char *buf;
+ char *desc_end;
+
+ u8 total_len = 0;
+
+ buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL);
+ if (!buf)
+ return false;
+ desc = buf;
+
+ total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */
+ 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE);
+ if (total_len > 5) {
+ DRM_INFO("vendor descriptor length:%x data:%02x %02x %02x %02x" \
+ "%02x %02x %02x %02x %02x %02x %02x\n",
+ total_len, desc[0],
+ desc[1], desc[2], desc[3], desc[4], desc[5], desc[6],
+ desc[7], desc[8], desc[9], desc[10]);
+
+ if ((desc[0] != total_len) || /* descriptor length */
+ (desc[1] != 0x5f) || /* vendor descriptor type */
+ (desc[2] != 0x01) || /* version (2 bytes) */
+ (desc[3] != 0x00) ||
+ (desc[4] != total_len - 2)) /* length after type */
+ goto unrecognized;
+
+ desc_end = desc + total_len;
+ desc += 5; /* the fixed header we've already parsed */
+
+ while (desc < desc_end) {
+ u8 length;
+ u16 key;
+
+ key = *((u16 *) desc);
+ desc += sizeof(u16);
+ length = *desc;
+ desc++;
+
+ switch (key) {
+ case 0x0200: { /* max_area */
+ u32 max_area;
+ max_area = le32_to_cpu(*((u32 *)desc));
+ DRM_DEBUG("DL chip limited to %d pixel modes\n",
+ max_area);
+ udl->sku_pixel_limit = max_area;
+ break;
+ }
+ default:
+ break;
+ }
+ desc += length;
+ }
+ }
+
+ goto success;
+
+unrecognized:
+ /* allow udlfb to load for now even if firmware unrecognized */
+ DRM_ERROR("Unrecognized vendor firmware descriptor\n");
+
+success:
+ kfree(buf);
+ return true;
+}
+
+static void udl_release_urb_work(struct work_struct *work)
+{
+ struct urb_node *unode = container_of(work, struct urb_node,
+ release_urb_work.work);
+
+ up(&unode->dev->urbs.limit_sem);
+}
+
+void udl_urb_completion(struct urb *urb)
+{
+ struct urb_node *unode = urb->context;
+ struct udl_device *udl = unode->dev;
+ unsigned long flags;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN)) {
+ DRM_ERROR("%s - nonzero write bulk status received: %d\n",
+ __func__, urb->status);
+ atomic_set(&udl->lost_pixels, 1);
+ }
+ }
+
+ urb->transfer_buffer_length = udl->urbs.size; /* reset to actual */
+
+ spin_lock_irqsave(&udl->urbs.lock, flags);
+ list_add_tail(&unode->entry, &udl->urbs.list);
+ udl->urbs.available++;
+ spin_unlock_irqrestore(&udl->urbs.lock, flags);
+
+#if 0
+ /*
+ * When using fb_defio, we deadlock if up() is called
+ * while another is waiting. So queue to another process.
+ */
+ if (fb_defio)
+ schedule_delayed_work(&unode->release_urb_work, 0);
+ else
+#endif
+ up(&udl->urbs.limit_sem);
+}
+
+static void udl_free_urb_list(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ int count = udl->urbs.count;
+ struct list_head *node;
+ struct urb_node *unode;
+ struct urb *urb;
+ int ret;
+ unsigned long flags;
+
+ DRM_DEBUG("Waiting for completes and freeing all render urbs\n");
+
+ /* keep waiting and freeing, until we've got 'em all */
+ while (count--) {
+
+ /* Getting interrupted means a leak, but ok at shutdown*/
+ ret = down_interruptible(&udl->urbs.limit_sem);
+ if (ret)
+ break;
+
+ spin_lock_irqsave(&udl->urbs.lock, flags);
+
+ node = udl->urbs.list.next; /* have reserved one with sem */
+ list_del_init(node);
+
+ spin_unlock_irqrestore(&udl->urbs.lock, flags);
+
+ unode = list_entry(node, struct urb_node, entry);
+ urb = unode->urb;
+
+ /* Free each separately allocated piece */
+ usb_free_coherent(urb->dev, udl->urbs.size,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ kfree(node);
+ }
+ udl->urbs.count = 0;
+}
+
+static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size)
+{
+ struct udl_device *udl = dev->dev_private;
+ int i = 0;
+ struct urb *urb;
+ struct urb_node *unode;
+ char *buf;
+
+ spin_lock_init(&udl->urbs.lock);
+
+ udl->urbs.size = size;
+ INIT_LIST_HEAD(&udl->urbs.list);
+
+ while (i < count) {
+ unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL);
+ if (!unode)
+ break;
+ unode->dev = udl;
+
+ INIT_DELAYED_WORK(&unode->release_urb_work,
+ udl_release_urb_work);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ kfree(unode);
+ break;
+ }
+ unode->urb = urb;
+
+ buf = usb_alloc_coherent(udl->ddev->usbdev, MAX_TRANSFER, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ kfree(unode);
+ usb_free_urb(urb);
+ break;
+ }
+
+ /* urb->transfer_buffer_length set to actual before submit */
+ usb_fill_bulk_urb(urb, udl->ddev->usbdev, usb_sndbulkpipe(udl->ddev->usbdev, 1),
+ buf, size, udl_urb_completion, unode);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ list_add_tail(&unode->entry, &udl->urbs.list);
+
+ i++;
+ }
+
+ sema_init(&udl->urbs.limit_sem, i);
+ udl->urbs.count = i;
+ udl->urbs.available = i;
+
+ DRM_DEBUG("allocated %d %d byte urbs\n", i, (int) size);
+
+ return i;
+}
+
+struct urb *udl_get_urb(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ int ret = 0;
+ struct list_head *entry;
+ struct urb_node *unode;
+ struct urb *urb = NULL;
+ unsigned long flags;
+
+ /* Wait for an in-flight buffer to complete and get re-queued */
+ ret = down_timeout(&udl->urbs.limit_sem, GET_URB_TIMEOUT);
+ if (ret) {
+ atomic_set(&udl->lost_pixels, 1);
+ DRM_INFO("wait for urb interrupted: %x available: %d\n",
+ ret, udl->urbs.available);
+ goto error;
+ }
+
+ spin_lock_irqsave(&udl->urbs.lock, flags);
+
+ BUG_ON(list_empty(&udl->urbs.list)); /* reserved one with limit_sem */
+ entry = udl->urbs.list.next;
+ list_del_init(entry);
+ udl->urbs.available--;
+
+ spin_unlock_irqrestore(&udl->urbs.lock, flags);
+
+ unode = list_entry(entry, struct urb_node, entry);
+ urb = unode->urb;
+
+error:
+ return urb;
+}
+
+int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len)
+{
+ struct udl_device *udl = dev->dev_private;
+ int ret;
+
+ BUG_ON(len > udl->urbs.size);
+
+ urb->transfer_buffer_length = len; /* set to actual payload len */
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret) {
+ udl_urb_completion(urb); /* because no one else will */
+ atomic_set(&udl->lost_pixels, 1);
+ DRM_ERROR("usb_submit_urb error %x\n", ret);
+ }
+ return ret;
+}
+
+int udl_driver_load(struct drm_device *dev, unsigned long flags)
+{
+ struct udl_device *udl;
+ int ret;
+
+ DRM_DEBUG("\n");
+ udl = kzalloc(sizeof(struct udl_device), GFP_KERNEL);
+ if (!udl)
+ return -ENOMEM;
+
+ udl->ddev = dev;
+ dev->dev_private = udl;
+
+ if (!udl_parse_vendor_descriptor(dev, dev->usbdev)) {
+ DRM_ERROR("firmware not recognized. Assume incompatible device\n");
+ goto err;
+ }
+
+ if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
+ ret = -ENOMEM;
+ DRM_ERROR("udl_alloc_urb_list failed\n");
+ goto err;
+ }
+
+ DRM_DEBUG("\n");
+ ret = udl_modeset_init(dev);
+
+ ret = udl_fbdev_init(dev);
+ return 0;
+err:
+ kfree(udl);
+ DRM_ERROR("%d\n", ret);
+ return ret;
+}
+
+int udl_drop_usb(struct drm_device *dev)
+{
+ udl_free_urb_list(dev);
+ return 0;
+}
+
+int udl_driver_unload(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+
+ if (udl->urbs.count)
+ udl_free_urb_list(dev);
+
+ udl_fbdev_cleanup(dev);
+ udl_modeset_cleanup(dev);
+ kfree(udl);
+ return 0;
+}
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
new file mode 100644
index 000000000000..b3ecb3d12a1d
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+/*
+ * All DisplayLink bulk operations start with 0xAF, followed by specific code
+ * All operations are written to buffers which then later get sent to device
+ */
+static char *udl_set_register(char *buf, u8 reg, u8 val)
+{
+ *buf++ = 0xAF;
+ *buf++ = 0x20;
+ *buf++ = reg;
+ *buf++ = val;
+ return buf;
+}
+
+static char *udl_vidreg_lock(char *buf)
+{
+ return udl_set_register(buf, 0xFF, 0x00);
+}
+
+static char *udl_vidreg_unlock(char *buf)
+{
+ return udl_set_register(buf, 0xFF, 0xFF);
+}
+
+/*
+ * On/Off for driving the DisplayLink framebuffer to the display
+ * 0x00 H and V sync on
+ * 0x01 H and V sync off (screen blank but powered)
+ * 0x07 DPMS powerdown (requires modeset to come back)
+ */
+static char *udl_enable_hvsync(char *buf, bool enable)
+{
+ if (enable)
+ return udl_set_register(buf, 0x1F, 0x00);
+ else
+ return udl_set_register(buf, 0x1F, 0x07);
+}
+
+static char *udl_set_color_depth(char *buf, u8 selection)
+{
+ return udl_set_register(buf, 0x00, selection);
+}
+
+static char *udl_set_base16bpp(char *wrptr, u32 base)
+{
+ /* the base pointer is 16 bits wide, 0x20 is hi byte. */
+ wrptr = udl_set_register(wrptr, 0x20, base >> 16);
+ wrptr = udl_set_register(wrptr, 0x21, base >> 8);
+ return udl_set_register(wrptr, 0x22, base);
+}
+
+/*
+ * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
+ * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
+ */
+static char *udl_set_base8bpp(char *wrptr, u32 base)
+{
+ wrptr = udl_set_register(wrptr, 0x26, base >> 16);
+ wrptr = udl_set_register(wrptr, 0x27, base >> 8);
+ return udl_set_register(wrptr, 0x28, base);
+}
+
+static char *udl_set_register_16(char *wrptr, u8 reg, u16 value)
+{
+ wrptr = udl_set_register(wrptr, reg, value >> 8);
+ return udl_set_register(wrptr, reg+1, value);
+}
+
+/*
+ * This is kind of weird because the controller takes some
+ * register values in a different byte order than other registers.
+ */
+static char *udl_set_register_16be(char *wrptr, u8 reg, u16 value)
+{
+ wrptr = udl_set_register(wrptr, reg, value);
+ return udl_set_register(wrptr, reg+1, value >> 8);
+}
+
+/*
+ * LFSR is linear feedback shift register. The reason we have this is
+ * because the display controller needs to minimize the clock depth of
+ * various counters used in the display path. So this code reverses the
+ * provided value into the lfsr16 value by counting backwards to get
+ * the value that needs to be set in the hardware comparator to get the
+ * same actual count. This makes sense once you read above a couple of
+ * times and think about it from a hardware perspective.
+ */
+static u16 udl_lfsr16(u16 actual_count)
+{
+ u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */
+
+ while (actual_count--) {
+ lv = ((lv << 1) |
+ (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1))
+ & 0xFFFF;
+ }
+
+ return (u16) lv;
+}
+
+/*
+ * This does LFSR conversion on the value that is to be written.
+ * See LFSR explanation above for more detail.
+ */
+static char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
+{
+ return udl_set_register_16(wrptr, reg, udl_lfsr16(value));
+}
+
+/*
+ * This takes a standard fbdev screeninfo struct and all of its monitor mode
+ * details and converts them into the DisplayLink equivalent register commands.
+ ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
+ ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
+ ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
+ ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
+ ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
+ ERR(vreg_lfsr16(dev, 0x09, xEndCount));
+ ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
+ ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
+ ERR(vreg_big_endian(dev, 0x0F, hPixels));
+ ERR(vreg_lfsr16(dev, 0x11, yEndCount));
+ ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
+ ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
+ ERR(vreg_big_endian(dev, 0x17, vPixels));
+ ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
+
+ ERR(vreg(dev, 0x1F, 0));
+
+ ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
+ */
+static char *udl_set_vid_cmds(char *wrptr, struct drm_display_mode *mode)
+{
+ u16 xds, yds;
+ u16 xde, yde;
+ u16 yec;
+
+ /* x display start */
+ xds = mode->crtc_htotal - mode->crtc_hsync_start;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x01, xds);
+ /* x display end */
+ xde = xds + mode->crtc_hdisplay;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x03, xde);
+
+ /* y display start */
+ yds = mode->crtc_vtotal - mode->crtc_vsync_start;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x05, yds);
+ /* y display end */
+ yde = yds + mode->crtc_vdisplay;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x07, yde);
+
+ /* x end count is active + blanking - 1 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x09,
+ mode->crtc_htotal - 1);
+
+ /* libdlo hardcodes hsync start to 1 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x0B, 1);
+
+ /* hsync end is width of sync pulse + 1 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x0D,
+ mode->crtc_hsync_end - mode->crtc_hsync_start + 1);
+
+ /* hpixels is active pixels */
+ wrptr = udl_set_register_16(wrptr, 0x0F, mode->hdisplay);
+
+ /* yendcount is vertical active + vertical blanking */
+ yec = mode->crtc_vtotal;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x11, yec);
+
+ /* libdlo hardcodes vsync start to 0 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x13, 0);
+
+ /* vsync end is width of vsync pulse */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x15, mode->crtc_vsync_end - mode->crtc_vsync_start);
+
+ /* vpixels is active pixels */
+ wrptr = udl_set_register_16(wrptr, 0x17, mode->crtc_vdisplay);
+
+ wrptr = udl_set_register_16be(wrptr, 0x1B,
+ mode->clock / 5);
+
+ return wrptr;
+}
+
+static int udl_crtc_write_mode_to_hw(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct udl_device *udl = dev->dev_private;
+ struct urb *urb;
+ char *buf;
+ int retval;
+
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = (char *)urb->transfer_buffer;
+
+ memcpy(buf, udl->mode_buf, udl->mode_buf_len);
+ retval = udl_submit_urb(dev, urb, udl->mode_buf_len);
+ DRM_INFO("write mode info %d\n", udl->mode_buf_len);
+ return retval;
+}
+
+
+static void udl_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct udl_device *udl = dev->dev_private;
+ int retval;
+
+ if (mode == DRM_MODE_DPMS_OFF) {
+ char *buf;
+ struct urb *urb;
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return;
+
+ buf = (char *)urb->transfer_buffer;
+ buf = udl_vidreg_lock(buf);
+ buf = udl_enable_hvsync(buf, false);
+ buf = udl_vidreg_unlock(buf);
+
+ retval = udl_submit_urb(dev, urb, buf - (char *)
+ urb->transfer_buffer);
+ } else {
+ if (udl->mode_buf_len == 0) {
+ DRM_ERROR("Trying to enable DPMS with no mode\n");
+ return;
+ }
+ udl_crtc_write_mode_to_hw(crtc);
+ }
+
+}
+
+static bool udl_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+
+{
+ return true;
+}
+
+#if 0
+static int
+udl_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ return 0;
+}
+
+static int
+udl_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ return 0;
+}
+#endif
+
+static int udl_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+
+{
+ struct drm_device *dev = crtc->dev;
+ struct udl_framebuffer *ufb = to_udl_fb(crtc->fb);
+ struct udl_device *udl = dev->dev_private;
+ char *buf;
+ char *wrptr;
+ int color_depth = 0;
+
+ buf = (char *)udl->mode_buf;
+
+ /* for now we just clip 24 -> 16 - if we fix that fix this */
+ /*if (crtc->fb->bits_per_pixel != 16)
+ color_depth = 1; */
+
+ /* This first section has to do with setting the base address on the
+ * controller * associated with the display. There are 2 base
+ * pointers, currently, we only * use the 16 bpp segment.
+ */
+ wrptr = udl_vidreg_lock(buf);
+ wrptr = udl_set_color_depth(wrptr, color_depth);
+ /* set base for 16bpp segment to 0 */
+ wrptr = udl_set_base16bpp(wrptr, 0);
+ /* set base for 8bpp segment to end of fb */
+ wrptr = udl_set_base8bpp(wrptr, 2 * mode->vdisplay * mode->hdisplay);
+
+ wrptr = udl_set_vid_cmds(wrptr, adjusted_mode);
+ wrptr = udl_enable_hvsync(wrptr, true);
+ wrptr = udl_vidreg_unlock(wrptr);
+
+ ufb->active_16 = true;
+ if (old_fb) {
+ struct udl_framebuffer *uold_fb = to_udl_fb(old_fb);
+ uold_fb->active_16 = false;
+ }
+ udl->mode_buf_len = wrptr - buf;
+
+ /* damage all of it */
+ udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height);
+ return 0;
+}
+
+
+static void udl_crtc_disable(struct drm_crtc *crtc)
+{
+
+
+}
+
+static void udl_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_crtc_cleanup(crtc);
+ kfree(crtc);
+}
+
+static void udl_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static void udl_crtc_prepare(struct drm_crtc *crtc)
+{
+}
+
+static void udl_crtc_commit(struct drm_crtc *crtc)
+{
+ udl_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static struct drm_crtc_helper_funcs udl_helper_funcs = {
+ .dpms = udl_crtc_dpms,
+ .mode_fixup = udl_crtc_mode_fixup,
+ .mode_set = udl_crtc_mode_set,
+ .prepare = udl_crtc_prepare,
+ .commit = udl_crtc_commit,
+ .disable = udl_crtc_disable,
+ .load_lut = udl_load_lut,
+};
+
+static const struct drm_crtc_funcs udl_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = udl_crtc_destroy,
+};
+
+int udl_crtc_init(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+
+ crtc = kzalloc(sizeof(struct drm_crtc) + sizeof(struct drm_connector *), GFP_KERNEL);
+ if (crtc == NULL)
+ return -ENOMEM;
+
+ drm_crtc_init(dev, crtc, &udl_crtc_funcs);
+ drm_crtc_helper_add(crtc, &udl_helper_funcs);
+
+ return 0;
+}
+
+static const struct drm_mode_config_funcs udl_mode_funcs = {
+ .fb_create = udl_fb_user_fb_create,
+ .output_poll_changed = NULL,
+};
+
+int udl_modeset_init(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 640;
+ dev->mode_config.min_height = 480;
+
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+
+ dev->mode_config.prefer_shadow = 0;
+ dev->mode_config.preferred_depth = 24;
+
+ dev->mode_config.funcs = (void *)&udl_mode_funcs;
+
+ drm_mode_create_dirty_info_property(dev);
+
+ udl_crtc_init(dev);
+
+ encoder = udl_encoder_init(dev);
+
+ udl_connector_init(dev, encoder);
+
+ return 0;
+}
+
+void udl_modeset_cleanup(struct drm_device *dev)
+{
+ drm_mode_config_cleanup(dev);
+}
diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c
new file mode 100644
index 000000000000..b9320e2608dd
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_transfer.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/prefetch.h>
+
+#include "drmP.h"
+#include "udl_drv.h"
+
+#define MAX_CMD_PIXELS 255
+
+#define RLX_HEADER_BYTES 7
+#define MIN_RLX_PIX_BYTES 4
+#define MIN_RLX_CMD_BYTES (RLX_HEADER_BYTES + MIN_RLX_PIX_BYTES)
+
+#define RLE_HEADER_BYTES 6
+#define MIN_RLE_PIX_BYTES 3
+#define MIN_RLE_CMD_BYTES (RLE_HEADER_BYTES + MIN_RLE_PIX_BYTES)
+
+#define RAW_HEADER_BYTES 6
+#define MIN_RAW_PIX_BYTES 2
+#define MIN_RAW_CMD_BYTES (RAW_HEADER_BYTES + MIN_RAW_PIX_BYTES)
+
+/*
+ * Trims identical data from front and back of line
+ * Sets new front buffer address and width
+ * And returns byte count of identical pixels
+ * Assumes CPU natural alignment (unsigned long)
+ * for back and front buffer ptrs and width
+ */
+#if 0
+static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes)
+{
+ int j, k;
+ const unsigned long *back = (const unsigned long *) bback;
+ const unsigned long *front = (const unsigned long *) *bfront;
+ const int width = *width_bytes / sizeof(unsigned long);
+ int identical = width;
+ int start = width;
+ int end = width;
+
+ prefetch((void *) front);
+ prefetch((void *) back);
+
+ for (j = 0; j < width; j++) {
+ if (back[j] != front[j]) {
+ start = j;
+ break;
+ }
+ }
+
+ for (k = width - 1; k > j; k--) {
+ if (back[k] != front[k]) {
+ end = k+1;
+ break;
+ }
+ }
+
+ identical = start + (width - end);
+ *bfront = (u8 *) &front[start];
+ *width_bytes = (end - start) * sizeof(unsigned long);
+
+ return identical * sizeof(unsigned long);
+}
+#endif
+
+static inline u16 pixel32_to_be16p(const uint8_t *pixel)
+{
+ uint32_t pix = *(uint32_t *)pixel;
+ u16 retval;
+
+ retval = (((pix >> 3) & 0x001f) |
+ ((pix >> 5) & 0x07e0) |
+ ((pix >> 8) & 0xf800));
+ return retval;
+}
+
+/*
+ * Render a command stream for an encoded horizontal line segment of pixels.
+ *
+ * A command buffer holds several commands.
+ * It always begins with a fresh command header
+ * (the protocol doesn't require this, but we enforce it to allow
+ * multiple buffers to be potentially encoded and sent in parallel).
+ * A single command encodes one contiguous horizontal line of pixels
+ *
+ * The function relies on the client to do all allocation, so that
+ * rendering can be done directly to output buffers (e.g. USB URBs).
+ * The function fills the supplied command buffer, providing information
+ * on where it left off, so the client may call in again with additional
+ * buffers if the line will take several buffers to complete.
+ *
+ * A single command can transmit a maximum of 256 pixels,
+ * regardless of the compression ratio (protocol design limit).
+ * To the hardware, 0 for a size byte means 256
+ *
+ * Rather than 256 pixel commands which are either rl or raw encoded,
+ * the rlx command simply assumes alternating raw and rl spans within one cmd.
+ * This has a slightly larger header overhead, but produces more even results.
+ * It also processes all data (read and write) in a single pass.
+ * Performance benchmarks of common cases show it having just slightly better
+ * compression than 256 pixel raw or rle commands, with similar CPU consumpion.
+ * But for very rl friendly data, will compress not quite as well.
+ */
+static void udl_compress_hline16(
+ const u8 **pixel_start_ptr,
+ const u8 *const pixel_end,
+ uint32_t *device_address_ptr,
+ uint8_t **command_buffer_ptr,
+ const uint8_t *const cmd_buffer_end, int bpp)
+{
+ const u8 *pixel = *pixel_start_ptr;
+ uint32_t dev_addr = *device_address_ptr;
+ uint8_t *cmd = *command_buffer_ptr;
+
+ while ((pixel_end > pixel) &&
+ (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
+ uint8_t *raw_pixels_count_byte = 0;
+ uint8_t *cmd_pixels_count_byte = 0;
+ const u8 *raw_pixel_start = 0;
+ const u8 *cmd_pixel_start, *cmd_pixel_end = 0;
+
+ prefetchw((void *) cmd); /* pull in one cache line at least */
+
+ *cmd++ = 0xaf;
+ *cmd++ = 0x6b;
+ *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF);
+ *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF);
+ *cmd++ = (uint8_t) ((dev_addr) & 0xFF);
+
+ cmd_pixels_count_byte = cmd++; /* we'll know this later */
+ cmd_pixel_start = pixel;
+
+ raw_pixels_count_byte = cmd++; /* we'll know this later */
+ raw_pixel_start = pixel;
+
+ cmd_pixel_end = pixel + (min(MAX_CMD_PIXELS + 1,
+ min((int)(pixel_end - pixel) / bpp,
+ (int)(cmd_buffer_end - cmd) / 2))) * bpp;
+
+ prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp);
+
+ while (pixel < cmd_pixel_end) {
+ const u8 * const repeating_pixel = pixel;
+
+ if (bpp == 2)
+ *(uint16_t *)cmd = cpu_to_be16p((uint16_t *)pixel);
+ else if (bpp == 4)
+ *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16p(pixel));
+
+ cmd += 2;
+ pixel += bpp;
+
+ if (unlikely((pixel < cmd_pixel_end) &&
+ (!memcmp(pixel, repeating_pixel, bpp)))) {
+ /* go back and fill in raw pixel count */
+ *raw_pixels_count_byte = (((repeating_pixel -
+ raw_pixel_start) / bpp) + 1) & 0xFF;
+
+ while ((pixel < cmd_pixel_end)
+ && (!memcmp(pixel, repeating_pixel, bpp))) {
+ pixel += bpp;
+ }
+
+ /* immediately after raw data is repeat byte */
+ *cmd++ = (((pixel - repeating_pixel) / bpp) - 1) & 0xFF;
+
+ /* Then start another raw pixel span */
+ raw_pixel_start = pixel;
+ raw_pixels_count_byte = cmd++;
+ }
+ }
+
+ if (pixel > raw_pixel_start) {
+ /* finalize last RAW span */
+ *raw_pixels_count_byte = ((pixel-raw_pixel_start) / bpp) & 0xFF;
+ }
+
+ *cmd_pixels_count_byte = ((pixel - cmd_pixel_start) / bpp) & 0xFF;
+ dev_addr += ((pixel - cmd_pixel_start) / bpp) * 2;
+ }
+
+ if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) {
+ /* Fill leftover bytes with no-ops */
+ if (cmd_buffer_end > cmd)
+ memset(cmd, 0xAF, cmd_buffer_end - cmd);
+ cmd = (uint8_t *) cmd_buffer_end;
+ }
+
+ *command_buffer_ptr = cmd;
+ *pixel_start_ptr = pixel;
+ *device_address_ptr = dev_addr;
+
+ return;
+}
+
+/*
+ * There are 3 copies of every pixel: The front buffer that the fbdev
+ * client renders to, the actual framebuffer across the USB bus in hardware
+ * (that we can only write to, slowly, and can never read), and (optionally)
+ * our shadow copy that tracks what's been sent to that hardware buffer.
+ */
+int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr,
+ const char *front, char **urb_buf_ptr,
+ u32 byte_offset, u32 byte_width,
+ int *ident_ptr, int *sent_ptr)
+{
+ const u8 *line_start, *line_end, *next_pixel;
+ u32 base16 = 0 + (byte_offset / bpp) * 2;
+ struct urb *urb = *urb_ptr;
+ u8 *cmd = *urb_buf_ptr;
+ u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length;
+
+ line_start = (u8 *) (front + byte_offset);
+ next_pixel = line_start;
+ line_end = next_pixel + byte_width;
+
+ while (next_pixel < line_end) {
+
+ udl_compress_hline16(&next_pixel,
+ line_end, &base16,
+ (u8 **) &cmd, (u8 *) cmd_end, bpp);
+
+ if (cmd >= cmd_end) {
+ int len = cmd - (u8 *) urb->transfer_buffer;
+ if (udl_submit_urb(dev, urb, len))
+ return 1; /* lost pixels is set */
+ *sent_ptr += len;
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return 1; /* lost_pixels is set */
+ *urb_ptr = urb;
+ cmd = urb->transfer_buffer;
+ cmd_end = &cmd[urb->transfer_buffer_length];
+ }
+ }
+
+ *urb_buf_ptr = cmd;
+
+ return 0;
+}
+
diff --git a/drivers/gpu/drm/via/via_map.c b/drivers/gpu/drm/via/via_map.c
index a2ab34365151..1f182254e81e 100644
--- a/drivers/gpu/drm/via/via_map.c
+++ b/drivers/gpu/drm/via/via_map.c
@@ -106,6 +106,8 @@ int via_driver_load(struct drm_device *dev, unsigned long chipset)
idr_init(&dev->object_name_idr);
+ pci_set_master(dev->pdev);
+
ret = drm_vblank_init(dev, 1);
if (ret) {
kfree(dev_priv);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
index 2d6f573bfff2..ee24d216aa85 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
@@ -38,6 +38,10 @@
#define VMWGFX_CHIP_SVGAII 0
#define VMW_FB_RESERVATION 0
+#define VMW_MIN_INITIAL_WIDTH 800
+#define VMW_MIN_INITIAL_HEIGHT 600
+
+
/**
* Fully encoded drm commands. Might move to vmw_drm.h
*/
@@ -387,6 +391,41 @@ void vmw_3d_resource_dec(struct vmw_private *dev_priv,
BUG_ON(n3d < 0);
}
+/**
+ * Sets the initial_[width|height] fields on the given vmw_private.
+ *
+ * It does so by reading SVGA_REG_[WIDTH|HEIGHT] regs and then
+ * clamping the value to fb_max_[width|height] fields and the
+ * VMW_MIN_INITIAL_[WIDTH|HEIGHT].
+ * If the values appear to be invalid, set them to
+ * VMW_MIN_INITIAL_[WIDTH|HEIGHT].
+ */
+static void vmw_get_initial_size(struct vmw_private *dev_priv)
+{
+ uint32_t width;
+ uint32_t height;
+
+ width = vmw_read(dev_priv, SVGA_REG_WIDTH);
+ height = vmw_read(dev_priv, SVGA_REG_HEIGHT);
+
+ width = max_t(uint32_t, width, VMW_MIN_INITIAL_WIDTH);
+ height = max_t(uint32_t, height, VMW_MIN_INITIAL_HEIGHT);
+
+ if (width > dev_priv->fb_max_width ||
+ height > dev_priv->fb_max_height) {
+
+ /*
+ * This is a host error and shouldn't occur.
+ */
+
+ width = VMW_MIN_INITIAL_WIDTH;
+ height = VMW_MIN_INITIAL_HEIGHT;
+ }
+
+ dev_priv->initial_width = width;
+ dev_priv->initial_height = height;
+}
+
static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
{
struct vmw_private *dev_priv;
@@ -400,6 +439,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
}
memset(dev_priv, 0, sizeof(*dev_priv));
+ pci_set_master(dev->pdev);
+
dev_priv->dev = dev;
dev_priv->vmw_chipset = chipset;
dev_priv->last_read_seqno = (uint32_t) -100;
@@ -441,6 +482,9 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
+
+ vmw_get_initial_size(dev_priv);
+
if (dev_priv->capabilities & SVGA_CAP_GMR) {
dev_priv->max_gmr_descriptors =
vmw_read(dev_priv,
@@ -688,6 +732,15 @@ static int vmw_driver_unload(struct drm_device *dev)
return 0;
}
+static void vmw_preclose(struct drm_device *dev,
+ struct drm_file *file_priv)
+{
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ struct vmw_private *dev_priv = vmw_priv(dev);
+
+ vmw_event_fence_fpriv_gone(dev_priv->fman, &vmw_fp->fence_events);
+}
+
static void vmw_postclose(struct drm_device *dev,
struct drm_file *file_priv)
{
@@ -710,6 +763,7 @@ static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
if (unlikely(vmw_fp == NULL))
return ret;
+ INIT_LIST_HEAD(&vmw_fp->fence_events);
vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10);
if (unlikely(vmw_fp->tfile == NULL))
goto out_no_tfile;
@@ -1102,6 +1156,7 @@ static struct drm_driver driver = {
.master_set = vmw_master_set,
.master_drop = vmw_master_drop,
.open = vmw_driver_open,
+ .preclose = vmw_preclose,
.postclose = vmw_postclose,
.fops = &vmwgfx_driver_fops,
.name = VMWGFX_DRIVER_NAME,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
index dc279706ca70..d0f2c079ee27 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
@@ -40,9 +40,9 @@
#include "ttm/ttm_module.h"
#include "vmwgfx_fence.h"
-#define VMWGFX_DRIVER_DATE "20111025"
+#define VMWGFX_DRIVER_DATE "20120209"
#define VMWGFX_DRIVER_MAJOR 2
-#define VMWGFX_DRIVER_MINOR 3
+#define VMWGFX_DRIVER_MINOR 4
#define VMWGFX_DRIVER_PATCHLEVEL 0
#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
@@ -62,6 +62,7 @@
struct vmw_fpriv {
struct drm_master *locked_master;
struct ttm_object_file *tfile;
+ struct list_head fence_events;
};
struct vmw_dma_buffer {
@@ -202,6 +203,8 @@ struct vmw_private {
uint32_t mmio_size;
uint32_t fb_max_width;
uint32_t fb_max_height;
+ uint32_t initial_width;
+ uint32_t initial_height;
__le32 __iomem *mmio_virt;
int mmio_mtrr;
uint32_t capabilities;
@@ -533,7 +536,8 @@ extern int vmw_execbuf_process(struct drm_file *file_priv,
uint32_t command_size,
uint64_t throttle_us,
struct drm_vmw_fence_rep __user
- *user_fence_rep);
+ *user_fence_rep,
+ struct vmw_fence_obj **out_fence);
extern void
vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv,
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
index 40932fbdac0f..4acced44a623 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
@@ -1109,10 +1109,11 @@ int vmw_execbuf_process(struct drm_file *file_priv,
void *kernel_commands,
uint32_t command_size,
uint64_t throttle_us,
- struct drm_vmw_fence_rep __user *user_fence_rep)
+ struct drm_vmw_fence_rep __user *user_fence_rep,
+ struct vmw_fence_obj **out_fence)
{
struct vmw_sw_context *sw_context = &dev_priv->ctx;
- struct vmw_fence_obj *fence;
+ struct vmw_fence_obj *fence = NULL;
uint32_t handle;
void *cmd;
int ret;
@@ -1208,8 +1209,13 @@ int vmw_execbuf_process(struct drm_file *file_priv,
vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret,
user_fence_rep, fence, handle);
- if (likely(fence != NULL))
+ /* Don't unreference when handing fence out */
+ if (unlikely(out_fence != NULL)) {
+ *out_fence = fence;
+ fence = NULL;
+ } else if (likely(fence != NULL)) {
vmw_fence_obj_unreference(&fence);
+ }
mutex_unlock(&dev_priv->cmdbuf_mutex);
return 0;
@@ -1362,7 +1368,8 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
ret = vmw_execbuf_process(file_priv, dev_priv,
(void __user *)(unsigned long)arg->commands,
NULL, arg->command_size, arg->throttle_us,
- (void __user *)(unsigned long)arg->fence_rep);
+ (void __user *)(unsigned long)arg->fence_rep,
+ NULL);
if (unlikely(ret != 0))
goto out_unlock;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 34e51a1695b8..3c447bf317cb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -414,10 +414,6 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
unsigned fb_bpp, fb_depth, fb_offset, fb_pitch, fb_size;
int ret;
- /* XXX These shouldn't be hardcoded. */
- initial_width = 800;
- initial_height = 600;
-
fb_bpp = 32;
fb_depth = 24;
@@ -425,8 +421,8 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
fb_width = min(vmw_priv->fb_max_width, (unsigned)2048);
fb_height = min(vmw_priv->fb_max_height, (unsigned)2048);
- initial_width = min(fb_width, initial_width);
- initial_height = min(fb_height, initial_height);
+ initial_width = min(vmw_priv->initial_width, fb_width);
+ initial_height = min(vmw_priv->initial_height, fb_height);
fb_pitch = fb_width * fb_bpp / 8;
fb_size = fb_pitch * fb_height;
@@ -515,19 +511,7 @@ int vmw_fb_init(struct vmw_private *vmw_priv)
info->var.xres = initial_width;
info->var.yres = initial_height;
-#if 0
- info->pixmap.size = 64*1024;
- info->pixmap.buf_align = 8;
- info->pixmap.access_align = 32;
- info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->pixmap.scan_align = 1;
-#else
- info->pixmap.size = 0;
- info->pixmap.buf_align = 8;
- info->pixmap.access_align = 32;
- info->pixmap.flags = FB_PIXMAP_SYSTEM;
- info->pixmap.scan_align = 1;
-#endif
+ /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
info->apertures = alloc_apertures(1);
if (!info->apertures) {
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index 15fb26088d68..f2fb8f15e2f1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -69,12 +69,13 @@ struct vmw_user_fence {
* be assigned the current time tv_usec val when the fence signals.
*/
struct vmw_event_fence_action {
- struct drm_pending_event e;
struct vmw_fence_action action;
+ struct list_head fpriv_head;
+
+ struct drm_pending_event *event;
struct vmw_fence_obj *fence;
struct drm_device *dev;
- struct kref kref;
- uint32_t size;
+
uint32_t *tv_sec;
uint32_t *tv_usec;
};
@@ -784,46 +785,40 @@ int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data,
}
/**
- * vmw_event_fence_action_destroy
- *
- * @kref: The struct kref embedded in a struct vmw_event_fence_action.
- *
- * The vmw_event_fence_action destructor that may be called either after
- * the fence action cleanup, or when the event is delivered.
- * It frees both the vmw_event_fence_action struct and the actual
- * event structure copied to user-space.
- */
-static void vmw_event_fence_action_destroy(struct kref *kref)
-{
- struct vmw_event_fence_action *eaction =
- container_of(kref, struct vmw_event_fence_action, kref);
- struct ttm_mem_global *mem_glob =
- vmw_mem_glob(vmw_priv(eaction->dev));
- uint32_t size = eaction->size;
-
- kfree(eaction->e.event);
- kfree(eaction);
- ttm_mem_global_free(mem_glob, size);
-}
-
-
-/**
- * vmw_event_fence_action_delivered
+ * vmw_event_fence_fpriv_gone - Remove references to struct drm_file objects
*
- * @e: The struct drm_pending_event embedded in a struct
- * vmw_event_fence_action.
+ * @fman: Pointer to a struct vmw_fence_manager
+ * @event_list: Pointer to linked list of struct vmw_event_fence_action objects
+ * with pointers to a struct drm_file object about to be closed.
*
- * The struct drm_pending_event destructor that is called by drm
- * once the event is delivered. Since we don't know whether this function
- * will be called before or after the fence action destructor, we
- * free a refcount and destroy if it becomes zero.
+ * This function removes all pending fence events with references to a
+ * specific struct drm_file object about to be closed. The caller is required
+ * to pass a list of all struct vmw_event_fence_action objects with such
+ * events attached. This function is typically called before the
+ * struct drm_file object's event management is taken down.
*/
-static void vmw_event_fence_action_delivered(struct drm_pending_event *e)
+void vmw_event_fence_fpriv_gone(struct vmw_fence_manager *fman,
+ struct list_head *event_list)
{
- struct vmw_event_fence_action *eaction =
- container_of(e, struct vmw_event_fence_action, e);
+ struct vmw_event_fence_action *eaction;
+ struct drm_pending_event *event;
+ unsigned long irq_flags;
- kref_put(&eaction->kref, vmw_event_fence_action_destroy);
+ while (1) {
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ if (list_empty(event_list))
+ goto out_unlock;
+ eaction = list_first_entry(event_list,
+ struct vmw_event_fence_action,
+ fpriv_head);
+ list_del_init(&eaction->fpriv_head);
+ event = eaction->event;
+ eaction->event = NULL;
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+ event->destroy(event);
+ }
+out_unlock:
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
}
@@ -836,18 +831,21 @@ static void vmw_event_fence_action_delivered(struct drm_pending_event *e)
* This function is called when the seqno of the fence where @action is
* attached has passed. It queues the event on the submitter's event list.
* This function is always called from atomic context, and may be called
- * from irq context. It ups a refcount reflecting that we now have two
- * destructors.
+ * from irq context.
*/
static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
{
struct vmw_event_fence_action *eaction =
container_of(action, struct vmw_event_fence_action, action);
struct drm_device *dev = eaction->dev;
- struct drm_file *file_priv = eaction->e.file_priv;
+ struct drm_pending_event *event = eaction->event;
+ struct drm_file *file_priv;
unsigned long irq_flags;
- kref_get(&eaction->kref);
+ if (unlikely(event == NULL))
+ return;
+
+ file_priv = event->file_priv;
spin_lock_irqsave(&dev->event_lock, irq_flags);
if (likely(eaction->tv_sec != NULL)) {
@@ -858,7 +856,9 @@ static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action)
*eaction->tv_usec = tv.tv_usec;
}
- list_add_tail(&eaction->e.link, &file_priv->event_list);
+ list_del_init(&eaction->fpriv_head);
+ list_add_tail(&eaction->event->link, &file_priv->event_list);
+ eaction->event = NULL;
wake_up_all(&file_priv->event_wait);
spin_unlock_irqrestore(&dev->event_lock, irq_flags);
}
@@ -876,9 +876,15 @@ static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action)
{
struct vmw_event_fence_action *eaction =
container_of(action, struct vmw_event_fence_action, action);
+ struct vmw_fence_manager *fman = eaction->fence->fman;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ list_del(&eaction->fpriv_head);
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
vmw_fence_obj_unreference(&eaction->fence);
- kref_put(&eaction->kref, vmw_event_fence_action_destroy);
+ kfree(eaction);
}
@@ -946,39 +952,23 @@ void vmw_fence_obj_add_action(struct vmw_fence_obj *fence,
* an error code, the caller needs to free that object.
*/
-int vmw_event_fence_action_create(struct drm_file *file_priv,
- struct vmw_fence_obj *fence,
- struct drm_event *event,
- uint32_t *tv_sec,
- uint32_t *tv_usec,
- bool interruptible)
+int vmw_event_fence_action_queue(struct drm_file *file_priv,
+ struct vmw_fence_obj *fence,
+ struct drm_pending_event *event,
+ uint32_t *tv_sec,
+ uint32_t *tv_usec,
+ bool interruptible)
{
struct vmw_event_fence_action *eaction;
- struct ttm_mem_global *mem_glob =
- vmw_mem_glob(fence->fman->dev_priv);
struct vmw_fence_manager *fman = fence->fman;
- uint32_t size = fman->event_fence_action_size +
- ttm_round_pot(event->length);
- int ret;
-
- /*
- * Account for internal structure size as well as the
- * event size itself.
- */
-
- ret = ttm_mem_global_alloc(mem_glob, size, false, interruptible);
- if (unlikely(ret != 0))
- return ret;
+ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
+ unsigned long irq_flags;
eaction = kzalloc(sizeof(*eaction), GFP_KERNEL);
- if (unlikely(eaction == NULL)) {
- ttm_mem_global_free(mem_glob, size);
+ if (unlikely(eaction == NULL))
return -ENOMEM;
- }
- eaction->e.event = event;
- eaction->e.file_priv = file_priv;
- eaction->e.destroy = vmw_event_fence_action_delivered;
+ eaction->event = event;
eaction->action.seq_passed = vmw_event_fence_action_seq_passed;
eaction->action.cleanup = vmw_event_fence_action_cleanup;
@@ -986,16 +976,89 @@ int vmw_event_fence_action_create(struct drm_file *file_priv,
eaction->fence = vmw_fence_obj_reference(fence);
eaction->dev = fman->dev_priv->dev;
- eaction->size = size;
eaction->tv_sec = tv_sec;
eaction->tv_usec = tv_usec;
- kref_init(&eaction->kref);
+ spin_lock_irqsave(&fman->lock, irq_flags);
+ list_add_tail(&eaction->fpriv_head, &vmw_fp->fence_events);
+ spin_unlock_irqrestore(&fman->lock, irq_flags);
+
vmw_fence_obj_add_action(fence, &eaction->action);
return 0;
}
+struct vmw_event_fence_pending {
+ struct drm_pending_event base;
+ struct drm_vmw_event_fence event;
+};
+
+int vmw_event_fence_action_create(struct drm_file *file_priv,
+ struct vmw_fence_obj *fence,
+ uint32_t flags,
+ uint64_t user_data,
+ bool interruptible)
+{
+ struct vmw_event_fence_pending *event;
+ struct drm_device *dev = fence->fman->dev_priv->dev;
+ unsigned long irq_flags;
+ int ret;
+
+ spin_lock_irqsave(&dev->event_lock, irq_flags);
+
+ ret = (file_priv->event_space < sizeof(event->event)) ? -EBUSY : 0;
+ if (likely(ret == 0))
+ file_priv->event_space -= sizeof(event->event);
+
+ spin_unlock_irqrestore(&dev->event_lock, irq_flags);
+
+ if (unlikely(ret != 0)) {
+ DRM_ERROR("Failed to allocate event space for this file.\n");
+ goto out_no_space;
+ }
+
+
+ event = kzalloc(sizeof(event->event), GFP_KERNEL);
+ if (unlikely(event == NULL)) {
+ DRM_ERROR("Failed to allocate an event.\n");
+ ret = -ENOMEM;
+ goto out_no_event;
+ }
+
+ event->event.base.type = DRM_VMW_EVENT_FENCE_SIGNALED;
+ event->event.base.length = sizeof(*event);
+ event->event.user_data = user_data;
+
+ event->base.event = &event->event.base;
+ event->base.file_priv = file_priv;
+ event->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
+
+
+ if (flags & DRM_VMW_FE_FLAG_REQ_TIME)
+ ret = vmw_event_fence_action_queue(file_priv, fence,
+ &event->base,
+ &event->event.tv_sec,
+ &event->event.tv_usec,
+ interruptible);
+ else
+ ret = vmw_event_fence_action_queue(file_priv, fence,
+ &event->base,
+ NULL,
+ NULL,
+ interruptible);
+ if (ret != 0)
+ goto out_no_queue;
+
+out_no_queue:
+ event->base.destroy(&event->base);
+out_no_event:
+ spin_lock_irqsave(&dev->event_lock, irq_flags);
+ file_priv->event_space += sizeof(*event);
+ spin_unlock_irqrestore(&dev->event_lock, irq_flags);
+out_no_space:
+ return ret;
+}
+
int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -1008,8 +1071,6 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
(struct drm_vmw_fence_rep __user *)(unsigned long)
arg->fence_rep;
uint32_t handle;
- unsigned long irq_flags;
- struct drm_vmw_event_fence *event;
int ret;
/*
@@ -1062,59 +1123,28 @@ int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
BUG_ON(fence == NULL);
- spin_lock_irqsave(&dev->event_lock, irq_flags);
-
- ret = (file_priv->event_space < sizeof(*event)) ? -EBUSY : 0;
- if (likely(ret == 0))
- file_priv->event_space -= sizeof(*event);
-
- spin_unlock_irqrestore(&dev->event_lock, irq_flags);
-
- if (unlikely(ret != 0)) {
- DRM_ERROR("Failed to allocate event space for this file.\n");
- goto out_no_event_space;
- }
-
- event = kzalloc(sizeof(*event), GFP_KERNEL);
- if (unlikely(event == NULL)) {
- DRM_ERROR("Failed to allocate an event.\n");
- goto out_no_event;
- }
-
- event->base.type = DRM_VMW_EVENT_FENCE_SIGNALED;
- event->base.length = sizeof(*event);
- event->user_data = arg->user_data;
-
if (arg->flags & DRM_VMW_FE_FLAG_REQ_TIME)
ret = vmw_event_fence_action_create(file_priv, fence,
- &event->base,
- &event->tv_sec,
- &event->tv_usec,
+ arg->flags,
+ arg->user_data,
true);
else
ret = vmw_event_fence_action_create(file_priv, fence,
- &event->base,
- NULL,
- NULL,
+ arg->flags,
+ arg->user_data,
true);
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS)
DRM_ERROR("Failed to attach event to fence.\n");
- goto out_no_attach;
+ goto out_no_create;
}
vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence,
handle);
vmw_fence_obj_unreference(&fence);
return 0;
-out_no_attach:
- kfree(event);
-out_no_event:
- spin_lock_irqsave(&dev->event_lock, irq_flags);
- file_priv->event_space += sizeof(*event);
- spin_unlock_irqrestore(&dev->event_lock, irq_flags);
-out_no_event_space:
+out_no_create:
if (user_fence_rep != NULL)
ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
handle, TTM_REF_USAGE);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
index 0854a2096b55..faf2e7873860 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h
@@ -109,5 +109,12 @@ extern int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int vmw_fence_event_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
-
+extern void vmw_event_fence_fpriv_gone(struct vmw_fence_manager *fman,
+ struct list_head *event_list);
+extern int vmw_event_fence_action_queue(struct drm_file *filee_priv,
+ struct vmw_fence_obj *fence,
+ struct drm_pending_event *event,
+ uint32_t *tv_sec,
+ uint32_t *tv_usec,
+ bool interruptible);
#endif /* _VMWGFX_FENCE_H_ */
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index b66ef0e3cde1..2286d47e5022 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -422,7 +422,8 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
struct vmw_framebuffer *framebuffer,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
- unsigned num_clips, int inc)
+ unsigned num_clips, int inc,
+ struct vmw_fence_obj **out_fence)
{
struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
struct drm_clip_rect *clips_ptr;
@@ -542,12 +543,15 @@ static int do_surface_dirty_sou(struct vmw_private *dev_priv,
if (num == 0)
continue;
+ /* only return the last fence */
+ if (out_fence && *out_fence)
+ vmw_fence_obj_unreference(out_fence);
/* recalculate package length */
fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num;
cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
- fifo_size, 0, NULL);
+ fifo_size, 0, NULL, out_fence);
if (unlikely(ret != 0))
break;
@@ -598,7 +602,7 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
ret = do_surface_dirty_sou(dev_priv, file_priv, &vfbs->base,
flags, color,
- clips, num_clips, inc);
+ clips, num_clips, inc, NULL);
ttm_read_unlock(&vmaster->lock);
return 0;
@@ -809,7 +813,7 @@ static int do_dmabuf_define_gmrfb(struct drm_file *file_priv,
cmd->body.ptr.offset = 0;
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
- fifo_size, 0, NULL);
+ fifo_size, 0, NULL, NULL);
kfree(cmd);
@@ -821,7 +825,8 @@ static int do_dmabuf_dirty_sou(struct drm_file *file_priv,
struct vmw_framebuffer *framebuffer,
unsigned flags, unsigned color,
struct drm_clip_rect *clips,
- unsigned num_clips, int increment)
+ unsigned num_clips, int increment,
+ struct vmw_fence_obj **out_fence)
{
struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS];
struct drm_clip_rect *clips_ptr;
@@ -894,9 +899,13 @@ static int do_dmabuf_dirty_sou(struct drm_file *file_priv,
if (hit_num == 0)
continue;
+ /* only return the last fence */
+ if (out_fence && *out_fence)
+ vmw_fence_obj_unreference(out_fence);
+
fifo_size = sizeof(*blits) * hit_num;
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits,
- fifo_size, 0, NULL);
+ fifo_size, 0, NULL, out_fence);
if (unlikely(ret != 0))
break;
@@ -942,7 +951,7 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
} else {
ret = do_dmabuf_dirty_sou(file_priv, dev_priv, &vfbd->base,
flags, color,
- clips, num_clips, increment);
+ clips, num_clips, increment, NULL);
}
ttm_read_unlock(&vmaster->lock);
@@ -1296,7 +1305,7 @@ int vmw_kms_present(struct vmw_private *dev_priv,
fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num;
cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header));
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd,
- fifo_size, 0, NULL);
+ fifo_size, 0, NULL, NULL);
if (unlikely(ret != 0))
break;
@@ -1409,7 +1418,7 @@ int vmw_kms_readback(struct vmw_private *dev_priv,
fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos;
ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size,
- 0, user_fence_rep);
+ 0, user_fence_rep, NULL);
kfree(cmd);
@@ -1672,6 +1681,70 @@ int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
return 0;
}
+int vmw_du_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct vmw_private *dev_priv = vmw_priv(crtc->dev);
+ struct drm_framebuffer *old_fb = crtc->fb;
+ struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb);
+ struct drm_file *file_priv = event->base.file_priv;
+ struct vmw_fence_obj *fence = NULL;
+ struct drm_clip_rect clips;
+ int ret;
+
+ /* require ScreenObject support for page flipping */
+ if (!dev_priv->sou_priv)
+ return -ENOSYS;
+
+ if (!vmw_kms_screen_object_flippable(dev_priv, crtc))
+ return -EINVAL;
+
+ crtc->fb = fb;
+
+ /* do a full screen dirty update */
+ clips.x1 = clips.y1 = 0;
+ clips.x2 = fb->width;
+ clips.y2 = fb->height;
+
+ if (vfb->dmabuf)
+ ret = do_dmabuf_dirty_sou(file_priv, dev_priv, vfb,
+ 0, 0, &clips, 1, 1, &fence);
+ else
+ ret = do_surface_dirty_sou(dev_priv, file_priv, vfb,
+ 0, 0, &clips, 1, 1, &fence);
+
+
+ if (ret != 0)
+ goto out_no_fence;
+ if (!fence) {
+ ret = -EINVAL;
+ goto out_no_fence;
+ }
+
+ ret = vmw_event_fence_action_queue(file_priv, fence,
+ &event->base,
+ &event->event.tv_sec,
+ &event->event.tv_usec,
+ true);
+
+ /*
+ * No need to hold on to this now. The only cleanup
+ * we need to do if we fail is unref the fence.
+ */
+ vmw_fence_obj_unreference(&fence);
+
+ if (vmw_crtc_to_du(crtc)->is_implicit)
+ vmw_kms_screen_object_update_implicit_fb(dev_priv, crtc);
+
+ return ret;
+
+out_no_fence:
+ crtc->fb = old_fb;
+ return ret;
+}
+
+
void vmw_du_crtc_save(struct drm_crtc *crtc)
{
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
index a4f7f034996a..8184bc5b1730 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
@@ -121,6 +121,9 @@ struct vmw_display_unit {
* Shared display unit functions - vmwgfx_kms.c
*/
void vmw_display_unit_cleanup(struct vmw_display_unit *du);
+int vmw_du_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event);
void vmw_du_crtc_save(struct drm_crtc *crtc);
void vmw_du_crtc_restore(struct drm_crtc *crtc);
void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
@@ -154,5 +157,10 @@ int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv);
int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv);
int vmw_kms_sou_update_layout(struct vmw_private *dev_priv, unsigned num,
struct drm_vmw_rect *rects);
+bool vmw_kms_screen_object_flippable(struct vmw_private *dev_priv,
+ struct drm_crtc *crtc);
+void vmw_kms_screen_object_update_implicit_fb(struct vmw_private *dev_priv,
+ struct drm_crtc *crtc);
+
#endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index f77b184be807..070fb239c5af 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -354,8 +354,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
INIT_LIST_HEAD(&ldu->active);
ldu->base.pref_active = (unit == 0);
- ldu->base.pref_width = 800;
- ldu->base.pref_height = 600;
+ ldu->base.pref_width = dev_priv->initial_width;
+ ldu->base.pref_height = dev_priv->initial_height;
ldu->base.pref_mode = NULL;
ldu->base.is_implicit = true;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index 4defdcf1c72e..6deaf2f8bab1 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -394,6 +394,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_sou_crtc_destroy,
.set_config = vmw_sou_crtc_set_config,
+ .page_flip = vmw_du_page_flip,
};
/*
@@ -448,8 +449,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit)
sou->active_implicit = false;
sou->base.pref_active = (unit == 0);
- sou->base.pref_width = 800;
- sou->base.pref_height = 600;
+ sou->base.pref_width = dev_priv->initial_width;
+ sou->base.pref_height = dev_priv->initial_height;
sou->base.pref_mode = NULL;
sou->base.is_implicit = true;
@@ -535,3 +536,36 @@ int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv)
return 0;
}
+
+/**
+ * Returns if this unit can be page flipped.
+ * Must be called with the mode_config mutex held.
+ */
+bool vmw_kms_screen_object_flippable(struct vmw_private *dev_priv,
+ struct drm_crtc *crtc)
+{
+ struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc);
+
+ if (!sou->base.is_implicit)
+ return true;
+
+ if (dev_priv->sou_priv->num_implicit != 1)
+ return false;
+
+ return true;
+}
+
+/**
+ * Update the implicit fb to the current fb of this crtc.
+ * Must be called with the mode_config mutex held.
+ */
+void vmw_kms_screen_object_update_implicit_fb(struct vmw_private *dev_priv,
+ struct drm_crtc *crtc)
+{
+ struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc);
+
+ BUG_ON(!sou->base.is_implicit);
+
+ dev_priv->sou_priv->implicit_fb =
+ vmw_framebuffer_to_vfb(sou->base.crtc.fb);
+}
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 990fe19330e6..4da66b4b977c 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1935,6 +1935,16 @@ static const struct hid_device_id hid_ignore_list[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PANJIT, 0x0004) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PHILIPS, USB_DEVICE_ID_PHILIPS_IEEE802154_DONGLE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_POWERCOM, USB_DEVICE_ID_POWERCOM_UPS) },
+#if defined(CONFIG_MOUSE_SYNAPTICS_USB) || defined(CONFIG_MOUSE_SYNAPTICS_USB_MODULE)
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_TP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_INT_TP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_CPAD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_STICK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_COMP_TP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_WTP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, USB_DEVICE_ID_SYNAPTICS_DPAD) },
+#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
@@ -2016,6 +2026,16 @@ static bool hid_ignore(struct hid_device *hdev)
if (hdev->product >= USB_DEVICE_ID_LOGITECH_HARMONY_FIRST &&
hdev->product <= USB_DEVICE_ID_LOGITECH_HARMONY_LAST)
return true;
+ /*
+ * The Keene FM transmitter USB device has the same USB ID as
+ * the Logitech AudioHub Speaker, but it should ignore the hid.
+ * Check if the name is that of the Keene device.
+ * For reference: the name of the AudioHub is
+ * "HOLTEK AudioHub Speaker".
+ */
+ if (hdev->product == USB_DEVICE_ID_LOGITECH_AUDIOHUB &&
+ !strcmp(hdev->name, "HOLTEK B-LINK USB Audio "))
+ return true;
break;
case USB_VENDOR_ID_SOUNDGRAPH:
if (hdev->product >= USB_DEVICE_ID_SOUNDGRAPH_IMON_FIRST &&
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3eb00902ca40..e39aecb1f9f2 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -471,6 +471,7 @@
#define USB_DEVICE_ID_LG_MULTITOUCH 0x0064
#define USB_VENDOR_ID_LOGITECH 0x046d
+#define USB_DEVICE_ID_LOGITECH_AUDIOHUB 0x0a0e
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
@@ -677,6 +678,17 @@
#define USB_DEVICE_ID_SYMBOL_SCANNER_1 0x0800
#define USB_DEVICE_ID_SYMBOL_SCANNER_2 0x1300
+#define USB_VENDOR_ID_SYNAPTICS 0x06cb
+#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001
+#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002
+#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003
+#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006
+#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007
+#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008
+#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009
+#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010
+#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013
+
#define USB_VENDOR_ID_THRUSTMASTER 0x044f
#define USB_VENDOR_ID_TIVO 0x150a
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 811e6c47e7e6..5b32d56dbb4d 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -648,7 +648,8 @@ config SENSORS_LM90
LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6657, MAX6658, MAX6659,
MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008,
- Winbond/Nuvoton W83L771W/G/AWG/ASG and Philips SA56004 sensor chips.
+ Winbond/Nuvoton W83L771W/G/AWG/ASG, Philips SA56004, and GMT G781
+ sensor chips.
This driver can also be built as a module. If so, the module
will be called lm90.
@@ -812,6 +813,16 @@ config SENSORS_MAX6650
This driver can also be built as a module. If so, the module
will be called max6650.
+config SENSORS_MCP3021
+ tristate "Microchip MCP3021"
+ depends on I2C && EXPERIMENTAL
+ help
+ If you say yes here you get support for the MCP3021 chip
+ that is a A/D converter (ADC) with 10-bit resolution.
+
+ This driver can also be built as a module. If so, the module
+ will be called mcp3021.
+
config SENSORS_NTC_THERMISTOR
tristate "NTC thermistor support"
depends on EXPERIMENTAL
@@ -1229,18 +1240,19 @@ config SENSORS_W83795
depends on I2C && EXPERIMENTAL
help
If you say yes here you get support for the Winbond W83795G and
- W83795ADG hardware monitoring chip.
+ W83795ADG hardware monitoring chip, including manual fan speed
+ control.
This driver can also be built as a module. If so, the module
will be called w83795.
config SENSORS_W83795_FANCTRL
- boolean "Include fan control support (DANGEROUS)"
+ boolean "Include automatic fan control support (DANGEROUS)"
depends on SENSORS_W83795 && EXPERIMENTAL
default n
help
- If you say yes here, support for the both manual and automatic
- fan control features will be included in the driver.
+ If you say yes here, support for automatic fan speed control
+ will be included in the driver.
This part of the code wasn't carefully reviewed and tested yet,
so enabling this option is strongly discouraged on production
@@ -1358,10 +1370,10 @@ config SENSORS_APPLESMC
the awesome power of applesmc.
config SENSORS_MC13783_ADC
- tristate "Freescale MC13783 ADC"
- depends on MFD_MC13783
+ tristate "Freescale MC13783/MC13892 ADC"
+ depends on MFD_MC13XXX
help
- Support for the A/D converter on MC13783 PMIC.
+ Support for the A/D converter on MC13783 and MC13892 PMIC.
if ACPI
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 8251ce8cd035..6d3f11f71815 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
+obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 523f8fb9e7d9..b7494af1e4a9 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -60,15 +60,15 @@ static ssize_t show_power(struct device *dev,
pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
REG_TDP_RUNNING_AVERAGE, &val);
running_avg_capture = (val >> 4) & 0x3fffff;
- running_avg_capture = sign_extend32(running_avg_capture, 22);
- running_avg_range = val & 0xf;
+ running_avg_capture = sign_extend32(running_avg_capture, 21);
+ running_avg_range = (val & 0xf) + 1;
pci_bus_read_config_dword(f4->bus, PCI_DEVFN(PCI_SLOT(f4->devfn), 5),
REG_TDP_LIMIT3, &val);
tdp_limit = val >> 16;
- curr_pwr_watts = tdp_limit + data->base_tdp -
- (s32)(running_avg_capture >> (running_avg_range + 1));
+ curr_pwr_watts = (tdp_limit + data->base_tdp) << running_avg_range;
+ curr_pwr_watts -= running_avg_capture;
curr_pwr_watts *= data->tdp_to_watts;
/*
@@ -78,7 +78,7 @@ static ssize_t show_power(struct device *dev,
* scaling factor 1/(2^16). For conversion we use
* (10^6)/(2^16) = 15625/(2^10)
*/
- curr_pwr_watts = (curr_pwr_watts * 15625) >> 10;
+ curr_pwr_watts = (curr_pwr_watts * 15625) >> (10 + running_avg_range);
return sprintf(buf, "%u\n", (unsigned int) curr_pwr_watts);
}
static DEVICE_ATTR(power1_input, S_IRUGO, show_power, NULL);
diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c
index 8305d29459bd..519ce8b9c142 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/lm63.c b/drivers/hwmon/lm63.c
index 15c05cc83e2c..602a0f0b0de8 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -148,46 +148,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
#define UPDATE_INTERVAL(max, rate) \
((1000 << (LM63_MAX_CONVRATE - (rate))) / (max))
-/*
- * Functions declaration
- */
-
-static int lm63_probe(struct i2c_client *client,
- const struct i2c_device_id *id);
-static int lm63_remove(struct i2c_client *client);
-
-static struct lm63_data *lm63_update_device(struct device *dev);
-
-static int lm63_detect(struct i2c_client *client, struct i2c_board_info *info);
-static void lm63_init_client(struct i2c_client *client);
-
enum chips { lm63, lm64, lm96163 };
/*
- * Driver data (common to all clients)
- */
-
-static const struct i2c_device_id lm63_id[] = {
- { "lm63", lm63 },
- { "lm64", lm64 },
- { "lm96163", lm96163 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, lm63_id);
-
-static struct i2c_driver lm63_driver = {
- .class = I2C_CLASS_HWMON,
- .driver = {
- .name = "lm63",
- },
- .probe = lm63_probe,
- .remove = lm63_remove,
- .id_table = lm63_id,
- .detect = lm63_detect,
- .address_list = normal_i2c,
-};
-
-/*
* Client data (each client gets its own)
*/
@@ -242,6 +205,145 @@ static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000);
}
+static inline int lut_temp_to_reg(struct lm63_data *data, long val)
+{
+ val -= data->temp2_offset;
+ if (data->lut_temp_highres)
+ return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127500), 500);
+ else
+ return DIV_ROUND_CLOSEST(SENSORS_LIMIT(val, 0, 127000), 1000);
+}
+
+/*
+ * Update the lookup table register cache.
+ * client->update_lock must be held when calling this function.
+ */
+static void lm63_update_lut(struct i2c_client *client)
+{
+ struct lm63_data *data = i2c_get_clientdata(client);
+ int i;
+
+ if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
+ !data->lut_valid) {
+ for (i = 0; i < data->lut_size; i++) {
+ data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LUT_PWM(i));
+ data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LUT_TEMP(i));
+ }
+ data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
+ LM63_REG_LUT_TEMP_HYST);
+
+ data->lut_last_updated = jiffies;
+ data->lut_valid = 1;
+ }
+}
+
+static struct lm63_data *lm63_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm63_data *data = i2c_get_clientdata(client);
+ unsigned long next_update;
+
+ mutex_lock(&data->update_lock);
+
+ next_update = data->last_updated
+ + msecs_to_jiffies(data->update_interval) + 1;
+
+ if (time_after(jiffies, next_update) || !data->valid) {
+ if (data->config & 0x04) { /* tachometer enabled */
+ /* order matters for fan1_input */
+ data->fan[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_COUNT_LSB) & 0xFC;
+ data->fan[0] |= i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_COUNT_MSB) << 8;
+ data->fan[1] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_LIMIT_LSB) & 0xFC)
+ | (i2c_smbus_read_byte_data(client,
+ LM63_REG_TACH_LIMIT_MSB) << 8);
+ }
+
+ data->pwm1_freq = i2c_smbus_read_byte_data(client,
+ LM63_REG_PWM_FREQ);
+ if (data->pwm1_freq == 0)
+ data->pwm1_freq = 1;
+ data->pwm1[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_PWM_VALUE);
+
+ data->temp8[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LOCAL_TEMP);
+ data->temp8[1] = i2c_smbus_read_byte_data(client,
+ LM63_REG_LOCAL_HIGH);
+
+ /* order matters for temp2_input */
+ data->temp11[0] = i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TEMP_MSB) << 8;
+ data->temp11[0] |= i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TEMP_LSB);
+ data->temp11[1] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_LOW_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_LOW_LSB);
+ data->temp11[2] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_HIGH_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_HIGH_LSB);
+ data->temp11[3] = (i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_OFFSET_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_OFFSET_LSB);
+
+ if (data->kind == lm96163)
+ data->temp11u = (i2c_smbus_read_byte_data(client,
+ LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
+ | i2c_smbus_read_byte_data(client,
+ LM96163_REG_REMOTE_TEMP_U_LSB);
+
+ data->temp8[2] = i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TCRIT);
+ data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
+ LM63_REG_REMOTE_TCRIT_HYST);
+
+ data->alarms = i2c_smbus_read_byte_data(client,
+ LM63_REG_ALERT_STATUS) & 0x7F;
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ lm63_update_lut(client);
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/*
+ * Trip points in the lookup table should be in ascending order for both
+ * temperatures and PWM output values.
+ */
+static int lm63_lut_looks_bad(struct i2c_client *client)
+{
+ struct lm63_data *data = i2c_get_clientdata(client);
+ int i;
+
+ mutex_lock(&data->update_lock);
+ lm63_update_lut(client);
+
+ for (i = 1; i < data->lut_size; i++) {
+ if (data->pwm1[1 + i - 1] > data->pwm1[1 + i]
+ || data->temp8[3 + i - 1] > data->temp8[3 + i]) {
+ dev_warn(&client->dev,
+ "Lookup table doesn't look sane (check entries %d and %d)\n",
+ i, i + 1);
+ break;
+ }
+ }
+ mutex_unlock(&data->update_lock);
+
+ return i == data->lut_size ? 0 : 1;
+}
+
/*
* Sysfs callback functions and files
*/
@@ -294,13 +396,16 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
return sprintf(buf, "%d\n", pwm);
}
-static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
+static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
+ int nr = attr->index;
unsigned long val;
int err;
+ u8 reg;
if (!(data->config_fan & 0x20)) /* register is read-only */
return -EPERM;
@@ -309,11 +414,13 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy,
if (err)
return err;
+ reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE;
val = SENSORS_LIMIT(val, 0, 255);
+
mutex_lock(&data->update_lock);
- data->pwm1[0] = data->pwm_highres ? val :
+ data->pwm1[nr] = data->pwm_highres ? val :
(val * data->pwm1_freq * 2 + 127) / 255;
- i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]);
+ i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]);
mutex_unlock(&data->update_lock);
return count;
}
@@ -325,6 +432,41 @@ static ssize_t show_pwm1_enable(struct device *dev,
return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
}
+static ssize_t set_pwm1_enable(struct device *dev,
+ struct device_attribute *dummy,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct lm63_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int err;
+
+ err = kstrtoul(buf, 10, &val);
+ if (err)
+ return err;
+ if (val < 1 || val > 2)
+ return -EINVAL;
+
+ /*
+ * Only let the user switch to automatic mode if the lookup table
+ * looks sane.
+ */
+ if (val == 2 && lm63_lut_looks_bad(client))
+ return -EPERM;
+
+ mutex_lock(&data->update_lock);
+ data->config_fan = i2c_smbus_read_byte_data(client,
+ LM63_REG_CONFIG_FAN);
+ if (val == 1)
+ data->config_fan |= 0x20;
+ else
+ data->config_fan &= ~0x20;
+ i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
+ data->config_fan);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
/*
* There are 8bit registers for both local(temp1) and remote(temp2) sensor.
* For remote sensor registers temp2_offset has to be considered,
@@ -367,23 +509,31 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
int nr = attr->index;
- int reg = nr == 2 ? LM63_REG_REMOTE_TCRIT : LM63_REG_LOCAL_HIGH;
long val;
int err;
int temp;
+ u8 reg;
err = kstrtol(buf, 10, &val);
if (err)
return err;
mutex_lock(&data->update_lock);
- if (nr == 2) {
+ switch (nr) {
+ case 2:
+ reg = LM63_REG_REMOTE_TCRIT;
if (data->remote_unsigned)
temp = TEMP8U_TO_REG(val - data->temp2_offset);
else
temp = TEMP8_TO_REG(val - data->temp2_offset);
- } else {
+ break;
+ case 1:
+ reg = LM63_REG_LOCAL_HIGH;
temp = TEMP8_TO_REG(val);
+ break;
+ default: /* lookup table */
+ reg = LM63_REG_LUT_TEMP(nr - 3);
+ temp = lut_temp_to_reg(data, val);
}
data->temp8[nr] = temp;
i2c_smbus_write_byte_data(client, reg, temp);
@@ -613,65 +763,78 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
set_fan, 1);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
-static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO,
- show_lut_temp, NULL, 3);
+static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwm1_enable, set_pwm1_enable);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 1);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 3);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 3);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO,
- show_lut_temp, NULL, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 2);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 4);
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 4);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO,
- show_lut_temp, NULL, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 3);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 5);
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 5);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO,
- show_lut_temp, NULL, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 4);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 6);
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 6);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO,
- show_lut_temp, NULL, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 5);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 7);
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 7);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO,
- show_lut_temp, NULL, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 6);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 8);
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 8);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO,
- show_lut_temp, NULL, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 7);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 9);
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 9);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO,
- show_lut_temp, NULL, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 8);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 10);
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 10);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IRUGO, show_pwm1, NULL, 9);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IRUGO,
- show_lut_temp, NULL, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 9);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 11);
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 11);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IRUGO, show_pwm1, NULL, 10);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IRUGO,
- show_lut_temp, NULL, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 10);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 12);
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 12);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IRUGO, show_pwm1, NULL, 11);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IRUGO,
- show_lut_temp, NULL, 13);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 11);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 13);
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 13);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IRUGO, show_pwm1, NULL, 12);
-static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IRUGO,
- show_lut_temp, NULL, 14);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO,
+ show_pwm1, set_pwm1, 12);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO,
+ show_lut_temp, set_temp8, 14);
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO,
show_lut_temp_hyst, NULL, 14);
@@ -817,28 +980,25 @@ static const struct attribute_group lm63_group_fan1 = {
*/
/* Return 0 if detection is successful, -ENODEV otherwise */
-static int lm63_detect(struct i2c_client *new_client,
+static int lm63_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
- struct i2c_adapter *adapter = new_client->adapter;
+ struct i2c_adapter *adapter = client->adapter;
u8 man_id, chip_id, reg_config1, reg_config2;
u8 reg_alert_status, reg_alert_mask;
- int address = new_client->addr;
+ int address = client->addr;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID);
+ man_id = i2c_smbus_read_byte_data(client, LM63_REG_MAN_ID);
+ chip_id = i2c_smbus_read_byte_data(client, LM63_REG_CHIP_ID);
- reg_config1 = i2c_smbus_read_byte_data(new_client,
- LM63_REG_CONFIG1);
- reg_config2 = i2c_smbus_read_byte_data(new_client,
- LM63_REG_CONFIG2);
- reg_alert_status = i2c_smbus_read_byte_data(new_client,
+ reg_config1 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
+ reg_config2 = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG2);
+ reg_alert_status = i2c_smbus_read_byte_data(client,
LM63_REG_ALERT_STATUS);
- reg_alert_mask = i2c_smbus_read_byte_data(new_client,
- LM63_REG_ALERT_MASK);
+ reg_alert_mask = i2c_smbus_read_byte_data(client, LM63_REG_ALERT_MASK);
if (man_id != 0x01 /* National Semiconductor */
|| (reg_config1 & 0x18) != 0x00
@@ -863,74 +1023,6 @@ static int lm63_detect(struct i2c_client *new_client,
return 0;
}
-static int lm63_probe(struct i2c_client *new_client,
- const struct i2c_device_id *id)
-{
- struct lm63_data *data;
- int err;
-
- data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
-
- i2c_set_clientdata(new_client, data);
- data->valid = 0;
- mutex_init(&data->update_lock);
-
- /* Set the device type */
- data->kind = id->driver_data;
- if (data->kind == lm64)
- data->temp2_offset = 16000;
-
- /* Initialize chip */
- lm63_init_client(new_client);
-
- /* Register sysfs hooks */
- err = sysfs_create_group(&new_client->dev.kobj, &lm63_group);
- if (err)
- goto exit_free;
- if (data->config & 0x04) { /* tachometer enabled */
- err = sysfs_create_group(&new_client->dev.kobj,
- &lm63_group_fan1);
- if (err)
- goto exit_remove_files;
- }
- if (data->kind == lm96163) {
- err = device_create_file(&new_client->dev,
- &dev_attr_temp2_type);
- if (err)
- goto exit_remove_files;
-
- err = sysfs_create_group(&new_client->dev.kobj,
- &lm63_group_extra_lut);
- if (err)
- goto exit_remove_files;
- }
-
- data->hwmon_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->hwmon_dev)) {
- err = PTR_ERR(data->hwmon_dev);
- goto exit_remove_files;
- }
-
- return 0;
-
-exit_remove_files:
- sysfs_remove_group(&new_client->dev.kobj, &lm63_group);
- sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1);
- if (data->kind == lm96163) {
- device_remove_file(&new_client->dev, &dev_attr_temp2_type);
- sysfs_remove_group(&new_client->dev.kobj,
- &lm63_group_extra_lut);
- }
-exit_free:
- kfree(data);
-exit:
- return err;
-}
-
/*
* Ideally we shouldn't have to initialize anything, since the BIOS
* should have taken care of everything
@@ -1010,114 +1102,110 @@ static void lm63_init_client(struct i2c_client *client)
(data->config_fan & 0x20) ? "manual" : "auto");
}
-static int lm63_remove(struct i2c_client *client)
+static int lm63_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
{
- struct lm63_data *data = i2c_get_clientdata(client);
+ struct lm63_data *data;
+ int err;
- hwmon_device_unregister(data->hwmon_dev);
+ data = kzalloc(sizeof(struct lm63_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->valid = 0;
+ mutex_init(&data->update_lock);
+
+ /* Set the device type */
+ data->kind = id->driver_data;
+ if (data->kind == lm64)
+ data->temp2_offset = 16000;
+
+ /* Initialize chip */
+ lm63_init_client(client);
+
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&client->dev.kobj, &lm63_group);
+ if (err)
+ goto exit_free;
+ if (data->config & 0x04) { /* tachometer enabled */
+ err = sysfs_create_group(&client->dev.kobj, &lm63_group_fan1);
+ if (err)
+ goto exit_remove_files;
+ }
+ if (data->kind == lm96163) {
+ err = device_create_file(&client->dev, &dev_attr_temp2_type);
+ if (err)
+ goto exit_remove_files;
+
+ err = sysfs_create_group(&client->dev.kobj,
+ &lm63_group_extra_lut);
+ if (err)
+ goto exit_remove_files;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove_files;
+ }
+
+ return 0;
+
+exit_remove_files:
sysfs_remove_group(&client->dev.kobj, &lm63_group);
sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
if (data->kind == lm96163) {
device_remove_file(&client->dev, &dev_attr_temp2_type);
sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
}
-
+exit_free:
kfree(data);
- return 0;
+exit:
+ return err;
}
-static struct lm63_data *lm63_update_device(struct device *dev)
+static int lm63_remove(struct i2c_client *client)
{
- struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client);
- unsigned long next_update;
- int i;
-
- mutex_lock(&data->update_lock);
-
- next_update = data->last_updated
- + msecs_to_jiffies(data->update_interval) + 1;
-
- if (time_after(jiffies, next_update) || !data->valid) {
- if (data->config & 0x04) { /* tachometer enabled */
- /* order matters for fan1_input */
- data->fan[0] = i2c_smbus_read_byte_data(client,
- LM63_REG_TACH_COUNT_LSB) & 0xFC;
- data->fan[0] |= i2c_smbus_read_byte_data(client,
- LM63_REG_TACH_COUNT_MSB) << 8;
- data->fan[1] = (i2c_smbus_read_byte_data(client,
- LM63_REG_TACH_LIMIT_LSB) & 0xFC)
- | (i2c_smbus_read_byte_data(client,
- LM63_REG_TACH_LIMIT_MSB) << 8);
- }
-
- data->pwm1_freq = i2c_smbus_read_byte_data(client,
- LM63_REG_PWM_FREQ);
- if (data->pwm1_freq == 0)
- data->pwm1_freq = 1;
- data->pwm1[0] = i2c_smbus_read_byte_data(client,
- LM63_REG_PWM_VALUE);
-
- data->temp8[0] = i2c_smbus_read_byte_data(client,
- LM63_REG_LOCAL_TEMP);
- data->temp8[1] = i2c_smbus_read_byte_data(client,
- LM63_REG_LOCAL_HIGH);
-
- /* order matters for temp2_input */
- data->temp11[0] = i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_TEMP_MSB) << 8;
- data->temp11[0] |= i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_TEMP_LSB);
- data->temp11[1] = (i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_LOW_MSB) << 8)
- | i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_LOW_LSB);
- data->temp11[2] = (i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_HIGH_MSB) << 8)
- | i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_HIGH_LSB);
- data->temp11[3] = (i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_OFFSET_MSB) << 8)
- | i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_OFFSET_LSB);
-
- if (data->kind == lm96163)
- data->temp11u = (i2c_smbus_read_byte_data(client,
- LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
- | i2c_smbus_read_byte_data(client,
- LM96163_REG_REMOTE_TEMP_U_LSB);
-
- data->temp8[2] = i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_TCRIT);
- data->temp2_crit_hyst = i2c_smbus_read_byte_data(client,
- LM63_REG_REMOTE_TCRIT_HYST);
-
- data->alarms = i2c_smbus_read_byte_data(client,
- LM63_REG_ALERT_STATUS) & 0x7F;
- data->last_updated = jiffies;
- data->valid = 1;
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
+ if (data->kind == lm96163) {
+ device_remove_file(&client->dev, &dev_attr_temp2_type);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group_extra_lut);
}
- if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
- !data->lut_valid) {
- for (i = 0; i < data->lut_size; i++) {
- data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
- LM63_REG_LUT_PWM(i));
- data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
- LM63_REG_LUT_TEMP(i));
- }
- data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
- LM63_REG_LUT_TEMP_HYST);
+ kfree(data);
+ return 0;
+}
- data->lut_last_updated = jiffies;
- data->lut_valid = 1;
- }
+/*
+ * Driver data (common to all clients)
+ */
- mutex_unlock(&data->update_lock);
+static const struct i2c_device_id lm63_id[] = {
+ { "lm63", lm63 },
+ { "lm64", lm64 },
+ { "lm96163", lm96163 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lm63_id);
- return data;
-}
+static struct i2c_driver lm63_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "lm63",
+ },
+ .probe = lm63_probe,
+ .remove = lm63_remove,
+ .id_table = lm63_id,
+ .detect = lm63_detect,
+ .address_list = normal_i2c,
+};
module_i2c_driver(lm63_driver);
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 248f2b40dfaf..22b14a68e35e 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -57,6 +57,9 @@
* This driver also supports the SA56004 from Philips. This device is
* pin-compatible with the LM86, the ED/EDP parts are also address-compatible.
*
+ * This driver also supports the G781 from GMT. This device is compatible
+ * with the ADM1032.
+ *
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
@@ -107,7 +110,7 @@ static const unsigned short normal_i2c[] = {
0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680,
- max6646, w83l771, max6696, sa56004 };
+ max6646, w83l771, max6696, sa56004, g781 };
/*
* The LM90 registers
@@ -184,6 +187,7 @@ static const struct i2c_device_id lm90_id[] = {
{ "adm1032", adm1032 },
{ "adt7461", adt7461 },
{ "adt7461a", adt7461 },
+ { "g781", g781 },
{ "lm90", lm90 },
{ "lm86", lm86 },
{ "lm89", lm86 },
@@ -229,6 +233,12 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7c,
.max_convrate = 10,
},
+ [g781] = {
+ .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT
+ | LM90_HAVE_BROKEN_ALERT,
+ .alert_alarms = 0x7c,
+ .max_convrate = 8,
+ },
[lm86] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT,
.alert_alarms = 0x7b,
@@ -308,22 +318,24 @@ struct lm90_data {
/* registers values */
s8 temp8[8]; /* 0: local low limit
- 1: local high limit
- 2: local critical limit
- 3: remote critical limit
- 4: local emergency limit (max6659 and max6695/96)
- 5: remote emergency limit (max6659 and max6695/96)
- 6: remote 2 critical limit (max6695/96 only)
- 7: remote 2 emergency limit (max6695/96 only) */
+ * 1: local high limit
+ * 2: local critical limit
+ * 3: remote critical limit
+ * 4: local emergency limit (max6659 and max6695/96)
+ * 5: remote emergency limit (max6659 and max6695/96)
+ * 6: remote 2 critical limit (max6695/96 only)
+ * 7: remote 2 emergency limit (max6695/96 only)
+ */
s16 temp11[8]; /* 0: remote input
- 1: remote low limit
- 2: remote high limit
- 3: remote offset (except max6646, max6657/58/59,
- and max6695/96)
- 4: local input
- 5: remote 2 input (max6695/96 only)
- 6: remote 2 low limit (max6695/96 only)
- 7: remote 2 high limit (ma6695/96 only) */
+ * 1: remote low limit
+ * 2: remote high limit
+ * 3: remote offset (except max6646, max6657/58/59,
+ * and max6695/96)
+ * 4: local input
+ * 5: remote 2 input (max6695/96 only)
+ * 6: remote 2 low limit (max6695/96 only)
+ * 7: remote 2 high limit (max6695/96 only)
+ */
u8 temp_hyst;
u16 alarms; /* bitvector (upper 8 bits for max6695/96) */
};
@@ -533,8 +545,10 @@ static struct lm90_data *lm90_update_device(struct device *dev)
data->alarms |= alarms << 8;
}
- /* Re-enable ALERT# output if it was originally enabled and
- * relevant alarms are all clear */
+ /*
+ * Re-enable ALERT# output if it was originally enabled and
+ * relevant alarms are all clear
+ */
if ((data->config_orig & 0x80) == 0
&& (data->alarms & data->alert_alarms) == 0) {
u8 config;
@@ -1162,8 +1176,10 @@ static int lm90_detect(struct i2c_client *client,
&& (config1 & 0x3F) == 0x00
&& convrate <= 0x0A) {
name = "adm1032";
- /* The ADM1032 supports PEC, but only if combined
- transactions are not used. */
+ /*
+ * The ADM1032 supports PEC, but only if combined
+ * transactions are not used.
+ */
if (i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE))
info->flags |= I2C_CLIENT_PEC;
@@ -1283,6 +1299,13 @@ static int lm90_detect(struct i2c_client *client,
&& convrate <= 0x09) {
name = "sa56004";
}
+ } else
+ if ((address == 0x4C || address == 0x4D)
+ && man_id == 0x47) { /* GMT */
+ if (chip_id == 0x01 /* G781 */
+ && (config1 & 0x3F) == 0x00
+ && convrate <= 0x08)
+ name = "g781";
}
if (!name) { /* identification failed */
@@ -1313,6 +1336,15 @@ static void lm90_remove_files(struct i2c_client *client, struct lm90_data *data)
sysfs_remove_group(&dev->kobj, &lm90_group);
}
+static void lm90_restore_conf(struct i2c_client *client, struct lm90_data *data)
+{
+ /* Restore initial configuration */
+ i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
+ data->convrate_orig);
+ i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
+ data->config_orig);
+}
+
static void lm90_init_client(struct i2c_client *client)
{
u8 config, convrate;
@@ -1382,8 +1414,10 @@ static int lm90_probe(struct i2c_client *client,
client->flags &= ~I2C_CLIENT_PEC;
}
- /* Different devices have different alarm bits triggering the
- * ALERT# output */
+ /*
+ * Different devices have different alarm bits triggering the
+ * ALERT# output
+ */
data->alert_alarms = lm90_params[data->kind].alert_alarms;
/* Set chip capabilities */
@@ -1399,7 +1433,7 @@ static int lm90_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&dev->kobj, &lm90_group);
if (err)
- goto exit_free;
+ goto exit_restore;
if (client->flags & I2C_CLIENT_PEC) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
@@ -1438,7 +1472,8 @@ static int lm90_probe(struct i2c_client *client,
exit_remove_files:
lm90_remove_files(client, data);
-exit_free:
+exit_restore:
+ lm90_restore_conf(client, data);
kfree(data);
exit:
return err;
@@ -1450,12 +1485,7 @@ static int lm90_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
lm90_remove_files(client, data);
-
- /* Restore initial configuration */
- i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE,
- data->convrate_orig);
- i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1,
- data->config_orig);
+ lm90_restore_conf(client, data);
kfree(data);
return 0;
@@ -1488,9 +1518,11 @@ static void lm90_alert(struct i2c_client *client, unsigned int flag)
dev_warn(&client->dev,
"temp%d out of range, please check!\n", 3);
- /* Disable ALERT# output, because these chips don't implement
- SMBus alert correctly; they should only hold the alert line
- low briefly. */
+ /*
+ * Disable ALERT# output, because these chips don't implement
+ * SMBus alert correctly; they should only hold the alert line
+ * low briefly.
+ */
if ((data->flags & LM90_HAVE_BROKEN_ALERT)
&& (alarms & data->alert_alarms)) {
dev_dbg(&client->dev, "Disabling ALERT#\n");
diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c
index ef65ab56b094..ce86c5e3c2c2 100644
--- a/drivers/hwmon/mc13783-adc.c
+++ b/drivers/hwmon/mc13783-adc.c
@@ -1,5 +1,5 @@
/*
- * Driver for the Freescale Semiconductor MC13783 adc.
+ * Driver for the ADC on Freescale Semiconductor MC13783 and MC13892 PMICs.
*
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2009 Sascha Hauer, Pengutronix
@@ -18,7 +18,7 @@
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <linux/mfd/mc13783.h>
+#include <linux/mfd/mc13xxx.h>
#include <linux/platform_device.h>
#include <linux/hwmon-sysfs.h>
#include <linux/kernel.h>
@@ -28,24 +28,30 @@
#include <linux/init.h>
#include <linux/err.h>
-#define MC13783_ADC_NAME "mc13783-adc"
+#define DRIVER_NAME "mc13783-adc"
+
+/* platform device id driver data */
+#define MC13783_ADC_16CHANS 1
+#define MC13783_ADC_BPDIV2 2
struct mc13783_adc_priv {
struct mc13xxx *mc13xxx;
struct device *hwmon_dev;
+ char name[10];
};
static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- return sprintf(buf, "mc13783_adc\n");
+ struct mc13783_adc_priv *priv = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", priv->name);
}
static int mc13783_adc_read(struct device *dev,
struct device_attribute *devattr, unsigned int *val)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ struct mc13783_adc_priv *priv = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
unsigned int channel = attr->index;
unsigned int sample[4];
@@ -53,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;
@@ -68,16 +74,21 @@ static ssize_t mc13783_adc_read_bp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
unsigned val;
+ struct platform_device *pdev = to_platform_device(dev);
+ kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
int ret = mc13783_adc_read(dev, devattr, &val);
if (ret)
return ret;
- /*
- * BP (channel 2) reports with offset 2.4V to the actual value to fit
- * the input range of the ADC. unit = 2.25mV = 9/4 mV.
- */
- val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400;
+ if (driver_data & MC13783_ADC_BPDIV2)
+ val = DIV_ROUND_CLOSEST(val * 9, 2);
+ else
+ /*
+ * BP (channel 2) reports with offset 2.4V to the actual value
+ * to fit the input range of the ADC. unit = 2.25mV = 9/4 mV.
+ */
+ val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400;
return sprintf(buf, "%u\n", val);
}
@@ -114,12 +125,21 @@ static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13);
static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14);
static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15);
-static struct attribute *mc13783_attr[] = {
+static struct attribute *mc13783_attr_base[] = {
&dev_attr_name.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group mc13783_group_base = {
+ .attrs = mc13783_attr_base,
+};
+
+/* these are only used if MC13783_ADC_16CHANS is provided in driver data */
+static struct attribute *mc13783_attr_16chans[] = {
&sensor_dev_attr_in8_input.dev_attr.attr,
&sensor_dev_attr_in9_input.dev_attr.attr,
&sensor_dev_attr_in10_input.dev_attr.attr,
@@ -127,8 +147,8 @@ static struct attribute *mc13783_attr[] = {
NULL
};
-static const struct attribute_group mc13783_group = {
- .attrs = mc13783_attr,
+static const struct attribute_group mc13783_group_16chans = {
+ .attrs = mc13783_attr_16chans,
};
/* last four channels may be occupied by the touchscreen */
@@ -156,24 +176,37 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv;
int ret;
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ char *dash;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);
+ snprintf(priv->name, ARRAY_SIZE(priv->name), "%s", id->name);
+ dash = strchr(priv->name, '-');
+ if (dash)
+ *dash = '\0';
platform_set_drvdata(pdev, priv);
/* Register sysfs hooks */
- ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group);
+ ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_base);
if (ret)
- goto out_err_create1;
+ goto out_err_create_base;
+
+ if (id->driver_data & MC13783_ADC_16CHANS) {
+ ret = sysfs_create_group(&pdev->dev.kobj,
+ &mc13783_group_16chans);
+ if (ret)
+ goto out_err_create_16chans;
+ }
if (!mc13783_adc_use_touchscreen(pdev)) {
ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts);
if (ret)
- goto out_err_create2;
+ goto out_err_create_ts;
}
priv->hwmon_dev = hwmon_device_register(&pdev->dev);
@@ -184,17 +217,20 @@ static int __init mc13783_adc_probe(struct platform_device *pdev)
goto out_err_register;
}
-
return 0;
out_err_register:
if (!mc13783_adc_use_touchscreen(pdev))
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
-out_err_create2:
+out_err_create_ts:
- sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
-out_err_create1:
+ if (id->driver_data & MC13783_ADC_16CHANS)
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans);
+out_err_create_16chans:
+
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base);
+out_err_create_base:
platform_set_drvdata(pdev, NULL);
kfree(priv);
@@ -205,13 +241,17 @@ out_err_create1:
static int __devexit mc13783_adc_remove(struct platform_device *pdev)
{
struct mc13783_adc_priv *priv = platform_get_drvdata(pdev);
+ kernel_ulong_t driver_data = platform_get_device_id(pdev)->driver_data;
hwmon_device_unregister(priv->hwmon_dev);
if (!mc13783_adc_use_touchscreen(pdev))
sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts);
- sysfs_remove_group(&pdev->dev.kobj, &mc13783_group);
+ if (driver_data & MC13783_ADC_16CHANS)
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_16chans);
+
+ sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_base);
platform_set_drvdata(pdev, NULL);
kfree(priv);
@@ -219,12 +259,26 @@ static int __devexit mc13783_adc_remove(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id mc13783_adc_idtable[] = {
+ {
+ .name = "mc13783-adc",
+ .driver_data = MC13783_ADC_16CHANS,
+ }, {
+ .name = "mc13892-adc",
+ .driver_data = MC13783_ADC_BPDIV2,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable);
+
static struct platform_driver mc13783_adc_driver = {
- .remove = __devexit_p(mc13783_adc_remove),
+ .remove = __devexit_p(mc13783_adc_remove),
.driver = {
.owner = THIS_MODULE,
- .name = MC13783_ADC_NAME,
+ .name = DRIVER_NAME,
},
+ .id_table = mc13783_adc_idtable,
};
static int __init mc13783_adc_init(void)
@@ -243,4 +297,3 @@ module_exit(mc13783_adc_exit);
MODULE_DESCRIPTION("MC13783 ADC driver");
MODULE_AUTHOR("Luotao Fu <l.fu@pengutronix.de>");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" MC13783_ADC_NAME);
diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c
new file mode 100644
index 000000000000..d0afc0cd3ff4
--- /dev/null
+++ b/drivers/hwmon/mcp3021.c
@@ -0,0 +1,171 @@
+/*
+ * mcp3021.c - driver for the Microchip MCP3021 chip
+ *
+ * Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc.
+ * Author: Mingkai Hu <Mingkai.hu@freescale.com>
+ *
+ * This driver export the value of analog input voltage to sysfs, the
+ * voltage unit is mV. Through the sysfs interface, lm-sensors tool
+ * can also display the input voltage.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+/* Vdd info */
+#define MCP3021_VDD_MAX 5500
+#define MCP3021_VDD_MIN 2700
+#define MCP3021_VDD_REF 3300
+
+/* output format */
+#define MCP3021_SAR_SHIFT 2
+#define MCP3021_SAR_MASK 0x3ff
+
+#define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */
+#define MCP3021_OUTPUT_SCALE 4
+
+/*
+ * Client data (each client gets its own)
+ */
+struct mcp3021_data {
+ struct device *hwmon_dev;
+ u32 vdd; /* device power supply */
+};
+
+static int mcp3021_read16(struct i2c_client *client)
+{
+ int ret;
+ u16 reg;
+ __be16 buf;
+
+ ret = i2c_master_recv(client, (char *)&buf, 2);
+ if (ret < 0)
+ return ret;
+ if (ret != 2)
+ return -EIO;
+
+ /* The output code of the MCP3021 is transmitted with MSB first. */
+ reg = be16_to_cpu(buf);
+
+ /*
+ * The ten-bit output code is composed of the lower 4-bit of the
+ * first byte and the upper 6-bit of the second byte.
+ */
+ reg = (reg >> MCP3021_SAR_SHIFT) & MCP3021_SAR_MASK;
+
+ return reg;
+}
+
+static inline u16 volts_from_reg(u16 vdd, u16 val)
+{
+ if (val == 0)
+ return 0;
+
+ val = val * MCP3021_OUTPUT_SCALE - MCP3021_OUTPUT_SCALE / 2;
+
+ return val * DIV_ROUND_CLOSEST(vdd,
+ (1 << MCP3021_OUTPUT_RES) * MCP3021_OUTPUT_SCALE);
+}
+
+static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mcp3021_data *data = i2c_get_clientdata(client);
+ int reg, in_input;
+
+ reg = mcp3021_read16(client);
+ if (reg < 0)
+ return reg;
+
+ in_input = volts_from_reg(data->vdd, reg);
+ return sprintf(buf, "%d\n", in_input);
+}
+
+static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input, NULL);
+
+static int mcp3021_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+ struct mcp3021_data *data = NULL;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ data = kzalloc(sizeof(struct mcp3021_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+
+ if (client->dev.platform_data) {
+ data->vdd = *(u32 *)client->dev.platform_data;
+ if (data->vdd > MCP3021_VDD_MAX ||
+ data->vdd < MCP3021_VDD_MIN) {
+ err = -EINVAL;
+ goto exit_free;
+ }
+ } else
+ data->vdd = MCP3021_VDD_REF;
+
+ err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
+ if (err)
+ goto exit_free;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ err = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
+exit_free:
+ kfree(data);
+ return err;
+}
+
+static int mcp3021_remove(struct i2c_client *client)
+{
+ struct mcp3021_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id mcp3021_id[] = {
+ { "mcp3021", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp3021_id);
+
+static struct i2c_driver mcp3021_driver = {
+ .driver = {
+ .name = "mcp3021",
+ },
+ .probe = mcp3021_probe,
+ .remove = mcp3021_remove,
+ .id_table = mcp3021_id,
+};
+
+module_i2c_driver(mcp3021_driver);
+
+MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>");
+MODULE_DESCRIPTION("Microchip MCP3021 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index 834e49d1827b..d6b0bdd48651 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/hwmon/w83795.c b/drivers/hwmon/w83795.c
index deb12c982800..d887cb3b72e8 100644
--- a/drivers/hwmon/w83795.c
+++ b/drivers/hwmon/w83795.c
@@ -72,8 +72,10 @@ MODULE_PARM_DESC(reset, "Set to 1 to reset chip, not recommended");
#define TEMP_CRIT_HYST 2
#define TEMP_WARN 3
#define TEMP_WARN_HYST 4
-/* only crit and crit_hyst affect real-time alarm status
- * current crit crit_hyst warn warn_hyst */
+/*
+ * only crit and crit_hyst affect real-time alarm status
+ * current crit crit_hyst warn warn_hyst
+ */
static const u16 W83795_REG_TEMP[][5] = {
{0x21, 0x96, 0x97, 0x98, 0x99}, /* TD1/TR1 */
{0x22, 0x9a, 0x9b, 0x9c, 0x9d}, /* TD2/TR2 */
@@ -354,26 +356,34 @@ struct w83795_data {
u8 temp_mode; /* Bit vector, 0 = TR, 1 = TD */
u8 temp_src[3]; /* Register value */
- u8 enable_dts; /* Enable PECI and SB-TSI,
+ u8 enable_dts; /*
+ * Enable PECI and SB-TSI,
* bit 0: =1 enable, =0 disable,
- * bit 1: =1 AMD SB-TSI, =0 Intel PECI */
+ * bit 1: =1 AMD SB-TSI, =0 Intel PECI
+ */
u8 has_dts; /* Enable monitor DTS temp */
s8 dts[8]; /* Register value */
u8 dts_read_vrlsb[8]; /* Register value */
s8 dts_ext[4]; /* Register value */
- u8 has_pwm; /* 795g supports 8 pwm, 795adg only supports 2,
+ u8 has_pwm; /*
+ * 795g supports 8 pwm, 795adg only supports 2,
* no config register, only affected by chip
- * type */
- u8 pwm[8][5]; /* Register value, output, freq, start,
- * non stop, stop time */
+ * type
+ */
+ u8 pwm[8][5]; /*
+ * Register value, output, freq, start,
+ * non stop, stop time
+ */
u16 clkin; /* CLKIN frequency in kHz */
u8 pwm_fcms[2]; /* Register value */
u8 pwm_tfmr[6]; /* Register value */
u8 pwm_fomc; /* Register value */
- u16 target_speed[8]; /* Register value, target speed for speed
- * cruise */
+ u16 target_speed[8]; /*
+ * Register value, target speed for speed
+ * cruise
+ */
u8 tol_speed; /* tolerance of target speed */
u8 pwm_temp[6][4]; /* TTTI, CTFS, HCT, HOT */
u8 sf4_reg[6][2][7]; /* 6 temp, temp/dcpwm, 7 registers */
@@ -482,8 +492,10 @@ static void w83795_update_limits(struct i2c_client *client)
/* Read the fan limits */
lsb = 0; /* Silent false gcc warning */
for (i = 0; i < ARRAY_SIZE(data->fan); i++) {
- /* Each register contains LSB for 2 fans, but we want to
- * read it only once to save time */
+ /*
+ * Each register contains LSB for 2 fans, but we want to
+ * read it only once to save time
+ */
if ((i & 1) == 0 && (data->has_fan & (3 << i)))
lsb = w83795_read(client, W83795_REG_FAN_MIN_LSB(i));
@@ -665,9 +677,11 @@ static struct w83795_data *w83795_update_device(struct device *dev)
w83795_read(client, W83795_REG_PWM(i, PWM_OUTPUT));
}
- /* Update intrusion and alarms
+ /*
+ * Update intrusion and alarms
* It is important to read intrusion first, because reading from
- * register SMI STS6 clears the interrupt status temporarily. */
+ * register SMI STS6 clears the interrupt status temporarily.
+ */
tmp = w83795_read(client, W83795_REG_ALARM_CTRL);
/* Switch to interrupt status for intrusion if needed */
if (tmp & ALARM_CTRL_RTSACS)
@@ -929,6 +943,14 @@ store_pwm_enable(struct device *dev, struct device_attribute *attr,
if (val < 1 || val > 2)
return -EINVAL;
+#ifndef CONFIG_SENSORS_W83795_FANCTRL
+ if (val > 1) {
+ dev_warn(dev, "Automatic fan speed control support disabled\n");
+ dev_warn(dev, "Build with CONFIG_SENSORS_W83795_FANCTRL=y if you want it\n");
+ return -EOPNOTSUPP;
+ }
+#endif
+
mutex_lock(&data->update_lock);
switch (val) {
case 1:
@@ -1595,8 +1617,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
#define NOT_USED -1
-/* Don't change the attribute order, _max, _min and _beep are accessed by index
- * somewhere else in the code */
+/*
+ * Don't change the attribute order, _max, _min and _beep are accessed by index
+ * somewhere else in the code
+ */
#define SENSOR_ATTR_IN(index) { \
SENSOR_ATTR_2(in##index##_input, S_IRUGO, show_in, NULL, \
IN_READ, index), \
@@ -1610,8 +1634,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
show_alarm_beep, store_beep, BEEP_ENABLE, \
index + ((index > 14) ? 1 : 0)) }
-/* Don't change the attribute order, _beep is accessed by index
- * somewhere else in the code */
+/*
+ * Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code
+ */
#define SENSOR_ATTR_FAN(index) { \
SENSOR_ATTR_2(fan##index##_input, S_IRUGO, show_fan, \
NULL, FAN_INPUT, index - 1), \
@@ -1625,23 +1651,25 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
#define SENSOR_ATTR_PWM(index) { \
SENSOR_ATTR_2(pwm##index, S_IWUSR | S_IRUGO, show_pwm, \
store_pwm, PWM_OUTPUT, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \
+ show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \
+ show_pwm_mode, NULL, NOT_USED, index - 1), \
+ SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \
+ show_pwm, store_pwm, PWM_FREQ, index - 1), \
SENSOR_ATTR_2(pwm##index##_nonstop, S_IWUSR | S_IRUGO, \
show_pwm, store_pwm, PWM_NONSTOP, index - 1), \
SENSOR_ATTR_2(pwm##index##_start, S_IWUSR | S_IRUGO, \
show_pwm, store_pwm, PWM_START, index - 1), \
SENSOR_ATTR_2(pwm##index##_stop_time, S_IWUSR | S_IRUGO, \
show_pwm, store_pwm, PWM_STOP_TIME, index - 1), \
- SENSOR_ATTR_2(pwm##index##_freq, S_IWUSR | S_IRUGO, \
- show_pwm, store_pwm, PWM_FREQ, index - 1), \
- SENSOR_ATTR_2(pwm##index##_enable, S_IWUSR | S_IRUGO, \
- show_pwm_enable, store_pwm_enable, NOT_USED, index - 1), \
- SENSOR_ATTR_2(pwm##index##_mode, S_IRUGO, \
- show_pwm_mode, NULL, NOT_USED, index - 1), \
SENSOR_ATTR_2(fan##index##_target, S_IWUSR | S_IRUGO, \
show_fanin, store_fanin, FANIN_TARGET, index - 1) }
-/* Don't change the attribute order, _beep is accessed by index
- * somewhere else in the code */
+/*
+ * Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code
+ */
#define SENSOR_ATTR_DTS(index) { \
SENSOR_ATTR_2(temp##index##_type, S_IRUGO , \
show_dts_mode, NULL, NOT_USED, index - 7), \
@@ -1660,8 +1688,10 @@ store_sf_setup(struct device *dev, struct device_attribute *attr,
SENSOR_ATTR_2(temp##index##_beep, S_IWUSR | S_IRUGO, \
show_alarm_beep, store_beep, BEEP_ENABLE, index + 17) }
-/* Don't change the attribute order, _beep is accessed by index
- * somewhere else in the code */
+/*
+ * Don't change the attribute order, _beep is accessed by index
+ * somewhere else in the code
+ */
#define SENSOR_ATTR_TEMP(index) { \
SENSOR_ATTR_2(temp##index##_type, S_IRUGO | (index < 4 ? S_IWUSR : 0), \
show_temp_mode, store_temp_mode, NOT_USED, index - 1), \
@@ -1867,8 +1897,10 @@ static int w83795_get_device_id(struct i2c_client *client)
device_id = i2c_smbus_read_byte_data(client, W83795_REG_DEVICEID);
- /* Special case for rev. A chips; can't be checked first because later
- revisions emulate this for compatibility */
+ /*
+ * Special case for rev. A chips; can't be checked first because later
+ * revisions emulate this for compatibility
+ */
if (device_id < 0 || (device_id & 0xf0) != 0x50) {
int alt_id;
@@ -1920,8 +1952,10 @@ static int w83795_detect(struct i2c_client *client,
return -ENODEV;
}
- /* If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR
- should match */
+ /*
+ * If Nuvoton chip, address of chip and W83795_REG_I2C_ADDR
+ * should match
+ */
if ((bank & 0x07) == 0) {
i2c_addr = i2c_smbus_read_byte_data(client,
W83795_REG_I2C_ADDR);
@@ -1933,10 +1967,12 @@ static int w83795_detect(struct i2c_client *client,
}
}
- /* Check 795 chip type: 795G or 795ADG
- Usually we don't write to chips during detection, but here we don't
- quite have the choice; hopefully it's OK, we are about to return
- success anyway */
+ /*
+ * Check 795 chip type: 795G or 795ADG
+ * Usually we don't write to chips during detection, but here we don't
+ * quite have the choice; hopefully it's OK, we are about to return
+ * success anyway
+ */
if ((bank & 0x07) != 0)
i2c_smbus_write_byte_data(client, W83795_REG_BANKSEL,
bank & ~0x07);
@@ -1953,6 +1989,14 @@ static int w83795_detect(struct i2c_client *client,
return 0;
}
+#ifdef CONFIG_SENSORS_W83795_FANCTRL
+#define NUM_PWM_ATTRIBUTES ARRAY_SIZE(w83795_pwm[0])
+#define NUM_TEMP_ATTRIBUTES ARRAY_SIZE(w83795_temp[0])
+#else
+#define NUM_PWM_ATTRIBUTES 4
+#define NUM_TEMP_ATTRIBUTES 8
+#endif
+
static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
const struct device_attribute *))
{
@@ -2006,24 +2050,18 @@ static int w83795_handle_files(struct device *dev, int (*fn)(struct device *,
}
}
-#ifdef CONFIG_SENSORS_W83795_FANCTRL
for (i = 0; i < data->has_pwm; i++) {
- for (j = 0; j < ARRAY_SIZE(w83795_pwm[0]); j++) {
+ for (j = 0; j < NUM_PWM_ATTRIBUTES; j++) {
err = fn(dev, &w83795_pwm[i][j].dev_attr);
if (err)
return err;
}
}
-#endif
for (i = 0; i < ARRAY_SIZE(w83795_temp); i++) {
if (!(data->has_temp & (1 << i)))
continue;
-#ifdef CONFIG_SENSORS_W83795_FANCTRL
- for (j = 0; j < ARRAY_SIZE(w83795_temp[0]); j++) {
-#else
- for (j = 0; j < 8; j++) {
-#endif
+ for (j = 0; j < NUM_TEMP_ATTRIBUTES; j++) {
if (j == 7 && !data->enable_beep)
continue;
err = fn(dev, &w83795_temp[i][j].dev_attr);
@@ -2183,8 +2221,10 @@ static int w83795_probe(struct i2c_client *client,
/* The W83795G has a dedicated BEEP pin */
data->enable_beep = 1;
} else {
- /* The W83795ADG has a shared pin for OVT# and BEEP, so you
- * can't have both */
+ /*
+ * The W83795ADG has a shared pin for OVT# and BEEP, so you
+ * can't have both
+ */
tmp = w83795_read(client, W83795_REG_OVT_CFG);
if ((tmp & OVT_CFG_SEL) == 0)
data->enable_beep = 1;
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index 24f94f4ae395..7f0b83219744 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)
@@ -616,10 +617,11 @@ static u32 bit_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
-static const struct i2c_algorithm i2c_bit_algo = {
+const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
+EXPORT_SYMBOL(i2c_bit_algo);
/*
* registering functions to load algorithms at runtime
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index beb9ffe2564b..73133b1063f0 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 5eebf562ff31..5c2379522aa9 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 5263a9eeb8d7..1ec703ee788d 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 3101dd59e379..d2c5095deeac 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.
@@ -369,6 +370,21 @@ config I2C_DESIGNWARE_PCI
This driver can also be built as a module. If so, the module
will be called i2c-designware-pci.
+config I2C_EG20T
+ tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
+ depends on PCI
+ help
+ This driver is for PCH(Platform controller Hub) I2C of EG20T which
+ is an IOH(Input/Output Hub) for x86 embedded processor.
+ This driver can access PCH I2C bus device.
+
+ This driver also can be used for LAPIS Semiconductor IOH(Input/
+ Output Hub), ML7213, ML7223 and ML7831.
+ ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
+ for MP(Media Phone) use and ML7831 IOH is for general purpose use.
+ ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
+
config I2C_GPIO
tristate "GPIO-based bitbanging I2C"
depends on GENERIC_GPIO
@@ -630,6 +646,16 @@ config I2C_SIMTEC
This driver can also be built as a module. If so, the module
will be called i2c-simtec.
+config I2C_SIRF
+ tristate "CSR SiRFprimaII I2C interface"
+ depends on ARCH_PRIMA2
+ help
+ If you say yes to this option, support will be included for the
+ CSR SiRFprimaII I2C interface.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-sirf.
+
config I2C_STU300
tristate "ST Microelectronics DDC I2C interface"
depends on MACH_U300
@@ -681,20 +707,15 @@ config I2C_XILINX
This driver can also be built as a module. If so, the module
will be called xilinx_i2c.
-config I2C_EG20T
- tristate "Intel EG20T PCH/LAPIS Semicon IOH(ML7213/ML7223/ML7831) I2C"
- depends on PCI
+config I2C_XLR
+ tristate "XLR I2C support"
+ depends on CPU_XLR
help
- This driver is for PCH(Platform controller Hub) I2C of EG20T which
- is an IOH(Input/Output Hub) for x86 embedded processor.
- This driver can access PCH I2C bus device.
+ This driver enables support for the on-chip I2C interface of
+ the Netlogic XLR/XLS MIPS processors.
- This driver also can be used for LAPIS Semiconductor IOH(Input/
- Output Hub), ML7213, ML7223 and ML7831.
- ML7213 IOH is for IVI(In-Vehicle Infotainment) use, ML7223 IOH is
- for MP(Media Phone) use and ML7831 IOH is for general purpose use.
- ML7213/ML7223/ML7831 is companion chip for Intel Atom E6xx series.
- ML7213/ML7223/ML7831 is completely compatible for Intel EG20T PCH.
+ This driver can also be built as a module. If so, the module
+ will be called i2c-xlr.
comment "External I2C/SMBus adapter drivers"
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index fba6da60aa0e..569567b0d027 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -37,6 +37,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o
i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o
obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o
i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o
+obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o
obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o
obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o
@@ -63,12 +64,13 @@ obj-$(CONFIG_I2C_S6000) += i2c-s6000.o
obj-$(CONFIG_I2C_SH7760) += i2c-sh7760.o
obj-$(CONFIG_I2C_SH_MOBILE) += i2c-sh_mobile.o
obj-$(CONFIG_I2C_SIMTEC) += i2c-simtec.o
+obj-$(CONFIG_I2C_SIRF) += i2c-sirf.o
obj-$(CONFIG_I2C_STU300) += i2c-stu300.o
obj-$(CONFIG_I2C_TEGRA) += i2c-tegra.o
obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o
obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o
obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o
-obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o
+obj-$(CONFIG_I2C_XLR) += i2c-xlr.o
# External I2C/SMBus adapter drivers
obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o
diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c
index 86796488ef4f..ed9f48d566db 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-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 5244c4724df7..4ba589ab8614 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -214,7 +214,7 @@ static int __init dw_i2c_init_driver(void)
{
return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe);
}
-module_init(dw_i2c_init_driver);
+subsys_initcall(dw_i2c_init_driver);
static void __exit dw_i2c_exit_driver(void)
{
diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c
index ca8877641040..f086131cb1c7 100644
--- a/drivers/i2c/busses/i2c-eg20t.c
+++ b/drivers/i2c/busses/i2c-eg20t.c
@@ -271,30 +271,36 @@ static inline bool ktime_lt(const ktime_t cmp1, const ktime_t cmp2)
/**
* pch_i2c_wait_for_bus_idle() - check the status of bus.
* @adap: Pointer to struct i2c_algo_pch_data.
- * @timeout: waiting time counter (us).
+ * @timeout: waiting time counter (ms).
*/
static s32 pch_i2c_wait_for_bus_idle(struct i2c_algo_pch_data *adap,
s32 timeout)
{
void __iomem *p = adap->pch_base_address;
- ktime_t ns_val;
+ int schedule = 0;
+ unsigned long end = jiffies + msecs_to_jiffies(timeout);
+
+ while (ioread32(p + PCH_I2CSR) & I2CMBB_BIT) {
+ if (time_after(jiffies, end)) {
+ pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR));
+ pch_err(adap, "%s: Timeout Error.return%d\n",
+ __func__, -ETIME);
+ pch_i2c_init(adap);
- if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
- return 0;
+ return -ETIME;
+ }
- /* MAX timeout value is timeout*1000*1000nsec */
- ns_val = ktime_add_ns(ktime_get(), timeout*1000*1000);
- do {
- msleep(20);
- if ((ioread32(p + PCH_I2CSR) & I2CMBB_BIT) == 0)
- return 0;
- } while (ktime_lt(ktime_get(), ns_val));
+ if (!schedule)
+ /* Retry after some usecs */
+ udelay(5);
+ else
+ /* Wait a bit more without consuming CPU */
+ usleep_range(20, 1000);
- pch_dbg(adap, "I2CSR = %x\n", ioread32(p + PCH_I2CSR));
- pch_err(adap, "%s: Timeout Error.return%d\n", __func__, -ETIME);
- pch_i2c_init(adap);
+ schedule = 1;
+ }
- return -ETIME;
+ return 0;
}
/**
@@ -778,8 +784,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
struct i2c_msg *pmsg;
u32 i = 0;
u32 status;
- u32 msglen;
- u32 subaddrlen;
s32 ret;
struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
@@ -804,12 +808,6 @@ static s32 pch_i2c_xfer(struct i2c_adapter *i2c_adap,
status = pmsg->flags;
pch_dbg(adap,
"After invoking I2C_MODE_SEL :flag= 0x%x\n", status);
- /* calculate sub address length and message length */
- /* these are applicable only for buffer mode */
- subaddrlen = pmsg->buf[0];
- /* calculate actual message length excluding
- * the sub address fields */
- msglen = (pmsg->len) - (subaddrlen + 1);
if ((status & (I2C_M_RD)) != false) {
ret = pch_i2c_readbytes(i2c_adap, pmsg, (i + 1 == num),
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index a651779d9ff7..c0330a41db03 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 5d2e2816831f..ae2945a5e007 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-imx.c b/drivers/i2c/busses/i2c-imx.c
index 58832e578fff..dfb84b7ee550 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -149,11 +149,6 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy)
break;
if (!for_busy && !(temp & I2SR_IBB))
break;
- if (signal_pending(current)) {
- dev_dbg(&i2c_imx->adapter.dev,
- "<%s> I2C Interrupted\n", __func__);
- return -EINTR;
- }
if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
dev_dbg(&i2c_imx->adapter.dev,
"<%s> I2C bus is busy\n", __func__);
@@ -196,7 +191,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
- clk_enable(i2c_imx->clk);
+ clk_prepare_enable(i2c_imx->clk);
writeb(i2c_imx->ifdr, i2c_imx->base + IMX_I2C_IFDR);
/* Enable I2C controller */
writeb(0, i2c_imx->base + IMX_I2C_I2SR);
@@ -245,7 +240,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
/* Disable I2C controller */
writeb(0, i2c_imx->base + IMX_I2C_I2CR);
- clk_disable(i2c_imx->clk);
+ clk_disable_unprepare(i2c_imx->clk);
}
static void __init i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx,
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index 6561d275b8cf..f90a6057508d 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/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index a8ebb84e23f9..206caacd30d7 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -454,7 +454,7 @@ static int mpc_write(struct mpc_i2c *i2c, int target,
}
static int mpc_read(struct mpc_i2c *i2c, int target,
- u8 *data, int length, int restart)
+ u8 *data, int length, int restart, bool recv_len)
{
unsigned timeout = i2c->adap.timeout;
int i, result;
@@ -470,7 +470,7 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
return result;
if (length) {
- if (length == 1)
+ if (length == 1 && !recv_len)
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
else
writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA);
@@ -479,17 +479,46 @@ static int mpc_read(struct mpc_i2c *i2c, int target,
}
for (i = 0; i < length; i++) {
+ u8 byte;
+
result = i2c_wait(i2c, timeout, 0);
if (result < 0)
return result;
- /* Generate txack on next to last byte */
- if (i == length - 2)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_TXAK);
- /* Do not generate stop on last byte */
- if (i == length - 1)
- writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA | CCR_MTX);
- data[i] = readb(i2c->base + MPC_I2C_DR);
+ /*
+ * For block reads, we have to know the total length (1st byte)
+ * before we can determine if we are done.
+ */
+ if (i || !recv_len) {
+ /* Generate txack on next to last byte */
+ if (i == length - 2)
+ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
+ | CCR_TXAK);
+ /* Do not generate stop on last byte */
+ if (i == length - 1)
+ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
+ | CCR_MTX);
+ }
+
+ byte = readb(i2c->base + MPC_I2C_DR);
+
+ /*
+ * Adjust length if first received byte is length.
+ * The length is 1 length byte plus actually data length
+ */
+ if (i == 0 && recv_len) {
+ if (byte == 0 || byte > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+ length += byte;
+ /*
+ * For block reads, generate txack here if data length
+ * is 1 byte (total length is 2 bytes).
+ */
+ if (length == 2)
+ writeccr(i2c, CCR_MIEN | CCR_MEN | CCR_MSTA
+ | CCR_TXAK);
+ }
+ data[i] = byte;
}
return length;
@@ -532,12 +561,17 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
"Doing %s %d bytes to 0x%02x - %d of %d messages\n",
pmsg->flags & I2C_M_RD ? "read" : "write",
pmsg->len, pmsg->addr, i + 1, num);
- if (pmsg->flags & I2C_M_RD)
- ret =
- mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
- else
+ if (pmsg->flags & I2C_M_RD) {
+ bool recv_len = pmsg->flags & I2C_M_RECV_LEN;
+
+ ret = mpc_read(i2c, pmsg->addr, pmsg->buf, pmsg->len, i,
+ recv_len);
+ if (recv_len && ret > 0)
+ pmsg->len = ret;
+ } else {
ret =
mpc_write(i2c, pmsg->addr, pmsg->buf, pmsg->len, i);
+ }
}
mpc_i2c_stop(i2c);
return (ret < 0) ? ret : num;
@@ -545,7 +579,8 @@ static int mpc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
static u32 mpc_functionality(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
+ | I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL;
}
static const struct i2c_algorithm mpc_algo = {
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index d60364650990..f6733267fa9c 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -29,6 +29,8 @@
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/i2c-pxa.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_i2c.h>
#include <linux/platform_device.h>
#include <linux/err.h>
@@ -1044,23 +1046,60 @@ static const struct i2c_algorithm i2c_pxa_pio_algorithm = {
.functionality = i2c_pxa_functionality,
};
-static int i2c_pxa_probe(struct platform_device *dev)
+static struct of_device_id i2c_pxa_dt_ids[] = {
+ { .compatible = "mrvl,pxa-i2c", .data = (void *)REGS_PXA2XX },
+ { .compatible = "mrvl,pwri2c", .data = (void *)REGS_PXA3XX },
+ { .compatible = "mrvl,mmp-twsi", .data = (void *)REGS_PXA2XX },
+ {}
+};
+MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
+
+static int i2c_pxa_probe_dt(struct platform_device *pdev, struct pxa_i2c *i2c,
+ enum pxa_i2c_types *i2c_types)
{
- struct pxa_i2c *i2c;
- struct resource *res;
- struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
- const struct platform_device_id *id = platform_get_device_id(dev);
- enum pxa_i2c_types i2c_type = id->driver_data;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(i2c_pxa_dt_ids, &pdev->dev);
int ret;
- int irq;
- res = platform_get_resource(dev, IORESOURCE_MEM, 0);
- irq = platform_get_irq(dev, 0);
- if (res == NULL || irq < 0)
- return -ENODEV;
+ if (!of_id)
+ return 1;
+ ret = of_alias_get_id(np, "i2c");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ pdev->id = ret;
+ if (of_get_property(np, "mrvl,i2c-polling", NULL))
+ i2c->use_pio = 1;
+ if (of_get_property(np, "mrvl,i2c-fast-mode", NULL))
+ i2c->fast_mode = 1;
+ *i2c_types = (u32)(of_id->data);
+ return 0;
+}
- if (!request_mem_region(res->start, resource_size(res), res->name))
- return -ENOMEM;
+static int i2c_pxa_probe_pdata(struct platform_device *pdev,
+ struct pxa_i2c *i2c,
+ enum pxa_i2c_types *i2c_types)
+{
+ struct i2c_pxa_platform_data *plat = pdev->dev.platform_data;
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+
+ *i2c_types = id->driver_data;
+ if (plat) {
+ i2c->use_pio = plat->use_pio;
+ i2c->fast_mode = plat->fast_mode;
+ }
+ return 0;
+}
+
+static int i2c_pxa_probe(struct platform_device *dev)
+{
+ struct i2c_pxa_platform_data *plat = dev->dev.platform_data;
+ enum pxa_i2c_types i2c_type;
+ struct pxa_i2c *i2c;
+ struct resource *res = NULL;
+ int ret, irq;
i2c = kzalloc(sizeof(struct pxa_i2c), GFP_KERNEL);
if (!i2c) {
@@ -1068,6 +1107,24 @@ static int i2c_pxa_probe(struct platform_device *dev)
goto emalloc;
}
+ ret = i2c_pxa_probe_dt(dev, i2c, &i2c_type);
+ if (ret > 0)
+ ret = i2c_pxa_probe_pdata(dev, i2c, &i2c_type);
+ if (ret < 0)
+ goto eclk;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(dev, 0);
+ if (res == NULL || irq < 0) {
+ ret = -ENODEV;
+ goto eclk;
+ }
+
+ if (!request_mem_region(res->start, resource_size(res), res->name)) {
+ ret = -ENOMEM;
+ goto eclk;
+ }
+
i2c->adap.owner = THIS_MODULE;
i2c->adap.retries = 5;
@@ -1109,21 +1166,16 @@ static int i2c_pxa_probe(struct platform_device *dev)
i2c->slave_addr = I2C_PXA_SLAVE_ADDR;
-#ifdef CONFIG_I2C_PXA_SLAVE
if (plat) {
+#ifdef CONFIG_I2C_PXA_SLAVE
i2c->slave_addr = plat->slave_addr;
i2c->slave = plat->slave;
- }
#endif
-
- clk_enable(i2c->clk);
-
- if (plat) {
i2c->adap.class = plat->class;
- i2c->use_pio = plat->use_pio;
- i2c->fast_mode = plat->fast_mode;
}
+ clk_enable(i2c->clk);
+
if (i2c->use_pio) {
i2c->adap.algo = &i2c_pxa_pio_algorithm;
} else {
@@ -1234,6 +1286,7 @@ static struct platform_driver i2c_pxa_driver = {
.name = "pxa2xx-i2c",
.owner = THIS_MODULE,
.pm = I2C_PXA_DEV_PM_OPS,
+ .of_match_table = i2c_pxa_dt_ids,
},
.id_table = i2c_pxa_id_table,
};
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 4c1718081685..737f7218a32c 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -31,6 +31,7 @@
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
@@ -564,6 +565,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
int retry;
int ret;
+ pm_runtime_get_sync(&adap->dev);
clk_enable(i2c->clk);
for (retry = 0; retry < adap->retries; retry++) {
@@ -572,6 +574,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
if (ret != -EAGAIN) {
clk_disable(i2c->clk);
+ pm_runtime_put_sync(&adap->dev);
return ret;
}
@@ -581,6 +584,7 @@ static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
}
clk_disable(i2c->clk);
+ pm_runtime_put_sync(&adap->dev);
return -EREMOTEIO;
}
@@ -890,7 +894,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
}
}
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
+ i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
@@ -1013,6 +1017,9 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
of_i2c_register_devices(&i2c->adap);
platform_set_drvdata(pdev, i2c);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_enable(&i2c->adap.dev);
+
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
clk_disable(i2c->clk);
return 0;
@@ -1035,7 +1042,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
clk_put(i2c->clk);
err_noclk:
- kfree(i2c);
return ret;
}
@@ -1048,6 +1054,9 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
+ pm_runtime_disable(&i2c->adap.dev);
+ pm_runtime_disable(&pdev->dev);
+
s3c24xx_i2c_deregister_cpufreq(i2c);
i2c_del_adapter(&i2c->adap);
@@ -1061,7 +1070,6 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev)
release_resource(i2c->ioarea);
s3c24xx_i2c_dt_gpio_free(i2c);
kfree(i2c->ioarea);
- kfree(i2c);
return 0;
}
diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c
new file mode 100644
index 000000000000..5574a47792fb
--- /dev/null
+++ b/drivers/i2c/busses/i2c-sirf.c
@@ -0,0 +1,459 @@
+/*
+ * I2C bus driver for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#define SIRFSOC_I2C_CLK_CTRL 0x00
+#define SIRFSOC_I2C_STATUS 0x0C
+#define SIRFSOC_I2C_CTRL 0x10
+#define SIRFSOC_I2C_IO_CTRL 0x14
+#define SIRFSOC_I2C_SDA_DELAY 0x18
+#define SIRFSOC_I2C_CMD_START 0x1C
+#define SIRFSOC_I2C_CMD_BUF 0x30
+#define SIRFSOC_I2C_DATA_BUF 0x80
+
+#define SIRFSOC_I2C_CMD_BUF_MAX 16
+#define SIRFSOC_I2C_DATA_BUF_MAX 16
+
+#define SIRFSOC_I2C_CMD(x) (SIRFSOC_I2C_CMD_BUF + (x)*0x04)
+#define SIRFSOC_I2C_DATA_MASK(x) (0xFF<<(((x)&3)*8))
+#define SIRFSOC_I2C_DATA_SHIFT(x) (((x)&3)*8)
+
+#define SIRFSOC_I2C_DIV_MASK (0xFFFF)
+
+/* I2C status flags */
+#define SIRFSOC_I2C_STAT_BUSY BIT(0)
+#define SIRFSOC_I2C_STAT_TIP BIT(1)
+#define SIRFSOC_I2C_STAT_NACK BIT(2)
+#define SIRFSOC_I2C_STAT_TR_INT BIT(4)
+#define SIRFSOC_I2C_STAT_STOP BIT(6)
+#define SIRFSOC_I2C_STAT_CMD_DONE BIT(8)
+#define SIRFSOC_I2C_STAT_ERR BIT(9)
+#define SIRFSOC_I2C_CMD_INDEX (0x1F<<16)
+
+/* I2C control flags */
+#define SIRFSOC_I2C_RESET BIT(0)
+#define SIRFSOC_I2C_CORE_EN BIT(1)
+#define SIRFSOC_I2C_MASTER_MODE BIT(2)
+#define SIRFSOC_I2C_CMD_DONE_EN BIT(11)
+#define SIRFSOC_I2C_ERR_INT_EN BIT(12)
+
+#define SIRFSOC_I2C_SDA_DELAY_MASK (0xFF)
+#define SIRFSOC_I2C_SCLF_FILTER (3<<8)
+
+#define SIRFSOC_I2C_START_CMD BIT(0)
+
+#define SIRFSOC_I2C_CMD_RP(x) ((x)&0x7)
+#define SIRFSOC_I2C_NACK BIT(3)
+#define SIRFSOC_I2C_WRITE BIT(4)
+#define SIRFSOC_I2C_READ BIT(5)
+#define SIRFSOC_I2C_STOP BIT(6)
+#define SIRFSOC_I2C_START BIT(7)
+
+#define SIRFSOC_I2C_DEFAULT_SPEED 100000
+
+struct sirfsoc_i2c {
+ void __iomem *base;
+ struct clk *clk;
+ u32 cmd_ptr; /* Current position in CMD buffer */
+ u8 *buf; /* Buffer passed by user */
+ u32 msg_len; /* Message length */
+ u32 finished_len; /* number of bytes read/written */
+ u32 read_cmd_len; /* number of read cmd sent */
+ int msg_read; /* 1 indicates a read message */
+ int err_status; /* 1 indicates an error on bus */
+
+ u32 sda_delay; /* For suspend/resume */
+ u32 clk_div;
+ int last; /* Last message in transfer, STOP cmd can be sent */
+
+ struct completion done; /* indicates completion of message transfer */
+ struct i2c_adapter adapter;
+};
+
+static void i2c_sirfsoc_read_data(struct sirfsoc_i2c *siic)
+{
+ u32 data = 0;
+ int i;
+
+ for (i = 0; i < siic->read_cmd_len; i++) {
+ if (!(i & 0x3))
+ data = readl(siic->base + SIRFSOC_I2C_DATA_BUF + i);
+ siic->buf[siic->finished_len++] =
+ (u8)((data & SIRFSOC_I2C_DATA_MASK(i)) >>
+ SIRFSOC_I2C_DATA_SHIFT(i));
+ }
+}
+
+static void i2c_sirfsoc_queue_cmd(struct sirfsoc_i2c *siic)
+{
+ u32 regval;
+ int i = 0;
+
+ if (siic->msg_read) {
+ while (((siic->finished_len + i) < siic->msg_len)
+ && (siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX)) {
+ regval = SIRFSOC_I2C_READ | SIRFSOC_I2C_CMD_RP(0);
+ if (((siic->finished_len + i) ==
+ (siic->msg_len - 1)) && siic->last)
+ regval |= SIRFSOC_I2C_STOP | SIRFSOC_I2C_NACK;
+ writel(regval,
+ siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+ i++;
+ }
+
+ siic->read_cmd_len = i;
+ } else {
+ while ((siic->cmd_ptr < SIRFSOC_I2C_CMD_BUF_MAX - 1)
+ && (siic->finished_len < siic->msg_len)) {
+ regval = SIRFSOC_I2C_WRITE | SIRFSOC_I2C_CMD_RP(0);
+ if ((siic->finished_len == (siic->msg_len - 1))
+ && siic->last)
+ regval |= SIRFSOC_I2C_STOP;
+ writel(regval,
+ siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+ writel(siic->buf[siic->finished_len++],
+ siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+ }
+ }
+ siic->cmd_ptr = 0;
+
+ /* Trigger the transfer */
+ writel(SIRFSOC_I2C_START_CMD, siic->base + SIRFSOC_I2C_CMD_START);
+}
+
+static irqreturn_t i2c_sirfsoc_irq(int irq, void *dev_id)
+{
+ struct sirfsoc_i2c *siic = (struct sirfsoc_i2c *)dev_id;
+ u32 i2c_stat = readl(siic->base + SIRFSOC_I2C_STATUS);
+
+ if (i2c_stat & SIRFSOC_I2C_STAT_ERR) {
+ /* Error conditions */
+ siic->err_status = 1;
+ writel(SIRFSOC_I2C_STAT_ERR, siic->base + SIRFSOC_I2C_STATUS);
+
+ if (i2c_stat & SIRFSOC_I2C_STAT_NACK)
+ dev_err(&siic->adapter.dev, "ACK not received\n");
+ else
+ dev_err(&siic->adapter.dev, "I2C error\n");
+
+ complete(&siic->done);
+ } else if (i2c_stat & SIRFSOC_I2C_STAT_CMD_DONE) {
+ /* CMD buffer execution complete */
+ if (siic->msg_read)
+ i2c_sirfsoc_read_data(siic);
+ if (siic->finished_len == siic->msg_len)
+ complete(&siic->done);
+ else /* Fill a new CMD buffer for left data */
+ i2c_sirfsoc_queue_cmd(siic);
+
+ writel(SIRFSOC_I2C_STAT_CMD_DONE, siic->base + SIRFSOC_I2C_STATUS);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic,
+ struct i2c_msg *msg)
+{
+ unsigned char addr;
+ u32 regval = SIRFSOC_I2C_START | SIRFSOC_I2C_CMD_RP(0) | SIRFSOC_I2C_WRITE;
+
+ /* no data and last message -> add STOP */
+ if (siic->last && (msg->len == 0))
+ regval |= SIRFSOC_I2C_STOP;
+
+ writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+
+ addr = msg->addr << 1; /* Generate address */
+ if (msg->flags & I2C_M_RD)
+ addr |= 1;
+
+ writel(addr, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
+}
+
+static int i2c_sirfsoc_xfer_msg(struct sirfsoc_i2c *siic, struct i2c_msg *msg)
+{
+ u32 regval = readl(siic->base + SIRFSOC_I2C_CTRL);
+ /* timeout waiting for the xfer to finish or fail */
+ int timeout = msecs_to_jiffies((msg->len + 1) * 50);
+ int ret = 0;
+
+ i2c_sirfsoc_set_address(siic, msg);
+
+ writel(regval | SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN,
+ siic->base + SIRFSOC_I2C_CTRL);
+ i2c_sirfsoc_queue_cmd(siic);
+
+ if (wait_for_completion_timeout(&siic->done, timeout) == 0) {
+ siic->err_status = 1;
+ dev_err(&siic->adapter.dev, "Transfer timeout\n");
+ }
+
+ writel(regval & ~(SIRFSOC_I2C_CMD_DONE_EN | SIRFSOC_I2C_ERR_INT_EN),
+ siic->base + SIRFSOC_I2C_CTRL);
+ writel(0, siic->base + SIRFSOC_I2C_CMD_START);
+
+ if (siic->err_status) {
+ writel(readl(siic->base + SIRFSOC_I2C_CTRL) | SIRFSOC_I2C_RESET,
+ siic->base + SIRFSOC_I2C_CTRL);
+ while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
+ cpu_relax();
+
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static u32 i2c_sirfsoc_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static int i2c_sirfsoc_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num)
+{
+ struct sirfsoc_i2c *siic = adap->algo_data;
+ int i, ret;
+
+ clk_enable(siic->clk);
+
+ for (i = 0; i < num; i++) {
+ siic->buf = msgs[i].buf;
+ siic->msg_len = msgs[i].len;
+ siic->msg_read = !!(msgs[i].flags & I2C_M_RD);
+ siic->err_status = 0;
+ siic->cmd_ptr = 0;
+ siic->finished_len = 0;
+ siic->last = (i == (num - 1));
+
+ ret = i2c_sirfsoc_xfer_msg(siic, &msgs[i]);
+ if (ret) {
+ clk_disable(siic->clk);
+ return ret;
+ }
+ }
+
+ clk_disable(siic->clk);
+ return num;
+}
+
+/* I2C algorithms associated with this master controller driver */
+static const struct i2c_algorithm i2c_sirfsoc_algo = {
+ .master_xfer = i2c_sirfsoc_xfer,
+ .functionality = i2c_sirfsoc_func,
+};
+
+static int __devinit i2c_sirfsoc_probe(struct platform_device *pdev)
+{
+ struct sirfsoc_i2c *siic;
+ struct i2c_adapter *adap;
+ struct resource *mem_res;
+ struct clk *clk;
+ int bitrate;
+ int ctrl_speed;
+ int irq;
+
+ int err;
+ u32 regval;
+
+ clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ err = PTR_ERR(clk);
+ dev_err(&pdev->dev, "Clock get failed\n");
+ goto err_get_clk;
+ }
+
+ err = clk_prepare(clk);
+ if (err) {
+ dev_err(&pdev->dev, "Clock prepare failed\n");
+ goto err_clk_prep;
+ }
+
+ err = clk_enable(clk);
+ if (err) {
+ dev_err(&pdev->dev, "Clock enable failed\n");
+ goto err_clk_en;
+ }
+
+ ctrl_speed = clk_get_rate(clk);
+
+ siic = devm_kzalloc(&pdev->dev, sizeof(*siic), GFP_KERNEL);
+ if (!siic) {
+ dev_err(&pdev->dev, "Can't allocate driver data\n");
+ err = -ENOMEM;
+ goto out;
+ }
+ adap = &siic->adapter;
+ adap->class = I2C_CLASS_HWMON;
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (mem_res == NULL) {
+ dev_err(&pdev->dev, "Unable to get MEM resource\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ siic->base = devm_request_and_ioremap(&pdev->dev, mem_res);
+ if (siic->base == NULL) {
+ dev_err(&pdev->dev, "IO remap failed!\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ err = irq;
+ goto out;
+ }
+ err = devm_request_irq(&pdev->dev, irq, i2c_sirfsoc_irq, 0,
+ dev_name(&pdev->dev), siic);
+ if (err)
+ goto out;
+
+ adap->algo = &i2c_sirfsoc_algo;
+ adap->algo_data = siic;
+
+ adap->dev.parent = &pdev->dev;
+ adap->nr = pdev->id;
+
+ strlcpy(adap->name, "sirfsoc-i2c", sizeof(adap->name));
+
+ platform_set_drvdata(pdev, adap);
+ init_completion(&siic->done);
+
+ /* Controller Initalisation */
+
+ writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
+ while (readl(siic->base + SIRFSOC_I2C_CTRL) & SIRFSOC_I2C_RESET)
+ cpu_relax();
+ writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
+ siic->base + SIRFSOC_I2C_CTRL);
+
+ siic->clk = clk;
+
+ err = of_property_read_u32(pdev->dev.of_node,
+ "clock-frequency", &bitrate);
+ if (err < 0)
+ bitrate = SIRFSOC_I2C_DEFAULT_SPEED;
+
+ if (bitrate < 100000)
+ regval =
+ (2 * ctrl_speed) / (2 * bitrate * 11);
+ else
+ regval = ctrl_speed / (bitrate * 5);
+
+ writel(regval, siic->base + SIRFSOC_I2C_CLK_CTRL);
+ if (regval > 0xFF)
+ writel(0xFF, siic->base + SIRFSOC_I2C_SDA_DELAY);
+ else
+ writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY);
+
+ err = i2c_add_numbered_adapter(adap);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Can't add new i2c adapter\n");
+ goto out;
+ }
+
+ clk_disable(clk);
+
+ dev_info(&pdev->dev, " I2C adapter ready to operate\n");
+
+ return 0;
+
+out:
+ clk_disable(clk);
+err_clk_en:
+ clk_unprepare(clk);
+err_clk_prep:
+ clk_put(clk);
+err_get_clk:
+ return err;
+}
+
+static int __devexit i2c_sirfsoc_remove(struct platform_device *pdev)
+{
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct sirfsoc_i2c *siic = adapter->algo_data;
+
+ writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
+ i2c_del_adapter(adapter);
+ clk_unprepare(siic->clk);
+ clk_put(siic->clk);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int i2c_sirfsoc_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct sirfsoc_i2c *siic = adapter->algo_data;
+
+ clk_enable(siic->clk);
+ siic->sda_delay = readl(siic->base + SIRFSOC_I2C_SDA_DELAY);
+ siic->clk_div = readl(siic->base + SIRFSOC_I2C_CLK_CTRL);
+ clk_disable(siic->clk);
+ return 0;
+}
+
+static int i2c_sirfsoc_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct i2c_adapter *adapter = platform_get_drvdata(pdev);
+ struct sirfsoc_i2c *siic = adapter->algo_data;
+
+ clk_enable(siic->clk);
+ writel(SIRFSOC_I2C_RESET, siic->base + SIRFSOC_I2C_CTRL);
+ writel(SIRFSOC_I2C_CORE_EN | SIRFSOC_I2C_MASTER_MODE,
+ siic->base + SIRFSOC_I2C_CTRL);
+ writel(siic->clk_div, siic->base + SIRFSOC_I2C_CLK_CTRL);
+ writel(siic->sda_delay, siic->base + SIRFSOC_I2C_SDA_DELAY);
+ clk_disable(siic->clk);
+ return 0;
+}
+
+static const struct dev_pm_ops i2c_sirfsoc_pm_ops = {
+ .suspend = i2c_sirfsoc_suspend,
+ .resume = i2c_sirfsoc_resume,
+};
+#endif
+
+static const struct of_device_id sirfsoc_i2c_of_match[] __devinitconst = {
+ { .compatible = "sirf,prima2-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sirfsoc_i2c_of_match);
+
+static struct platform_driver i2c_sirfsoc_driver = {
+ .driver = {
+ .name = "sirfsoc_i2c",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &i2c_sirfsoc_pm_ops,
+#endif
+ .of_match_table = sirfsoc_i2c_of_match,
+ },
+ .probe = i2c_sirfsoc_probe,
+ .remove = __devexit_p(i2c_sirfsoc_remove),
+};
+module_platform_driver(i2c_sirfsoc_driver);
+
+MODULE_DESCRIPTION("SiRF SoC I2C master controller driver");
+MODULE_AUTHOR("Zhiwu Song <Zhiwu.Song@csr.com>, "
+ "Xiangzhen Ye <Xiangzhen.Ye@csr.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 0ab4a9548745..e978635e60f0 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -457,7 +457,6 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_dev *i2c_dev,
int ret;
tegra_i2c_flush_fifos(i2c_dev);
- i2c_writel(i2c_dev, 0xFF, I2C_INT_STATUS);
if (msg->len == 0)
return -EINVAL;
diff --git a/drivers/i2c/busses/i2c-versatile.c b/drivers/i2c/busses/i2c-versatile.c
index 60556012312f..f585aead50cc 100644
--- a/drivers/i2c/busses/i2c-versatile.c
+++ b/drivers/i2c/busses/i2c-versatile.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/of_i2c.h>
#define I2C_CONTROL 0x00
#define I2C_CONTROLS 0x00
@@ -99,6 +100,7 @@ static int i2c_versatile_probe(struct platform_device *dev)
strlcpy(i2c->adap.name, "Versatile I2C adapter", sizeof(i2c->adap.name));
i2c->adap.algo_data = &i2c->algo;
i2c->adap.dev.parent = &dev->dev;
+ i2c->adap.dev.of_node = dev->dev.of_node;
i2c->algo = i2c_versatile_algo;
i2c->algo.data = i2c;
@@ -111,6 +113,7 @@ static int i2c_versatile_probe(struct platform_device *dev)
ret = i2c_bit_add_bus(&i2c->adap);
if (ret >= 0) {
platform_set_drvdata(dev, i2c);
+ of_i2c_register_devices(&i2c->adap);
return 0;
}
@@ -133,12 +136,19 @@ static int i2c_versatile_remove(struct platform_device *dev)
return 0;
}
+static const struct of_device_id i2c_versatile_match[] = {
+ { .compatible = "arm,versatile-i2c", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, i2c_versatile_match);
+
static struct platform_driver i2c_versatile_driver = {
.probe = i2c_versatile_probe,
.remove = i2c_versatile_remove,
.driver = {
.name = "versatile-i2c",
.owner = THIS_MODULE,
+ .of_match_table = i2c_versatile_match,
},
};
diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c
new file mode 100644
index 000000000000..96d3fabd8883
--- /dev/null
+++ b/drivers/i2c/busses/i2c-xlr.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2011, Netlogic Microsystems Inc.
+ * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+/* XLR I2C REGISTERS */
+#define XLR_I2C_CFG 0x00
+#define XLR_I2C_CLKDIV 0x01
+#define XLR_I2C_DEVADDR 0x02
+#define XLR_I2C_ADDR 0x03
+#define XLR_I2C_DATAOUT 0x04
+#define XLR_I2C_DATAIN 0x05
+#define XLR_I2C_STATUS 0x06
+#define XLR_I2C_STARTXFR 0x07
+#define XLR_I2C_BYTECNT 0x08
+#define XLR_I2C_HDSTATIM 0x09
+
+/* XLR I2C REGISTERS FLAGS */
+#define XLR_I2C_BUS_BUSY 0x01
+#define XLR_I2C_SDOEMPTY 0x02
+#define XLR_I2C_RXRDY 0x04
+#define XLR_I2C_ACK_ERR 0x08
+#define XLR_I2C_ARB_STARTERR 0x30
+
+/* Register Values */
+#define XLR_I2C_CFG_ADDR 0xF8
+#define XLR_I2C_CFG_NOADDR 0xFA
+#define XLR_I2C_STARTXFR_ND 0x02 /* No Data */
+#define XLR_I2C_STARTXFR_RD 0x01 /* Read */
+#define XLR_I2C_STARTXFR_WR 0x00 /* Write */
+
+#define XLR_I2C_TIMEOUT 10 /* timeout per byte in msec */
+
+/*
+ * On XLR/XLS, we need to use __raw_ IO to read the I2C registers
+ * because they are in the big-endian MMIO area on the SoC.
+ *
+ * The readl/writel implementation on XLR/XLS byteswaps, because
+ * those are for its little-endian PCI space (see arch/mips/Kconfig).
+ */
+static inline void xlr_i2c_wreg(u32 __iomem *base, unsigned int reg, u32 val)
+{
+ __raw_writel(val, base + reg);
+}
+
+static inline u32 xlr_i2c_rdreg(u32 __iomem *base, unsigned int reg)
+{
+ return __raw_readl(base + reg);
+}
+
+struct xlr_i2c_private {
+ struct i2c_adapter adap;
+ u32 __iomem *iobase;
+};
+
+static int xlr_i2c_tx(struct xlr_i2c_private *priv, u16 len,
+ u8 *buf, u16 addr)
+{
+ struct i2c_adapter *adap = &priv->adap;
+ unsigned long timeout, stoptime, checktime;
+ u32 i2c_status;
+ int pos, timedout;
+ u8 offset, byte;
+
+ offset = buf[0];
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_ADDR, offset);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_ADDR);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len - 1);
+
+ timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
+ stoptime = jiffies + timeout;
+ timedout = 0;
+ pos = 1;
+retry:
+ if (len == 1) {
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
+ XLR_I2C_STARTXFR_ND);
+ } else {
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, buf[pos]);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR,
+ XLR_I2C_STARTXFR_WR);
+ }
+
+ while (!timedout) {
+ checktime = jiffies;
+ i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+
+ if (i2c_status & XLR_I2C_SDOEMPTY) {
+ pos++;
+ /* need to do a empty dataout after the last byte */
+ byte = (pos < len) ? buf[pos] : 0;
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DATAOUT, byte);
+
+ /* reset timeout on successful xmit */
+ stoptime = jiffies + timeout;
+ }
+ timedout = time_after(checktime, stoptime);
+
+ if (i2c_status & XLR_I2C_ARB_STARTERR) {
+ if (timedout)
+ break;
+ goto retry;
+ }
+
+ if (i2c_status & XLR_I2C_ACK_ERR)
+ return -EIO;
+
+ if ((i2c_status & XLR_I2C_BUS_BUSY) == 0 && pos >= len)
+ return 0;
+ }
+ dev_err(&adap->dev, "I2C transmit timeout\n");
+ return -ETIMEDOUT;
+}
+
+static int xlr_i2c_rx(struct xlr_i2c_private *priv, u16 len, u8 *buf, u16 addr)
+{
+ struct i2c_adapter *adap = &priv->adap;
+ u32 i2c_status;
+ unsigned long timeout, stoptime, checktime;
+ int nbytes, timedout;
+ u8 byte;
+
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_CFG, XLR_I2C_CFG_NOADDR);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_BYTECNT, len);
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_DEVADDR, addr);
+
+ timeout = msecs_to_jiffies(XLR_I2C_TIMEOUT);
+ stoptime = jiffies + timeout;
+ timedout = 0;
+ nbytes = 0;
+retry:
+ xlr_i2c_wreg(priv->iobase, XLR_I2C_STARTXFR, XLR_I2C_STARTXFR_RD);
+
+ while (!timedout) {
+ checktime = jiffies;
+ i2c_status = xlr_i2c_rdreg(priv->iobase, XLR_I2C_STATUS);
+ if (i2c_status & XLR_I2C_RXRDY) {
+ if (nbytes > len)
+ return -EIO; /* should not happen */
+
+ /* we need to do a dummy datain when nbytes == len */
+ byte = xlr_i2c_rdreg(priv->iobase, XLR_I2C_DATAIN);
+ if (nbytes < len)
+ buf[nbytes] = byte;
+ nbytes++;
+
+ /* reset timeout on successful read */
+ stoptime = jiffies + timeout;
+ }
+
+ timedout = time_after(checktime, stoptime);
+ if (i2c_status & XLR_I2C_ARB_STARTERR) {
+ if (timedout)
+ break;
+ goto retry;
+ }
+
+ if (i2c_status & XLR_I2C_ACK_ERR)
+ return -EIO;
+
+ if ((i2c_status & XLR_I2C_BUS_BUSY) == 0)
+ return 0;
+ }
+
+ dev_err(&adap->dev, "I2C receive timeout\n");
+ return -ETIMEDOUT;
+}
+
+static int xlr_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ struct i2c_msg *msg;
+ int i;
+ int ret = 0;
+ struct xlr_i2c_private *priv = i2c_get_adapdata(adap);
+
+ for (i = 0; ret == 0 && i < num; i++) {
+ msg = &msgs[i];
+ if (msg->flags & I2C_M_RD)
+ ret = xlr_i2c_rx(priv, msg->len, &msg->buf[0],
+ msg->addr);
+ else
+ ret = xlr_i2c_tx(priv, msg->len, &msg->buf[0],
+ msg->addr);
+ }
+
+ return (ret != 0) ? ret : num;
+}
+
+static u32 xlr_func(struct i2c_adapter *adap)
+{
+ /* Emulate SMBUS over I2C */
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm xlr_i2c_algo = {
+ .master_xfer = xlr_i2c_xfer,
+ .functionality = xlr_func,
+};
+
+static int __devinit xlr_i2c_probe(struct platform_device *pdev)
+{
+ struct xlr_i2c_private *priv;
+ struct resource *res;
+ int ret;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->iobase = devm_request_and_ioremap(&pdev->dev, res);
+ if (!priv->iobase) {
+ dev_err(&pdev->dev, "devm_request_and_ioremap failed\n");
+ return -EBUSY;
+ }
+
+ priv->adap.dev.parent = &pdev->dev;
+ priv->adap.owner = THIS_MODULE;
+ priv->adap.algo_data = priv;
+ priv->adap.algo = &xlr_i2c_algo;
+ priv->adap.nr = pdev->id;
+ priv->adap.class = I2C_CLASS_HWMON;
+ snprintf(priv->adap.name, sizeof(priv->adap.name), "xlr-i2c");
+
+ i2c_set_adapdata(&priv->adap, priv);
+ ret = i2c_add_numbered_adapter(&priv->adap);
+ if (ret < 0) {
+ dev_err(&priv->adap.dev, "Failed to add i2c bus.\n");
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ dev_info(&priv->adap.dev, "Added I2C Bus.\n");
+ return 0;
+}
+
+static int __devexit xlr_i2c_remove(struct platform_device *pdev)
+{
+ struct xlr_i2c_private *priv;
+
+ priv = platform_get_drvdata(pdev);
+ i2c_del_adapter(&priv->adap);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver xlr_i2c_driver = {
+ .probe = xlr_i2c_probe,
+ .remove = __devexit_p(xlr_i2c_remove),
+ .driver = {
+ .name = "xlr-i2cbus",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(xlr_i2c_driver);
+
+MODULE_AUTHOR("Ganesan Ramalingam <ganesanr@netlogicmicro.com>");
+MODULE_DESCRIPTION("XLR/XLS SoC I2C Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:xlr-i2cbus");
diff --git a/drivers/i2c/i2c-boardinfo.c b/drivers/i2c/i2c-boardinfo.c
index 10274ffb66d7..f24cc64e2e8c 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 e9c18939eda7..feb7dc359186 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 9f9c57ff6708..18a8fd21d2c2 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 10e7f1e76586..45048323b75e 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 f61ccc1e5ea3..9836d08f7a77 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 ed699c5aa79d..e0df9b6c66b3 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 6f8953664636..0e37ef27aa12 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 d2f3db3cf3ed..28e344ea514c 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 8bbfe5557c7b..e03f4f19c1d6 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 9a3fbfca9b41..fd05f48f6b0b 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/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index ebe33d960d77..69e2ad06e515 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1378,7 +1378,9 @@ static int srpt_abort_cmd(struct srpt_send_ioctx *ioctx)
break;
case SRPT_STATE_NEED_DATA:
/* DMA_TO_DEVICE (write) - RDMA read error. */
- atomic_set(&ioctx->cmd.transport_lun_stop, 1);
+ spin_lock_irqsave(&ioctx->cmd.t_state_lock, flags);
+ ioctx->cmd.transport_state |= CMD_T_LUN_STOP;
+ spin_unlock_irqrestore(&ioctx->cmd.t_state_lock, flags);
transport_generic_handle_data(&ioctx->cmd);
break;
case SRPT_STATE_CMD_RSP_SENT:
@@ -1387,7 +1389,9 @@ static int srpt_abort_cmd(struct srpt_send_ioctx *ioctx)
* not been received in time.
*/
srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx);
- atomic_set(&ioctx->cmd.transport_lun_stop, 1);
+ spin_lock_irqsave(&ioctx->cmd.t_state_lock, flags);
+ ioctx->cmd.transport_state |= CMD_T_LUN_STOP;
+ spin_unlock_irqrestore(&ioctx->cmd.t_state_lock, flags);
kref_put(&ioctx->kref, srpt_put_send_ioctx_kref);
break;
case SRPT_STATE_MGMT_RSP_SENT:
@@ -1494,6 +1498,7 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch,
{
struct se_cmd *cmd;
enum srpt_command_state state;
+ unsigned long flags;
cmd = &ioctx->cmd;
state = srpt_get_cmd_state(ioctx);
@@ -1513,7 +1518,9 @@ static void srpt_handle_rdma_err_comp(struct srpt_rdma_ch *ch,
__func__, __LINE__, state);
break;
case SRPT_RDMA_WRITE_LAST:
- atomic_set(&ioctx->cmd.transport_lun_stop, 1);
+ spin_lock_irqsave(&ioctx->cmd.t_state_lock, flags);
+ ioctx->cmd.transport_state |= CMD_T_LUN_STOP;
+ spin_unlock_irqrestore(&ioctx->cmd.t_state_lock, flags);
break;
default:
printk(KERN_ERR "%s[%d]: opcode = %u\n", __func__,
@@ -1750,6 +1757,7 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch,
srp_cmd->tag);
cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
+ kref_put(&send_ioctx->kref, srpt_put_send_ioctx_kref);
goto send_sense;
}
@@ -1757,15 +1765,19 @@ static int srpt_handle_cmd(struct srpt_rdma_ch *ch,
cmd->data_direction = dir;
unpacked_lun = srpt_unpack_lun((uint8_t *)&srp_cmd->lun,
sizeof(srp_cmd->lun));
- if (transport_lookup_cmd_lun(cmd, unpacked_lun) < 0)
+ if (transport_lookup_cmd_lun(cmd, unpacked_lun) < 0) {
+ kref_put(&send_ioctx->kref, srpt_put_send_ioctx_kref);
goto send_sense;
+ }
ret = transport_generic_allocate_tasks(cmd, srp_cmd->cdb);
- if (cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
- srpt_queue_status(cmd);
- else if (cmd->se_cmd_flags & SCF_SCSI_CDB_EXCEPTION)
- goto send_sense;
- else
- WARN_ON_ONCE(ret);
+ if (ret < 0) {
+ kref_put(&send_ioctx->kref, srpt_put_send_ioctx_kref);
+ if (cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) {
+ srpt_queue_status(cmd);
+ return 0;
+ } else
+ goto send_sense;
+ }
transport_handle_cdb_direct(cmd);
return 0;
@@ -1871,8 +1883,8 @@ static void srpt_handle_tsk_mgmt(struct srpt_rdma_ch *ch,
TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED;
goto process_tmr;
}
- cmd->se_tmr_req = core_tmr_alloc_req(cmd, NULL, tcm_tmr, GFP_KERNEL);
- if (!cmd->se_tmr_req) {
+ res = core_tmr_alloc_req(cmd, NULL, tcm_tmr, GFP_KERNEL);
+ if (res < 0) {
send_ioctx->cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
send_ioctx->cmd.se_tmr_req->response = TMR_FUNCTION_REJECTED;
goto process_tmr;
@@ -3514,25 +3526,6 @@ static void srpt_close_session(struct se_session *se_sess)
}
/**
- * To do: Find out whether stop_session() has a meaning for transports
- * other than iSCSI.
- */
-static void srpt_stop_session(struct se_session *se_sess, int sess_sleep,
- int conn_sleep)
-{
-}
-
-static void srpt_reset_nexus(struct se_session *sess)
-{
- printk(KERN_ERR "This is the SRP protocol, not iSCSI\n");
-}
-
-static int srpt_sess_logged_in(struct se_session *se_sess)
-{
- return true;
-}
-
-/**
* srpt_sess_get_index() - Return the value of scsiAttIntrPortIndex (SCSI-MIB).
*
* A quote from RFC 4455 (SCSI-MIB) about this MIB object:
@@ -3576,11 +3569,6 @@ static u16 srpt_get_fabric_sense_len(void)
return 0;
}
-static int srpt_is_state_remove(struct se_cmd *se_cmd)
-{
- return 0;
-}
-
/**
* srpt_parse_i_port_id() - Parse an initiator port ID.
* @name: ASCII representation of a 128-bit initiator port ID.
@@ -3950,9 +3938,6 @@ static struct target_core_fabric_ops srpt_template = {
.check_stop_free = srpt_check_stop_free,
.shutdown_session = srpt_shutdown_session,
.close_session = srpt_close_session,
- .stop_session = srpt_stop_session,
- .fall_back_to_erl0 = srpt_reset_nexus,
- .sess_logged_in = srpt_sess_logged_in,
.sess_get_index = srpt_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = srpt_write_pending,
@@ -3965,7 +3950,6 @@ static struct target_core_fabric_ops srpt_template = {
.queue_tm_rsp = srpt_queue_response,
.get_fabric_sense_len = srpt_get_fabric_sense_len,
.set_fabric_sense_len = srpt_set_fabric_sense_len,
- .is_state_remove = srpt_is_state_remove,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 001b147c7f95..332597980817 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -25,6 +25,10 @@ config INPUT
if INPUT
+config INPUT_OF_MATRIX_KEYMAP
+ depends on USE_OF
+ bool
+
config INPUT_FF_MEMLESS
tristate "Support for memoryless force-feedback devices"
help
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 0c789490e0b3..b173a13a73ca 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
obj-$(CONFIG_INPUT_MISC) += misc/
obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 7df5bfef2624..4b2e10d5d641 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -20,7 +20,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>
-#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/major.h>
#include <linux/device.h>
#include "input-compat.h"
@@ -46,6 +46,7 @@ struct evdev_client {
struct fasync_struct *fasync;
struct evdev *evdev;
struct list_head node;
+ int clkid;
unsigned int bufsize;
struct input_event buffer[];
};
@@ -54,8 +55,12 @@ static struct evdev *evdev_table[EVDEV_MINORS];
static DEFINE_MUTEX(evdev_table_mutex);
static void evdev_pass_event(struct evdev_client *client,
- struct input_event *event)
+ struct input_event *event,
+ ktime_t mono, ktime_t real)
{
+ event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
+ mono : real);
+
/* Interrupts are disabled, just acquire the lock. */
spin_lock(&client->buffer_lock);
@@ -94,8 +99,11 @@ static void evdev_event(struct input_handle *handle,
struct evdev *evdev = handle->private;
struct evdev_client *client;
struct input_event event;
+ ktime_t time_mono, time_real;
+
+ time_mono = ktime_get();
+ time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
- do_gettimeofday(&event.time);
event.type = type;
event.code = code;
event.value = value;
@@ -103,11 +111,12 @@ static void evdev_event(struct input_handle *handle,
rcu_read_lock();
client = rcu_dereference(evdev->grab);
+
if (client)
- evdev_pass_event(client, &event);
+ evdev_pass_event(client, &event, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
- evdev_pass_event(client, &event);
+ evdev_pass_event(client, &event, time_mono, time_real);
rcu_read_unlock();
@@ -623,6 +632,28 @@ static int evdev_handle_set_keycode_v2(struct input_dev *dev, void __user *p)
return input_set_keycode(dev, &ke);
}
+static int evdev_handle_mt_request(struct input_dev *dev,
+ unsigned int size,
+ int __user *ip)
+{
+ const struct input_mt_slot *mt = dev->mt;
+ unsigned int code;
+ int max_slots;
+ int i;
+
+ if (get_user(code, &ip[0]))
+ return -EFAULT;
+ if (!input_is_mt_value(code))
+ return -EINVAL;
+
+ max_slots = (size - sizeof(__u32)) / sizeof(__s32);
+ for (i = 0; i < dev->mtsize && i < max_slots; i++)
+ if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i]))
+ return -EFAULT;
+
+ return 0;
+}
+
static long evdev_do_ioctl(struct file *file, unsigned int cmd,
void __user *p, int compat_mode)
{
@@ -685,6 +716,14 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
else
return evdev_ungrab(evdev, client);
+ case EVIOCSCLOCKID:
+ if (copy_from_user(&i, p, sizeof(unsigned int)))
+ return -EFAULT;
+ if (i != CLOCK_MONOTONIC && i != CLOCK_REALTIME)
+ return -EINVAL;
+ client->clkid = i;
+ return 0;
+
case EVIOCGKEYCODE:
return evdev_handle_get_keycode(dev, p);
@@ -708,6 +747,9 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
return bits_to_user(dev->propbit, INPUT_PROP_MAX,
size, p, compat_mode);
+ case EVIOCGMTSLOTS(0):
+ return evdev_handle_mt_request(dev, size, ip);
+
case EVIOCGKEY(0):
return bits_to_user(dev->key, KEY_MAX, size, p, compat_mode);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 1f78c957a75a..8921c6180c51 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -180,7 +180,7 @@ static int input_handle_abs_event(struct input_dev *dev,
return INPUT_IGNORE_EVENT;
}
- is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST;
+ is_mt_event = input_is_mt_value(code);
if (!is_mt_event) {
pold = &dev->absinfo[code].value;
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index c24ec2d5f926..26043cc6a016 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 0bc86204213e..24044dacbf70 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>
diff --git a/drivers/input/joystick/as5011.c b/drivers/input/joystick/as5011.c
index 6d6e7418dc21..3063464474bf 100644
--- a/drivers/input/joystick/as5011.c
+++ b/drivers/input/joystick/as5011.c
@@ -355,14 +355,4 @@ static struct i2c_driver as5011_driver = {
.id_table = as5011_id,
};
-static int __init as5011_init(void)
-{
- return i2c_add_driver(&as5011_driver);
-}
-module_init(as5011_init);
-
-static void __exit as5011_exit(void)
-{
- i2c_del_driver(&as5011_driver);
-}
-module_exit(as5011_exit);
+module_i2c_driver(as5011_driver);
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index cdc385b2cf7d..f354813a13e8 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -394,6 +394,7 @@ config KEYBOARD_NOMADIK
config KEYBOARD_TEGRA
tristate "NVIDIA Tegra internal matrix keyboard controller support"
depends on ARCH_TEGRA
+ select INPUT_OF_MATRIX_KEYMAP if USE_OF
help
Say Y here if you want to use a matrix keyboard connected directly
to the internal keyboard controller on Tegra SoCs.
@@ -512,7 +513,6 @@ config KEYBOARD_OMAP
config KEYBOARD_OMAP4
tristate "TI OMAP4 keypad support"
- depends on ARCH_OMAP4
help
Say Y here if you want to use the OMAP4 keypad.
diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
index 4a7f534cf64b..39ebffac207e 100644
--- a/drivers/input/keyboard/adp5588-keys.c
+++ b/drivers/input/keyboard/adp5588-keys.c
@@ -653,17 +653,7 @@ static struct i2c_driver adp5588_driver = {
.id_table = adp5588_id,
};
-static int __init adp5588_init(void)
-{
- return i2c_add_driver(&adp5588_driver);
-}
-module_init(adp5588_init);
-
-static void __exit adp5588_exit(void)
-{
- i2c_del_driver(&adp5588_driver);
-}
-module_exit(adp5588_exit);
+module_i2c_driver(adp5588_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index 02b5d53031bf..74e603213386 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -1108,17 +1108,7 @@ static struct i2c_driver adp5589_driver = {
.id_table = adp5589_id,
};
-static int __init adp5589_init(void)
-{
- return i2c_add_driver(&adp5589_driver);
-}
-module_init(adp5589_init);
-
-static void __exit adp5589_exit(void)
-{
- i2c_del_driver(&adp5589_driver);
-}
-module_exit(adp5589_exit);
+module_i2c_driver(adp5589_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c
index eeafc30b207b..9d639fa1afbd 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/lm8323.c b/drivers/input/keyboard/lm8323.c
index 21823bfc7911..39ac2787e275 100644
--- a/drivers/input/keyboard/lm8323.c
+++ b/drivers/input/keyboard/lm8323.c
@@ -851,17 +851,7 @@ static struct i2c_driver lm8323_i2c_driver = {
};
MODULE_DEVICE_TABLE(i2c, lm8323_id);
-static int __init lm8323_init(void)
-{
- return i2c_add_driver(&lm8323_i2c_driver);
-}
-module_init(lm8323_init);
-
-static void __exit lm8323_exit(void)
-{
- i2c_del_driver(&lm8323_i2c_driver);
-}
-module_exit(lm8323_exit);
+module_i2c_driver(lm8323_i2c_driver);
MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>");
MODULE_AUTHOR("Daniel Stone");
diff --git a/drivers/input/keyboard/max7359_keypad.c b/drivers/input/keyboard/max7359_keypad.c
index 5afe35ad24d3..8edada8ae712 100644
--- a/drivers/input/keyboard/max7359_keypad.c
+++ b/drivers/input/keyboard/max7359_keypad.c
@@ -316,17 +316,7 @@ static struct i2c_driver max7359_i2c_driver = {
.id_table = max7359_ids,
};
-static int __init max7359_init(void)
-{
- return i2c_add_driver(&max7359_i2c_driver);
-}
-module_init(max7359_init);
-
-static void __exit max7359_exit(void)
-{
- i2c_del_driver(&max7359_i2c_driver);
-}
-module_exit(max7359_exit);
+module_i2c_driver(max7359_i2c_driver);
MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
MODULE_DESCRIPTION("MAX7359 Key Switch Controller Driver");
diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c
index af1aab324a4c..64a0ca4c92f3 100644
--- a/drivers/input/keyboard/mcs_touchkey.c
+++ b/drivers/input/keyboard/mcs_touchkey.c
@@ -274,18 +274,7 @@ static struct i2c_driver mcs_touchkey_driver = {
.id_table = mcs_touchkey_id,
};
-static int __init mcs_touchkey_init(void)
-{
- return i2c_add_driver(&mcs_touchkey_driver);
-}
-
-static void __exit mcs_touchkey_exit(void)
-{
- i2c_del_driver(&mcs_touchkey_driver);
-}
-
-module_init(mcs_touchkey_init);
-module_exit(mcs_touchkey_exit);
+module_i2c_driver(mcs_touchkey_driver);
/* Module information */
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
diff --git a/drivers/input/keyboard/mpr121_touchkey.c b/drivers/input/keyboard/mpr121_touchkey.c
index 1c1615d9a7f9..caa218a51b5a 100644
--- a/drivers/input/keyboard/mpr121_touchkey.c
+++ b/drivers/input/keyboard/mpr121_touchkey.c
@@ -330,17 +330,7 @@ static struct i2c_driver mpr_touchkey_driver = {
.remove = __devexit_p(mpr_touchkey_remove),
};
-static int __init mpr_touchkey_init(void)
-{
- return i2c_add_driver(&mpr_touchkey_driver);
-}
-module_init(mpr_touchkey_init);
-
-static void __exit mpr_touchkey_exit(void)
-{
- i2c_del_driver(&mpr_touchkey_driver);
-}
-module_exit(mpr_touchkey_exit);
+module_i2c_driver(mpr_touchkey_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index e35566aa102f..101e245944e7 100644
--- a/drivers/input/keyboard/nomadik-ske-keypad.c
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -88,7 +88,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
*
* Enable Multi key press detection, auto scan mode
*/
-static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
+static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
{
u32 value;
int timeout = 50;
@@ -198,7 +198,7 @@ static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __devinit ske_keypad_probe(struct platform_device *pdev)
+static int __init ske_keypad_probe(struct platform_device *pdev)
{
const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
struct ske_keypad *keypad;
@@ -344,7 +344,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int ske_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -372,22 +372,17 @@ static int ske_keypad_resume(struct device *dev)
return 0;
}
-
-static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
- .suspend = ske_keypad_suspend,
- .resume = ske_keypad_resume,
-};
#endif
+static SIMPLE_DEV_PM_OPS(ske_keypad_dev_pm_ops,
+ ske_keypad_suspend, ske_keypad_resume);
+
static struct platform_driver ske_keypad_driver = {
.driver = {
.name = "nmk-ske-keypad",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &ske_keypad_dev_pm_ops,
-#endif
},
- .probe = ske_keypad_probe,
.remove = __devexit_p(ske_keypad_remove),
};
diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c
index d5c5d77f4b82..e809ac095a38 100644
--- a/drivers/input/keyboard/omap4-keypad.c
+++ b/drivers/input/keyboard/omap4-keypad.c
@@ -31,7 +31,7 @@
#include <linux/slab.h>
#include <linux/pm_runtime.h>
-#include <plat/omap4-keypad.h>
+#include <linux/platform_data/omap4-keypad.h>
/* OMAP4 registers */
#define OMAP4_KBD_REVISION 0x00
diff --git a/drivers/input/keyboard/qt1070.c b/drivers/input/keyboard/qt1070.c
index b21bf5b876bb..0b7b2f891752 100644
--- a/drivers/input/keyboard/qt1070.c
+++ b/drivers/input/keyboard/qt1070.c
@@ -258,17 +258,7 @@ static struct i2c_driver qt1070_driver = {
.remove = __devexit_p(qt1070_remove),
};
-static int __init qt1070_init(void)
-{
- return i2c_add_driver(&qt1070_driver);
-}
-module_init(qt1070_init);
-
-static void __exit qt1070_exit(void)
-{
- i2c_del_driver(&qt1070_driver);
-}
-module_exit(qt1070_exit);
+module_i2c_driver(qt1070_driver);
MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
MODULE_DESCRIPTION("Driver for AT42QT1070 QTouch sensor");
diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c
index fac695157e8a..e7a5e36e1203 100644
--- a/drivers/input/keyboard/qt2160.c
+++ b/drivers/input/keyboard/qt2160.c
@@ -379,17 +379,7 @@ static struct i2c_driver qt2160_driver = {
.remove = __devexit_p(qt2160_remove),
};
-static int __init qt2160_init(void)
-{
- return i2c_add_driver(&qt2160_driver);
-}
-module_init(qt2160_init);
-
-static void __exit qt2160_cleanup(void)
-{
- i2c_del_driver(&qt2160_driver);
-}
-module_exit(qt2160_cleanup);
+module_i2c_driver(qt2160_driver);
MODULE_AUTHOR("Raphael Derosso Pereira <raphaelpereira@gmail.com>");
MODULE_DESCRIPTION("Driver for AT42QT2160 Touch Sensor");
diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c
index 17ba7f9f80f3..2391ae884fee 100644
--- a/drivers/input/keyboard/samsung-keypad.c
+++ b/drivers/input/keyboard/samsung-keypad.c
@@ -175,7 +175,7 @@ static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
} while (key_down && !keypad->stopped);
- pm_runtime_put_sync(&keypad->pdev->dev);
+ pm_runtime_put(&keypad->pdev->dev);
return IRQ_HANDLED;
}
@@ -199,7 +199,7 @@ static void samsung_keypad_start(struct samsung_keypad *keypad)
/* KEYIFCOL reg clear. */
writel(0, keypad->base + SAMSUNG_KEYIFCOL);
- pm_runtime_put_sync(&keypad->pdev->dev);
+ pm_runtime_put(&keypad->pdev->dev);
}
static void samsung_keypad_stop(struct samsung_keypad *keypad)
@@ -229,7 +229,7 @@ static void samsung_keypad_stop(struct samsung_keypad *keypad)
*/
enable_irq(keypad->irq);
- pm_runtime_put_sync(&keypad->pdev->dev);
+ pm_runtime_put(&keypad->pdev->dev);
}
static int samsung_keypad_open(struct input_dev *input_dev)
diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c
index c88bd63dc9cc..3b6b528f02fd 100644
--- a/drivers/input/keyboard/spear-keyboard.c
+++ b/drivers/input/keyboard/spear-keyboard.c
@@ -50,6 +50,7 @@
#define ROW_MASK 0xF0
#define COLUMN_MASK 0x0F
#define ROW_SHIFT 4
+#define KEY_MATRIX_SHIFT 6
struct spear_kbd {
struct input_dev *input;
@@ -57,6 +58,7 @@ struct spear_kbd {
void __iomem *io_base;
struct clk *clk;
unsigned int irq;
+ unsigned int mode;
unsigned short last_key;
unsigned short keycodes[256];
};
@@ -106,7 +108,8 @@ static int spear_kbd_open(struct input_dev *dev)
return error;
/* program keyboard */
- val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK;
+ val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK |
+ (kbd->mode << KEY_MATRIX_SHIFT);
writew(val, kbd->io_base + MODE_REG);
writeb(1, kbd->io_base + STATUS_REG);
@@ -176,6 +179,8 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev)
kbd->input = input_dev;
kbd->irq = irq;
+ kbd->mode = pdata->mode;
+
kbd->res = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!kbd->res) {
@@ -308,22 +313,17 @@ static int spear_kbd_resume(struct device *dev)
return 0;
}
-
-static const struct dev_pm_ops spear_kbd_pm_ops = {
- .suspend = spear_kbd_suspend,
- .resume = spear_kbd_resume,
-};
#endif
+static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
+
static struct platform_driver spear_kbd_driver = {
.probe = spear_kbd_probe,
.remove = __devexit_p(spear_kbd_remove),
.driver = {
.name = "keyboard",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
.pm = &spear_kbd_pm_ops,
-#endif
},
};
module_platform_driver(spear_kbd_driver);
diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c
index a136e2e832be..21c42f852343 100644
--- a/drivers/input/keyboard/tegra-kbc.c
+++ b/drivers/input/keyboard/tegra-kbc.c
@@ -48,6 +48,7 @@
#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14)
#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4)
#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3)
+#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1)
#define KBC_CONTROL_KBC_EN (1 << 0)
/* KBC Interrupt Register */
@@ -356,6 +357,18 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable)
writel(val, kbc->mmio + KBC_CONTROL_0);
}
+static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable)
+{
+ u32 val;
+
+ val = readl(kbc->mmio + KBC_CONTROL_0);
+ if (enable)
+ val |= KBC_CONTROL_KEYPRESS_INT_EN;
+ else
+ val &= ~KBC_CONTROL_KEYPRESS_INT_EN;
+ writel(val, kbc->mmio + KBC_CONTROL_0);
+}
+
static void tegra_kbc_keypress_timer(unsigned long data)
{
struct tegra_kbc *kbc = (struct tegra_kbc *)data;
@@ -455,10 +468,18 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc)
row_cfg &= ~r_mask;
col_cfg &= ~c_mask;
- if (pdata->pin_cfg[i].is_row)
+ switch (pdata->pin_cfg[i].type) {
+ case PIN_CFG_ROW:
row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft;
- else
+ break;
+
+ case PIN_CFG_COL:
col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft;
+ break;
+
+ case PIN_CFG_IGNORE:
+ break;
+ }
writel(row_cfg, kbc->mmio + r_offs);
writel(col_cfg, kbc->mmio + c_offs);
@@ -563,7 +584,8 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
for (i = 0; i < KBC_MAX_GPIO; i++) {
const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i];
- if (pin_cfg->is_row) {
+ switch (pin_cfg->type) {
+ case PIN_CFG_ROW:
if (pin_cfg->num >= KBC_MAX_ROW) {
dev_err(dev,
"pin_cfg[%d]: invalid row number %d\n",
@@ -571,13 +593,25 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata,
return false;
}
(*num_rows)++;
- } else {
+ break;
+
+ case PIN_CFG_COL:
if (pin_cfg->num >= KBC_MAX_COL) {
dev_err(dev,
"pin_cfg[%d]: invalid column number %d\n",
i, pin_cfg->num);
return false;
}
+ break;
+
+ case PIN_CFG_IGNORE:
+ break;
+
+ default:
+ dev_err(dev,
+ "pin_cfg[%d]: invalid entry type %d\n",
+ pin_cfg->type, pin_cfg->num);
+ return false;
}
}
@@ -590,24 +624,25 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
{
struct tegra_kbc_platform_data *pdata;
struct device_node *np = pdev->dev.of_node;
+ u32 prop;
+ int i;
if (!np)
return NULL;
- pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
- if (!of_property_read_u32(np, "debounce-delay", &prop))
+ if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop))
pdata->debounce_cnt = prop;
- if (!of_property_read_u32(np, "repeat-delay", &prop))
+ if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop))
pdata->repeat_cnt = prop;
- if (of_find_property(np, "needs-ghost-filter", NULL))
+ if (of_find_property(np, "nvidia,needs-ghost-filter", NULL))
pdata->use_ghost_filter = true;
- if (of_find_property(np, "wakeup-source", NULL))
+ if (of_find_property(np, "nvidia,wakeup-source", NULL))
pdata->wakeup = true;
/*
@@ -616,14 +651,18 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev)
*/
for (i = 0; i < KBC_MAX_ROW; i++) {
pdata->pin_cfg[i].num = i;
- pdata->pin_cfg[i].is_row = true;
+ pdata->pin_cfg[i].type = PIN_CFG_ROW;
}
for (i = 0; i < KBC_MAX_COL; i++) {
pdata->pin_cfg[KBC_MAX_ROW + i].num = i;
- pdata->pin_cfg[KBC_MAX_ROW + i].is_row = false;
+ pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL;
}
+ pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap");
+
+ /* FIXME: Add handling of linux,fn-keymap here */
+
return pdata;
}
#else
@@ -759,6 +798,9 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, kbc);
device_init_wakeup(&pdev->dev, pdata->wakeup);
+ if (!pdev->dev.platform_data)
+ matrix_keyboard_of_free_keymap(pdata->keymap_data);
+
return 0;
err_free_irq:
@@ -773,8 +815,10 @@ err_free_mem:
input_free_device(input_dev);
kfree(kbc);
err_free_pdata:
- if (!pdev->dev.platform_data)
+ if (!pdev->dev.platform_data) {
+ matrix_keyboard_of_free_keymap(pdata->keymap_data);
kfree(pdata);
+ }
return err;
}
@@ -831,6 +875,8 @@ static int tegra_kbc_suspend(struct device *dev)
msleep(30);
kbc->keypress_caused_wake = false;
+ /* Enable keypress interrupt before going into suspend. */
+ tegra_kbc_set_keypress_interrupt(kbc, true);
enable_irq(kbc->irq);
enable_irq_wake(kbc->irq);
} else {
@@ -852,6 +898,8 @@ static int tegra_kbc_resume(struct device *dev)
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(kbc->irq);
tegra_kbc_setup_wakekeys(kbc, false);
+ /* We will use fifo interrupts for key detection. */
+ tegra_kbc_set_keypress_interrupt(kbc, false);
/* Restore the resident time of continuous polling mode. */
writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0);
diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c
index f2e0cbc5ab64..f9ce1835e4d7 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/misc/Kconfig b/drivers/input/misc/Kconfig
index 7b46781c30c9..2d787796bf50 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -134,6 +134,18 @@ config INPUT_MAX8925_ONKEY
To compile this driver as a module, choose M here: the module
will be called max8925_onkey.
+config INPUT_MAX8997_HAPTIC
+ tristate "MAXIM MAX8997 haptic controller support"
+ depends on HAVE_PWM && MFD_MAX8997
+ select INPUT_FF_MEMLESS
+ help
+ This option enables device driver support for the haptic controller
+ on MAXIM MAX8997 chip. This driver supports ff-memless interface
+ from input framework.
+
+ To compile this driver as module, choose M here: the
+ module will be called max8997-haptic.
+
config INPUT_MC13783_PWRBUTTON
tristate "MC13783 ON buttons"
depends on MFD_MC13783
@@ -415,7 +427,7 @@ config INPUT_PCF8574
tristate "PCF8574 Keypad input device"
depends on I2C && EXPERIMENTAL
help
- Say Y here if you want to support a keypad connetced via I2C
+ Say Y here if you want to support a keypad connected via I2C
with a PCF8574.
To compile this driver as a module, choose M here: the
@@ -455,6 +467,16 @@ config INPUT_RB532_BUTTON
To compile this driver as a module, choose M here: the
module will be called rb532_button.
+config INPUT_DA9052_ONKEY
+ tristate "Dialog DA9052/DA9053 Onkey"
+ depends on PMIC_DA9052
+ help
+ Support the ONKEY of Dialog DA9052 PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da9052_onkey.
+
config INPUT_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
@@ -558,7 +580,7 @@ config INPUT_CMA3000_I2C
config INPUT_XEN_KBDDEV_FRONTEND
tristate "Xen virtual keyboard and mouse support"
- depends on XEN_FBDEV_FRONTEND
+ depends on XEN
default y
select XEN_XENBUS_FRONTEND
help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 46671a875b91..f55cdf4916fa 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
+obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
+obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
index 56810fb4eadd..c8a79015472a 100644
--- a/drivers/input/misc/ad714x-i2c.c
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -116,17 +116,7 @@ static struct i2c_driver ad714x_i2c_driver = {
.id_table = ad714x_id,
};
-static int __init ad714x_i2c_init(void)
-{
- return i2c_add_driver(&ad714x_i2c_driver);
-}
-module_init(ad714x_i2c_init);
-
-static void __exit ad714x_i2c_exit(void)
-{
- i2c_del_driver(&ad714x_i2c_driver);
-}
-module_exit(ad714x_i2c_exit);
+module_i2c_driver(ad714x_i2c_driver);
MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
index 875b50811361..75f6136d608e 100644
--- a/drivers/input/misc/ad714x-spi.c
+++ b/drivers/input/misc/ad714x-spi.c
@@ -123,17 +123,7 @@ static struct spi_driver ad714x_spi_driver = {
.remove = __devexit_p(ad714x_spi_remove),
};
-static __init int ad714x_spi_init(void)
-{
- return spi_register_driver(&ad714x_spi_driver);
-}
-module_init(ad714x_spi_init);
-
-static __exit void ad714x_spi_exit(void)
-{
- spi_unregister_driver(&ad714x_spi_driver);
-}
-module_exit(ad714x_spi_exit);
+module_spi_driver(ad714x_spi_driver);
MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver");
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
index ccacf2bb06a4..dd1d1c145a7f 100644
--- a/drivers/input/misc/adxl34x-i2c.c
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -148,17 +148,7 @@ static struct i2c_driver adxl34x_driver = {
.id_table = adxl34x_id,
};
-static int __init adxl34x_i2c_init(void)
-{
- return i2c_add_driver(&adxl34x_driver);
-}
-module_init(adxl34x_i2c_init);
-
-static void __exit adxl34x_i2c_exit(void)
-{
- i2c_del_driver(&adxl34x_driver);
-}
-module_exit(adxl34x_i2c_exit);
+module_i2c_driver(adxl34x_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver");
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
index 34d401efd4a1..820a802a1e6e 100644
--- a/drivers/input/misc/adxl34x-spi.c
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -129,17 +129,7 @@ static struct spi_driver adxl34x_driver = {
.remove = __devexit_p(adxl34x_spi_remove),
};
-static int __init adxl34x_spi_init(void)
-{
- return spi_register_driver(&adxl34x_driver);
-}
-module_init(adxl34x_spi_init);
-
-static void __exit adxl34x_spi_exit(void)
-{
- spi_unregister_driver(&adxl34x_driver);
-}
-module_exit(adxl34x_spi_exit);
+module_spi_driver(adxl34x_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver");
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
index 8f55b54352b6..e2f1e9f952b1 100644
--- a/drivers/input/misc/bma150.c
+++ b/drivers/input/misc/bma150.c
@@ -673,19 +673,8 @@ static struct i2c_driver bma150_driver = {
.remove = __devexit_p(bma150_remove),
};
-static int __init BMA150_init(void)
-{
- return i2c_add_driver(&bma150_driver);
-}
-
-static void __exit BMA150_exit(void)
-{
- i2c_del_driver(&bma150_driver);
-}
+module_i2c_driver(bma150_driver);
MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>");
MODULE_DESCRIPTION("BMA150 driver");
MODULE_LICENSE("GPL");
-
-module_init(BMA150_init);
-module_exit(BMA150_exit);
diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c
index d100cc5c5783..fe9b85f07792 100644
--- a/drivers/input/misc/cma3000_d0x_i2c.c
+++ b/drivers/input/misc/cma3000_d0x_i2c.c
@@ -125,18 +125,7 @@ static struct i2c_driver cma3000_i2c_driver = {
},
};
-static int __init cma3000_i2c_init(void)
-{
- return i2c_add_driver(&cma3000_i2c_driver);
-}
-
-static void __exit cma3000_i2c_exit(void)
-{
- i2c_del_driver(&cma3000_i2c_driver);
-}
-
-module_init(cma3000_i2c_init);
-module_exit(cma3000_i2c_exit);
+module_i2c_driver(cma3000_i2c_driver);
MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c
new file mode 100644
index 000000000000..34aebb8cd080
--- /dev/null
+++ b/drivers/input/misc/da9052_onkey.c
@@ -0,0 +1,169 @@
+/*
+ * ON pin driver for Dialog DA9052 PMICs
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+struct da9052_onkey {
+ struct da9052 *da9052;
+ struct input_dev *input;
+ struct delayed_work work;
+ unsigned int irq;
+};
+
+static void da9052_onkey_query(struct da9052_onkey *onkey)
+{
+ int key_stat;
+
+ key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG);
+ if (key_stat < 0) {
+ dev_err(onkey->da9052->dev,
+ "Failed to read onkey event %d\n", key_stat);
+ } else {
+ /*
+ * Since interrupt for deassertion of ONKEY pin is not
+ * generated, onkey event state determines the onkey
+ * button state.
+ */
+ key_stat &= DA9052_EVENTB_ENONKEY;
+ input_report_key(onkey->input, KEY_POWER, key_stat);
+ input_sync(onkey->input);
+ }
+
+ /*
+ * Interrupt is generated only when the ONKEY pin is asserted.
+ * Hence the deassertion of the pin is simulated through work queue.
+ */
+ if (key_stat)
+ schedule_delayed_work(&onkey->work, msecs_to_jiffies(50));
+}
+
+static void da9052_onkey_work(struct work_struct *work)
+{
+ struct da9052_onkey *onkey = container_of(work, struct da9052_onkey,
+ work.work);
+
+ da9052_onkey_query(onkey);
+}
+
+static irqreturn_t da9052_onkey_irq(int irq, void *data)
+{
+ struct da9052_onkey *onkey = data;
+
+ da9052_onkey_query(onkey);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit da9052_onkey_probe(struct platform_device *pdev)
+{
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ struct da9052_onkey *onkey;
+ struct input_dev *input_dev;
+ int irq;
+ int error;
+
+ if (!da9052) {
+ dev_err(&pdev->dev, "Failed to get the driver's data\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq_byname(pdev, "ONKEY");
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "Failed to get an IRQ for input device, %d\n", irq);
+ return -EINVAL;
+ }
+
+ onkey = kzalloc(sizeof(*onkey), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!onkey || !input_dev) {
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ onkey->input = input_dev;
+ onkey->da9052 = da9052;
+ onkey->irq = irq;
+ INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work);
+
+ input_dev->name = "da9052-onkey";
+ input_dev->phys = "da9052-onkey/input0";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ __set_bit(KEY_POWER, input_dev->keybit);
+
+ error = request_threaded_irq(onkey->irq, NULL, da9052_onkey_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "ONKEY", onkey);
+ if (error < 0) {
+ dev_err(onkey->da9052->dev,
+ "Failed to register ONKEY IRQ %d, error = %d\n",
+ onkey->irq, error);
+ goto err_free_mem;
+ }
+
+ error = input_register_device(onkey->input);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to register input device, %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ return 0;
+
+err_free_irq:
+ free_irq(onkey->irq, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(onkey);
+
+ return error;
+}
+
+static int __devexit da9052_onkey_remove(struct platform_device *pdev)
+{
+ struct da9052_onkey *onkey = platform_get_drvdata(pdev);
+
+ free_irq(onkey->irq, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+
+ input_unregister_device(onkey->input);
+ kfree(onkey);
+
+ return 0;
+}
+
+static struct platform_driver da9052_onkey_driver = {
+ .probe = da9052_onkey_probe,
+ .remove = __devexit_p(da9052_onkey_remove),
+ .driver = {
+ .name = "da9052-onkey",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(da9052_onkey_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Onkey driver for DA9052");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-onkey");
diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c
index 71fba8c2fc66..b6664cfa340a 100644
--- a/drivers/input/misc/gp2ap002a00f.c
+++ b/drivers/input/misc/gp2ap002a00f.c
@@ -281,18 +281,7 @@ static struct i2c_driver gp2a_i2c_driver = {
.id_table = gp2a_i2c_id,
};
-static int __init gp2a_init(void)
-{
- return i2c_add_driver(&gp2a_i2c_driver);
-}
-
-static void __exit gp2a_exit(void)
-{
- i2c_del_driver(&gp2a_i2c_driver);
-}
-
-module_init(gp2a_init);
-module_exit(gp2a_exit);
+module_i2c_driver(gp2a_i2c_driver);
MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>");
MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver");
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
index 783597a9a64a..f46139f19ff1 100644
--- a/drivers/input/misc/kxtj9.c
+++ b/drivers/input/misc/kxtj9.c
@@ -41,6 +41,14 @@
#define PC1_ON (1 << 7)
/* Data ready funtion enable bit: set during probe if using irq mode */
#define DRDYE (1 << 5)
+/* DATA CONTROL REGISTER BITS */
+#define ODR12_5F 0
+#define ODR25F 1
+#define ODR50F 2
+#define ODR100F 3
+#define ODR200F 4
+#define ODR400F 5
+#define ODR800F 6
/* INTERRUPT CONTROL REGISTER 1 BITS */
/* Set these during probe if using irq mode */
#define KXTJ9_IEL (1 << 3)
@@ -116,9 +124,13 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
if (err < 0)
dev_err(&tj9->client->dev, "accelerometer data read failed\n");
- x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift;
- y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift;
- z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift;
+ x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]);
+ y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]);
+ z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]);
+
+ x >>= tj9->shift;
+ y >>= tj9->shift;
+ z >>= tj9->shift;
input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
@@ -487,7 +499,7 @@ static int __devinit kxtj9_verify(struct kxtj9_data *tj9)
goto out;
}
- retval = retval != 0x06 ? -EIO : 0;
+ retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
out:
kxtj9_device_power_off(tj9);
@@ -537,7 +549,7 @@ static int __devinit kxtj9_probe(struct i2c_client *client,
i2c_set_clientdata(client, tj9);
tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
- tj9->data_ctrl = tj9->pdata.data_odr_init;
+ tj9->last_poll_interval = tj9->pdata.init_interval;
if (client->irq) {
/* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
@@ -655,17 +667,7 @@ static struct i2c_driver kxtj9_driver = {
.id_table = kxtj9_id,
};
-static int __init kxtj9_init(void)
-{
- return i2c_add_driver(&kxtj9_driver);
-}
-module_init(kxtj9_init);
-
-static void __exit kxtj9_exit(void)
-{
- i2c_del_driver(&kxtj9_driver);
-}
-module_exit(kxtj9_exit);
+module_i2c_driver(kxtj9_driver);
MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c
index 23cf08271049..0a12b74140d3 100644
--- a/drivers/input/misc/max8925_onkey.c
+++ b/drivers/input/misc/max8925_onkey.c
@@ -1,5 +1,5 @@
/**
- * max8925_onkey.c - MAX8925 ONKEY driver
+ * MAX8925 ONKEY driver
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
@@ -35,7 +35,7 @@ struct max8925_onkey_info {
struct input_dev *idev;
struct i2c_client *i2c;
struct device *dev;
- int irq[2];
+ unsigned int irq[2];
};
/*
@@ -46,17 +46,14 @@ struct max8925_onkey_info {
static irqreturn_t max8925_onkey_handler(int irq, void *data)
{
struct max8925_onkey_info *info = data;
- int ret, event;
-
- ret = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS);
- if (ret & SW_INPUT)
- event = 1;
- else
- event = 0;
- input_report_key(info->idev, KEY_POWER, event);
+ int state;
+
+ state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS);
+
+ input_report_key(info->idev, KEY_POWER, state & SW_INPUT);
input_sync(info->idev);
- dev_dbg(info->dev, "onkey event:%d\n", event);
+ dev_dbg(info->dev, "onkey state:%d\n", state);
/* Enable hardreset to halt if system isn't shutdown on time */
max8925_set_bits(info->i2c, MAX8925_SYSENSEL,
@@ -69,6 +66,7 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev)
{
struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct max8925_onkey_info *info;
+ struct input_dev *input;
int irq[2], error;
irq[0] = platform_get_irq(pdev, 0);
@@ -76,6 +74,7 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "No IRQ resource!\n");
return -EINVAL;
}
+
irq[1] = platform_get_irq(pdev, 1);
if (irq[1] < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
@@ -83,11 +82,24 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev)
}
info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
+ input = input_allocate_device();
+ if (!info || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+ info->idev = input;
info->i2c = chip->i2c;
info->dev = &pdev->dev;
+ info->irq[0] = irq[0];
+ info->irq[1] = irq[1];
+
+ input->name = "max8925_on";
+ input->phys = "max8925_on/input0";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &pdev->dev;
+ input_set_capability(input, EV_KEY, KEY_POWER);
+
irq[0] += chip->irq_base;
irq[1] += chip->irq_base;
@@ -96,60 +108,46 @@ static int __devinit max8925_onkey_probe(struct platform_device *pdev)
if (error < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
irq[0], error);
- goto out;
+ goto err_free_mem;
}
+
error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler,
IRQF_ONESHOT, "onkey-up", info);
if (error < 0) {
dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
irq[1], error);
- goto out_irq;
+ goto err_free_irq0;
}
- info->idev = input_allocate_device();
- if (!info->idev) {
- dev_err(chip->dev, "Failed to allocate input dev\n");
- error = -ENOMEM;
- goto out_input;
- }
-
- info->idev->name = "max8925_on";
- info->idev->phys = "max8925_on/input0";
- info->idev->id.bustype = BUS_I2C;
- info->idev->dev.parent = &pdev->dev;
- info->irq[0] = irq[0];
- info->irq[1] = irq[1];
- info->idev->evbit[0] = BIT_MASK(EV_KEY);
- info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
-
-
error = input_register_device(info->idev);
if (error) {
dev_err(chip->dev, "Can't register input device: %d\n", error);
- goto out_reg;
+ goto err_free_irq1;
}
platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
return 0;
-out_reg:
- input_free_device(info->idev);
-out_input:
- free_irq(info->irq[1], info);
-out_irq:
- free_irq(info->irq[0], info);
-out:
+err_free_irq1:
+ free_irq(irq[1], info);
+err_free_irq0:
+ free_irq(irq[0], info);
+err_free_mem:
+ input_free_device(input);
kfree(info);
+
return error;
}
static int __devexit max8925_onkey_remove(struct platform_device *pdev)
{
struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
- free_irq(info->irq[0], info);
- free_irq(info->irq[1], info);
+ free_irq(info->irq[0] + chip->irq_base, info);
+ free_irq(info->irq[1] + chip->irq_base, info);
input_unregister_device(info->idev);
kfree(info);
@@ -158,10 +156,43 @@ static int __devexit max8925_onkey_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int max8925_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev)) {
+ chip->wakeup_flag |= 1 << info->irq[0];
+ chip->wakeup_flag |= 1 << info->irq[1];
+ }
+
+ return 0;
+}
+
+static int max8925_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev)) {
+ chip->wakeup_flag &= ~(1 << info->irq[0]);
+ chip->wakeup_flag &= ~(1 << info->irq[1]);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume);
+
static struct platform_driver max8925_onkey_driver = {
.driver = {
.name = "max8925-onkey",
.owner = THIS_MODULE,
+ .pm = &max8925_onkey_pm_ops,
},
.probe = max8925_onkey_probe,
.remove = __devexit_p(max8925_onkey_remove),
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
new file mode 100644
index 000000000000..05b7b8bfaf0a
--- /dev/null
+++ b/drivers/input/misc/max8997_haptic.c
@@ -0,0 +1,407 @@
+/*
+ * MAX8997-haptic controller driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/input.h>
+#include <linux/mfd/max8997-private.h>
+#include <linux/mfd/max8997.h>
+#include <linux/regulator/consumer.h>
+
+/* Haptic configuration 2 register */
+#define MAX8997_MOTOR_TYPE_SHIFT 7
+#define MAX8997_ENABLE_SHIFT 6
+#define MAX8997_MODE_SHIFT 5
+
+/* Haptic driver configuration register */
+#define MAX8997_CYCLE_SHIFT 6
+#define MAX8997_SIG_PERIOD_SHIFT 4
+#define MAX8997_SIG_DUTY_SHIFT 2
+#define MAX8997_PWM_DUTY_SHIFT 0
+
+struct max8997_haptic {
+ struct device *dev;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct regulator *regulator;
+
+ struct work_struct work;
+ struct mutex mutex;
+
+ bool enabled;
+ unsigned int level;
+
+ struct pwm_device *pwm;
+ unsigned int pwm_period;
+ enum max8997_haptic_pwm_divisor pwm_divisor;
+
+ enum max8997_haptic_motor_type type;
+ enum max8997_haptic_pulse_mode mode;
+
+ unsigned int internal_mode_pattern;
+ unsigned int pattern_cycle;
+ unsigned int pattern_signal_period;
+};
+
+static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
+{
+ int ret = 0;
+
+ if (chip->mode == MAX8997_EXTERNAL_MODE) {
+ unsigned int duty = chip->pwm_period * chip->level / 100;
+ ret = pwm_config(chip->pwm, duty, chip->pwm_period);
+ } else {
+ int i;
+ u8 duty_index = 0;
+
+ for (i = 0; i <= 64; i++) {
+ if (chip->level <= i * 100 / 64) {
+ duty_index = i;
+ break;
+ }
+ }
+ switch (chip->internal_mode_pattern) {
+ case 0:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
+ break;
+ case 1:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
+ break;
+ case 2:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
+ break;
+ case 3:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
+ break;
+ default:
+ break;
+ }
+ }
+ return ret;
+}
+
+static void max8997_haptic_configure(struct max8997_haptic *chip)
+{
+ u8 value;
+
+ value = chip->type << MAX8997_MOTOR_TYPE_SHIFT |
+ chip->enabled << MAX8997_ENABLE_SHIFT |
+ chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor;
+ max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value);
+
+ if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) {
+ value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT |
+ chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT |
+ chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT |
+ chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_DRVCONF, value);
+
+ switch (chip->internal_mode_pattern) {
+ case 0:
+ value = chip->pattern_cycle << 4;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF1, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF1, value);
+ break;
+
+ case 1:
+ value = chip->pattern_cycle;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF1, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF2, value);
+ break;
+
+ case 2:
+ value = chip->pattern_cycle << 4;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF2, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF3, value);
+ break;
+
+ case 3:
+ value = chip->pattern_cycle;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF2, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF4, value);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void max8997_haptic_enable(struct max8997_haptic *chip)
+{
+ int error;
+
+ mutex_lock(&chip->mutex);
+
+ error = max8997_haptic_set_duty_cycle(chip);
+ if (error) {
+ dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error);
+ goto out;
+ }
+
+ if (!chip->enabled) {
+ chip->enabled = true;
+ regulator_enable(chip->regulator);
+ max8997_haptic_configure(chip);
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_enable(chip->pwm);
+ }
+
+out:
+ mutex_unlock(&chip->mutex);
+}
+
+static void max8997_haptic_disable(struct max8997_haptic *chip)
+{
+ mutex_lock(&chip->mutex);
+
+ if (chip->enabled) {
+ chip->enabled = false;
+ max8997_haptic_configure(chip);
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_disable(chip->pwm);
+ regulator_disable(chip->regulator);
+ }
+
+ mutex_unlock(&chip->mutex);
+}
+
+static void max8997_haptic_play_effect_work(struct work_struct *work)
+{
+ struct max8997_haptic *chip =
+ container_of(work, struct max8997_haptic, work);
+
+ if (chip->level)
+ max8997_haptic_enable(chip);
+ else
+ max8997_haptic_disable(chip);
+}
+
+static int max8997_haptic_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct max8997_haptic *chip = input_get_drvdata(dev);
+
+ chip->level = effect->u.rumble.strong_magnitude;
+ if (!chip->level)
+ chip->level = effect->u.rumble.weak_magnitude;
+
+ schedule_work(&chip->work);
+
+ return 0;
+}
+
+static void max8997_haptic_close(struct input_dev *dev)
+{
+ struct max8997_haptic *chip = input_get_drvdata(dev);
+
+ cancel_work_sync(&chip->work);
+ max8997_haptic_disable(chip);
+}
+
+static int __devinit max8997_haptic_probe(struct platform_device *pdev)
+{
+ struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ const struct max8997_platform_data *pdata =
+ dev_get_platdata(iodev->dev);
+ const struct max8997_haptic_platform_data *haptic_pdata =
+ pdata->haptic_pdata;
+ struct max8997_haptic *chip;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!haptic_pdata) {
+ dev_err(&pdev->dev, "no haptic platform data\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!chip || !input_dev) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ INIT_WORK(&chip->work, max8997_haptic_play_effect_work);
+ mutex_init(&chip->mutex);
+
+ chip->client = iodev->haptic;
+ chip->dev = &pdev->dev;
+ chip->input_dev = input_dev;
+ chip->pwm_period = haptic_pdata->pwm_period;
+ chip->type = haptic_pdata->type;
+ chip->mode = haptic_pdata->mode;
+ chip->pwm_divisor = haptic_pdata->pwm_divisor;
+
+ switch (chip->mode) {
+ case MAX8997_INTERNAL_MODE:
+ chip->internal_mode_pattern =
+ haptic_pdata->internal_mode_pattern;
+ chip->pattern_cycle = haptic_pdata->pattern_cycle;
+ chip->pattern_signal_period =
+ haptic_pdata->pattern_signal_period;
+ break;
+
+ case MAX8997_EXTERNAL_MODE:
+ chip->pwm = pwm_request(haptic_pdata->pwm_channel_id,
+ "max8997-haptic");
+ if (IS_ERR(chip->pwm)) {
+ error = PTR_ERR(chip->pwm);
+ dev_err(&pdev->dev,
+ "unable to request PWM for haptic, error: %d\n",
+ error);
+ goto err_free_mem;
+ }
+ break;
+
+ default:
+ dev_err(&pdev->dev,
+ "Invalid chip mode specified (%d)\n", chip->mode);
+ error = -EINVAL;
+ goto err_free_mem;
+ }
+
+ chip->regulator = regulator_get(&pdev->dev, "inmotor");
+ if (IS_ERR(chip->regulator)) {
+ error = PTR_ERR(chip->regulator);
+ dev_err(&pdev->dev,
+ "unable to get regulator, error: %d\n",
+ error);
+ goto err_free_pwm;
+ }
+
+ input_dev->name = "max8997-haptic";
+ input_dev->id.version = 1;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->close = max8997_haptic_close;
+ input_set_drvdata(input_dev, chip);
+ input_set_capability(input_dev, EV_FF, FF_RUMBLE);
+
+ error = input_ff_create_memless(input_dev, NULL,
+ max8997_haptic_play_effect);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to create FF device, error: %d\n",
+ error);
+ goto err_put_regulator;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device, error: %d\n",
+ error);
+ goto err_destroy_ff;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ return 0;
+
+err_destroy_ff:
+ input_ff_destroy(input_dev);
+err_put_regulator:
+ regulator_put(chip->regulator);
+err_free_pwm:
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_free(chip->pwm);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(chip);
+
+ return error;
+}
+
+static int __devexit max8997_haptic_remove(struct platform_device *pdev)
+{
+ struct max8997_haptic *chip = platform_get_drvdata(pdev);
+
+ input_unregister_device(chip->input_dev);
+ regulator_put(chip->regulator);
+
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_free(chip->pwm);
+
+ kfree(chip);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max8997_haptic_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8997_haptic *chip = platform_get_drvdata(pdev);
+
+ max8997_haptic_disable(chip);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL);
+
+static const struct platform_device_id max8997_haptic_id[] = {
+ { "max8997-haptic", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max8997_haptic_id);
+
+static struct platform_driver max8997_haptic_driver = {
+ .driver = {
+ .name = "max8997-haptic",
+ .owner = THIS_MODULE,
+ .pm = &max8997_haptic_pm_ops,
+ },
+ .probe = max8997_haptic_probe,
+ .remove = __devexit_p(max8997_haptic_remove),
+ .id_table = max8997_haptic_id,
+};
+module_platform_driver(max8997_haptic_driver);
+
+MODULE_ALIAS("platform:max8997-haptic");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_DESCRIPTION("max8997_haptic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
index 4d60080bb5d5..873ebced544e 100644
--- a/drivers/input/misc/mma8450.c
+++ b/drivers/input/misc/mma8450.c
@@ -247,17 +247,7 @@ static struct i2c_driver mma8450_driver = {
.id_table = mma8450_id,
};
-static int __init mma8450_init(void)
-{
- return i2c_add_driver(&mma8450_driver);
-}
-module_init(mma8450_init);
-
-static void __exit mma8450_exit(void)
-{
- i2c_del_driver(&mma8450_driver);
-}
-module_exit(mma8450_exit);
+module_i2c_driver(mma8450_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver");
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 208d1a1cc7f3..5403c571b6a5 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -475,17 +475,7 @@ static struct i2c_driver mpu3050_i2c_driver = {
.id_table = mpu3050_ids,
};
-static int __init mpu3050_init(void)
-{
- return i2c_add_driver(&mpu3050_i2c_driver);
-}
-module_init(mpu3050_init);
-
-static void __exit mpu3050_exit(void)
-{
- i2c_del_driver(&mpu3050_i2c_driver);
-}
-module_exit(mpu3050_exit);
+module_i2c_driver(mpu3050_i2c_driver);
MODULE_AUTHOR("Wistron Corp.");
MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver");
diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
index 08be1a355956..544c6635abe9 100644
--- a/drivers/input/misc/pcf8574_keypad.c
+++ b/drivers/input/misc/pcf8574_keypad.c
@@ -216,17 +216,7 @@ static struct i2c_driver pcf8574_kp_driver = {
.id_table = pcf8574_kp_id,
};
-static int __init pcf8574_kp_init(void)
-{
- return i2c_add_driver(&pcf8574_kp_driver);
-}
-module_init(pcf8574_kp_init);
-
-static void __exit pcf8574_kp_exit(void)
-{
- i2c_del_driver(&pcf8574_kp_driver);
-}
-module_exit(pcf8574_kp_exit);
+module_i2c_driver(pcf8574_kp_driver);
MODULE_AUTHOR("Michael Hennerich");
MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
index f3bc4189a7ba..fc0ed9b43424 100644
--- a/drivers/input/misc/twl4030-vibra.c
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -172,7 +172,7 @@ static void twl4030_vibra_close(struct input_dev *input)
}
/*** Module ***/
-#if CONFIG_PM_SLEEP
+#ifdef CONFIG_PM_SLEEP
static int twl4030_vibra_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 9c1e6ee83531..9b8db821d5f0 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -322,4 +322,21 @@ config MOUSE_SYNAPTICS_I2C
To compile this driver as a module, choose M here: the
module will be called synaptics_i2c.
+config MOUSE_SYNAPTICS_USB
+ tristate "Synaptics USB device support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Synaptics USB touchpad or pointing
+ stick.
+
+ While these devices emulate an USB mouse by default and can be used
+ with standard usbhid driver, this driver, together with its X.Org
+ counterpart, allows you to fully utilize capabilities of the device.
+ More information can be found at:
+ <http://jan-steinhoff.de/linux/synaptics-usb.html>
+
+ To compile this driver as a module, choose M here: the
+ module will be called synaptics_usb.
+
endif
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 570c84a4a654..4718effeb8d9 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
+obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
psmouse-objs := psmouse-base.o synaptics.o
diff --git a/drivers/input/mouse/amimouse.c b/drivers/input/mouse/amimouse.c
index ff5f61a0fd3a..5fa99341a39d 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 5c4a692bf73a..d1c43236b125 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/bcm5974.c b/drivers/input/mouse/bcm5974.c
index 927e479c2649..f9e2758b9f46 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -433,6 +433,7 @@ static void setup_events_to_report(struct input_dev *input_dev,
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
__set_bit(BTN_LEFT, input_dev->keybit);
+ __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
if (cfg->caps & HAS_INTEGRATED_BUTTON)
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
diff --git a/drivers/input/mouse/hgpk.c b/drivers/input/mouse/hgpk.c
index 1c5d521de600..575f880727fe 100644
--- a/drivers/input/mouse/hgpk.c
+++ b/drivers/input/mouse/hgpk.c
@@ -640,7 +640,6 @@ static int hgpk_reset_device(struct psmouse *psmouse, bool recalibrate)
static int hgpk_force_recalibrate(struct psmouse *psmouse)
{
- struct ps2dev *ps2dev = &psmouse->ps2dev;
struct hgpk_data *priv = psmouse->private;
int err;
@@ -669,12 +668,9 @@ static int hgpk_force_recalibrate(struct psmouse *psmouse)
* we don't have a good way to deal with it. The 2s window stuff
* (below) is our best option for now.
*/
-
- if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+ if (psmouse_activate(psmouse))
return -1;
- psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
-
if (tpdebug)
psmouse_dbg(psmouse, "touchpad reactivated\n");
@@ -733,8 +729,7 @@ static int hgpk_toggle_powersave(struct psmouse *psmouse, int enable)
}
/* should be all set, enable the touchpad */
- ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE);
- psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+ psmouse_activate(psmouse);
psmouse_dbg(psmouse, "Touchpad powered up.\n");
} else {
psmouse_dbg(psmouse, "Powering off touchpad.\n");
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index e6c9931f02c7..22fe2547e169 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -1092,28 +1092,33 @@ static void psmouse_initialize(struct psmouse *psmouse)
* psmouse_activate() enables the mouse so that we get motion reports from it.
*/
-static void psmouse_activate(struct psmouse *psmouse)
+int psmouse_activate(struct psmouse *psmouse)
{
- if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE))
+ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
psmouse_warn(psmouse, "Failed to enable mouse on %s\n",
psmouse->ps2dev.serio->phys);
+ return -1;
+ }
psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+ return 0;
}
-
/*
* psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
* reports from it unless we explicitly request it.
*/
-static void psmouse_deactivate(struct psmouse *psmouse)
+int psmouse_deactivate(struct psmouse *psmouse)
{
- if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE))
+ if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
psmouse_warn(psmouse, "Failed to deactivate mouse on %s\n",
psmouse->ps2dev.serio->phys);
+ return -1;
+ }
psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ return 0;
}
diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h
index 6a417092d010..fe1df231ba4c 100644
--- a/drivers/input/mouse/psmouse.h
+++ b/drivers/input/mouse/psmouse.h
@@ -105,6 +105,8 @@ int psmouse_reset(struct psmouse *psmouse);
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state);
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution);
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse);
+int psmouse_activate(struct psmouse *psmouse);
+int psmouse_deactivate(struct psmouse *psmouse);
struct psmouse_attribute {
struct device_attribute dattr;
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index e36847de7617..2a77a52d2e62 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -90,8 +90,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
* to do that for writes because sysfs set helper does this for
* us.
*/
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_deactivate(psmouse);
ps2_begin_command(ps2dev);
@@ -128,8 +127,7 @@ static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
out:
ps2_end_command(ps2dev);
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
- psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+ psmouse_activate(psmouse);
dev_dbg(&ps2dev->serio->dev, "READ REG: 0x%02x is 0x%02x (rc = %d)\n",
reg_addr, *reg_val, rc);
return rc;
@@ -213,8 +211,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
unsigned char param[3];
int rc = -1;
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE);
- psmouse_set_state(psmouse, PSMOUSE_CMD_MODE);
+ psmouse_deactivate(psmouse);
ps2_begin_command(ps2dev);
@@ -239,8 +236,7 @@ static int fsp_page_reg_read(struct psmouse *psmouse, int *reg_val)
out:
ps2_end_command(ps2dev);
- ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE);
- psmouse_set_state(psmouse, PSMOUSE_ACTIVATED);
+ psmouse_activate(psmouse);
dev_dbg(&ps2dev->serio->dev, "READ PAGE REG: 0x%02x (rc = %d)\n",
*reg_val, rc);
return rc;
diff --git a/drivers/input/mouse/synaptics_i2c.c b/drivers/input/mouse/synaptics_i2c.c
index 1c58aafa523f..f14675702c0f 100644
--- a/drivers/input/mouse/synaptics_i2c.c
+++ b/drivers/input/mouse/synaptics_i2c.c
@@ -672,18 +672,7 @@ static struct i2c_driver synaptics_i2c_driver = {
.id_table = synaptics_i2c_id_table,
};
-static int __init synaptics_i2c_init(void)
-{
- return i2c_add_driver(&synaptics_i2c_driver);
-}
-
-static void __exit synaptics_i2c_exit(void)
-{
- i2c_del_driver(&synaptics_i2c_driver);
-}
-
-module_init(synaptics_i2c_init);
-module_exit(synaptics_i2c_exit);
+module_i2c_driver(synaptics_i2c_driver);
MODULE_DESCRIPTION("Synaptics I2C touchpad driver");
MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab");
diff --git a/drivers/input/mouse/synaptics_usb.c b/drivers/input/mouse/synaptics_usb.c
new file mode 100644
index 000000000000..3c5eaaa5d154
--- /dev/null
+++ b/drivers/input/mouse/synaptics_usb.c
@@ -0,0 +1,557 @@
+/*
+ * USB Synaptics device driver
+ *
+ * Copyright (c) 2002 Rob Miller (rob@inpharmatica . co . uk)
+ * Copyright (c) 2003 Ron Lee (ron@debian.org)
+ * cPad driver for kernel 2.4
+ *
+ * Copyright (c) 2004 Jan Steinhoff (cpad@jan-steinhoff . de)
+ * Copyright (c) 2004 Ron Lee (ron@debian.org)
+ * rewritten for kernel 2.6
+ *
+ * cPad display character device part is not included. It can be found at
+ * http://jan-steinhoff.de/linux/synaptics-usb.html
+ *
+ * Bases on: usb_skeleton.c v2.2 by Greg Kroah-Hartman
+ * drivers/hid/usbhid/usbmouse.c by Vojtech Pavlik
+ * drivers/input/mouse/synaptics.c by Peter Osterlund
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+/*
+ * There are three different types of Synaptics USB devices: Touchpads,
+ * touchsticks (or trackpoints), and touchscreens. Touchpads are well supported
+ * by this driver, touchstick support has not been tested much yet, and
+ * touchscreens have not been tested at all.
+ *
+ * Up to three alternate settings are possible:
+ * setting 0: one int endpoint for relative movement (used by usbhid.ko)
+ * setting 1: one int endpoint for absolute finger position
+ * setting 2 (cPad only): one int endpoint for absolute finger position and
+ * two bulk endpoints for the display (in/out)
+ * This driver uses setting 1.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/usb.h>
+#include <linux/input.h>
+#include <linux/usb/input.h>
+
+#define USB_VENDOR_ID_SYNAPTICS 0x06cb
+#define USB_DEVICE_ID_SYNAPTICS_TP 0x0001 /* Synaptics USB TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_INT_TP 0x0002 /* Integrated USB TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_CPAD 0x0003 /* Synaptics cPad */
+#define USB_DEVICE_ID_SYNAPTICS_TS 0x0006 /* Synaptics TouchScreen */
+#define USB_DEVICE_ID_SYNAPTICS_STICK 0x0007 /* Synaptics USB Styk */
+#define USB_DEVICE_ID_SYNAPTICS_WP 0x0008 /* Synaptics USB WheelPad */
+#define USB_DEVICE_ID_SYNAPTICS_COMP_TP 0x0009 /* Composite USB TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_WTP 0x0010 /* Wireless TouchPad */
+#define USB_DEVICE_ID_SYNAPTICS_DPAD 0x0013 /* DisplayPad */
+
+#define SYNUSB_TOUCHPAD (1 << 0)
+#define SYNUSB_STICK (1 << 1)
+#define SYNUSB_TOUCHSCREEN (1 << 2)
+#define SYNUSB_AUXDISPLAY (1 << 3) /* For cPad */
+#define SYNUSB_COMBO (1 << 4) /* Composite device (TP + stick) */
+#define SYNUSB_IO_ALWAYS (1 << 5)
+
+#define USB_DEVICE_SYNAPTICS(prod, kind) \
+ USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, \
+ USB_DEVICE_ID_SYNAPTICS_##prod), \
+ .driver_info = (kind),
+
+#define SYNUSB_RECV_SIZE 8
+
+#define XMIN_NOMINAL 1472
+#define XMAX_NOMINAL 5472
+#define YMIN_NOMINAL 1408
+#define YMAX_NOMINAL 4448
+
+struct synusb {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct urb *urb;
+ unsigned char *data;
+
+ /* input device related data structures */
+ struct input_dev *input;
+ char name[128];
+ char phys[64];
+
+ /* characteristics of the device */
+ unsigned long flags;
+};
+
+static void synusb_report_buttons(struct synusb *synusb)
+{
+ struct input_dev *input_dev = synusb->input;
+
+ input_report_key(input_dev, BTN_LEFT, synusb->data[1] & 0x04);
+ input_report_key(input_dev, BTN_RIGHT, synusb->data[1] & 0x01);
+ input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x02);
+}
+
+static void synusb_report_stick(struct synusb *synusb)
+{
+ struct input_dev *input_dev = synusb->input;
+ int x, y;
+ unsigned int pressure;
+
+ pressure = synusb->data[6];
+ x = (s16)(be16_to_cpup((__be16 *)&synusb->data[2]) << 3) >> 7;
+ y = (s16)(be16_to_cpup((__be16 *)&synusb->data[4]) << 3) >> 7;
+
+ if (pressure > 0) {
+ input_report_rel(input_dev, REL_X, x);
+ input_report_rel(input_dev, REL_Y, -y);
+ }
+
+ input_report_abs(input_dev, ABS_PRESSURE, pressure);
+
+ synusb_report_buttons(synusb);
+
+ input_sync(input_dev);
+}
+
+static void synusb_report_touchpad(struct synusb *synusb)
+{
+ struct input_dev *input_dev = synusb->input;
+ unsigned int num_fingers, tool_width;
+ unsigned int x, y;
+ unsigned int pressure, w;
+
+ pressure = synusb->data[6];
+ x = be16_to_cpup((__be16 *)&synusb->data[2]);
+ y = be16_to_cpup((__be16 *)&synusb->data[4]);
+ w = synusb->data[0] & 0x0f;
+
+ if (pressure > 0) {
+ num_fingers = 1;
+ tool_width = 5;
+ switch (w) {
+ case 0 ... 1:
+ num_fingers = 2 + w;
+ break;
+
+ case 2: /* pen, pretend its a finger */
+ break;
+
+ case 4 ... 15:
+ tool_width = w;
+ break;
+ }
+ } else {
+ num_fingers = 0;
+ tool_width = 0;
+ }
+
+ /*
+ * Post events
+ * BTN_TOUCH has to be first as mousedev relies on it when doing
+ * absolute -> relative conversion
+ */
+
+ if (pressure > 30)
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ if (pressure < 25)
+ input_report_key(input_dev, BTN_TOUCH, 0);
+
+ if (num_fingers > 0) {
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y,
+ YMAX_NOMINAL + YMIN_NOMINAL - y);
+ }
+
+ input_report_abs(input_dev, ABS_PRESSURE, pressure);
+ input_report_abs(input_dev, ABS_TOOL_WIDTH, tool_width);
+
+ input_report_key(input_dev, BTN_TOOL_FINGER, num_fingers == 1);
+ input_report_key(input_dev, BTN_TOOL_DOUBLETAP, num_fingers == 2);
+ input_report_key(input_dev, BTN_TOOL_TRIPLETAP, num_fingers == 3);
+
+ synusb_report_buttons(synusb);
+ if (synusb->flags & SYNUSB_AUXDISPLAY)
+ input_report_key(input_dev, BTN_MIDDLE, synusb->data[1] & 0x08);
+
+ input_sync(input_dev);
+}
+
+static void synusb_irq(struct urb *urb)
+{
+ struct synusb *synusb = urb->context;
+ int error;
+
+ /* Check our status in case we need to bail out early. */
+ switch (urb->status) {
+ case 0:
+ usb_mark_last_busy(synusb->udev);
+ break;
+
+ /* Device went away so don't keep trying to read from it. */
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ goto resubmit;
+ break;
+ }
+
+ if (synusb->flags & SYNUSB_STICK)
+ synusb_report_stick(synusb);
+ else
+ synusb_report_touchpad(synusb);
+
+resubmit:
+ error = usb_submit_urb(urb, GFP_ATOMIC);
+ if (error && error != -EPERM)
+ dev_err(&synusb->intf->dev,
+ "%s - usb_submit_urb failed with result: %d",
+ __func__, error);
+}
+
+static struct usb_endpoint_descriptor *
+synusb_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
+static int synusb_open(struct input_dev *dev)
+{
+ struct synusb *synusb = input_get_drvdata(dev);
+ int retval;
+
+ retval = usb_autopm_get_interface(synusb->intf);
+ if (retval) {
+ dev_err(&synusb->intf->dev,
+ "%s - usb_autopm_get_interface failed, error: %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ retval = usb_submit_urb(synusb->urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&synusb->intf->dev,
+ "%s - usb_submit_urb failed, error: %d\n",
+ __func__, retval);
+ retval = -EIO;
+ goto out;
+ }
+
+ synusb->intf->needs_remote_wakeup = 1;
+
+out:
+ usb_autopm_put_interface(synusb->intf);
+ return retval;
+}
+
+static void synusb_close(struct input_dev *dev)
+{
+ struct synusb *synusb = input_get_drvdata(dev);
+ int autopm_error;
+
+ autopm_error = usb_autopm_get_interface(synusb->intf);
+
+ usb_kill_urb(synusb->urb);
+ synusb->intf->needs_remote_wakeup = 0;
+
+ if (!autopm_error)
+ usb_autopm_put_interface(synusb->intf);
+}
+
+static int synusb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *ep;
+ struct synusb *synusb;
+ struct input_dev *input_dev;
+ unsigned int intf_num = intf->cur_altsetting->desc.bInterfaceNumber;
+ unsigned int altsetting = min(intf->num_altsetting, 1U);
+ int error;
+
+ error = usb_set_interface(udev, intf_num, altsetting);
+ if (error) {
+ dev_err(&udev->dev,
+ "Can not set alternate setting to %i, error: %i",
+ altsetting, error);
+ return error;
+ }
+
+ ep = synusb_get_in_endpoint(intf->cur_altsetting);
+ if (!ep)
+ return -ENODEV;
+
+ synusb = kzalloc(sizeof(*synusb), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!synusb || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ synusb->udev = udev;
+ synusb->intf = intf;
+ synusb->input = input_dev;
+
+ synusb->flags = id->driver_info;
+ if (synusb->flags & SYNUSB_COMBO) {
+ /*
+ * This is a combo device, we need to set proper
+ * capability, depending on the interface.
+ */
+ synusb->flags |= intf_num == 1 ?
+ SYNUSB_STICK : SYNUSB_TOUCHPAD;
+ }
+
+ synusb->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!synusb->urb) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ synusb->data = usb_alloc_coherent(udev, SYNUSB_RECV_SIZE, GFP_KERNEL,
+ &synusb->urb->transfer_dma);
+ if (!synusb->data) {
+ error = -ENOMEM;
+ goto err_free_urb;
+ }
+
+ usb_fill_int_urb(synusb->urb, udev,
+ usb_rcvintpipe(udev, ep->bEndpointAddress),
+ synusb->data, SYNUSB_RECV_SIZE,
+ synusb_irq, synusb,
+ ep->bInterval);
+ synusb->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ if (udev->manufacturer)
+ strlcpy(synusb->name, udev->manufacturer,
+ sizeof(synusb->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(synusb->name, " ", sizeof(synusb->name));
+ strlcat(synusb->name, udev->product, sizeof(synusb->name));
+ }
+
+ if (!strlen(synusb->name))
+ snprintf(synusb->name, sizeof(synusb->name),
+ "USB Synaptics Device %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ if (synusb->flags & SYNUSB_STICK)
+ strlcat(synusb->name, " (Stick) ", sizeof(synusb->name));
+
+ usb_make_path(udev, synusb->phys, sizeof(synusb->phys));
+ strlcat(synusb->phys, "/input0", sizeof(synusb->phys));
+
+ input_dev->name = synusb->name;
+ input_dev->phys = synusb->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &synusb->intf->dev;
+
+ if (!(synusb->flags & SYNUSB_IO_ALWAYS)) {
+ input_dev->open = synusb_open;
+ input_dev->close = synusb_close;
+ }
+
+ input_set_drvdata(input_dev, synusb);
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(EV_KEY, input_dev->evbit);
+
+ if (synusb->flags & SYNUSB_STICK) {
+ __set_bit(EV_REL, input_dev->evbit);
+ __set_bit(REL_X, input_dev->relbit);
+ __set_bit(REL_Y, input_dev->relbit);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 127, 0, 0);
+ } else {
+ input_set_abs_params(input_dev, ABS_X,
+ XMIN_NOMINAL, XMAX_NOMINAL, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ YMIN_NOMINAL, YMAX_NOMINAL, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+ input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 15, 0, 0);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+ __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
+ }
+
+ __set_bit(BTN_LEFT, input_dev->keybit);
+ __set_bit(BTN_RIGHT, input_dev->keybit);
+ __set_bit(BTN_MIDDLE, input_dev->keybit);
+
+ usb_set_intfdata(intf, synusb);
+
+ if (synusb->flags & SYNUSB_IO_ALWAYS) {
+ error = synusb_open(input_dev);
+ if (error)
+ goto err_free_dma;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&udev->dev,
+ "Failed to register input device, error %d\n",
+ error);
+ goto err_stop_io;
+ }
+
+ return 0;
+
+err_stop_io:
+ if (synusb->flags & SYNUSB_IO_ALWAYS)
+ synusb_close(synusb->input);
+err_free_dma:
+ usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
+ synusb->urb->transfer_dma);
+err_free_urb:
+ usb_free_urb(synusb->urb);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(synusb);
+ usb_set_intfdata(intf, NULL);
+
+ return error;
+}
+
+static void synusb_disconnect(struct usb_interface *intf)
+{
+ struct synusb *synusb = usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ if (synusb->flags & SYNUSB_IO_ALWAYS)
+ synusb_close(synusb->input);
+
+ input_unregister_device(synusb->input);
+
+ usb_free_coherent(udev, SYNUSB_RECV_SIZE, synusb->data,
+ synusb->urb->transfer_dma);
+ usb_free_urb(synusb->urb);
+ kfree(synusb);
+
+ usb_set_intfdata(intf, NULL);
+}
+
+static int synusb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct synusb *synusb = usb_get_intfdata(intf);
+ struct input_dev *input_dev = synusb->input;
+
+ mutex_lock(&input_dev->mutex);
+ usb_kill_urb(synusb->urb);
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+
+static int synusb_resume(struct usb_interface *intf)
+{
+ struct synusb *synusb = usb_get_intfdata(intf);
+ struct input_dev *input_dev = synusb->input;
+ int retval = 0;
+
+ mutex_lock(&input_dev->mutex);
+
+ if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
+ usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
+ retval = -EIO;
+ }
+
+ mutex_unlock(&input_dev->mutex);
+
+ return retval;
+}
+
+static int synusb_pre_reset(struct usb_interface *intf)
+{
+ struct synusb *synusb = usb_get_intfdata(intf);
+ struct input_dev *input_dev = synusb->input;
+
+ mutex_lock(&input_dev->mutex);
+ usb_kill_urb(synusb->urb);
+
+ return 0;
+}
+
+static int synusb_post_reset(struct usb_interface *intf)
+{
+ struct synusb *synusb = usb_get_intfdata(intf);
+ struct input_dev *input_dev = synusb->input;
+ int retval = 0;
+
+ if ((input_dev->users || (synusb->flags & SYNUSB_IO_ALWAYS)) &&
+ usb_submit_urb(synusb->urb, GFP_NOIO) < 0) {
+ retval = -EIO;
+ }
+
+ mutex_unlock(&input_dev->mutex);
+
+ return retval;
+}
+
+static int synusb_reset_resume(struct usb_interface *intf)
+{
+ return synusb_resume(intf);
+}
+
+static struct usb_device_id synusb_idtable[] = {
+ { USB_DEVICE_SYNAPTICS(TP, SYNUSB_TOUCHPAD) },
+ { USB_DEVICE_SYNAPTICS(INT_TP, SYNUSB_TOUCHPAD) },
+ { USB_DEVICE_SYNAPTICS(CPAD,
+ SYNUSB_TOUCHPAD | SYNUSB_AUXDISPLAY | SYNUSB_IO_ALWAYS) },
+ { USB_DEVICE_SYNAPTICS(TS, SYNUSB_TOUCHSCREEN) },
+ { USB_DEVICE_SYNAPTICS(STICK, SYNUSB_STICK) },
+ { USB_DEVICE_SYNAPTICS(WP, SYNUSB_TOUCHPAD) },
+ { USB_DEVICE_SYNAPTICS(COMP_TP, SYNUSB_COMBO) },
+ { USB_DEVICE_SYNAPTICS(WTP, SYNUSB_TOUCHPAD) },
+ { USB_DEVICE_SYNAPTICS(DPAD, SYNUSB_TOUCHPAD) },
+ { }
+};
+MODULE_DEVICE_TABLE(usb, synusb_idtable);
+
+static struct usb_driver synusb_driver = {
+ .name = "synaptics_usb",
+ .probe = synusb_probe,
+ .disconnect = synusb_disconnect,
+ .id_table = synusb_idtable,
+ .suspend = synusb_suspend,
+ .resume = synusb_resume,
+ .pre_reset = synusb_pre_reset,
+ .post_reset = synusb_post_reset,
+ .reset_resume = synusb_reset_resume,
+ .supports_autosuspend = 1,
+};
+
+module_usb_driver(synusb_driver);
+
+MODULE_AUTHOR("Rob Miller <rob@inpharmatica.co.uk>, "
+ "Ron Lee <ron@debian.org>, "
+ "Jan Steinhoff <cpad@jan-steinhoff.de>");
+MODULE_DESCRIPTION("Synaptics USB device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c
new file mode 100644
index 000000000000..061493d57682
--- /dev/null
+++ b/drivers/input/of_keymap.c
@@ -0,0 +1,87 @@
+/*
+ * Helpers for open firmware matrix keyboard bindings
+ *
+ * Copyright (C) 2012 Google, Inc
+ *
+ * Author:
+ * Olof Johansson <olof@lixom.net>
+ *
+ * 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/kernel.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/of.h>
+#include <linux/input/matrix_keypad.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+#include <linux/slab.h>
+
+struct matrix_keymap_data *
+matrix_keyboard_of_fill_keymap(struct device_node *np,
+ const char *propname)
+{
+ struct matrix_keymap_data *kd;
+ u32 *keymap;
+ int proplen, i;
+ const __be32 *prop;
+
+ if (!np)
+ return NULL;
+
+ if (!propname)
+ propname = "linux,keymap";
+
+ prop = of_get_property(np, propname, &proplen);
+ if (!prop)
+ return NULL;
+
+ if (proplen % sizeof(u32)) {
+ pr_warn("Malformed keymap property %s in %s\n",
+ propname, np->full_name);
+ return NULL;
+ }
+
+ kd = kzalloc(sizeof(*kd), GFP_KERNEL);
+ if (!kd)
+ return NULL;
+
+ kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL);
+ if (!kd->keymap) {
+ kfree(kd);
+ return NULL;
+ }
+
+ kd->keymap_size = proplen / sizeof(u32);
+
+ for (i = 0; i < kd->keymap_size; i++) {
+ u32 tmp = be32_to_cpup(prop + i);
+ int key_code, row, col;
+
+ row = (tmp >> 24) & 0xff;
+ col = (tmp >> 16) & 0xff;
+ key_code = tmp & 0xffff;
+ keymap[i] = KEY(row, col, key_code);
+ }
+
+ return kd;
+}
+EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap);
+
+void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd)
+{
+ if (kd) {
+ kfree(kd->keymap);
+ kfree(kd);
+ }
+}
+EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap);
diff --git a/drivers/input/serio/altera_ps2.c b/drivers/input/serio/altera_ps2.c
index 35864c6130bb..cc11f4efe119 100644
--- a/drivers/input/serio/altera_ps2.c
+++ b/drivers/input/serio/altera_ps2.c
@@ -180,8 +180,6 @@ static const struct of_device_id altera_ps2_match[] = {
{},
};
MODULE_DEVICE_TABLE(of, altera_ps2_match);
-#else /* CONFIG_OF */
-#define altera_ps2_match NULL
#endif /* CONFIG_OF */
/*
@@ -193,7 +191,7 @@ static struct platform_driver altera_ps2_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
- .of_match_table = altera_ps2_match,
+ .of_match_table = of_match_ptr(altera_ps2_match),
},
};
module_platform_driver(altera_ps2_driver);
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 8407d5b0ced8..2ffd110bd5bc 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -208,18 +208,7 @@ static struct amba_driver ambakmi_driver = {
.resume = amba_kmi_resume,
};
-static int __init amba_kmi_init(void)
-{
- return amba_driver_register(&ambakmi_driver);
-}
-
-static void __exit amba_kmi_exit(void)
-{
- amba_driver_unregister(&ambakmi_driver);
-}
-
-module_init(amba_kmi_init);
-module_exit(amba_kmi_exit);
+module_amba_driver(ambakmi_driver);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("AMBA KMI controller driver");
diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c
index d4d08bd9205b..bd5b10eeeb40 100644
--- a/drivers/input/serio/ams_delta_serio.c
+++ b/drivers/input/serio/ams_delta_serio.c
@@ -92,8 +92,7 @@ static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id)
static int ams_delta_serio_open(struct serio *serio)
{
/* enable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR,
- AMD_DELTA_LATCH2_KEYBRD_PWR);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1);
return 0;
}
@@ -101,9 +100,32 @@ static int ams_delta_serio_open(struct serio *serio)
static void ams_delta_serio_close(struct serio *serio)
{
/* disable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0);
}
+static const struct gpio ams_delta_gpios[] __initconst_or_module = {
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA,
+ .flags = GPIOF_DIR_IN,
+ .label = "serio-data",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK,
+ .flags = GPIOF_DIR_IN,
+ .label = "serio-clock",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "serio-power",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "serio-dataout",
+ },
+};
+
static int __init ams_delta_serio_init(void)
{
int err;
@@ -123,19 +145,12 @@ static int __init ams_delta_serio_init(void)
strlcpy(ams_delta_serio->phys, "GPIO/serio0",
sizeof(ams_delta_serio->phys));
- err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "serio-data");
+ err = gpio_request_array(ams_delta_gpios,
+ ARRAY_SIZE(ams_delta_gpios));
if (err) {
- pr_err("ams_delta_serio: Couldn't request gpio pin for data\n");
+ pr_err("ams_delta_serio: Couldn't request gpio pins\n");
goto serio;
}
- gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
-
- err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "serio-clock");
- if (err) {
- pr_err("ams_delta_serio: couldn't request gpio pin for clock\n");
- goto gpio_data;
- }
- gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING,
@@ -143,7 +158,7 @@ static int __init ams_delta_serio_init(void)
if (err < 0) {
pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n",
gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK));
- goto gpio_clk;
+ goto gpio;
}
/*
* Since GPIO register handling for keyboard clock pin is performed
@@ -157,10 +172,9 @@ static int __init ams_delta_serio_init(void)
dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name);
return 0;
-gpio_clk:
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
-gpio_data:
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
+gpio:
+ gpio_free_array(ams_delta_gpios,
+ ARRAY_SIZE(ams_delta_gpios));
serio:
kfree(ams_delta_serio);
return err;
@@ -171,7 +185,7 @@ 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);
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
+ gpio_free_array(ams_delta_gpios,
+ ARRAY_SIZE(ams_delta_gpios));
}
module_exit(ams_delta_serio_exit);
diff --git a/drivers/input/serio/at32psif.c b/drivers/input/serio/at32psif.c
index 95280f9207e1..36e799c31f5e 100644
--- a/drivers/input/serio/at32psif.c
+++ b/drivers/input/serio/at32psif.c
@@ -98,9 +98,9 @@ struct psif {
struct serio *io;
void __iomem *regs;
unsigned int irq;
- unsigned int open;
/* Prevent concurrent writes to PSIF THR. */
spinlock_t lock;
+ bool open;
};
static irqreturn_t psif_interrupt(int irq, void *_ptr)
@@ -164,7 +164,7 @@ static int psif_open(struct serio *io)
psif_writel(psif, CR, PSIF_BIT(CR_TXEN) | PSIF_BIT(CR_RXEN));
psif_writel(psif, IER, PSIF_BIT(RXRDY));
- psif->open = 1;
+ psif->open = true;
out:
return retval;
}
@@ -173,7 +173,7 @@ static void psif_close(struct serio *io)
{
struct psif *psif = io->port_data;
- psif->open = 0;
+ psif->open = false;
psif_writel(psif, IDR, ~0UL);
psif_writel(psif, CR, PSIF_BIT(CR_TXDIS) | PSIF_BIT(CR_RXDIS));
@@ -319,9 +319,10 @@ static int __exit psif_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int psif_suspend(struct platform_device *pdev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int psif_suspend(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct psif *psif = platform_get_drvdata(pdev);
if (psif->open) {
@@ -332,8 +333,9 @@ static int psif_suspend(struct platform_device *pdev, pm_message_t state)
return 0;
}
-static int psif_resume(struct platform_device *pdev)
+static int psif_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct psif *psif = platform_get_drvdata(pdev);
if (psif->open) {
@@ -344,19 +346,17 @@ static int psif_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define psif_suspend NULL
-#define psif_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(psif_pm_ops, psif_suspend, psif_resume);
+
static struct platform_driver psif_driver = {
.remove = __exit_p(psif_remove),
.driver = {
.name = "atmel_psif",
.owner = THIS_MODULE,
+ .pm = &psif_pm_ops,
},
- .suspend = psif_suspend,
- .resume = psif_resume,
};
static int __init psif_init(void)
diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c
index be3316073ae7..09a089996ded 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 558200e96d0f..61da763b1209 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/q40kbd.c b/drivers/input/serio/q40kbd.c
index 5eb84b3b67fb..0c0df7f73802 100644
--- a/drivers/input/serio/q40kbd.c
+++ b/drivers/input/serio/q40kbd.c
@@ -44,26 +44,31 @@
#include <asm/irq.h>
#include <asm/q40ints.h>
+#define DRV_NAME "q40kbd"
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Q40 PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
-static DEFINE_SPINLOCK(q40kbd_lock);
-static struct serio *q40kbd_port;
-static struct platform_device *q40kbd_device;
+struct q40kbd {
+ struct serio *port;
+ spinlock_t lock;
+};
static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
{
+ struct q40kbd *q40kbd = dev_id;
unsigned long flags;
- spin_lock_irqsave(&q40kbd_lock, flags);
+ spin_lock_irqsave(&q40kbd->lock, flags);
if (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG))
- serio_interrupt(q40kbd_port, master_inb(KEYCODE_REG), 0);
+ serio_interrupt(q40kbd->port, master_inb(KEYCODE_REG), 0);
master_outb(-1, KEYBOARD_UNLOCK_REG);
- spin_unlock_irqrestore(&q40kbd_lock, flags);
+ spin_unlock_irqrestore(&q40kbd->lock, flags);
return IRQ_HANDLED;
}
@@ -72,17 +77,23 @@ static irqreturn_t q40kbd_interrupt(int irq, void *dev_id)
* q40kbd_flush() flushes all data that may be in the keyboard buffers
*/
-static void q40kbd_flush(void)
+static void q40kbd_flush(struct q40kbd *q40kbd)
{
int maxread = 100;
unsigned long flags;
- spin_lock_irqsave(&q40kbd_lock, flags);
+ spin_lock_irqsave(&q40kbd->lock, flags);
while (maxread-- && (Q40_IRQ_KEYB_MASK & master_inb(INTERRUPT_REG)))
master_inb(KEYCODE_REG);
- spin_unlock_irqrestore(&q40kbd_lock, flags);
+ spin_unlock_irqrestore(&q40kbd->lock, flags);
+}
+
+static void q40kbd_stop(void)
+{
+ master_outb(0, KEY_IRQ_ENABLE_REG);
+ master_outb(-1, KEYBOARD_UNLOCK_REG);
}
/*
@@ -92,12 +103,9 @@ static void q40kbd_flush(void)
static int q40kbd_open(struct serio *port)
{
- q40kbd_flush();
+ struct q40kbd *q40kbd = port->port_data;
- if (request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0, "q40kbd", NULL)) {
- printk(KERN_ERR "q40kbd.c: Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
- return -EBUSY;
- }
+ q40kbd_flush(q40kbd);
/* off we go */
master_outb(-1, KEYBOARD_UNLOCK_REG);
@@ -108,36 +116,72 @@ static int q40kbd_open(struct serio *port)
static void q40kbd_close(struct serio *port)
{
- master_outb(0, KEY_IRQ_ENABLE_REG);
- master_outb(-1, KEYBOARD_UNLOCK_REG);
- free_irq(Q40_IRQ_KEYBOARD, NULL);
+ struct q40kbd *q40kbd = port->port_data;
- q40kbd_flush();
+ q40kbd_stop();
+ q40kbd_flush(q40kbd);
}
-static int __devinit q40kbd_probe(struct platform_device *dev)
+static int __devinit q40kbd_probe(struct platform_device *pdev)
{
- q40kbd_port = kzalloc(sizeof(struct serio), GFP_KERNEL);
- if (!q40kbd_port)
- return -ENOMEM;
-
- q40kbd_port->id.type = SERIO_8042;
- q40kbd_port->open = q40kbd_open;
- q40kbd_port->close = q40kbd_close;
- q40kbd_port->dev.parent = &dev->dev;
- strlcpy(q40kbd_port->name, "Q40 Kbd Port", sizeof(q40kbd_port->name));
- strlcpy(q40kbd_port->phys, "Q40", sizeof(q40kbd_port->phys));
-
- serio_register_port(q40kbd_port);
+ struct q40kbd *q40kbd;
+ struct serio *port;
+ int error;
+
+ q40kbd = kzalloc(sizeof(struct q40kbd), GFP_KERNEL);
+ port = kzalloc(sizeof(struct serio), GFP_KERNEL);
+ if (!q40kbd || !port) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ q40kbd->port = port;
+ spin_lock_init(&q40kbd->lock);
+
+ port->id.type = SERIO_8042;
+ port->open = q40kbd_open;
+ port->close = q40kbd_close;
+ port->port_data = q40kbd;
+ port->dev.parent = &pdev->dev;
+ strlcpy(port->name, "Q40 Kbd Port", sizeof(port->name));
+ strlcpy(port->phys, "Q40", sizeof(port->phys));
+
+ q40kbd_stop();
+
+ error = request_irq(Q40_IRQ_KEYBOARD, q40kbd_interrupt, 0,
+ DRV_NAME, q40kbd);
+ if (error) {
+ dev_err(&pdev->dev, "Can't get irq %d.\n", Q40_IRQ_KEYBOARD);
+ goto err_free_mem;
+ }
+
+ serio_register_port(q40kbd->port);
+
+ platform_set_drvdata(pdev, q40kbd);
printk(KERN_INFO "serio: Q40 kbd registered\n");
return 0;
+
+err_free_mem:
+ kfree(port);
+ kfree(q40kbd);
+ return error;
}
-static int __devexit q40kbd_remove(struct platform_device *dev)
+static int __devexit q40kbd_remove(struct platform_device *pdev)
{
- serio_unregister_port(q40kbd_port);
-
+ struct q40kbd *q40kbd = platform_get_drvdata(pdev);
+
+ /*
+ * q40kbd_close() will be called as part of unregistering
+ * and will ensure that IRQ is turned off, so it is safe
+ * to unregister port first and free IRQ later.
+ */
+ serio_unregister_port(q40kbd->port);
+ free_irq(Q40_IRQ_KEYBOARD, q40kbd);
+ kfree(q40kbd);
+
+ platform_set_drvdata(pdev, NULL);
return 0;
}
@@ -146,41 +190,16 @@ static struct platform_driver q40kbd_driver = {
.name = "q40kbd",
.owner = THIS_MODULE,
},
- .probe = q40kbd_probe,
.remove = __devexit_p(q40kbd_remove),
};
static int __init q40kbd_init(void)
{
- int error;
-
- if (!MACH_IS_Q40)
- return -ENODEV;
-
- error = platform_driver_register(&q40kbd_driver);
- if (error)
- return error;
-
- q40kbd_device = platform_device_alloc("q40kbd", -1);
- if (!q40kbd_device)
- goto err_unregister_driver;
-
- error = platform_device_add(q40kbd_device);
- if (error)
- goto err_free_device;
-
- return 0;
-
- err_free_device:
- platform_device_put(q40kbd_device);
- err_unregister_driver:
- platform_driver_unregister(&q40kbd_driver);
- return error;
+ return platform_driver_probe(&q40kbd_driver, q40kbd_probe);
}
static void __exit q40kbd_exit(void)
{
- platform_device_unregister(q40kbd_device);
platform_driver_unregister(&q40kbd_driver);
}
diff --git a/drivers/input/serio/rpckbd.c b/drivers/input/serio/rpckbd.c
index 8b44ddc8041c..2af5df6a8fba 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 44fc8b4bcd81..389766707534 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/wacom_sys.c b/drivers/input/tablet/wacom_sys.c
index 2a97b7e76db1..ca28066dc81e 100644
--- a/drivers/input/tablet/wacom_sys.c
+++ b/drivers/input/tablet/wacom_sys.c
@@ -176,7 +176,7 @@ static int wacom_parse_logical_collection(unsigned char *report,
/* Logical collection is only used by 3rd gen Bamboo Touch */
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
- features->device_type = BTN_TOOL_DOUBLETAP;
+ features->device_type = BTN_TOOL_FINGER;
/*
* Stylus and Touch have same active area
@@ -184,9 +184,9 @@ static int wacom_parse_logical_collection(unsigned char *report,
* data before its overwritten.
*/
features->x_phy =
- (features->x_max * features->x_resolution) / 100;
+ (features->x_max * 100) / features->x_resolution;
features->y_phy =
- (features->y_max * features->y_resolution) / 100;
+ (features->y_max * 100) / features->y_resolution;
features->x_max = features->y_max =
get_unaligned_le16(&report[10]);
@@ -286,12 +286,10 @@ static int wacom_parse_hid(struct usb_interface *intf,
if (features->type == TABLETPC2FG) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_TPC2FG;
- features->device_type = BTN_TOOL_DOUBLETAP;
}
if (features->type == BAMBOO_PT) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_BBTOUCH;
- features->device_type = BTN_TOOL_DOUBLETAP;
features->x_phy =
get_unaligned_le16(&report[i + 5]);
features->x_max =
@@ -325,7 +323,6 @@ static int wacom_parse_hid(struct usb_interface *intf,
if (features->type == TABLETPC2FG) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_TPC2FG;
- features->device_type = BTN_TOOL_DOUBLETAP;
features->y_max =
get_unaligned_le16(&report[i + 3]);
features->y_phy =
@@ -334,7 +331,6 @@ static int wacom_parse_hid(struct usb_interface *intf,
} else if (features->type == BAMBOO_PT) {
/* need to reset back */
features->pktlen = WACOM_PKGLEN_BBTOUCH;
- features->device_type = BTN_TOOL_DOUBLETAP;
features->y_phy =
get_unaligned_le16(&report[i + 3]);
features->y_max =
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index cd3ed29e0801..89a96427faa0 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -832,12 +832,24 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
dbg("wacom_tpc_irq: received report #%d", data[0]);
- if (len == WACOM_PKGLEN_TPC1FG || data[0] == WACOM_REPORT_TPC1FG)
- return wacom_tpc_single_touch(wacom, len);
- else if (data[0] == WACOM_REPORT_TPC2FG)
- return wacom_tpc_mt_touch(wacom);
- else if (data[0] == WACOM_REPORT_PENABLED)
- return wacom_tpc_pen(wacom);
+ switch (len) {
+ case WACOM_PKGLEN_TPC1FG:
+ return wacom_tpc_single_touch(wacom, len);
+
+ case WACOM_PKGLEN_TPC2FG:
+ return wacom_tpc_mt_touch(wacom);
+
+ default:
+ switch (data[0]) {
+ case WACOM_REPORT_TPC1FG:
+ case WACOM_REPORT_TPCHID:
+ case WACOM_REPORT_TPCST:
+ return wacom_tpc_single_touch(wacom, len);
+
+ case WACOM_REPORT_PENABLED:
+ return wacom_tpc_pen(wacom);
+ }
+ }
return 0;
}
@@ -1317,7 +1329,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
break;
case TABLETPC2FG:
- if (features->device_type == BTN_TOOL_DOUBLETAP) {
+ if (features->device_type == BTN_TOOL_FINGER) {
input_mt_init_slots(input_dev, 2);
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
@@ -1366,7 +1378,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- if (features->device_type == BTN_TOOL_DOUBLETAP) {
+ if (features->device_type == BTN_TOOL_FINGER) {
__set_bit(BTN_LEFT, input_dev->keybit);
__set_bit(BTN_FORWARD, input_dev->keybit);
__set_bit(BTN_BACK, input_dev->keybit);
diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h
index 050acaefee7d..4f0ba21b0196 100644
--- a/drivers/input/tablet/wacom_wac.h
+++ b/drivers/input/tablet/wacom_wac.h
@@ -39,6 +39,8 @@
#define WACOM_REPORT_INTUOSPAD 12
#define WACOM_REPORT_TPC1FG 6
#define WACOM_REPORT_TPC2FG 13
+#define WACOM_REPORT_TPCHID 15
+#define WACOM_REPORT_TPCST 16
/* device quirks */
#define WACOM_QUIRK_MULTI_INPUT 0x0001
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 4af2a18eb3ba..2a2141915aa0 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -139,7 +139,6 @@ config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C
depends on GPIOLIB
-
help
Say Y here if you have a cy8ctmg110 capacitive touchscreen on
an AAVA device.
@@ -149,6 +148,37 @@ config TOUCHSCREEN_CY8CTMG110
To compile this driver as a module, choose M here: the
module will be called cy8ctmg110_ts.
+config TOUCHSCREEN_CYTTSP_CORE
+ tristate "Cypress TTSP touchscreen"
+ help
+ Say Y here if you have a touchscreen using controller from
+ the Cypress TrueTouch(tm) Standard Product family connected
+ to your system. You will also need to select appropriate
+ bus connection below.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_core.
+
+config TOUCHSCREEN_CYTTSP_I2C
+ tristate "support I2C bus connection"
+ depends on TOUCHSCREEN_CYTTSP_CORE && I2C
+ help
+ Say Y here if the touchscreen is connected via I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_i2c.
+
+config TOUCHSCREEN_CYTTSP_SPI
+ tristate "support SPI bus connection"
+ depends on TOUCHSCREEN_CYTTSP_CORE && SPI_MASTER
+ help
+ Say Y here if the touchscreen is connected via SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cyttsp_spi.
+
config TOUCHSCREEN_DA9034
tristate "Touchscreen support for Dialog Semiconductor DA9034"
depends on PMIC_DA903X
@@ -213,9 +243,24 @@ config TOUCHSCREEN_FUJITSU
To compile this driver as a module, choose M here: the
module will be called fujitsu-ts.
+config TOUCHSCREEN_ILI210X
+ tristate "Ilitek ILI210X based touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a ILI210X based touchscreen
+ controller. This driver supports models ILI2102,
+ ILI2102s, ILI2103, ILI2103s and ILI2105.
+ Such kind of chipsets can be found in Amazon Kindle Fire
+ touchscreens.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ili210x.
+
config TOUCHSCREEN_S3C2410
tristate "Samsung S3C2410/generic touchscreen input driver"
- depends on ARCH_S3C2410 || SAMSUNG_DEV_TS
+ depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
select S3C_ADC
help
Say Y here if you have the s3c2410 touchscreen.
@@ -430,6 +475,18 @@ config TOUCHSCREEN_TOUCHWIN
To compile this driver as a module, choose M here: the
module will be called touchwin.
+config TOUCHSCREEN_TI_TSCADC
+ tristate "TI Touchscreen Interface"
+ depends on ARCH_OMAP2PLUS
+ help
+ Say Y here if you have 4/5/8 wire touchscreen controller
+ to be connected to the ADC controller on your TI AM335x SoC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ti_tscadc.
+
config TOUCHSCREEN_ATMEL_TSADCC
tristate "Atmel Touchscreen Interface"
depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
@@ -577,6 +634,7 @@ config TOUCHSCREEN_USB_COMPOSITE
- JASTEC USB Touch Controller/DigiTech DTR-02U
- Zytronic controllers
- Elo TouchSystems 2700 IntelliTouch
+ - EasyTouch USB Touch Controller from Data Modul
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
@@ -681,6 +739,14 @@ config TOUCHSCREEN_USB_NEXIO
bool "NEXIO/iNexio device support" if EXPERT
depends on TOUCHSCREEN_USB_COMPOSITE
+config TOUCHSCREEN_USB_EASYTOUCH
+ default y
+ bool "EasyTouch USB Touch controller device support" if EMBEDDED
+ depends on TOUCHSCREEN_USB_COMPOSITE
+ help
+ Say Y here if you have a EasyTouch USB Touch controller device support.
+ If unsure, say N.
+
config TOUCHSCREEN_TOUCHIT213
tristate "Sahara TouchIT-213 touchscreen"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 496091e88460..3d5cf8cbf89c 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -16,8 +16,11 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
-obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_CYTTSP_SPI) += cyttsp_spi.o
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
@@ -26,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
+obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
@@ -45,6 +49,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o
obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o
+obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o
obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
diff --git a/drivers/input/touchscreen/ad7877.c b/drivers/input/touchscreen/ad7877.c
index 49a36df0b752..2c7692108e6c 100644
--- a/drivers/input/touchscreen/ad7877.c
+++ b/drivers/input/touchscreen/ad7877.c
@@ -860,17 +860,7 @@ static struct spi_driver ad7877_driver = {
.remove = __devexit_p(ad7877_remove),
};
-static int __init ad7877_init(void)
-{
- return spi_register_driver(&ad7877_driver);
-}
-module_init(ad7877_init);
-
-static void __exit ad7877_exit(void)
-{
- spi_unregister_driver(&ad7877_driver);
-}
-module_exit(ad7877_exit);
+module_spi_driver(ad7877_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("AD7877 touchscreen Driver");
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
index 0dac6712f42b..3054354d0dd3 100644
--- a/drivers/input/touchscreen/ad7879-i2c.c
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -102,17 +102,7 @@ static struct i2c_driver ad7879_i2c_driver = {
.id_table = ad7879_id,
};
-static int __init ad7879_i2c_init(void)
-{
- return i2c_add_driver(&ad7879_i2c_driver);
-}
-module_init(ad7879_i2c_init);
-
-static void __exit ad7879_i2c_exit(void)
-{
- i2c_del_driver(&ad7879_i2c_driver);
-}
-module_exit(ad7879_i2c_exit);
+module_i2c_driver(ad7879_i2c_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver");
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
index 9b2e1c2b1971..db49abf056ba 100644
--- a/drivers/input/touchscreen/ad7879-spi.c
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -157,17 +157,7 @@ static struct spi_driver ad7879_spi_driver = {
.remove = __devexit_p(ad7879_spi_remove),
};
-static int __init ad7879_spi_init(void)
-{
- return spi_register_driver(&ad7879_spi_driver);
-}
-module_init(ad7879_spi_init);
-
-static void __exit ad7879_spi_exit(void)
-{
- spi_unregister_driver(&ad7879_spi_driver);
-}
-module_exit(ad7879_spi_exit);
+module_spi_driver(ad7879_spi_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver");
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c
index 23fd90185659..f02028ec3db6 100644
--- a/drivers/input/touchscreen/ads7846.c
+++ b/drivers/input/touchscreen/ads7846.c
@@ -1433,17 +1433,7 @@ static struct spi_driver ads7846_driver = {
.remove = __devexit_p(ads7846_remove),
};
-static int __init ads7846_init(void)
-{
- return spi_register_driver(&ads7846_driver);
-}
-module_init(ads7846_init);
-
-static void __exit ads7846_exit(void)
-{
- spi_unregister_driver(&ads7846_driver);
-}
-module_exit(ads7846_exit);
+module_spi_driver(ads7846_driver);
MODULE_DESCRIPTION("ADS7846 TouchScreen Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/atmel-wm97xx.c b/drivers/input/touchscreen/atmel-wm97xx.c
index 8034cbb20f74..c5c2dbb93869 100644
--- a/drivers/input/touchscreen/atmel-wm97xx.c
+++ b/drivers/input/touchscreen/atmel-wm97xx.c
@@ -392,9 +392,10 @@ static int __exit atmel_wm97xx_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM
-static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg)
+#ifdef CONFIG_PM_SLEEP
+static int atmel_wm97xx_suspend(struct *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
@@ -404,8 +405,9 @@ static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg)
return 0;
}
-static int atmel_wm97xx_resume(struct platform_device *pdev)
+static int atmel_wm97xx_resume(struct device *dev)
{
+ struct platform_device *pdev = to_platform_device(dev);
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
struct wm97xx *wm = atmel_wm97xx->wm;
@@ -416,18 +418,18 @@ static int atmel_wm97xx_resume(struct platform_device *pdev)
return 0;
}
-#else
-#define atmel_wm97xx_suspend NULL
-#define atmel_wm97xx_resume NULL
#endif
+static SIMPLE_DEV_PM_OPS(atmel_wm97xx_pm_ops,
+ atmel_wm97xx_suspend, atmel_wm97xx_resume);
+
static struct platform_driver atmel_wm97xx_driver = {
.remove = __exit_p(atmel_wm97xx_remove),
.driver = {
- .name = "wm97xx-touch",
+ .name = "wm97xx-touch",
+ .owner = THIS_MODULE,
+ .pm = &atmel_wm97xx_pm_ops,
},
- .suspend = atmel_wm97xx_suspend,
- .resume = atmel_wm97xx_resume,
};
static int __init atmel_wm97xx_init(void)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index a596c2775d1a..19d4ea65ea01 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1267,18 +1267,7 @@ static struct i2c_driver mxt_driver = {
.id_table = mxt_id,
};
-static int __init mxt_init(void)
-{
- return i2c_add_driver(&mxt_driver);
-}
-
-static void __exit mxt_exit(void)
-{
- i2c_del_driver(&mxt_driver);
-}
-
-module_init(mxt_init);
-module_exit(mxt_exit);
+module_i2c_driver(mxt_driver);
/* Module information */
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
diff --git a/drivers/input/touchscreen/auo-pixcir-ts.c b/drivers/input/touchscreen/auo-pixcir-ts.c
index 94fb9fbb08a9..c7047b6bb020 100644
--- a/drivers/input/touchscreen/auo-pixcir-ts.c
+++ b/drivers/input/touchscreen/auo-pixcir-ts.c
@@ -635,17 +635,7 @@ static struct i2c_driver auo_pixcir_driver = {
.id_table = auo_pixcir_idtable,
};
-static int __init auo_pixcir_init(void)
-{
- return i2c_add_driver(&auo_pixcir_driver);
-}
-module_init(auo_pixcir_init);
-
-static void __exit auo_pixcir_exit(void)
-{
- i2c_del_driver(&auo_pixcir_driver);
-}
-module_exit(auo_pixcir_exit);
+module_i2c_driver(auo_pixcir_driver);
MODULE_DESCRIPTION("AUO-PIXCIR touchscreen driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/bu21013_ts.c b/drivers/input/touchscreen/bu21013_ts.c
index 902c7214e887..f2d03c06c2da 100644
--- a/drivers/input/touchscreen/bu21013_ts.c
+++ b/drivers/input/touchscreen/bu21013_ts.c
@@ -652,30 +652,7 @@ static struct i2c_driver bu21013_driver = {
.id_table = bu21013_id,
};
-/**
- * bu21013_init() - initializes the bu21013 touchscreen driver
- *
- * This function used to initializes the bu21013
- * touchscreen driver and returns integer.
- */
-static int __init bu21013_init(void)
-{
- return i2c_add_driver(&bu21013_driver);
-}
-
-/**
- * bu21013_exit() - de-initializes the bu21013 touchscreen driver
- *
- * This function uses to de-initializes the bu21013
- * touchscreen driver and returns none.
- */
-static void __exit bu21013_exit(void)
-{
- i2c_del_driver(&bu21013_driver);
-}
-
-module_init(bu21013_init);
-module_exit(bu21013_exit);
+module_i2c_driver(bu21013_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Naveen Kumar G <naveen.gaddipati@stericsson.com>");
diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c
index d8815c5d54ad..237753ad1031 100644
--- a/drivers/input/touchscreen/cy8ctmg110_ts.c
+++ b/drivers/input/touchscreen/cy8ctmg110_ts.c
@@ -350,18 +350,7 @@ static struct i2c_driver cy8ctmg110_driver = {
.remove = __devexit_p(cy8ctmg110_remove),
};
-static int __init cy8ctmg110_init(void)
-{
- return i2c_add_driver(&cy8ctmg110_driver);
-}
-
-static void __exit cy8ctmg110_exit(void)
-{
- i2c_del_driver(&cy8ctmg110_driver);
-}
-
-module_init(cy8ctmg110_init);
-module_exit(cy8ctmg110_exit);
+module_i2c_driver(cy8ctmg110_driver);
MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>");
MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver");
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
new file mode 100644
index 000000000000..f030d9ec795d
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -0,0 +1,625 @@
+/*
+ * Core Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.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, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include "cyttsp_core.h"
+
+/* Bootloader number of command keys */
+#define CY_NUM_BL_KEYS 8
+
+/* helpers */
+#define GET_NUM_TOUCHES(x) ((x) & 0x0F)
+#define IS_LARGE_AREA(x) (((x) & 0x10) >> 4)
+#define IS_BAD_PKT(x) ((x) & 0x20)
+#define IS_VALID_APP(x) ((x) & 0x01)
+#define IS_OPERATIONAL_ERR(x) ((x) & 0x3F)
+#define GET_HSTMODE(reg) (((reg) & 0x70) >> 4)
+#define GET_BOOTLOADERMODE(reg) (((reg) & 0x10) >> 4)
+
+#define CY_REG_BASE 0x00
+#define CY_REG_ACT_DIST 0x1E
+#define CY_REG_ACT_INTRVL 0x1D
+#define CY_REG_TCH_TMOUT (CY_REG_ACT_INTRVL + 1)
+#define CY_REG_LP_INTRVL (CY_REG_TCH_TMOUT + 1)
+#define CY_MAXZ 255
+#define CY_DELAY_DFLT 20 /* ms */
+#define CY_DELAY_MAX 500
+#define CY_ACT_DIST_DFLT 0xF8
+#define CY_HNDSHK_BIT 0x80
+/* device mode bits */
+#define CY_OPERATE_MODE 0x00
+#define CY_SYSINFO_MODE 0x10
+/* power mode select bits */
+#define CY_SOFT_RESET_MODE 0x01 /* return to Bootloader mode */
+#define CY_DEEP_SLEEP_MODE 0x02
+#define CY_LOW_POWER_MODE 0x04
+
+/* Slots management */
+#define CY_MAX_FINGER 4
+#define CY_MAX_ID 16
+
+static const u8 bl_command[] = {
+ 0x00, /* file offset */
+ 0xFF, /* command */
+ 0xA5, /* exit bootloader command */
+ 0, 1, 2, 3, 4, 5, 6, 7 /* default keys */
+};
+
+static int ttsp_read_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int error;
+ int tries;
+
+ for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+ error = ts->bus_ops->read(ts, command, length, buf);
+ if (!error)
+ return 0;
+
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return -EIO;
+}
+
+static int ttsp_write_block_data(struct cyttsp *ts, u8 command,
+ u8 length, void *buf)
+{
+ int error;
+ int tries;
+
+ for (tries = 0; tries < CY_NUM_RETRY; tries++) {
+ error = ts->bus_ops->write(ts, command, length, buf);
+ if (!error)
+ return 0;
+
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return -EIO;
+}
+
+static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
+{
+ return ttsp_write_block_data(ts, CY_REG_BASE, sizeof(cmd), &cmd);
+}
+
+static int cyttsp_load_bl_regs(struct cyttsp *ts)
+{
+ memset(&ts->bl_data, 0, sizeof(ts->bl_data));
+ ts->bl_data.bl_status = 0x10;
+
+ return ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->bl_data), &ts->bl_data);
+}
+
+static int cyttsp_exit_bl_mode(struct cyttsp *ts)
+{
+ int error;
+ u8 bl_cmd[sizeof(bl_command)];
+
+ memcpy(bl_cmd, bl_command, sizeof(bl_command));
+ if (ts->pdata->bl_keys)
+ memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
+ ts->pdata->bl_keys, sizeof(bl_command));
+
+ error = ttsp_write_block_data(ts, CY_REG_BASE,
+ sizeof(bl_cmd), bl_cmd);
+ if (error)
+ return error;
+
+ /* wait for TTSP Device to complete the operation */
+ msleep(CY_DELAY_DFLT);
+
+ error = cyttsp_load_bl_regs(ts);
+ if (error)
+ return error;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status))
+ return -EIO;
+
+ return 0;
+}
+
+static int cyttsp_set_operational_mode(struct cyttsp *ts)
+{
+ int error;
+
+ error = ttsp_send_command(ts, CY_OPERATE_MODE);
+ if (error)
+ return error;
+
+ /* wait for TTSP Device to complete switch to Operational mode */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->xy_data), &ts->xy_data);
+ if (error)
+ return error;
+
+ return ts->xy_data.act_dist == CY_ACT_DIST_DFLT ? -EIO : 0;
+}
+
+static int cyttsp_set_sysinfo_mode(struct cyttsp *ts)
+{
+ int error;
+
+ memset(&ts->sysinfo_data, 0, sizeof(ts->sysinfo_data));
+
+ /* switch to sysinfo mode */
+ error = ttsp_send_command(ts, CY_SYSINFO_MODE);
+ if (error)
+ return error;
+
+ /* read sysinfo registers */
+ msleep(CY_DELAY_DFLT);
+ error = ttsp_read_block_data(ts, CY_REG_BASE, sizeof(ts->sysinfo_data),
+ &ts->sysinfo_data);
+ if (error)
+ return error;
+
+ if (!ts->sysinfo_data.tts_verh && !ts->sysinfo_data.tts_verl)
+ return -EIO;
+
+ return 0;
+}
+
+static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
+{
+ int retval = 0;
+
+ if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
+ ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
+ ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {
+
+ u8 intrvl_ray[] = {
+ ts->pdata->act_intrvl,
+ ts->pdata->tch_tmout,
+ ts->pdata->lp_intrvl
+ };
+
+ /* set intrvl registers */
+ retval = ttsp_write_block_data(ts, CY_REG_ACT_INTRVL,
+ sizeof(intrvl_ray), intrvl_ray);
+ msleep(CY_DELAY_DFLT);
+ }
+
+ return retval;
+}
+
+static int cyttsp_soft_reset(struct cyttsp *ts)
+{
+ unsigned long timeout;
+ int retval;
+
+ /* wait for interrupt to set ready completion */
+ INIT_COMPLETION(ts->bl_ready);
+ ts->state = CY_BL_STATE;
+
+ enable_irq(ts->irq);
+
+ retval = ttsp_send_command(ts, CY_SOFT_RESET_MODE);
+ if (retval)
+ goto out;
+
+ timeout = wait_for_completion_timeout(&ts->bl_ready,
+ msecs_to_jiffies(CY_DELAY_DFLT * CY_DELAY_MAX));
+ retval = timeout ? 0 : -EIO;
+
+out:
+ ts->state = CY_IDLE_STATE;
+ disable_irq(ts->irq);
+ return retval;
+}
+
+static int cyttsp_act_dist_setup(struct cyttsp *ts)
+{
+ u8 act_dist_setup = ts->pdata->act_dist;
+
+ /* Init gesture; active distance setup */
+ return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
+ sizeof(act_dist_setup), &act_dist_setup);
+}
+
+static void cyttsp_extract_track_ids(struct cyttsp_xydata *xy_data, int *ids)
+{
+ ids[0] = xy_data->touch12_id >> 4;
+ ids[1] = xy_data->touch12_id & 0xF;
+ ids[2] = xy_data->touch34_id >> 4;
+ ids[3] = xy_data->touch34_id & 0xF;
+}
+
+static const struct cyttsp_tch *cyttsp_get_tch(struct cyttsp_xydata *xy_data,
+ int idx)
+{
+ switch (idx) {
+ case 0:
+ return &xy_data->tch1;
+ case 1:
+ return &xy_data->tch2;
+ case 2:
+ return &xy_data->tch3;
+ case 3:
+ return &xy_data->tch4;
+ default:
+ return NULL;
+ }
+}
+
+static void cyttsp_report_tchdata(struct cyttsp *ts)
+{
+ struct cyttsp_xydata *xy_data = &ts->xy_data;
+ struct input_dev *input = ts->input;
+ int num_tch = GET_NUM_TOUCHES(xy_data->tt_stat);
+ const struct cyttsp_tch *tch;
+ int ids[CY_MAX_ID];
+ int i;
+ DECLARE_BITMAP(used, CY_MAX_ID);
+
+ if (IS_LARGE_AREA(xy_data->tt_stat) == 1) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Large area detected\n", __func__);
+ } else if (num_tch > CY_MAX_FINGER) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Num touch error detected\n", __func__);
+ } else if (IS_BAD_PKT(xy_data->tt_mode)) {
+ /* terminate all active tracks */
+ num_tch = 0;
+ dev_dbg(ts->dev, "%s: Invalid buffer detected\n", __func__);
+ }
+
+ cyttsp_extract_track_ids(xy_data, ids);
+
+ bitmap_zero(used, CY_MAX_ID);
+
+ for (i = 0; i < num_tch; i++) {
+ tch = cyttsp_get_tch(xy_data, i);
+
+ input_mt_slot(input, ids[i]);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+ input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
+ input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, tch->z);
+
+ __set_bit(ids[i], used);
+ }
+
+ for (i = 0; i < CY_MAX_ID; i++) {
+ if (test_bit(i, used))
+ continue;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, false);
+ }
+
+ input_sync(input);
+}
+
+static irqreturn_t cyttsp_irq(int irq, void *handle)
+{
+ struct cyttsp *ts = handle;
+ int error;
+
+ if (unlikely(ts->state == CY_BL_STATE)) {
+ complete(&ts->bl_ready);
+ goto out;
+ }
+
+ /* Get touch data from CYTTSP device */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(struct cyttsp_xydata), &ts->xy_data);
+ if (error)
+ goto out;
+
+ /* provide flow control handshake */
+ if (ts->pdata->use_hndshk) {
+ error = ttsp_send_command(ts,
+ ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
+ if (error)
+ goto out;
+ }
+
+ if (unlikely(ts->state == CY_IDLE_STATE))
+ goto out;
+
+ if (GET_BOOTLOADERMODE(ts->xy_data.tt_mode)) {
+ /*
+ * TTSP device has reset back to bootloader mode.
+ * Restore to operational mode.
+ */
+ error = cyttsp_exit_bl_mode(ts);
+ if (error) {
+ dev_err(ts->dev,
+ "Could not return to operational mode, err: %d\n",
+ error);
+ ts->state = CY_IDLE_STATE;
+ }
+ } else {
+ cyttsp_report_tchdata(ts);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int cyttsp_power_on(struct cyttsp *ts)
+{
+ int error;
+
+ error = cyttsp_soft_reset(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_load_bl_regs(ts);
+ if (error)
+ return error;
+
+ if (GET_BOOTLOADERMODE(ts->bl_data.bl_status) &&
+ IS_VALID_APP(ts->bl_data.bl_status)) {
+ error = cyttsp_exit_bl_mode(ts);
+ if (error)
+ return error;
+ }
+
+ if (GET_HSTMODE(ts->bl_data.bl_file) != CY_OPERATE_MODE ||
+ IS_OPERATIONAL_ERR(ts->bl_data.bl_status)) {
+ return -ENODEV;
+ }
+
+ error = cyttsp_set_sysinfo_mode(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_set_sysinfo_regs(ts);
+ if (error)
+ return error;
+
+ error = cyttsp_set_operational_mode(ts);
+ if (error)
+ return error;
+
+ /* init active distance */
+ error = cyttsp_act_dist_setup(ts);
+ if (error)
+ return error;
+
+ ts->state = CY_ACTIVE_STATE;
+
+ return 0;
+}
+
+static int cyttsp_enable(struct cyttsp *ts)
+{
+ int error;
+
+ /*
+ * The device firmware can wake on an I2C or SPI memory slave
+ * address match. So just reading a register is sufficient to
+ * wake up the device. The first read attempt will fail but it
+ * will wake it up making the second read attempt successful.
+ */
+ error = ttsp_read_block_data(ts, CY_REG_BASE,
+ sizeof(ts->xy_data), &ts->xy_data);
+ if (error)
+ return error;
+
+ if (GET_HSTMODE(ts->xy_data.hst_mode))
+ return -EIO;
+
+ enable_irq(ts->irq);
+
+ return 0;
+}
+
+static int cyttsp_disable(struct cyttsp *ts)
+{
+ int error;
+
+ error = ttsp_send_command(ts, CY_LOW_POWER_MODE);
+ if (error)
+ return error;
+
+ disable_irq(ts->irq);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int cyttsp_suspend(struct device *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+ int retval = 0;
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->input->users) {
+ retval = cyttsp_disable(ts);
+ if (retval == 0)
+ ts->suspended = true;
+ }
+
+ mutex_unlock(&ts->input->mutex);
+
+ return retval;
+}
+
+static int cyttsp_resume(struct device *dev)
+{
+ struct cyttsp *ts = dev_get_drvdata(dev);
+
+ mutex_lock(&ts->input->mutex);
+
+ if (ts->input->users)
+ cyttsp_enable(ts);
+
+ ts->suspended = false;
+
+ mutex_unlock(&ts->input->mutex);
+
+ return 0;
+}
+
+#endif
+
+SIMPLE_DEV_PM_OPS(cyttsp_pm_ops, cyttsp_suspend, cyttsp_resume);
+EXPORT_SYMBOL_GPL(cyttsp_pm_ops);
+
+static int cyttsp_open(struct input_dev *dev)
+{
+ struct cyttsp *ts = input_get_drvdata(dev);
+ int retval = 0;
+
+ if (!ts->suspended)
+ retval = cyttsp_enable(ts);
+
+ return retval;
+}
+
+static void cyttsp_close(struct input_dev *dev)
+{
+ struct cyttsp *ts = input_get_drvdata(dev);
+
+ if (!ts->suspended)
+ cyttsp_disable(ts);
+}
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, int irq, size_t xfer_buf_size)
+{
+ const struct cyttsp_platform_data *pdata = dev->platform_data;
+ struct cyttsp *ts;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!pdata || !pdata->name || irq <= 0) {
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts->dev = dev;
+ ts->input = input_dev;
+ ts->pdata = dev->platform_data;
+ ts->bus_ops = bus_ops;
+ ts->irq = irq;
+
+ init_completion(&ts->bl_ready);
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+ if (pdata->init) {
+ error = pdata->init();
+ if (error) {
+ dev_err(ts->dev, "platform init failed, err: %d\n",
+ error);
+ goto err_free_mem;
+ }
+ }
+
+ input_dev->name = pdata->name;
+ input_dev->phys = ts->phys;
+ input_dev->id.bustype = bus_ops->bustype;
+ input_dev->dev.parent = ts->dev;
+
+ input_dev->open = cyttsp_open;
+ input_dev->close = cyttsp_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, pdata->maxx, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, pdata->maxy, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, CY_MAXZ, 0, 0);
+
+ input_mt_init_slots(input_dev, CY_MAX_ID);
+
+ error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ pdata->name, ts);
+ if (error) {
+ dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
+ ts->irq, error);
+ goto err_platform_exit;
+ }
+
+ disable_irq(ts->irq);
+
+ error = cyttsp_power_on(ts);
+ if (error)
+ goto err_free_irq;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(ts->dev, "failed to register input device: %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ return ts;
+
+err_free_irq:
+ free_irq(ts->irq, ts);
+err_platform_exit:
+ if (pdata->exit)
+ pdata->exit();
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+err_out:
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL_GPL(cyttsp_probe);
+
+void cyttsp_remove(struct cyttsp *ts)
+{
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ if (ts->pdata->exit)
+ ts->pdata->exit();
+ kfree(ts);
+}
+EXPORT_SYMBOL_GPL(cyttsp_remove);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
+MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h
new file mode 100644
index 000000000000..1aa3c6967e70
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -0,0 +1,149 @@
+/*
+ * Header file for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.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, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+
+#ifndef __CYTTSP_CORE_H__
+#define __CYTTSP_CORE_H__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/input/cyttsp.h>
+
+#define CY_NUM_RETRY 16 /* max number of retries for read ops */
+
+struct cyttsp_tch {
+ __be16 x, y;
+ u8 z;
+} __packed;
+
+/* TrueTouch Standard Product Gen3 interface definition */
+struct cyttsp_xydata {
+ u8 hst_mode;
+ u8 tt_mode;
+ u8 tt_stat;
+ struct cyttsp_tch tch1;
+ u8 touch12_id;
+ struct cyttsp_tch tch2;
+ u8 gest_cnt;
+ u8 gest_id;
+ struct cyttsp_tch tch3;
+ u8 touch34_id;
+ struct cyttsp_tch tch4;
+ u8 tt_undef[3];
+ u8 act_dist;
+ u8 tt_reserved;
+} __packed;
+
+
+/* TTSP System Information interface definition */
+struct cyttsp_sysinfo_data {
+ u8 hst_mode;
+ u8 mfg_cmd;
+ u8 mfg_stat;
+ u8 cid[3];
+ u8 tt_undef1;
+ u8 uid[8];
+ u8 bl_verh;
+ u8 bl_verl;
+ u8 tts_verh;
+ u8 tts_verl;
+ u8 app_idh;
+ u8 app_idl;
+ u8 app_verh;
+ u8 app_verl;
+ u8 tt_undef[5];
+ u8 scn_typ;
+ u8 act_intrvl;
+ u8 tch_tmout;
+ u8 lp_intrvl;
+};
+
+/* TTSP Bootloader Register Map interface definition */
+#define CY_BL_CHKSUM_OK 0x01
+struct cyttsp_bootloader_data {
+ u8 bl_file;
+ u8 bl_status;
+ u8 bl_error;
+ u8 blver_hi;
+ u8 blver_lo;
+ u8 bld_blver_hi;
+ u8 bld_blver_lo;
+ u8 ttspver_hi;
+ u8 ttspver_lo;
+ u8 appid_hi;
+ u8 appid_lo;
+ u8 appver_hi;
+ u8 appver_lo;
+ u8 cid_0;
+ u8 cid_1;
+ u8 cid_2;
+};
+
+struct cyttsp;
+
+struct cyttsp_bus_ops {
+ u16 bustype;
+ int (*write)(struct cyttsp *ts,
+ u8 addr, u8 length, const void *values);
+ int (*read)(struct cyttsp *ts, u8 addr, u8 length, void *values);
+};
+
+enum cyttsp_state {
+ CY_IDLE_STATE,
+ CY_ACTIVE_STATE,
+ CY_BL_STATE,
+};
+
+struct cyttsp {
+ struct device *dev;
+ int irq;
+ struct input_dev *input;
+ char phys[32];
+ const struct cyttsp_platform_data *pdata;
+ const struct cyttsp_bus_ops *bus_ops;
+ struct cyttsp_bootloader_data bl_data;
+ struct cyttsp_sysinfo_data sysinfo_data;
+ struct cyttsp_xydata xy_data;
+ struct completion bl_ready;
+ enum cyttsp_state state;
+ bool suspended;
+
+ u8 xfer_buf[] ____cacheline_aligned;
+};
+
+struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
+ struct device *dev, int irq, size_t xfer_buf_size);
+void cyttsp_remove(struct cyttsp *ts);
+
+extern const struct dev_pm_ops cyttsp_pm_ops;
+
+#endif /* __CYTTSP_CORE_H__ */
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c
new file mode 100644
index 000000000000..2af1d0c52bcd
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_i2c.c
@@ -0,0 +1,136 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) I2C touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.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, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/i2c.h>
+#include <linux/input.h>
+
+#define CY_I2C_DATA_SIZE 128
+
+static int cyttsp_i2c_read_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, void *values)
+{
+ struct i2c_client *client = to_i2c_client(ts->dev);
+ struct i2c_msg msgs[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = values,
+ },
+ };
+ int retval;
+
+ retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (retval < 0)
+ return retval;
+
+ return retval != ARRAY_SIZE(msgs) ? -EIO : 0;
+}
+
+static int cyttsp_i2c_write_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, const void *values)
+{
+ struct i2c_client *client = to_i2c_client(ts->dev);
+ int retval;
+
+ ts->xfer_buf[0] = addr;
+ memcpy(&ts->xfer_buf[1], values, length);
+
+ retval = i2c_master_send(client, ts->xfer_buf, length + 1);
+
+ return retval < 0 ? retval : 0;
+}
+
+static const struct cyttsp_bus_ops cyttsp_i2c_bus_ops = {
+ .bustype = BUS_I2C,
+ .write = cyttsp_i2c_write_block_data,
+ .read = cyttsp_i2c_read_block_data,
+};
+
+static int __devinit cyttsp_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cyttsp *ts;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C functionality not Supported\n");
+ return -EIO;
+ }
+
+ ts = cyttsp_probe(&cyttsp_i2c_bus_ops, &client->dev, client->irq,
+ CY_I2C_DATA_SIZE);
+
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ i2c_set_clientdata(client, ts);
+
+ return 0;
+}
+
+static int __devexit cyttsp_i2c_remove(struct i2c_client *client)
+{
+ struct cyttsp *ts = i2c_get_clientdata(client);
+
+ cyttsp_remove(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id cyttsp_i2c_id[] = {
+ { CY_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id);
+
+static struct i2c_driver cyttsp_i2c_driver = {
+ .driver = {
+ .name = CY_I2C_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp_pm_ops,
+ },
+ .probe = cyttsp_i2c_probe,
+ .remove = __devexit_p(cyttsp_i2c_remove),
+ .id_table = cyttsp_i2c_id,
+};
+
+module_i2c_driver(cyttsp_i2c_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) I2C driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("i2c:cyttsp");
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
new file mode 100644
index 000000000000..9f263410407b
--- /dev/null
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -0,0 +1,200 @@
+/*
+ * Source for:
+ * Cypress TrueTouch(TM) Standard Product (TTSP) SPI touchscreen driver.
+ * For use with Cypress Txx3xx parts.
+ * Supported parts include:
+ * CY8CTST341
+ * CY8CTMA340
+ *
+ * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc.
+ * Copyright (C) 2012 Javier Martinez Canillas <javier@dowhile0.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, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contact Cypress Semiconductor at www.cypress.com <kev@cypress.com>
+ *
+ */
+
+#include "cyttsp_core.h"
+
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+
+#define CY_SPI_WR_OP 0x00 /* r/~w */
+#define CY_SPI_RD_OP 0x01
+#define CY_SPI_CMD_BYTES 4
+#define CY_SPI_SYNC_BYTE 2
+#define CY_SPI_SYNC_ACK1 0x62 /* from protocol v.2 */
+#define CY_SPI_SYNC_ACK2 0x9D /* from protocol v.2 */
+#define CY_SPI_DATA_SIZE 128
+#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE)
+#define CY_SPI_BITS_PER_WORD 8
+
+static int cyttsp_spi_xfer(struct cyttsp *ts,
+ u8 op, u8 reg, u8 *buf, int length)
+{
+ struct spi_device *spi = to_spi_device(ts->dev);
+ struct spi_message msg;
+ struct spi_transfer xfer[2];
+ u8 *wr_buf = &ts->xfer_buf[0];
+ u8 *rd_buf = &ts->xfer_buf[CY_SPI_DATA_BUF_SIZE];
+ int retval;
+ int i;
+
+ if (length > CY_SPI_DATA_SIZE) {
+ dev_err(ts->dev, "%s: length %d is too big.\n",
+ __func__, length);
+ return -EINVAL;
+ }
+
+ memset(wr_buf, 0, CY_SPI_DATA_BUF_SIZE);
+ memset(rd_buf, 0, CY_SPI_DATA_BUF_SIZE);
+
+ wr_buf[0] = 0x00; /* header byte 0 */
+ wr_buf[1] = 0xFF; /* header byte 1 */
+ wr_buf[2] = reg; /* reg index */
+ wr_buf[3] = op; /* r/~w */
+ if (op == CY_SPI_WR_OP)
+ memcpy(wr_buf + CY_SPI_CMD_BYTES, buf, length);
+
+ memset(xfer, 0, sizeof(xfer));
+ spi_message_init(&msg);
+
+ /*
+ We set both TX and RX buffers because Cypress TTSP
+ requires full duplex operation.
+ */
+ xfer[0].tx_buf = wr_buf;
+ xfer[0].rx_buf = rd_buf;
+ switch (op) {
+ case CY_SPI_WR_OP:
+ xfer[0].len = length + CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+ break;
+
+ case CY_SPI_RD_OP:
+ xfer[0].len = CY_SPI_CMD_BYTES;
+ spi_message_add_tail(&xfer[0], &msg);
+
+ xfer[1].rx_buf = buf;
+ xfer[1].len = length;
+ spi_message_add_tail(&xfer[1], &msg);
+ break;
+
+ default:
+ dev_err(ts->dev, "%s: bad operation code=%d\n", __func__, op);
+ return -EINVAL;
+ }
+
+ retval = spi_sync(spi, &msg);
+ if (retval < 0) {
+ dev_dbg(ts->dev, "%s: spi_sync() error %d, len=%d, op=%d\n",
+ __func__, retval, xfer[1].len, op);
+
+ /*
+ * do not return here since was a bad ACK sequence
+ * let the following ACK check handle any errors and
+ * allow silent retries
+ */
+ }
+
+ if (rd_buf[CY_SPI_SYNC_BYTE] != CY_SPI_SYNC_ACK1 ||
+ rd_buf[CY_SPI_SYNC_BYTE + 1] != CY_SPI_SYNC_ACK2) {
+
+ dev_dbg(ts->dev, "%s: operation %d failed\n", __func__, op);
+
+ for (i = 0; i < CY_SPI_CMD_BYTES; i++)
+ dev_dbg(ts->dev, "%s: test rd_buf[%d]:0x%02x\n",
+ __func__, i, rd_buf[i]);
+ for (i = 0; i < length; i++)
+ dev_dbg(ts->dev, "%s: test buf[%d]:0x%02x\n",
+ __func__, i, buf[i]);
+
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cyttsp_spi_read_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, void *data)
+{
+ return cyttsp_spi_xfer(ts, CY_SPI_RD_OP, addr, data, length);
+}
+
+static int cyttsp_spi_write_block_data(struct cyttsp *ts,
+ u8 addr, u8 length, const void *data)
+{
+ return cyttsp_spi_xfer(ts, CY_SPI_WR_OP, addr, (void *)data, length);
+}
+
+static const struct cyttsp_bus_ops cyttsp_spi_bus_ops = {
+ .bustype = BUS_SPI,
+ .write = cyttsp_spi_write_block_data,
+ .read = cyttsp_spi_read_block_data,
+};
+
+static int __devinit cyttsp_spi_probe(struct spi_device *spi)
+{
+ struct cyttsp *ts;
+ int error;
+
+ /* Set up SPI*/
+ spi->bits_per_word = CY_SPI_BITS_PER_WORD;
+ spi->mode = SPI_MODE_0;
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "%s: SPI setup error %d\n",
+ __func__, error);
+ return error;
+ }
+
+ ts = cyttsp_probe(&cyttsp_spi_bus_ops, &spi->dev, spi->irq,
+ CY_SPI_DATA_BUF_SIZE * 2);
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+
+ spi_set_drvdata(spi, ts);
+
+ return 0;
+}
+
+static int __devexit cyttsp_spi_remove(struct spi_device *spi)
+{
+ struct cyttsp *ts = spi_get_drvdata(spi);
+
+ cyttsp_remove(ts);
+
+ return 0;
+}
+
+static struct spi_driver cyttsp_spi_driver = {
+ .driver = {
+ .name = CY_SPI_NAME,
+ .owner = THIS_MODULE,
+ .pm = &cyttsp_pm_ops,
+ },
+ .probe = cyttsp_spi_probe,
+ .remove = __devexit_p(cyttsp_spi_remove),
+};
+
+module_spi_driver(cyttsp_spi_driver);
+
+MODULE_ALIAS("spi:cyttsp");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver");
+MODULE_AUTHOR("Cypress");
+MODULE_ALIAS("spi:cyttsp");
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 1df19bb8534a..503c7096ed36 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -320,20 +320,8 @@ static struct i2c_driver eeti_ts_driver = {
.id_table = eeti_ts_id,
};
-static int __init eeti_ts_init(void)
-{
- return i2c_add_driver(&eeti_ts_driver);
-}
-
-static void __exit eeti_ts_exit(void)
-{
- i2c_del_driver(&eeti_ts_driver);
-}
+module_i2c_driver(eeti_ts_driver);
MODULE_DESCRIPTION("EETI Touchscreen driver");
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_LICENSE("GPL");
-
-module_init(eeti_ts_init);
-module_exit(eeti_ts_exit);
-
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
index eadcc2e83c77..70524dd34f42 100644
--- a/drivers/input/touchscreen/egalax_ts.c
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -285,18 +285,7 @@ static struct i2c_driver egalax_ts_driver = {
.remove = __devexit_p(egalax_ts_remove),
};
-static int __init egalax_ts_init(void)
-{
- return i2c_add_driver(&egalax_ts_driver);
-}
-
-static void __exit egalax_ts_exit(void)
-{
- i2c_del_driver(&egalax_ts_driver);
-}
-
-module_init(egalax_ts_init);
-module_exit(egalax_ts_exit);
+module_i2c_driver(egalax_ts_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Touchscreen driver for EETI eGalax touch controller");
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index 639a6044183d..85cf9bee8018 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -93,7 +93,7 @@ static int __init hp680_ts_init(void)
hp680_ts_dev->phys = "hp680_ts/input0";
if (request_irq(HP680_TS_IRQ, hp680_ts_interrupt,
- 0, MODNAME, 0) < 0) {
+ 0, MODNAME, NULL) < 0) {
printk(KERN_ERR "hp680_touchscreen.c: Can't allocate irq %d\n",
HP680_TS_IRQ);
err = -EBUSY;
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
new file mode 100644
index 000000000000..c0044175a921
--- /dev/null
+++ b/drivers/input/touchscreen/ili210x.c
@@ -0,0 +1,360 @@
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/input/ili210x.h>
+
+#define MAX_TOUCHES 2
+#define DEFAULT_POLL_PERIOD 20
+
+/* Touchscreen commands */
+#define REG_TOUCHDATA 0x10
+#define REG_PANEL_INFO 0x20
+#define REG_FIRMWARE_VERSION 0x40
+#define REG_CALIBRATE 0xcc
+
+struct finger {
+ u8 x_low;
+ u8 x_high;
+ u8 y_low;
+ u8 y_high;
+} __packed;
+
+struct touchdata {
+ u8 status;
+ struct finger finger[MAX_TOUCHES];
+} __packed;
+
+struct panel_info {
+ struct finger finger_max;
+ u8 xchannel_num;
+ u8 ychannel_num;
+} __packed;
+
+struct firmware_version {
+ u8 id;
+ u8 major;
+ u8 minor;
+} __packed;
+
+struct ili210x {
+ struct i2c_client *client;
+ struct input_dev *input;
+ bool (*get_pendown_state)(void);
+ unsigned int poll_period;
+ struct delayed_work dwork;
+};
+
+static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
+ size_t len)
+{
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ if (i2c_transfer(client->adapter, msg, 2) != 2) {
+ dev_err(&client->dev, "i2c transfer failed\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ili210x_report_events(struct input_dev *input,
+ const struct touchdata *touchdata)
+{
+ int i;
+ bool touch;
+ unsigned int x, y;
+ const struct finger *finger;
+
+ for (i = 0; i < MAX_TOUCHES; i++) {
+ input_mt_slot(input, i);
+
+ finger = &touchdata->finger[i];
+
+ touch = touchdata->status & (1 << i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
+ if (touch) {
+ x = finger->x_low | (finger->x_high << 8);
+ y = finger->y_low | (finger->y_high << 8);
+
+ input_report_abs(input, ABS_MT_POSITION_X, x);
+ input_report_abs(input, ABS_MT_POSITION_Y, y);
+ }
+ }
+
+ input_mt_report_pointer_emulation(input, false);
+ input_sync(input);
+}
+
+static bool get_pendown_state(const struct ili210x *priv)
+{
+ bool state = false;
+
+ if (priv->get_pendown_state)
+ state = priv->get_pendown_state();
+
+ return state;
+}
+
+static void ili210x_work(struct work_struct *work)
+{
+ struct ili210x *priv = container_of(work, struct ili210x,
+ dwork.work);
+ struct i2c_client *client = priv->client;
+ struct touchdata touchdata;
+ int error;
+
+ error = ili210x_read_reg(client, REG_TOUCHDATA,
+ &touchdata, sizeof(touchdata));
+ if (error) {
+ dev_err(&client->dev,
+ "Unable to get touchdata, err = %d\n", error);
+ return;
+ }
+
+ ili210x_report_events(priv->input, &touchdata);
+
+ if ((touchdata.status & 0xf3) || get_pendown_state(priv))
+ schedule_delayed_work(&priv->dwork,
+ msecs_to_jiffies(priv->poll_period));
+}
+
+static irqreturn_t ili210x_irq(int irq, void *irq_data)
+{
+ struct ili210x *priv = irq_data;
+
+ schedule_delayed_work(&priv->dwork, 0);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t ili210x_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct ili210x *priv = i2c_get_clientdata(client);
+ unsigned long calibrate;
+ int rc;
+ u8 cmd = REG_CALIBRATE;
+
+ if (kstrtoul(buf, 10, &calibrate))
+ return -EINVAL;
+
+ if (calibrate > 1)
+ return -EINVAL;
+
+ if (calibrate) {
+ rc = i2c_master_send(priv->client, &cmd, sizeof(cmd));
+ if (rc != sizeof(cmd))
+ return -EIO;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate);
+
+static struct attribute *ili210x_attributes[] = {
+ &dev_attr_calibrate.attr,
+ NULL,
+};
+
+static const struct attribute_group ili210x_attr_group = {
+ .attrs = ili210x_attributes,
+};
+
+static int __devinit ili210x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ const struct ili210x_platform_data *pdata = dev->platform_data;
+ struct ili210x *priv;
+ struct input_dev *input;
+ struct panel_info panel;
+ struct firmware_version firmware;
+ int xmax, ymax;
+ int error;
+
+ dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
+
+ if (!pdata) {
+ dev_err(dev, "No platform data!\n");
+ return -EINVAL;
+ }
+
+ if (client->irq <= 0) {
+ dev_err(dev, "No IRQ!\n");
+ return -EINVAL;
+ }
+
+ /* Get firmware version */
+ error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
+ &firmware, sizeof(firmware));
+ if (error) {
+ dev_err(dev, "Failed to get firmware version, err: %d\n",
+ error);
+ return error;
+ }
+
+ /* get panel info */
+ error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
+ if (error) {
+ dev_err(dev, "Failed to get panel informations, err: %d\n",
+ error);
+ return error;
+ }
+
+ xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
+ ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!priv || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ priv->client = client;
+ priv->input = input;
+ priv->get_pendown_state = pdata->get_pendown_state;
+ priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
+ INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
+
+ /* Setup input device */
+ input->name = "ILI210x Touchscreen";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = dev;
+
+ __set_bit(EV_SYN, input->evbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+ __set_bit(BTN_TOUCH, input->keybit);
+
+ /* Single touch */
+ input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
+
+ /* Multi touch */
+ input_mt_init_slots(input, MAX_TOUCHES);
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
+
+ input_set_drvdata(input, priv);
+ i2c_set_clientdata(client, priv);
+
+ error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
+ client->name, priv);
+ if (error) {
+ dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
+ error);
+ goto err_free_mem;
+ }
+
+ error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
+ if (error) {
+ dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ error = input_register_device(priv->input);
+ if (error) {
+ dev_err(dev, "Cannot regiser input device, err: %d\n", error);
+ goto err_remove_sysfs;
+ }
+
+ device_init_wakeup(&client->dev, 1);
+
+ dev_dbg(dev,
+ "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d",
+ client->irq, firmware.id, firmware.major, firmware.minor);
+
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
+err_free_irq:
+ free_irq(client->irq, priv);
+err_free_mem:
+ input_free_device(input);
+ kfree(priv);
+ return error;
+}
+
+static int __devexit ili210x_i2c_remove(struct i2c_client *client)
+{
+ struct ili210x *priv = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
+ free_irq(priv->client->irq, priv);
+ cancel_delayed_work_sync(&priv->dwork);
+ input_unregister_device(priv->input);
+ kfree(priv);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ili210x_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ return 0;
+}
+
+static int ili210x_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
+ ili210x_i2c_suspend, ili210x_i2c_resume);
+
+static const struct i2c_device_id ili210x_i2c_id[] = {
+ { "ili210x", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
+
+static struct i2c_driver ili210x_ts_driver = {
+ .driver = {
+ .name = "ili210x_i2c",
+ .owner = THIS_MODULE,
+ .pm = &ili210x_i2c_pm,
+ },
+ .id_table = ili210x_i2c_id,
+ .probe = ili210x_i2c_probe,
+ .remove = __devexit_p(ili210x_i2c_remove),
+};
+
+module_i2c_driver(ili210x_ts_driver);
+
+MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>");
+MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/jornada720_ts.c b/drivers/input/touchscreen/jornada720_ts.c
index c3848ad2325b..d9be6eac99b1 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/max11801_ts.c b/drivers/input/touchscreen/max11801_ts.c
index 4627fe55b401..4eab50b856d7 100644
--- a/drivers/input/touchscreen/max11801_ts.c
+++ b/drivers/input/touchscreen/max11801_ts.c
@@ -255,18 +255,7 @@ static struct i2c_driver max11801_ts_driver = {
.remove = __devexit_p(max11801_ts_remove),
};
-static int __init max11801_ts_init(void)
-{
- return i2c_add_driver(&max11801_ts_driver);
-}
-
-static void __exit max11801_ts_exit(void)
-{
- i2c_del_driver(&max11801_ts_driver);
-}
-
-module_init(max11801_ts_init);
-module_exit(max11801_ts_exit);
+module_i2c_driver(max11801_ts_driver);
MODULE_AUTHOR("Zhang Jiejing <jiejing.zhang@freescale.com>");
MODULE_DESCRIPTION("Touchscreen driver for MAXI MAX11801 controller");
diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c
index ede02743eac1..48dc5b0d26f1 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/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c
index 2d84c80ceb66..b528511861ce 100644
--- a/drivers/input/touchscreen/mcs5000_ts.c
+++ b/drivers/input/touchscreen/mcs5000_ts.c
@@ -302,18 +302,7 @@ static struct i2c_driver mcs5000_ts_driver = {
.id_table = mcs5000_ts_id,
};
-static int __init mcs5000_ts_init(void)
-{
- return i2c_add_driver(&mcs5000_ts_driver);
-}
-
-static void __exit mcs5000_ts_exit(void)
-{
- i2c_del_driver(&mcs5000_ts_driver);
-}
-
-module_init(mcs5000_ts_init);
-module_exit(mcs5000_ts_exit);
+module_i2c_driver(mcs5000_ts_driver);
/* Module information */
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
diff --git a/drivers/input/touchscreen/migor_ts.c b/drivers/input/touchscreen/migor_ts.c
index 5226194aa78e..c038db93e2c3 100644
--- a/drivers/input/touchscreen/migor_ts.c
+++ b/drivers/input/touchscreen/migor_ts.c
@@ -242,19 +242,8 @@ static struct i2c_driver migor_ts_driver = {
.id_table = migor_ts_id,
};
-static int __init migor_ts_init(void)
-{
- return i2c_add_driver(&migor_ts_driver);
-}
-
-static void __exit migor_ts_exit(void)
-{
- i2c_del_driver(&migor_ts_driver);
-}
+module_i2c_driver(migor_ts_driver);
MODULE_DESCRIPTION("MigoR Touchscreen driver");
MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
MODULE_LICENSE("GPL");
-
-module_init(migor_ts_init);
-module_exit(migor_ts_exit);
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index d5ac09a1ee56..72f6ba3a4709 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -222,17 +222,7 @@ static struct i2c_driver pixcir_i2c_ts_driver = {
.id_table = pixcir_i2c_ts_id,
};
-static int __init pixcir_i2c_ts_init(void)
-{
- return i2c_add_driver(&pixcir_i2c_ts_driver);
-}
-module_init(pixcir_i2c_ts_init);
-
-static void __exit pixcir_i2c_ts_exit(void)
-{
- i2c_del_driver(&pixcir_i2c_ts_driver);
-}
-module_exit(pixcir_i2c_ts_exit);
+module_i2c_driver(pixcir_i2c_ts_driver);
MODULE_AUTHOR("Jianchun Bian <jcbian@pixcir.com.cn>, Dequan Meng <dqmeng@pixcir.com.cn>");
MODULE_DESCRIPTION("Pixcir I2C Touchscreen Driver");
diff --git a/drivers/input/touchscreen/st1232.c b/drivers/input/touchscreen/st1232.c
index 8825fe37d433..cbbf71b22696 100644
--- a/drivers/input/touchscreen/st1232.c
+++ b/drivers/input/touchscreen/st1232.c
@@ -268,17 +268,7 @@ static struct i2c_driver st1232_ts_driver = {
},
};
-static int __init st1232_ts_init(void)
-{
- return i2c_add_driver(&st1232_ts_driver);
-}
-module_init(st1232_ts_init);
-
-static void __exit st1232_ts_exit(void)
-{
- i2c_del_driver(&st1232_ts_driver);
-}
-module_exit(st1232_ts_exit);
+module_i2c_driver(st1232_ts_driver);
MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c
new file mode 100644
index 000000000000..d229c741d544
--- /dev/null
+++ b/drivers/input/touchscreen/ti_tscadc.c
@@ -0,0 +1,486 @@
+/*
+ * TI Touch Screen 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/init.h>
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/input/ti_tscadc.h>
+#include <linux/delay.h>
+
+#define REG_IRQEOI 0x020
+#define REG_RAWIRQSTATUS 0x024
+#define REG_IRQSTATUS 0x028
+#define REG_IRQENABLE 0x02C
+#define REG_IRQWAKEUP 0x034
+#define REG_CTRL 0x040
+#define REG_ADCFSM 0x044
+#define REG_CLKDIV 0x04C
+#define REG_SE 0x054
+#define REG_IDLECONFIG 0x058
+#define REG_CHARGECONFIG 0x05C
+#define REG_CHARGEDELAY 0x060
+#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8))
+#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8))
+#define REG_STEPCONFIG13 0x0C4
+#define REG_STEPDELAY13 0x0C8
+#define REG_STEPCONFIG14 0x0CC
+#define REG_STEPDELAY14 0x0D0
+#define REG_FIFO0CNT 0xE4
+#define REG_FIFO1THR 0xF4
+#define REG_FIFO0 0x100
+#define REG_FIFO1 0x200
+
+/* Register Bitfields */
+#define IRQWKUP_ENB BIT(0)
+#define STPENB_STEPENB 0x7FFF
+#define IRQENB_FIFO1THRES BIT(5)
+#define IRQENB_PENUP BIT(9)
+#define STEPCONFIG_MODE_HWSYNC 0x2
+#define STEPCONFIG_SAMPLES_AVG (1 << 4)
+#define STEPCONFIG_XPP (1 << 5)
+#define STEPCONFIG_XNN (1 << 6)
+#define STEPCONFIG_YPP (1 << 7)
+#define STEPCONFIG_YNN (1 << 8)
+#define STEPCONFIG_XNP (1 << 9)
+#define STEPCONFIG_YPN (1 << 10)
+#define STEPCONFIG_INM (1 << 18)
+#define STEPCONFIG_INP (1 << 20)
+#define STEPCONFIG_INP_5 (1 << 21)
+#define STEPCONFIG_FIFO1 (1 << 26)
+#define STEPCONFIG_OPENDLY 0xff
+#define STEPCONFIG_Z1 (3 << 19)
+#define STEPIDLE_INP (1 << 22)
+#define STEPCHARGE_RFP (1 << 12)
+#define STEPCHARGE_INM (1 << 15)
+#define STEPCHARGE_INP (1 << 19)
+#define STEPCHARGE_RFM (1 << 23)
+#define STEPCHARGE_DELAY 0x1
+#define CNTRLREG_TSCSSENB (1 << 0)
+#define CNTRLREG_STEPID (1 << 1)
+#define CNTRLREG_STEPCONFIGWRT (1 << 2)
+#define CNTRLREG_4WIRE (1 << 5)
+#define CNTRLREG_5WIRE (1 << 6)
+#define CNTRLREG_8WIRE (3 << 5)
+#define CNTRLREG_TSCENB (1 << 7)
+#define ADCFSM_STEPID 0x10
+
+#define SEQ_SETTLE 275
+#define ADC_CLK 3000000
+#define MAX_12BIT ((1 << 12) - 1)
+#define TSCADC_DELTA_X 15
+#define TSCADC_DELTA_Y 15
+
+struct tscadc {
+ struct input_dev *input;
+ struct clk *tsc_ick;
+ void __iomem *tsc_base;
+ unsigned int irq;
+ unsigned int wires;
+ unsigned int x_plate_resistance;
+ bool pen_down;
+};
+
+static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg)
+{
+ return readl(ts->tsc_base + reg);
+}
+
+static void tscadc_writel(struct tscadc *tsc, unsigned int reg,
+ unsigned int val)
+{
+ writel(val, tsc->tsc_base + reg);
+}
+
+static void tscadc_step_config(struct tscadc *ts_dev)
+{
+ unsigned int config;
+ int i;
+
+ /* Configure the Step registers */
+
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_INP | STEPCONFIG_XNN;
+ break;
+ case 5:
+ config |= STEPCONFIG_YNN |
+ STEPCONFIG_INP_5 | STEPCONFIG_XNN |
+ STEPCONFIG_YPP;
+ break;
+ case 8:
+ config |= STEPCONFIG_INP | STEPCONFIG_XNN;
+ break;
+ }
+
+ for (i = 1; i < 7; i++) {
+ tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN |
+ STEPCONFIG_INM | STEPCONFIG_FIFO1;
+ switch (ts_dev->wires) {
+ case 4:
+ config |= STEPCONFIG_YPP;
+ break;
+ case 5:
+ config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 |
+ STEPCONFIG_XNP | STEPCONFIG_YPN;
+ break;
+ case 8:
+ config |= STEPCONFIG_YPP;
+ break;
+ }
+
+ for (i = 7; i < 13; i++) {
+ tscadc_writel(ts_dev, REG_STEPCONFIG(i), config);
+ tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY);
+ }
+
+ config = 0;
+ /* Charge step configuration */
+ config = STEPCONFIG_XPP | STEPCONFIG_YNN |
+ STEPCHARGE_RFP | STEPCHARGE_RFM |
+ STEPCHARGE_INM | STEPCHARGE_INP;
+
+ tscadc_writel(ts_dev, REG_CHARGECONFIG, config);
+ tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY);
+
+ config = 0;
+ /* Configure to calculate pressure */
+ config = STEPCONFIG_MODE_HWSYNC |
+ STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP |
+ STEPCONFIG_XNN | STEPCONFIG_INM;
+ tscadc_writel(ts_dev, REG_STEPCONFIG13, config);
+ tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY);
+
+ config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1;
+ tscadc_writel(ts_dev, REG_STEPCONFIG14, config);
+ tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY);
+
+ tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
+}
+
+static void tscadc_idle_config(struct tscadc *ts_config)
+{
+ unsigned int idleconfig;
+
+ idleconfig = STEPCONFIG_YNN |
+ STEPCONFIG_INM |
+ STEPCONFIG_YPN | STEPIDLE_INP;
+ tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig);
+}
+
+static void tscadc_read_coordinates(struct tscadc *ts_dev,
+ unsigned int *x, unsigned int *y)
+{
+ unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT);
+ unsigned int prev_val_x = ~0, prev_val_y = ~0;
+ unsigned int prev_diff_x = ~0, prev_diff_y = ~0;
+ unsigned int read, diff;
+ unsigned int i;
+
+ /*
+ * Delta filter is used to remove large variations in sampled
+ * values from ADC. The filter tries to predict where the next
+ * coordinate could be. This is done by taking a previous
+ * coordinate and subtracting it form current one. Further the
+ * algorithm compares the difference with that of a present value,
+ * if true the value is reported to the sub system.
+ */
+ for (i = 0; i < fifocount - 1; i++) {
+ read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
+ diff = abs(read - prev_val_x);
+ if (diff < prev_diff_x) {
+ prev_diff_x = diff;
+ *x = read;
+ }
+ prev_val_x = read;
+
+ read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
+ diff = abs(read - prev_val_y);
+ if (diff < prev_diff_y) {
+ prev_diff_y = diff;
+ *y = read;
+ }
+ prev_val_y = read;
+ }
+}
+
+static irqreturn_t tscadc_irq(int irq, void *dev)
+{
+ struct tscadc *ts_dev = dev;
+ struct input_dev *input_dev = ts_dev->input;
+ unsigned int status, irqclr = 0;
+ unsigned int x = 0, y = 0;
+ unsigned int z1, z2, z;
+ unsigned int fsm;
+
+ status = tscadc_readl(ts_dev, REG_IRQSTATUS);
+ if (status & IRQENB_FIFO1THRES) {
+ tscadc_read_coordinates(ts_dev, &x, &y);
+
+ z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff;
+ z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff;
+
+ if (ts_dev->pen_down && z1 != 0 && z2 != 0) {
+ /*
+ * Calculate pressure using formula
+ * Resistance(touch) = x plate resistance *
+ * x postion/4096 * ((z2 / z1) - 1)
+ */
+ z = z2 - z1;
+ z *= x;
+ z *= ts_dev->x_plate_resistance;
+ z /= z1;
+ z = (z + 2047) >> 12;
+
+ if (z <= MAX_12BIT) {
+ input_report_abs(input_dev, ABS_X, x);
+ input_report_abs(input_dev, ABS_Y, y);
+ input_report_abs(input_dev, ABS_PRESSURE, z);
+ input_report_key(input_dev, BTN_TOUCH, 1);
+ input_sync(input_dev);
+ }
+ }
+ irqclr |= IRQENB_FIFO1THRES;
+ }
+
+ /*
+ * Time for sequencer to settle, to read
+ * correct state of the sequencer.
+ */
+ udelay(SEQ_SETTLE);
+
+ status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS);
+ if (status & IRQENB_PENUP) {
+ /* Pen up event */
+ fsm = tscadc_readl(ts_dev, REG_ADCFSM);
+ if (fsm == ADCFSM_STEPID) {
+ ts_dev->pen_down = false;
+ input_report_key(input_dev, BTN_TOUCH, 0);
+ input_report_abs(input_dev, ABS_PRESSURE, 0);
+ input_sync(input_dev);
+ } else {
+ ts_dev->pen_down = true;
+ }
+ irqclr |= IRQENB_PENUP;
+ }
+
+ tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr);
+ /* check pending interrupts */
+ tscadc_writel(ts_dev, REG_IRQEOI, 0x0);
+
+ tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB);
+ return IRQ_HANDLED;
+}
+
+/*
+ * The functions for inserting/removing driver as a module.
+ */
+
+static int __devinit tscadc_probe(struct platform_device *pdev)
+{
+ const struct tsc_data *pdata = pdev->dev.platform_data;
+ struct resource *res;
+ struct tscadc *ts_dev;
+ struct input_dev *input_dev;
+ struct clk *clk;
+ int err;
+ int clk_value, ctrl, irq;
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data.\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no memory resource defined.\n");
+ return -EINVAL;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq ID is specified.\n");
+ return -EINVAL;
+ }
+
+ /* Allocate memory for device */
+ ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ts_dev || !input_dev) {
+ dev_err(&pdev->dev, "failed to allocate memory.\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ts_dev->input = input_dev;
+ ts_dev->irq = irq;
+ ts_dev->wires = pdata->wires;
+ ts_dev->x_plate_resistance = pdata->x_plate_resistance;
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to reserve registers.\n");
+ err = -EBUSY;
+ goto err_free_mem;
+ }
+
+ ts_dev->tsc_base = ioremap(res->start, resource_size(res));
+ if (!ts_dev->tsc_base) {
+ dev_err(&pdev->dev, "failed to map registers.\n");
+ err = -ENOMEM;
+ goto err_release_mem_region;
+ }
+
+ err = request_irq(ts_dev->irq, tscadc_irq,
+ 0, pdev->dev.driver->name, ts_dev);
+ if (err) {
+ dev_err(&pdev->dev, "failed to allocate irq.\n");
+ goto err_unmap_regs;
+ }
+
+ ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick");
+ if (IS_ERR(ts_dev->tsc_ick)) {
+ dev_err(&pdev->dev, "failed to get TSC ick\n");
+ goto err_free_irq;
+ }
+ clk_enable(ts_dev->tsc_ick);
+
+ clk = clk_get(&pdev->dev, "adc_tsc_fck");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get TSC fck\n");
+ err = PTR_ERR(clk);
+ goto err_disable_clk;
+ }
+
+ clk_value = clk_get_rate(clk) / ADC_CLK;
+ clk_put(clk);
+
+ if (clk_value < 7) {
+ dev_err(&pdev->dev, "clock input less than min clock requirement\n");
+ goto err_disable_clk;
+ }
+ /* CLKDIV needs to be configured to the value minus 1 */
+ tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1);
+
+ /* Enable wake-up of the SoC using touchscreen */
+ tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB);
+
+ ctrl = CNTRLREG_STEPCONFIGWRT |
+ CNTRLREG_TSCENB |
+ CNTRLREG_STEPID;
+ switch (ts_dev->wires) {
+ case 4:
+ ctrl |= CNTRLREG_4WIRE;
+ break;
+ case 5:
+ ctrl |= CNTRLREG_5WIRE;
+ break;
+ case 8:
+ ctrl |= CNTRLREG_8WIRE;
+ break;
+ }
+ tscadc_writel(ts_dev, REG_CTRL, ctrl);
+
+ tscadc_idle_config(ts_dev);
+ tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
+ tscadc_step_config(ts_dev);
+ tscadc_writel(ts_dev, REG_FIFO1THR, 6);
+
+ ctrl |= CNTRLREG_TSCSSENB;
+ tscadc_writel(ts_dev, REG_CTRL, ctrl);
+
+ input_dev->name = "ti-tsc-adc";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);
+
+ /* register to the input system */
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_disable_clk;
+
+ platform_set_drvdata(pdev, ts_dev);
+ return 0;
+
+err_disable_clk:
+ clk_disable(ts_dev->tsc_ick);
+ clk_put(ts_dev->tsc_ick);
+err_free_irq:
+ free_irq(ts_dev->irq, ts_dev);
+err_unmap_regs:
+ iounmap(ts_dev->tsc_base);
+err_release_mem_region:
+ release_mem_region(res->start, resource_size(res));
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts_dev);
+ return err;
+}
+
+static int __devexit tscadc_remove(struct platform_device *pdev)
+{
+ struct tscadc *ts_dev = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(ts_dev->irq, ts_dev);
+
+ input_unregister_device(ts_dev->input);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ iounmap(ts_dev->tsc_base);
+ release_mem_region(res->start, resource_size(res));
+
+ clk_disable(ts_dev->tsc_ick);
+ clk_put(ts_dev->tsc_ick);
+
+ kfree(ts_dev);
+
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct platform_driver ti_tsc_driver = {
+ .probe = tscadc_probe,
+ .remove = __devexit_p(tscadc_remove),
+ .driver = {
+ .name = "tsc",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(ti_tsc_driver);
+
+MODULE_DESCRIPTION("TI touchscreen controller driver");
+MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
index 067d95662997..b6adeaee9cc5 100644
--- a/drivers/input/touchscreen/tsc2005.c
+++ b/drivers/input/touchscreen/tsc2005.c
@@ -747,17 +747,7 @@ static struct spi_driver tsc2005_driver = {
.remove = __devexit_p(tsc2005_remove),
};
-static int __init tsc2005_init(void)
-{
- return spi_register_driver(&tsc2005_driver);
-}
-module_init(tsc2005_init);
-
-static void __exit tsc2005_exit(void)
-{
- spi_unregister_driver(&tsc2005_driver);
-}
-module_exit(tsc2005_exit);
+module_spi_driver(tsc2005_driver);
MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
MODULE_DESCRIPTION("TSC2005 Touchscreen Driver");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 1f674cb6c55b..1473d2382afd 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -399,18 +399,7 @@ static struct i2c_driver tsc2007_driver = {
.remove = __devexit_p(tsc2007_remove),
};
-static int __init tsc2007_init(void)
-{
- return i2c_add_driver(&tsc2007_driver);
-}
-
-static void __exit tsc2007_exit(void)
-{
- i2c_del_driver(&tsc2007_driver);
-}
-
-module_init(tsc2007_init);
-module_exit(tsc2007_exit);
+module_i2c_driver(tsc2007_driver);
MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");
MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index 3a5ebf452e81..22cd96f58c99 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -17,6 +17,7 @@
* - Zytronic capacitive touchscreen
* - NEXIO/iNexio
* - Elo TouchSystems 2700 IntelliTouch
+ * - EasyTouch USB Dual/Multi touch controller from Data Modul
*
* Copyright (C) 2004-2007 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -140,6 +141,7 @@ enum {
DEVTYPE_TC45USB,
DEVTYPE_NEXIO,
DEVTYPE_ELO,
+ DEVTYPE_ETOUCH,
};
#define USB_DEVICE_HID_CLASS(vend, prod) \
@@ -245,6 +247,10 @@ static const struct usb_device_id usbtouch_devices[] = {
{USB_DEVICE(0x04e7, 0x0020), .driver_info = DEVTYPE_ELO},
#endif
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+ {USB_DEVICE(0x7374, 0x0001), .driver_info = DEVTYPE_ETOUCH},
+#endif
+
{}
};
@@ -326,6 +332,51 @@ static int egalax_get_pkt_len(unsigned char *buf, int len)
}
#endif
+/*****************************************************************************
+ * EasyTouch part
+ */
+
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+
+#ifndef MULTI_PACKET
+#define MULTI_PACKET
+#endif
+
+#define ETOUCH_PKT_TYPE_MASK 0xFE
+#define ETOUCH_PKT_TYPE_REPT 0x80
+#define ETOUCH_PKT_TYPE_REPT2 0xB0
+#define ETOUCH_PKT_TYPE_DIAG 0x0A
+
+static int etouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
+{
+ if ((pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT &&
+ (pkt[0] & ETOUCH_PKT_TYPE_MASK) != ETOUCH_PKT_TYPE_REPT2)
+ return 0;
+
+ dev->x = ((pkt[1] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ dev->y = ((pkt[3] & 0x1F) << 7) | (pkt[4] & 0x7F);
+ dev->touch = pkt[0] & 0x01;
+
+ return 1;
+}
+
+static int etouch_get_pkt_len(unsigned char *buf, int len)
+{
+ switch (buf[0] & ETOUCH_PKT_TYPE_MASK) {
+ case ETOUCH_PKT_TYPE_REPT:
+ case ETOUCH_PKT_TYPE_REPT2:
+ return 5;
+
+ case ETOUCH_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
+ return buf[1] + 2;
+ }
+
+ return 0;
+}
+#endif
/*****************************************************************************
* PanJit Part
@@ -1175,6 +1226,18 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.exit = nexio_exit,
},
#endif
+#ifdef CONFIG_TOUCHSCREEN_USB_EASYTOUCH
+ [DEVTYPE_ETOUCH] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 16,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = etouch_get_pkt_len,
+ .read_data = etouch_read_data,
+ },
+#endif
};
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6bea6962f8ee..3bd9fff5c589 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -142,4 +142,24 @@ config OMAP_IOMMU_DEBUG
Say N unless you know you need this.
+config TEGRA_IOMMU_GART
+ bool "Tegra GART IOMMU Support"
+ depends on ARCH_TEGRA_2x_SOC
+ select IOMMU_API
+ help
+ Enables support for remapping discontiguous physical memory
+ shared with the operating system into contiguous I/O virtual
+ space through the GART (Graphics Address Relocation Table)
+ hardware included on Tegra SoCs.
+
+config TEGRA_IOMMU_SMMU
+ bool "Tegra SMMU IOMMU Support"
+ depends on ARCH_TEGRA_3x_SOC
+ select IOMMU_API
+ help
+ Enables support for remapping discontiguous physical memory
+ shared with the operating system into contiguous I/O virtual
+ space through the SMMU (System Memory Management Unit)
+ hardware included on Tegra SoCs.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 0e36b4934aff..7ad7a3bc1242 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -8,3 +8,5 @@ obj-$(CONFIG_IRQ_REMAP) += intr_remapping.o
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
+obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
+obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index a35e98ad9725..c56790375e0f 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -196,6 +196,8 @@ static u32 rlookup_table_size; /* size if the rlookup table */
*/
extern void iommu_flush_all_caches(struct amd_iommu *iommu);
+static int amd_iommu_enable_interrupts(void);
+
static inline void update_last_devid(u16 devid)
{
if (devid > amd_iommu_last_bdf)
@@ -358,8 +360,6 @@ static void iommu_disable(struct amd_iommu *iommu)
*/
static u8 * __init iommu_map_mmio_space(u64 address)
{
- u8 *ret;
-
if (!request_mem_region(address, MMIO_REGION_LENGTH, "amd_iommu")) {
pr_err("AMD-Vi: Can not reserve memory region %llx for mmio\n",
address);
@@ -367,13 +367,7 @@ static u8 * __init iommu_map_mmio_space(u64 address)
return NULL;
}
- ret = ioremap_nocache(address, MMIO_REGION_LENGTH);
- if (ret != NULL)
- return ret;
-
- release_mem_region(address, MMIO_REGION_LENGTH);
-
- return NULL;
+ return ioremap_nocache(address, MMIO_REGION_LENGTH);
}
static void __init iommu_unmap_mmio_space(struct amd_iommu *iommu)
@@ -1131,8 +1125,9 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
{
int r;
- if (pci_enable_msi(iommu->dev))
- return 1;
+ r = pci_enable_msi(iommu->dev);
+ if (r)
+ return r;
r = request_threaded_irq(iommu->dev->irq,
amd_iommu_int_handler,
@@ -1142,27 +1137,36 @@ static int iommu_setup_msi(struct amd_iommu *iommu)
if (r) {
pci_disable_msi(iommu->dev);
- return 1;
+ return r;
}
iommu->int_enabled = true;
- iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
-
- if (iommu->ppr_log != NULL)
- iommu_feature_enable(iommu, CONTROL_PPFINT_EN);
return 0;
}
static int iommu_init_msi(struct amd_iommu *iommu)
{
+ int ret;
+
if (iommu->int_enabled)
- return 0;
+ goto enable_faults;
if (pci_find_capability(iommu->dev, PCI_CAP_ID_MSI))
- return iommu_setup_msi(iommu);
+ ret = iommu_setup_msi(iommu);
+ else
+ ret = -ENODEV;
- return 1;
+ if (ret)
+ return ret;
+
+enable_faults:
+ iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
+
+ if (iommu->ppr_log != NULL)
+ iommu_feature_enable(iommu, CONTROL_PPFINT_EN);
+
+ return 0;
}
/****************************************************************************
@@ -1381,7 +1385,6 @@ static void enable_iommus(void)
iommu_enable_ppr_log(iommu);
iommu_enable_gt(iommu);
iommu_set_exclusion_range(iommu);
- iommu_init_msi(iommu);
iommu_enable(iommu);
iommu_flush_all_caches(iommu);
}
@@ -1409,6 +1412,8 @@ static void amd_iommu_resume(void)
/* re-load the hardware */
enable_iommus();
+
+ amd_iommu_enable_interrupts();
}
static int amd_iommu_suspend(void)
@@ -1424,10 +1429,40 @@ static struct syscore_ops amd_iommu_syscore_ops = {
.resume = amd_iommu_resume,
};
+static void __init free_on_init_error(void)
+{
+ amd_iommu_uninit_devices();
+
+ free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
+ get_order(MAX_DOMAIN_ID/8));
+
+ free_pages((unsigned long)amd_iommu_rlookup_table,
+ get_order(rlookup_table_size));
+
+ free_pages((unsigned long)amd_iommu_alias_table,
+ get_order(alias_table_size));
+
+ free_pages((unsigned long)amd_iommu_dev_table,
+ get_order(dev_table_size));
+
+ free_iommu_all();
+
+ free_unity_maps();
+
+#ifdef CONFIG_GART_IOMMU
+ /*
+ * We failed to initialize the AMD IOMMU - try fallback to GART
+ * if possible.
+ */
+ gart_iommu_init();
+
+#endif
+}
+
/*
- * This is the core init function for AMD IOMMU hardware in the system.
- * This function is called from the generic x86 DMA layer initialization
- * code.
+ * This is the hardware init function for AMD IOMMU in the system.
+ * This function is called either from amd_iommu_init or from the interrupt
+ * remapping setup code.
*
* This function basically parses the ACPI table for AMD IOMMU (IVRS)
* three times:
@@ -1446,16 +1481,21 @@ static struct syscore_ops amd_iommu_syscore_ops = {
* remapping requirements parsed out of the ACPI table in
* this last pass.
*
- * After that the hardware is initialized and ready to go. In the last
- * step we do some Linux specific things like registering the driver in
- * the dma_ops interface and initializing the suspend/resume support
- * functions. Finally it prints some information about AMD IOMMUs and
- * the driver state and enables the hardware.
+ * After everything is set up the IOMMUs are enabled and the necessary
+ * hotplug and suspend notifiers are registered.
*/
-static int __init amd_iommu_init(void)
+int __init amd_iommu_init_hardware(void)
{
int i, ret = 0;
+ if (!amd_iommu_detected)
+ return -ENODEV;
+
+ if (amd_iommu_dev_table != NULL) {
+ /* Hardware already initialized */
+ return 0;
+ }
+
/*
* First parse ACPI tables to find the largest Bus/Dev/Func
* we need to handle. Upon this information the shared data
@@ -1472,9 +1512,8 @@ static int __init amd_iommu_init(void)
alias_table_size = tbl_size(ALIAS_TABLE_ENTRY_SIZE);
rlookup_table_size = tbl_size(RLOOKUP_TABLE_ENTRY_SIZE);
- ret = -ENOMEM;
-
/* Device table - directly used by all IOMMUs */
+ ret = -ENOMEM;
amd_iommu_dev_table = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(dev_table_size));
if (amd_iommu_dev_table == NULL)
@@ -1546,20 +1585,65 @@ static int __init amd_iommu_init(void)
enable_iommus();
+ amd_iommu_init_notifier();
+
+ register_syscore_ops(&amd_iommu_syscore_ops);
+
+out:
+ return ret;
+
+free:
+ free_on_init_error();
+
+ return ret;
+}
+
+static int amd_iommu_enable_interrupts(void)
+{
+ struct amd_iommu *iommu;
+ int ret = 0;
+
+ for_each_iommu(iommu) {
+ ret = iommu_init_msi(iommu);
+ if (ret)
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+/*
+ * This is the core init function for AMD IOMMU hardware in the system.
+ * This function is called from the generic x86 DMA layer initialization
+ * code.
+ *
+ * The function calls amd_iommu_init_hardware() to setup and enable the
+ * IOMMU hardware if this has not happened yet. After that the driver
+ * registers for the DMA-API and for the IOMMU-API as necessary.
+ */
+static int __init amd_iommu_init(void)
+{
+ int ret = 0;
+
+ ret = amd_iommu_init_hardware();
+ if (ret)
+ goto out;
+
+ ret = amd_iommu_enable_interrupts();
+ if (ret)
+ goto free;
+
if (iommu_pass_through)
ret = amd_iommu_init_passthrough();
else
ret = amd_iommu_init_dma_ops();
if (ret)
- goto free_disable;
+ goto free;
amd_iommu_init_api();
- amd_iommu_init_notifier();
-
- register_syscore_ops(&amd_iommu_syscore_ops);
-
if (iommu_pass_through)
goto out;
@@ -1569,39 +1653,14 @@ static int __init amd_iommu_init(void)
printk(KERN_INFO "AMD-Vi: Lazy IO/TLB flushing enabled\n");
x86_platform.iommu_shutdown = disable_iommus;
+
out:
return ret;
-free_disable:
- disable_iommus();
-
free:
- amd_iommu_uninit_devices();
-
- free_pages((unsigned long)amd_iommu_pd_alloc_bitmap,
- get_order(MAX_DOMAIN_ID/8));
-
- free_pages((unsigned long)amd_iommu_rlookup_table,
- get_order(rlookup_table_size));
-
- free_pages((unsigned long)amd_iommu_alias_table,
- get_order(alias_table_size));
-
- free_pages((unsigned long)amd_iommu_dev_table,
- get_order(dev_table_size));
-
- free_iommu_all();
-
- free_unity_maps();
-
-#ifdef CONFIG_GART_IOMMU
- /*
- * We failed to initialize the AMD IOMMU - try fallback to GART
- * if possible.
- */
- gart_iommu_init();
+ disable_iommus();
-#endif
+ free_on_init_error();
goto out;
}
diff --git a/drivers/iommu/amd_iommu_v2.c b/drivers/iommu/amd_iommu_v2.c
index 8add9f125d3e..036fe9bf157e 100644
--- a/drivers/iommu/amd_iommu_v2.c
+++ b/drivers/iommu/amd_iommu_v2.c
@@ -921,7 +921,16 @@ static int __init amd_iommu_v2_init(void)
size_t state_table_size;
int ret;
- pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>");
+ pr_info("AMD IOMMUv2 driver by Joerg Roedel <joerg.roedel@amd.com>\n");
+
+ if (!amd_iommu_v2_supported()) {
+ pr_info("AMD IOMMUv2 functionality not available on this sytem\n");
+ /*
+ * Load anyway to provide the symbols to other modules
+ * which may use AMD IOMMUv2 optionally.
+ */
+ return 0;
+ }
spin_lock_init(&state_lock);
@@ -961,6 +970,9 @@ static void __exit amd_iommu_v2_exit(void)
size_t state_table_size;
int i;
+ if (!amd_iommu_v2_supported())
+ return;
+
profile_event_unregister(PROFILE_TASK_EXIT, &profile_nb);
amd_iommu_unregister_ppr_notifier(&ppr_nb);
diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c
new file mode 100644
index 000000000000..779306ee7b16
--- /dev/null
+++ b/drivers/iommu/tegra-gart.c
@@ -0,0 +1,451 @@
+/*
+ * IOMMU API for GART in Tegra20
+ *
+ * Copyright (c) 2010-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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define pr_fmt(fmt) "%s(): " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+
+#include <asm/cacheflush.h>
+
+/* bitmap of the page sizes currently supported */
+#define GART_IOMMU_PGSIZES (SZ_4K)
+
+#define GART_CONFIG 0x24
+#define GART_ENTRY_ADDR 0x28
+#define GART_ENTRY_DATA 0x2c
+#define GART_ENTRY_PHYS_ADDR_VALID (1 << 31)
+
+#define GART_PAGE_SHIFT 12
+#define GART_PAGE_SIZE (1 << GART_PAGE_SHIFT)
+#define GART_PAGE_MASK \
+ (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID)
+
+struct gart_client {
+ struct device *dev;
+ struct list_head list;
+};
+
+struct gart_device {
+ void __iomem *regs;
+ u32 *savedata;
+ u32 page_count; /* total remappable size */
+ dma_addr_t iovmm_base; /* offset to vmm_area */
+ spinlock_t pte_lock; /* for pagetable */
+ struct list_head client;
+ spinlock_t client_lock; /* for client list */
+ struct device *dev;
+};
+
+static struct gart_device *gart_handle; /* unique for a system */
+
+#define GART_PTE(_pfn) \
+ (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT))
+
+/*
+ * Any interaction between any block on PPSB and a block on APB or AHB
+ * must have these read-back to ensure the APB/AHB bus transaction is
+ * complete before initiating activity on the PPSB block.
+ */
+#define FLUSH_GART_REGS(gart) ((void)readl((gart)->regs + GART_CONFIG))
+
+#define for_each_gart_pte(gart, iova) \
+ for (iova = gart->iovmm_base; \
+ iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \
+ iova += GART_PAGE_SIZE)
+
+static inline void gart_set_pte(struct gart_device *gart,
+ unsigned long offs, u32 pte)
+{
+ writel(offs, gart->regs + GART_ENTRY_ADDR);
+ writel(pte, gart->regs + GART_ENTRY_DATA);
+
+ dev_dbg(gart->dev, "%s %08lx:%08x\n",
+ pte ? "map" : "unmap", offs, pte & GART_PAGE_MASK);
+}
+
+static inline unsigned long gart_read_pte(struct gart_device *gart,
+ unsigned long offs)
+{
+ unsigned long pte;
+
+ writel(offs, gart->regs + GART_ENTRY_ADDR);
+ pte = readl(gart->regs + GART_ENTRY_DATA);
+
+ return pte;
+}
+
+static void do_gart_setup(struct gart_device *gart, const u32 *data)
+{
+ unsigned long iova;
+
+ for_each_gart_pte(gart, iova)
+ gart_set_pte(gart, iova, data ? *(data++) : 0);
+
+ writel(1, gart->regs + GART_CONFIG);
+ FLUSH_GART_REGS(gart);
+}
+
+#ifdef DEBUG
+static void gart_dump_table(struct gart_device *gart)
+{
+ unsigned long iova;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ for_each_gart_pte(gart, iova) {
+ unsigned long pte;
+
+ pte = gart_read_pte(gart, iova);
+
+ dev_dbg(gart->dev, "%s %08lx:%08lx\n",
+ (GART_ENTRY_PHYS_ADDR_VALID & pte) ? "v" : " ",
+ iova, pte & GART_PAGE_MASK);
+ }
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+}
+#else
+static inline void gart_dump_table(struct gart_device *gart)
+{
+}
+#endif
+
+static inline bool gart_iova_range_valid(struct gart_device *gart,
+ unsigned long iova, size_t bytes)
+{
+ unsigned long iova_start, iova_end, gart_start, gart_end;
+
+ iova_start = iova;
+ iova_end = iova_start + bytes - 1;
+ gart_start = gart->iovmm_base;
+ gart_end = gart_start + gart->page_count * GART_PAGE_SIZE - 1;
+
+ if (iova_start < gart_start)
+ return false;
+ if (iova_end > gart_end)
+ return false;
+ return true;
+}
+
+static int gart_iommu_attach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct gart_device *gart;
+ struct gart_client *client, *c;
+ int err = 0;
+
+ gart = dev_get_drvdata(dev->parent);
+ if (!gart)
+ return -EINVAL;
+ domain->priv = gart;
+
+ client = devm_kzalloc(gart->dev, sizeof(*c), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+ client->dev = dev;
+
+ spin_lock(&gart->client_lock);
+ list_for_each_entry(c, &gart->client, list) {
+ if (c->dev == dev) {
+ dev_err(gart->dev,
+ "%s is already attached\n", dev_name(dev));
+ err = -EINVAL;
+ goto fail;
+ }
+ }
+ list_add(&client->list, &gart->client);
+ spin_unlock(&gart->client_lock);
+ dev_dbg(gart->dev, "Attached %s\n", dev_name(dev));
+ return 0;
+
+fail:
+ devm_kfree(gart->dev, client);
+ spin_unlock(&gart->client_lock);
+ return err;
+}
+
+static void gart_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct gart_device *gart = domain->priv;
+ struct gart_client *c;
+
+ spin_lock(&gart->client_lock);
+
+ list_for_each_entry(c, &gart->client, list) {
+ if (c->dev == dev) {
+ list_del(&c->list);
+ devm_kfree(gart->dev, c);
+ dev_dbg(gart->dev, "Detached %s\n", dev_name(dev));
+ goto out;
+ }
+ }
+ dev_err(gart->dev, "Couldn't find\n");
+out:
+ spin_unlock(&gart->client_lock);
+}
+
+static int gart_iommu_domain_init(struct iommu_domain *domain)
+{
+ return 0;
+}
+
+static void gart_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct gart_device *gart = domain->priv;
+
+ if (!gart)
+ return;
+
+ spin_lock(&gart->client_lock);
+ if (!list_empty(&gart->client)) {
+ struct gart_client *c;
+
+ list_for_each_entry(c, &gart->client, list)
+ gart_iommu_detach_dev(domain, c->dev);
+ }
+ spin_unlock(&gart->client_lock);
+ domain->priv = NULL;
+}
+
+static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t pa, size_t bytes, int prot)
+{
+ struct gart_device *gart = domain->priv;
+ unsigned long flags;
+ unsigned long pfn;
+
+ if (!gart_iova_range_valid(gart, iova, bytes))
+ return -EINVAL;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ pfn = __phys_to_pfn(pa);
+ if (!pfn_valid(pfn)) {
+ dev_err(gart->dev, "Invalid page: %08x\n", pa);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return -EINVAL;
+ }
+ gart_set_pte(gart, iova, GART_PTE(pfn));
+ FLUSH_GART_REGS(gart);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+ size_t bytes)
+{
+ struct gart_device *gart = domain->priv;
+ unsigned long flags;
+
+ if (!gart_iova_range_valid(gart, iova, bytes))
+ return 0;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ gart_set_pte(gart, iova, 0);
+ FLUSH_GART_REGS(gart);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long iova)
+{
+ struct gart_device *gart = domain->priv;
+ unsigned long pte;
+ phys_addr_t pa;
+ unsigned long flags;
+
+ if (!gart_iova_range_valid(gart, iova, 0))
+ return -EINVAL;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ pte = gart_read_pte(gart, iova);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+
+ pa = (pte & GART_PAGE_MASK);
+ if (!pfn_valid(__phys_to_pfn(pa))) {
+ dev_err(gart->dev, "No entry for %08lx:%08x\n", iova, pa);
+ gart_dump_table(gart);
+ return -EINVAL;
+ }
+ return pa;
+}
+
+static int gart_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static struct iommu_ops gart_iommu_ops = {
+ .domain_init = gart_iommu_domain_init,
+ .domain_destroy = gart_iommu_domain_destroy,
+ .attach_dev = gart_iommu_attach_dev,
+ .detach_dev = gart_iommu_detach_dev,
+ .map = gart_iommu_map,
+ .unmap = gart_iommu_unmap,
+ .iova_to_phys = gart_iommu_iova_to_phys,
+ .domain_has_cap = gart_iommu_domain_has_cap,
+ .pgsize_bitmap = GART_IOMMU_PGSIZES,
+};
+
+static int tegra_gart_suspend(struct device *dev)
+{
+ struct gart_device *gart = dev_get_drvdata(dev);
+ unsigned long iova;
+ u32 *data = gart->savedata;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ for_each_gart_pte(gart, iova)
+ *(data++) = gart_read_pte(gart, iova);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static int tegra_gart_resume(struct device *dev)
+{
+ struct gart_device *gart = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ do_gart_setup(gart, gart->savedata);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static int tegra_gart_probe(struct platform_device *pdev)
+{
+ struct gart_device *gart;
+ struct resource *res, *res_remap;
+ void __iomem *gart_regs;
+ int err;
+ struct device *dev = &pdev->dev;
+
+ if (gart_handle)
+ return -EIO;
+
+ BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT);
+
+ /* the GART memory aperture is required */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res || !res_remap) {
+ dev_err(dev, "GART memory aperture expected\n");
+ return -ENXIO;
+ }
+
+ gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL);
+ if (!gart) {
+ dev_err(dev, "failed to allocate gart_device\n");
+ return -ENOMEM;
+ }
+
+ gart_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!gart_regs) {
+ dev_err(dev, "failed to remap GART registers\n");
+ err = -ENXIO;
+ goto fail;
+ }
+
+ gart->dev = &pdev->dev;
+ spin_lock_init(&gart->pte_lock);
+ spin_lock_init(&gart->client_lock);
+ INIT_LIST_HEAD(&gart->client);
+ gart->regs = gart_regs;
+ gart->iovmm_base = (dma_addr_t)res_remap->start;
+ gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT);
+
+ gart->savedata = vmalloc(sizeof(u32) * gart->page_count);
+ if (!gart->savedata) {
+ dev_err(dev, "failed to allocate context save area\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, gart);
+ do_gart_setup(gart, NULL);
+
+ gart_handle = gart;
+ return 0;
+
+fail:
+ if (gart_regs)
+ devm_iounmap(dev, gart_regs);
+ if (gart && gart->savedata)
+ vfree(gart->savedata);
+ devm_kfree(dev, gart);
+ return err;
+}
+
+static int tegra_gart_remove(struct platform_device *pdev)
+{
+ struct gart_device *gart = platform_get_drvdata(pdev);
+ struct device *dev = gart->dev;
+
+ writel(0, gart->regs + GART_CONFIG);
+ if (gart->savedata)
+ vfree(gart->savedata);
+ if (gart->regs)
+ devm_iounmap(dev, gart->regs);
+ devm_kfree(dev, gart);
+ gart_handle = NULL;
+ return 0;
+}
+
+const struct dev_pm_ops tegra_gart_pm_ops = {
+ .suspend = tegra_gart_suspend,
+ .resume = tegra_gart_resume,
+};
+
+static struct platform_driver tegra_gart_driver = {
+ .probe = tegra_gart_probe,
+ .remove = tegra_gart_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tegra-gart",
+ .pm = &tegra_gart_pm_ops,
+ },
+};
+
+static int __devinit tegra_gart_init(void)
+{
+ bus_set_iommu(&platform_bus_type, &gart_iommu_ops);
+ return platform_driver_register(&tegra_gart_driver);
+}
+
+static void __exit tegra_gart_exit(void)
+{
+ platform_driver_unregister(&tegra_gart_driver);
+}
+
+subsys_initcall(tegra_gart_init);
+module_exit(tegra_gart_exit);
+
+MODULE_DESCRIPTION("IOMMU API for GART in Tegra20");
+MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
new file mode 100644
index 000000000000..eb93c821f592
--- /dev/null
+++ b/drivers/iommu/tegra-smmu.c
@@ -0,0 +1,1034 @@
+/*
+ * IOMMU API for SMMU in Tegra30
+ *
+ * Copyright (c) 2011-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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define pr_fmt(fmt) "%s(): " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/iommu.h>
+#include <linux/io.h>
+
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+
+#include <mach/iomap.h>
+#include <mach/smmu.h>
+
+/* bitmap of the page sizes currently supported */
+#define SMMU_IOMMU_PGSIZES (SZ_4K)
+
+#define SMMU_CONFIG 0x10
+#define SMMU_CONFIG_DISABLE 0
+#define SMMU_CONFIG_ENABLE 1
+
+#define SMMU_TLB_CONFIG 0x14
+#define SMMU_TLB_CONFIG_STATS__MASK (1 << 31)
+#define SMMU_TLB_CONFIG_STATS__ENABLE (1 << 31)
+#define SMMU_TLB_CONFIG_HIT_UNDER_MISS__ENABLE (1 << 29)
+#define SMMU_TLB_CONFIG_ACTIVE_LINES__VALUE 0x10
+#define SMMU_TLB_CONFIG_RESET_VAL 0x20000010
+
+#define SMMU_PTC_CONFIG 0x18
+#define SMMU_PTC_CONFIG_STATS__MASK (1 << 31)
+#define SMMU_PTC_CONFIG_STATS__ENABLE (1 << 31)
+#define SMMU_PTC_CONFIG_CACHE__ENABLE (1 << 29)
+#define SMMU_PTC_CONFIG_INDEX_MAP__PATTERN 0x3f
+#define SMMU_PTC_CONFIG_RESET_VAL 0x2000003f
+
+#define SMMU_PTB_ASID 0x1c
+#define SMMU_PTB_ASID_CURRENT_SHIFT 0
+
+#define SMMU_PTB_DATA 0x20
+#define SMMU_PTB_DATA_RESET_VAL 0
+#define SMMU_PTB_DATA_ASID_NONSECURE_SHIFT 29
+#define SMMU_PTB_DATA_ASID_WRITABLE_SHIFT 30
+#define SMMU_PTB_DATA_ASID_READABLE_SHIFT 31
+
+#define SMMU_TLB_FLUSH 0x30
+#define SMMU_TLB_FLUSH_VA_MATCH_ALL 0
+#define SMMU_TLB_FLUSH_VA_MATCH_SECTION 2
+#define SMMU_TLB_FLUSH_VA_MATCH_GROUP 3
+#define SMMU_TLB_FLUSH_ASID_SHIFT 29
+#define SMMU_TLB_FLUSH_ASID_MATCH_DISABLE 0
+#define SMMU_TLB_FLUSH_ASID_MATCH_ENABLE 1
+#define SMMU_TLB_FLUSH_ASID_MATCH_SHIFT 31
+
+#define SMMU_PTC_FLUSH 0x34
+#define SMMU_PTC_FLUSH_TYPE_ALL 0
+#define SMMU_PTC_FLUSH_TYPE_ADR 1
+#define SMMU_PTC_FLUSH_ADR_SHIFT 4
+
+#define SMMU_ASID_SECURITY 0x38
+
+#define SMMU_STATS_TLB_HIT_COUNT 0x1f0
+#define SMMU_STATS_TLB_MISS_COUNT 0x1f4
+#define SMMU_STATS_PTC_HIT_COUNT 0x1f8
+#define SMMU_STATS_PTC_MISS_COUNT 0x1fc
+
+#define SMMU_TRANSLATION_ENABLE_0 0x228
+#define SMMU_TRANSLATION_ENABLE_1 0x22c
+#define SMMU_TRANSLATION_ENABLE_2 0x230
+
+#define SMMU_AFI_ASID 0x238 /* PCIE */
+#define SMMU_AVPC_ASID 0x23c /* AVP */
+#define SMMU_DC_ASID 0x240 /* Display controller */
+#define SMMU_DCB_ASID 0x244 /* Display controller B */
+#define SMMU_EPP_ASID 0x248 /* Encoder pre-processor */
+#define SMMU_G2_ASID 0x24c /* 2D engine */
+#define SMMU_HC_ASID 0x250 /* Host1x */
+#define SMMU_HDA_ASID 0x254 /* High-def audio */
+#define SMMU_ISP_ASID 0x258 /* Image signal processor */
+#define SMMU_MPE_ASID 0x264 /* MPEG encoder */
+#define SMMU_NV_ASID 0x268 /* (3D) */
+#define SMMU_NV2_ASID 0x26c /* (3D) */
+#define SMMU_PPCS_ASID 0x270 /* AHB */
+#define SMMU_SATA_ASID 0x278 /* SATA */
+#define SMMU_VDE_ASID 0x27c /* Video decoder */
+#define SMMU_VI_ASID 0x280 /* Video input */
+
+#define SMMU_PDE_NEXT_SHIFT 28
+
+/* AHB Arbiter Registers */
+#define AHB_XBAR_CTRL 0xe0
+#define AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE 1
+#define AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT 17
+
+#define SMMU_NUM_ASIDS 4
+#define SMMU_TLB_FLUSH_VA_SECTION__MASK 0xffc00000
+#define SMMU_TLB_FLUSH_VA_SECTION__SHIFT 12 /* right shift */
+#define SMMU_TLB_FLUSH_VA_GROUP__MASK 0xffffc000
+#define SMMU_TLB_FLUSH_VA_GROUP__SHIFT 12 /* right shift */
+#define SMMU_TLB_FLUSH_VA(iova, which) \
+ ((((iova) & SMMU_TLB_FLUSH_VA_##which##__MASK) >> \
+ SMMU_TLB_FLUSH_VA_##which##__SHIFT) | \
+ SMMU_TLB_FLUSH_VA_MATCH_##which)
+#define SMMU_PTB_ASID_CUR(n) \
+ ((n) << SMMU_PTB_ASID_CURRENT_SHIFT)
+#define SMMU_TLB_FLUSH_ASID_MATCH_disable \
+ (SMMU_TLB_FLUSH_ASID_MATCH_DISABLE << \
+ SMMU_TLB_FLUSH_ASID_MATCH_SHIFT)
+#define SMMU_TLB_FLUSH_ASID_MATCH__ENABLE \
+ (SMMU_TLB_FLUSH_ASID_MATCH_ENABLE << \
+ SMMU_TLB_FLUSH_ASID_MATCH_SHIFT)
+
+#define SMMU_PAGE_SHIFT 12
+#define SMMU_PAGE_SIZE (1 << SMMU_PAGE_SHIFT)
+
+#define SMMU_PDIR_COUNT 1024
+#define SMMU_PDIR_SIZE (sizeof(unsigned long) * SMMU_PDIR_COUNT)
+#define SMMU_PTBL_COUNT 1024
+#define SMMU_PTBL_SIZE (sizeof(unsigned long) * SMMU_PTBL_COUNT)
+#define SMMU_PDIR_SHIFT 12
+#define SMMU_PDE_SHIFT 12
+#define SMMU_PTE_SHIFT 12
+#define SMMU_PFN_MASK 0x000fffff
+
+#define SMMU_ADDR_TO_PFN(addr) ((addr) >> 12)
+#define SMMU_ADDR_TO_PDN(addr) ((addr) >> 22)
+#define SMMU_PDN_TO_ADDR(addr) ((pdn) << 22)
+
+#define _READABLE (1 << SMMU_PTB_DATA_ASID_READABLE_SHIFT)
+#define _WRITABLE (1 << SMMU_PTB_DATA_ASID_WRITABLE_SHIFT)
+#define _NONSECURE (1 << SMMU_PTB_DATA_ASID_NONSECURE_SHIFT)
+#define _PDE_NEXT (1 << SMMU_PDE_NEXT_SHIFT)
+#define _MASK_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+
+#define _PDIR_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+
+#define _PDE_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+#define _PDE_ATTR_N (_PDE_ATTR | _PDE_NEXT)
+#define _PDE_VACANT(pdn) (((pdn) << 10) | _PDE_ATTR)
+
+#define _PTE_ATTR (_READABLE | _WRITABLE | _NONSECURE)
+#define _PTE_VACANT(addr) (((addr) >> SMMU_PAGE_SHIFT) | _PTE_ATTR)
+
+#define SMMU_MK_PDIR(page, attr) \
+ ((page_to_phys(page) >> SMMU_PDIR_SHIFT) | (attr))
+#define SMMU_MK_PDE(page, attr) \
+ (unsigned long)((page_to_phys(page) >> SMMU_PDE_SHIFT) | (attr))
+#define SMMU_EX_PTBL_PAGE(pde) \
+ pfn_to_page((unsigned long)(pde) & SMMU_PFN_MASK)
+#define SMMU_PFN_TO_PTE(pfn, attr) (unsigned long)((pfn) | (attr))
+
+#define SMMU_ASID_ENABLE(asid) ((asid) | (1 << 31))
+#define SMMU_ASID_DISABLE 0
+#define SMMU_ASID_ASID(n) ((n) & ~SMMU_ASID_ENABLE(0))
+
+#define smmu_client_enable_hwgrp(c, m) smmu_client_set_hwgrp(c, m, 1)
+#define smmu_client_disable_hwgrp(c) smmu_client_set_hwgrp(c, 0, 0)
+#define __smmu_client_enable_hwgrp(c, m) __smmu_client_set_hwgrp(c, m, 1)
+#define __smmu_client_disable_hwgrp(c) __smmu_client_set_hwgrp(c, 0, 0)
+
+#define HWGRP_INIT(client) [HWGRP_##client] = SMMU_##client##_ASID
+
+static const u32 smmu_hwgrp_asid_reg[] = {
+ HWGRP_INIT(AFI),
+ HWGRP_INIT(AVPC),
+ HWGRP_INIT(DC),
+ HWGRP_INIT(DCB),
+ HWGRP_INIT(EPP),
+ HWGRP_INIT(G2),
+ HWGRP_INIT(HC),
+ HWGRP_INIT(HDA),
+ HWGRP_INIT(ISP),
+ HWGRP_INIT(MPE),
+ HWGRP_INIT(NV),
+ HWGRP_INIT(NV2),
+ HWGRP_INIT(PPCS),
+ HWGRP_INIT(SATA),
+ HWGRP_INIT(VDE),
+ HWGRP_INIT(VI),
+};
+#define HWGRP_ASID_REG(x) (smmu_hwgrp_asid_reg[x])
+
+/*
+ * Per client for address space
+ */
+struct smmu_client {
+ struct device *dev;
+ struct list_head list;
+ struct smmu_as *as;
+ u32 hwgrp;
+};
+
+/*
+ * Per address space
+ */
+struct smmu_as {
+ struct smmu_device *smmu; /* back pointer to container */
+ unsigned int asid;
+ spinlock_t lock; /* for pagetable */
+ struct page *pdir_page;
+ unsigned long pdir_attr;
+ unsigned long pde_attr;
+ unsigned long pte_attr;
+ unsigned int *pte_count;
+
+ struct list_head client;
+ spinlock_t client_lock; /* for client list */
+};
+
+/*
+ * Per SMMU device - IOMMU device
+ */
+struct smmu_device {
+ void __iomem *regs, *regs_ahbarb;
+ unsigned long iovmm_base; /* remappable base address */
+ unsigned long page_count; /* total remappable size */
+ spinlock_t lock;
+ char *name;
+ struct device *dev;
+ int num_as;
+ struct smmu_as *as; /* Run-time allocated array */
+ struct page *avp_vector_page; /* dummy page shared by all AS's */
+
+ /*
+ * Register image savers for suspend/resume
+ */
+ unsigned long translation_enable_0;
+ unsigned long translation_enable_1;
+ unsigned long translation_enable_2;
+ unsigned long asid_security;
+};
+
+static struct smmu_device *smmu_handle; /* unique for a system */
+
+/*
+ * SMMU/AHB register accessors
+ */
+static inline u32 smmu_read(struct smmu_device *smmu, size_t offs)
+{
+ return readl(smmu->regs + offs);
+}
+static inline void smmu_write(struct smmu_device *smmu, u32 val, size_t offs)
+{
+ writel(val, smmu->regs + offs);
+}
+
+static inline u32 ahb_read(struct smmu_device *smmu, size_t offs)
+{
+ return readl(smmu->regs_ahbarb + offs);
+}
+static inline void ahb_write(struct smmu_device *smmu, u32 val, size_t offs)
+{
+ writel(val, smmu->regs_ahbarb + offs);
+}
+
+#define VA_PAGE_TO_PA(va, page) \
+ (page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK))
+
+#define FLUSH_CPU_DCACHE(va, page, size) \
+ do { \
+ unsigned long _pa_ = VA_PAGE_TO_PA(va, page); \
+ __cpuc_flush_dcache_area((void *)(va), (size_t)(size)); \
+ outer_flush_range(_pa_, _pa_+(size_t)(size)); \
+ } while (0)
+
+/*
+ * Any interaction between any block on PPSB and a block on APB or AHB
+ * must have these read-back barriers to ensure the APB/AHB bus
+ * transaction is complete before initiating activity on the PPSB
+ * block.
+ */
+#define FLUSH_SMMU_REGS(smmu) smmu_read(smmu, SMMU_CONFIG)
+
+#define smmu_client_hwgrp(c) (u32)((c)->dev->platform_data)
+
+static int __smmu_client_set_hwgrp(struct smmu_client *c,
+ unsigned long map, int on)
+{
+ int i;
+ struct smmu_as *as = c->as;
+ u32 val, offs, mask = SMMU_ASID_ENABLE(as->asid);
+ struct smmu_device *smmu = as->smmu;
+
+ WARN_ON(!on && map);
+ if (on && !map)
+ return -EINVAL;
+ if (!on)
+ map = smmu_client_hwgrp(c);
+
+ for_each_set_bit(i, &map, HWGRP_COUNT) {
+ offs = HWGRP_ASID_REG(i);
+ val = smmu_read(smmu, offs);
+ if (on) {
+ if (WARN_ON(val & mask))
+ goto err_hw_busy;
+ val |= mask;
+ } else {
+ WARN_ON((val & mask) == mask);
+ val &= ~mask;
+ }
+ smmu_write(smmu, val, offs);
+ }
+ FLUSH_SMMU_REGS(smmu);
+ c->hwgrp = map;
+ return 0;
+
+err_hw_busy:
+ for_each_set_bit(i, &map, HWGRP_COUNT) {
+ offs = HWGRP_ASID_REG(i);
+ val = smmu_read(smmu, offs);
+ val &= ~mask;
+ smmu_write(smmu, val, offs);
+ }
+ return -EBUSY;
+}
+
+static int smmu_client_set_hwgrp(struct smmu_client *c, u32 map, int on)
+{
+ u32 val;
+ unsigned long flags;
+ struct smmu_as *as = c->as;
+ struct smmu_device *smmu = as->smmu;
+
+ spin_lock_irqsave(&smmu->lock, flags);
+ val = __smmu_client_set_hwgrp(c, map, on);
+ spin_unlock_irqrestore(&smmu->lock, flags);
+ return val;
+}
+
+/*
+ * Flush all TLB entries and all PTC entries
+ * Caller must lock smmu
+ */
+static void smmu_flush_regs(struct smmu_device *smmu, int enable)
+{
+ u32 val;
+
+ smmu_write(smmu, SMMU_PTC_FLUSH_TYPE_ALL, SMMU_PTC_FLUSH);
+ FLUSH_SMMU_REGS(smmu);
+ val = SMMU_TLB_FLUSH_VA_MATCH_ALL |
+ SMMU_TLB_FLUSH_ASID_MATCH_disable;
+ smmu_write(smmu, val, SMMU_TLB_FLUSH);
+
+ if (enable)
+ smmu_write(smmu, SMMU_CONFIG_ENABLE, SMMU_CONFIG);
+ FLUSH_SMMU_REGS(smmu);
+}
+
+static void smmu_setup_regs(struct smmu_device *smmu)
+{
+ int i;
+ u32 val;
+
+ for (i = 0; i < smmu->num_as; i++) {
+ struct smmu_as *as = &smmu->as[i];
+ struct smmu_client *c;
+
+ smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID);
+ val = as->pdir_page ?
+ SMMU_MK_PDIR(as->pdir_page, as->pdir_attr) :
+ SMMU_PTB_DATA_RESET_VAL;
+ smmu_write(smmu, val, SMMU_PTB_DATA);
+
+ list_for_each_entry(c, &as->client, list)
+ __smmu_client_set_hwgrp(c, c->hwgrp, 1);
+ }
+
+ smmu_write(smmu, smmu->translation_enable_0, SMMU_TRANSLATION_ENABLE_0);
+ smmu_write(smmu, smmu->translation_enable_1, SMMU_TRANSLATION_ENABLE_1);
+ smmu_write(smmu, smmu->translation_enable_2, SMMU_TRANSLATION_ENABLE_2);
+ smmu_write(smmu, smmu->asid_security, SMMU_ASID_SECURITY);
+ smmu_write(smmu, SMMU_TLB_CONFIG_RESET_VAL, SMMU_TLB_CONFIG);
+ smmu_write(smmu, SMMU_PTC_CONFIG_RESET_VAL, SMMU_PTC_CONFIG);
+
+ smmu_flush_regs(smmu, 1);
+
+ val = ahb_read(smmu, AHB_XBAR_CTRL);
+ val |= AHB_XBAR_CTRL_SMMU_INIT_DONE_DONE <<
+ AHB_XBAR_CTRL_SMMU_INIT_DONE_SHIFT;
+ ahb_write(smmu, val, AHB_XBAR_CTRL);
+}
+
+static void flush_ptc_and_tlb(struct smmu_device *smmu,
+ struct smmu_as *as, dma_addr_t iova,
+ unsigned long *pte, struct page *page, int is_pde)
+{
+ u32 val;
+ unsigned long tlb_flush_va = is_pde
+ ? SMMU_TLB_FLUSH_VA(iova, SECTION)
+ : SMMU_TLB_FLUSH_VA(iova, GROUP);
+
+ val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pte, page);
+ smmu_write(smmu, val, SMMU_PTC_FLUSH);
+ FLUSH_SMMU_REGS(smmu);
+ val = tlb_flush_va |
+ SMMU_TLB_FLUSH_ASID_MATCH__ENABLE |
+ (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT);
+ smmu_write(smmu, val, SMMU_TLB_FLUSH);
+ FLUSH_SMMU_REGS(smmu);
+}
+
+static void free_ptbl(struct smmu_as *as, dma_addr_t iova)
+{
+ unsigned long pdn = SMMU_ADDR_TO_PDN(iova);
+ unsigned long *pdir = (unsigned long *)page_address(as->pdir_page);
+
+ if (pdir[pdn] != _PDE_VACANT(pdn)) {
+ dev_dbg(as->smmu->dev, "pdn: %lx\n", pdn);
+
+ ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn]));
+ __free_page(SMMU_EX_PTBL_PAGE(pdir[pdn]));
+ pdir[pdn] = _PDE_VACANT(pdn);
+ FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]);
+ flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn],
+ as->pdir_page, 1);
+ }
+}
+
+static void free_pdir(struct smmu_as *as)
+{
+ unsigned addr;
+ int count;
+ struct device *dev = as->smmu->dev;
+
+ if (!as->pdir_page)
+ return;
+
+ addr = as->smmu->iovmm_base;
+ count = as->smmu->page_count;
+ while (count-- > 0) {
+ free_ptbl(as, addr);
+ addr += SMMU_PAGE_SIZE * SMMU_PTBL_COUNT;
+ }
+ ClearPageReserved(as->pdir_page);
+ __free_page(as->pdir_page);
+ as->pdir_page = NULL;
+ devm_kfree(dev, as->pte_count);
+ as->pte_count = NULL;
+}
+
+/*
+ * Maps PTBL for given iova and returns the PTE address
+ * Caller must unmap the mapped PTBL returned in *ptbl_page_p
+ */
+static unsigned long *locate_pte(struct smmu_as *as,
+ dma_addr_t iova, bool allocate,
+ struct page **ptbl_page_p,
+ unsigned int **count)
+{
+ unsigned long ptn = SMMU_ADDR_TO_PFN(iova);
+ unsigned long pdn = SMMU_ADDR_TO_PDN(iova);
+ unsigned long *pdir = page_address(as->pdir_page);
+ unsigned long *ptbl;
+
+ if (pdir[pdn] != _PDE_VACANT(pdn)) {
+ /* Mapped entry table already exists */
+ *ptbl_page_p = SMMU_EX_PTBL_PAGE(pdir[pdn]);
+ ptbl = page_address(*ptbl_page_p);
+ } else if (!allocate) {
+ return NULL;
+ } else {
+ int pn;
+ unsigned long addr = SMMU_PDN_TO_ADDR(pdn);
+
+ /* Vacant - allocate a new page table */
+ dev_dbg(as->smmu->dev, "New PTBL pdn: %lx\n", pdn);
+
+ *ptbl_page_p = alloc_page(GFP_ATOMIC);
+ if (!*ptbl_page_p) {
+ dev_err(as->smmu->dev,
+ "failed to allocate smmu_device page table\n");
+ return NULL;
+ }
+ SetPageReserved(*ptbl_page_p);
+ ptbl = (unsigned long *)page_address(*ptbl_page_p);
+ for (pn = 0; pn < SMMU_PTBL_COUNT;
+ pn++, addr += SMMU_PAGE_SIZE) {
+ ptbl[pn] = _PTE_VACANT(addr);
+ }
+ FLUSH_CPU_DCACHE(ptbl, *ptbl_page_p, SMMU_PTBL_SIZE);
+ pdir[pdn] = SMMU_MK_PDE(*ptbl_page_p,
+ as->pde_attr | _PDE_NEXT);
+ FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]);
+ flush_ptc_and_tlb(as->smmu, as, iova, &pdir[pdn],
+ as->pdir_page, 1);
+ }
+ *count = &as->pte_count[pdn];
+
+ return &ptbl[ptn % SMMU_PTBL_COUNT];
+}
+
+#ifdef CONFIG_SMMU_SIG_DEBUG
+static void put_signature(struct smmu_as *as,
+ dma_addr_t iova, unsigned long pfn)
+{
+ struct page *page;
+ unsigned long *vaddr;
+
+ page = pfn_to_page(pfn);
+ vaddr = page_address(page);
+ if (!vaddr)
+ return;
+
+ vaddr[0] = iova;
+ vaddr[1] = pfn << PAGE_SHIFT;
+ FLUSH_CPU_DCACHE(vaddr, page, sizeof(vaddr[0]) * 2);
+}
+#else
+static inline void put_signature(struct smmu_as *as,
+ unsigned long addr, unsigned long pfn)
+{
+}
+#endif
+
+/*
+ * Caller must lock/unlock as
+ */
+static int alloc_pdir(struct smmu_as *as)
+{
+ unsigned long *pdir;
+ int pdn;
+ u32 val;
+ struct smmu_device *smmu = as->smmu;
+
+ if (as->pdir_page)
+ return 0;
+
+ as->pte_count = devm_kzalloc(smmu->dev,
+ sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT, GFP_KERNEL);
+ if (!as->pte_count) {
+ dev_err(smmu->dev,
+ "failed to allocate smmu_device PTE cunters\n");
+ return -ENOMEM;
+ }
+ as->pdir_page = alloc_page(GFP_KERNEL | __GFP_DMA);
+ if (!as->pdir_page) {
+ dev_err(smmu->dev,
+ "failed to allocate smmu_device page directory\n");
+ devm_kfree(smmu->dev, as->pte_count);
+ as->pte_count = NULL;
+ return -ENOMEM;
+ }
+ SetPageReserved(as->pdir_page);
+ pdir = page_address(as->pdir_page);
+
+ for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++)
+ pdir[pdn] = _PDE_VACANT(pdn);
+ FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE);
+ val = SMMU_PTC_FLUSH_TYPE_ADR | VA_PAGE_TO_PA(pdir, as->pdir_page);
+ smmu_write(smmu, val, SMMU_PTC_FLUSH);
+ FLUSH_SMMU_REGS(as->smmu);
+ val = SMMU_TLB_FLUSH_VA_MATCH_ALL |
+ SMMU_TLB_FLUSH_ASID_MATCH__ENABLE |
+ (as->asid << SMMU_TLB_FLUSH_ASID_SHIFT);
+ smmu_write(smmu, val, SMMU_TLB_FLUSH);
+ FLUSH_SMMU_REGS(as->smmu);
+
+ return 0;
+}
+
+static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova)
+{
+ unsigned long *pte;
+ struct page *page;
+ unsigned int *count;
+
+ pte = locate_pte(as, iova, false, &page, &count);
+ if (WARN_ON(!pte))
+ return;
+
+ if (WARN_ON(*pte == _PTE_VACANT(iova)))
+ return;
+
+ *pte = _PTE_VACANT(iova);
+ FLUSH_CPU_DCACHE(pte, page, sizeof(*pte));
+ flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0);
+ if (!--(*count)) {
+ free_ptbl(as, iova);
+ smmu_flush_regs(as->smmu, 0);
+ }
+}
+
+static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova,
+ unsigned long pfn)
+{
+ struct smmu_device *smmu = as->smmu;
+ unsigned long *pte;
+ unsigned int *count;
+ struct page *page;
+
+ pte = locate_pte(as, iova, true, &page, &count);
+ if (WARN_ON(!pte))
+ return;
+
+ if (*pte == _PTE_VACANT(iova))
+ (*count)++;
+ *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr);
+ if (unlikely((*pte == _PTE_VACANT(iova))))
+ (*count)--;
+ FLUSH_CPU_DCACHE(pte, page, sizeof(*pte));
+ flush_ptc_and_tlb(smmu, as, iova, pte, page, 0);
+ put_signature(as, iova, pfn);
+}
+
+static int smmu_iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t pa, size_t bytes, int prot)
+{
+ struct smmu_as *as = domain->priv;
+ unsigned long pfn = __phys_to_pfn(pa);
+ unsigned long flags;
+
+ dev_dbg(as->smmu->dev, "[%d] %08lx:%08x\n", as->asid, iova, pa);
+
+ if (!pfn_valid(pfn))
+ return -ENOMEM;
+
+ spin_lock_irqsave(&as->lock, flags);
+ __smmu_iommu_map_pfn(as, iova, pfn);
+ spin_unlock_irqrestore(&as->lock, flags);
+ return 0;
+}
+
+static size_t smmu_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+ size_t bytes)
+{
+ struct smmu_as *as = domain->priv;
+ unsigned long flags;
+
+ dev_dbg(as->smmu->dev, "[%d] %08lx\n", as->asid, iova);
+
+ spin_lock_irqsave(&as->lock, flags);
+ __smmu_iommu_unmap(as, iova);
+ spin_unlock_irqrestore(&as->lock, flags);
+ return SMMU_PAGE_SIZE;
+}
+
+static phys_addr_t smmu_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long iova)
+{
+ struct smmu_as *as = domain->priv;
+ unsigned long *pte;
+ unsigned int *count;
+ struct page *page;
+ unsigned long pfn;
+ unsigned long flags;
+
+ spin_lock_irqsave(&as->lock, flags);
+
+ pte = locate_pte(as, iova, true, &page, &count);
+ pfn = *pte & SMMU_PFN_MASK;
+ WARN_ON(!pfn_valid(pfn));
+ dev_dbg(as->smmu->dev,
+ "iova:%08lx pfn:%08lx asid:%d\n", iova, pfn, as->asid);
+
+ spin_unlock_irqrestore(&as->lock, flags);
+ return PFN_PHYS(pfn);
+}
+
+static int smmu_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static int smmu_iommu_attach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct smmu_as *as = domain->priv;
+ struct smmu_device *smmu = as->smmu;
+ struct smmu_client *client, *c;
+ u32 map;
+ int err;
+
+ client = devm_kzalloc(smmu->dev, sizeof(*c), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+ client->dev = dev;
+ client->as = as;
+ map = (unsigned long)dev->platform_data;
+ if (!map)
+ return -EINVAL;
+
+ err = smmu_client_enable_hwgrp(client, map);
+ if (err)
+ goto err_hwgrp;
+
+ spin_lock(&as->client_lock);
+ list_for_each_entry(c, &as->client, list) {
+ if (c->dev == dev) {
+ dev_err(smmu->dev,
+ "%s is already attached\n", dev_name(c->dev));
+ err = -EINVAL;
+ goto err_client;
+ }
+ }
+ list_add(&client->list, &as->client);
+ spin_unlock(&as->client_lock);
+
+ /*
+ * Reserve "page zero" for AVP vectors using a common dummy
+ * page.
+ */
+ if (map & HWG_AVPC) {
+ struct page *page;
+
+ page = as->smmu->avp_vector_page;
+ __smmu_iommu_map_pfn(as, 0, page_to_pfn(page));
+
+ pr_info("Reserve \"page zero\" for AVP vectors using a common dummy\n");
+ }
+
+ dev_dbg(smmu->dev, "%s is attached\n", dev_name(c->dev));
+ return 0;
+
+err_client:
+ smmu_client_disable_hwgrp(client);
+ spin_unlock(&as->client_lock);
+err_hwgrp:
+ devm_kfree(smmu->dev, client);
+ return err;
+}
+
+static void smmu_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct smmu_as *as = domain->priv;
+ struct smmu_device *smmu = as->smmu;
+ struct smmu_client *c;
+
+ spin_lock(&as->client_lock);
+
+ list_for_each_entry(c, &as->client, list) {
+ if (c->dev == dev) {
+ smmu_client_disable_hwgrp(c);
+ list_del(&c->list);
+ devm_kfree(smmu->dev, c);
+ c->as = NULL;
+ dev_dbg(smmu->dev,
+ "%s is detached\n", dev_name(c->dev));
+ goto out;
+ }
+ }
+ dev_err(smmu->dev, "Couldn't find %s\n", dev_name(c->dev));
+out:
+ spin_unlock(&as->client_lock);
+}
+
+static int smmu_iommu_domain_init(struct iommu_domain *domain)
+{
+ int i;
+ unsigned long flags;
+ struct smmu_as *as;
+ struct smmu_device *smmu = smmu_handle;
+
+ /* Look for a free AS with lock held */
+ for (i = 0; i < smmu->num_as; i++) {
+ struct smmu_as *tmp = &smmu->as[i];
+
+ spin_lock_irqsave(&tmp->lock, flags);
+ if (!tmp->pdir_page) {
+ as = tmp;
+ goto found;
+ }
+ spin_unlock_irqrestore(&tmp->lock, flags);
+ }
+ dev_err(smmu->dev, "no free AS\n");
+ return -ENODEV;
+
+found:
+ if (alloc_pdir(as) < 0)
+ goto err_alloc_pdir;
+
+ spin_lock(&smmu->lock);
+
+ /* Update PDIR register */
+ smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID);
+ smmu_write(smmu,
+ SMMU_MK_PDIR(as->pdir_page, as->pdir_attr), SMMU_PTB_DATA);
+ FLUSH_SMMU_REGS(smmu);
+
+ spin_unlock(&smmu->lock);
+
+ spin_unlock_irqrestore(&as->lock, flags);
+ domain->priv = as;
+
+ dev_dbg(smmu->dev, "smmu_as@%p\n", as);
+ return 0;
+
+err_alloc_pdir:
+ spin_unlock_irqrestore(&as->lock, flags);
+ return -ENODEV;
+}
+
+static void smmu_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct smmu_as *as = domain->priv;
+ struct smmu_device *smmu = as->smmu;
+ unsigned long flags;
+
+ spin_lock_irqsave(&as->lock, flags);
+
+ if (as->pdir_page) {
+ spin_lock(&smmu->lock);
+ smmu_write(smmu, SMMU_PTB_ASID_CUR(as->asid), SMMU_PTB_ASID);
+ smmu_write(smmu, SMMU_PTB_DATA_RESET_VAL, SMMU_PTB_DATA);
+ FLUSH_SMMU_REGS(smmu);
+ spin_unlock(&smmu->lock);
+
+ free_pdir(as);
+ }
+
+ if (!list_empty(&as->client)) {
+ struct smmu_client *c;
+
+ list_for_each_entry(c, &as->client, list)
+ smmu_iommu_detach_dev(domain, c->dev);
+ }
+
+ spin_unlock_irqrestore(&as->lock, flags);
+
+ domain->priv = NULL;
+ dev_dbg(smmu->dev, "smmu_as@%p\n", as);
+}
+
+static struct iommu_ops smmu_iommu_ops = {
+ .domain_init = smmu_iommu_domain_init,
+ .domain_destroy = smmu_iommu_domain_destroy,
+ .attach_dev = smmu_iommu_attach_dev,
+ .detach_dev = smmu_iommu_detach_dev,
+ .map = smmu_iommu_map,
+ .unmap = smmu_iommu_unmap,
+ .iova_to_phys = smmu_iommu_iova_to_phys,
+ .domain_has_cap = smmu_iommu_domain_has_cap,
+ .pgsize_bitmap = SMMU_IOMMU_PGSIZES,
+};
+
+static int tegra_smmu_suspend(struct device *dev)
+{
+ struct smmu_device *smmu = dev_get_drvdata(dev);
+
+ smmu->translation_enable_0 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_0);
+ smmu->translation_enable_1 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_1);
+ smmu->translation_enable_2 = smmu_read(smmu, SMMU_TRANSLATION_ENABLE_2);
+ smmu->asid_security = smmu_read(smmu, SMMU_ASID_SECURITY);
+ return 0;
+}
+
+static int tegra_smmu_resume(struct device *dev)
+{
+ struct smmu_device *smmu = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&smmu->lock, flags);
+ smmu_setup_regs(smmu);
+ spin_unlock_irqrestore(&smmu->lock, flags);
+ return 0;
+}
+
+static int tegra_smmu_probe(struct platform_device *pdev)
+{
+ struct smmu_device *smmu;
+ struct resource *regs, *regs2, *window;
+ struct device *dev = &pdev->dev;
+ int i, err = 0;
+
+ if (smmu_handle)
+ return -EIO;
+
+ BUILD_BUG_ON(PAGE_SHIFT != SMMU_PAGE_SHIFT);
+
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ window = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!regs || !regs2 || !window) {
+ dev_err(dev, "No SMMU resources\n");
+ return -ENODEV;
+ }
+
+ smmu = devm_kzalloc(dev, sizeof(*smmu), GFP_KERNEL);
+ if (!smmu) {
+ dev_err(dev, "failed to allocate smmu_device\n");
+ return -ENOMEM;
+ }
+
+ smmu->dev = dev;
+ smmu->num_as = SMMU_NUM_ASIDS;
+ smmu->iovmm_base = (unsigned long)window->start;
+ smmu->page_count = resource_size(window) >> SMMU_PAGE_SHIFT;
+ smmu->regs = devm_ioremap(dev, regs->start, resource_size(regs));
+ smmu->regs_ahbarb = devm_ioremap(dev, regs2->start,
+ resource_size(regs2));
+ if (!smmu->regs || !smmu->regs_ahbarb) {
+ dev_err(dev, "failed to remap SMMU registers\n");
+ err = -ENXIO;
+ goto fail;
+ }
+
+ smmu->translation_enable_0 = ~0;
+ smmu->translation_enable_1 = ~0;
+ smmu->translation_enable_2 = ~0;
+ smmu->asid_security = 0;
+
+ smmu->as = devm_kzalloc(dev,
+ sizeof(smmu->as[0]) * smmu->num_as, GFP_KERNEL);
+ if (!smmu->as) {
+ dev_err(dev, "failed to allocate smmu_as\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ for (i = 0; i < smmu->num_as; i++) {
+ struct smmu_as *as = &smmu->as[i];
+
+ as->smmu = smmu;
+ as->asid = i;
+ as->pdir_attr = _PDIR_ATTR;
+ as->pde_attr = _PDE_ATTR;
+ as->pte_attr = _PTE_ATTR;
+
+ spin_lock_init(&as->lock);
+ INIT_LIST_HEAD(&as->client);
+ }
+ spin_lock_init(&smmu->lock);
+ smmu_setup_regs(smmu);
+ platform_set_drvdata(pdev, smmu);
+
+ smmu->avp_vector_page = alloc_page(GFP_KERNEL);
+ if (!smmu->avp_vector_page)
+ goto fail;
+
+ smmu_handle = smmu;
+ return 0;
+
+fail:
+ if (smmu->avp_vector_page)
+ __free_page(smmu->avp_vector_page);
+ if (smmu->regs)
+ devm_iounmap(dev, smmu->regs);
+ if (smmu->regs_ahbarb)
+ devm_iounmap(dev, smmu->regs_ahbarb);
+ if (smmu && smmu->as) {
+ for (i = 0; i < smmu->num_as; i++) {
+ if (smmu->as[i].pdir_page) {
+ ClearPageReserved(smmu->as[i].pdir_page);
+ __free_page(smmu->as[i].pdir_page);
+ }
+ }
+ devm_kfree(dev, smmu->as);
+ }
+ devm_kfree(dev, smmu);
+ return err;
+}
+
+static int tegra_smmu_remove(struct platform_device *pdev)
+{
+ struct smmu_device *smmu = platform_get_drvdata(pdev);
+ struct device *dev = smmu->dev;
+
+ smmu_write(smmu, SMMU_CONFIG_DISABLE, SMMU_CONFIG);
+ platform_set_drvdata(pdev, NULL);
+ if (smmu->as) {
+ int i;
+
+ for (i = 0; i < smmu->num_as; i++)
+ free_pdir(&smmu->as[i]);
+ devm_kfree(dev, smmu->as);
+ }
+ if (smmu->avp_vector_page)
+ __free_page(smmu->avp_vector_page);
+ if (smmu->regs)
+ devm_iounmap(dev, smmu->regs);
+ if (smmu->regs_ahbarb)
+ devm_iounmap(dev, smmu->regs_ahbarb);
+ devm_kfree(dev, smmu);
+ smmu_handle = NULL;
+ return 0;
+}
+
+const struct dev_pm_ops tegra_smmu_pm_ops = {
+ .suspend = tegra_smmu_suspend,
+ .resume = tegra_smmu_resume,
+};
+
+static struct platform_driver tegra_smmu_driver = {
+ .probe = tegra_smmu_probe,
+ .remove = tegra_smmu_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tegra-smmu",
+ .pm = &tegra_smmu_pm_ops,
+ },
+};
+
+static int __devinit tegra_smmu_init(void)
+{
+ bus_set_iommu(&platform_bus_type, &smmu_iommu_ops);
+ return platform_driver_register(&tegra_smmu_driver);
+}
+
+static void __exit tegra_smmu_exit(void)
+{
+ platform_driver_unregister(&tegra_smmu_driver);
+}
+
+subsys_initcall(tegra_smmu_init);
+module_exit(tegra_smmu_exit);
+
+MODULE_DESCRIPTION("IOMMU API for SMMU in Tegra30");
+MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
index 44b50cc645e6..c21353d8e915 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 33e3c94887d8..c644557ae614 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 fe254e74a850..a8c4d3fc9a6d 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 68f50495d166..f0dfc0c976eb 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 bfe94284b0d5..4deac451807c 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 7f3c54d40474..c59e8d2c0675 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 682911f81138..a18e639b40d7 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/Kconfig b/drivers/leds/Kconfig
index 8c7a75d53101..ff4b8cfda585 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -17,7 +17,7 @@ menuconfig NEW_LEDS
if NEW_LEDS
config LEDS_CLASS
- bool "LED Class Support"
+ tristate "LED Class Support"
help
This option enables the led sysfs class in /sys/class/leds. You'll
need this to do anything useful with LEDs. If unsure, say N.
@@ -69,18 +69,11 @@ config LEDS_MIKROTIK_RB532
config LEDS_S3C24XX
tristate "LED Support for Samsung S3C24XX GPIO LEDs"
depends on LEDS_CLASS
- depends on ARCH_S3C2410
+ depends on ARCH_S3C24XX
help
This option enables support for LEDs connected to GPIO lines
on Samsung S3C24XX series CPUs, such as the S3C2410 and S3C2440.
-config LEDS_AMS_DELTA
- tristate "LED Support for the Amstrad Delta (E3)"
- depends on LEDS_CLASS
- depends on MACH_AMS_DELTA
- help
- This option enables support for the LEDs on Amstrad Delta (E3).
-
config LEDS_NET48XX
tristate "LED Support for Soekris net48xx series Error LED"
depends on LEDS_CLASS
@@ -234,6 +227,14 @@ config LEDS_PCA955X
LED driver chips accessed via the I2C bus. Supported
devices include PCA9550, PCA9551, PCA9552, and PCA9553.
+config LEDS_PCA9633
+ tristate "LED support for PCA9633 I2C chip"
+ depends on LEDS_CLASS
+ depends on I2C
+ help
+ This option enables support for LEDs connected to the PCA9633
+ LED driver chip accessed via the I2C bus.
+
config LEDS_WM831X_STATUS
tristate "LED support for status LEDs on WM831x PMICs"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 6bcf4f695515..890481cb09f6 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -12,7 +12,6 @@ obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o
-obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o
obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
@@ -30,6 +29,7 @@ obj-$(CONFIG_LEDS_HP6XX) += leds-hp6xx.o
obj-$(CONFIG_LEDS_OT200) += leds-ot200.o
obj-$(CONFIG_LEDS_FSG) += leds-fsg.o
obj-$(CONFIG_LEDS_PCA955X) += leds-pca955x.o
+obj-$(CONFIG_LEDS_PCA9633) += leds-pca9633.o
obj-$(CONFIG_LEDS_DA903X) += leds-da903x.o
obj-$(CONFIG_LEDS_WM831X_STATUS) += leds-wm831x-status.o
obj-$(CONFIG_LEDS_WM8350) += leds-wm8350.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index 0c8739c448b1..5bff8439dc68 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -110,50 +110,6 @@ static void led_timer_function(unsigned long data)
mod_timer(&led_cdev->blink_timer, jiffies + msecs_to_jiffies(delay));
}
-static void led_stop_software_blink(struct led_classdev *led_cdev)
-{
- /* deactivate previous settings */
- del_timer_sync(&led_cdev->blink_timer);
- led_cdev->blink_delay_on = 0;
- led_cdev->blink_delay_off = 0;
-}
-
-static void led_set_software_blink(struct led_classdev *led_cdev,
- unsigned long delay_on,
- unsigned long delay_off)
-{
- int current_brightness;
-
- current_brightness = led_get_brightness(led_cdev);
- if (current_brightness)
- led_cdev->blink_brightness = current_brightness;
- if (!led_cdev->blink_brightness)
- led_cdev->blink_brightness = led_cdev->max_brightness;
-
- if (led_get_trigger_data(led_cdev) &&
- delay_on == led_cdev->blink_delay_on &&
- delay_off == led_cdev->blink_delay_off)
- return;
-
- led_stop_software_blink(led_cdev);
-
- led_cdev->blink_delay_on = delay_on;
- led_cdev->blink_delay_off = delay_off;
-
- /* never on - don't blink */
- if (!delay_on)
- return;
-
- /* never off - just set to brightness */
- if (!delay_off) {
- led_set_brightness(led_cdev, led_cdev->blink_brightness);
- return;
- }
-
- mod_timer(&led_cdev->blink_timer, jiffies + 1);
-}
-
-
/**
* led_classdev_suspend - suspend an led_classdev.
* @led_cdev: the led_classdev to suspend.
@@ -262,32 +218,6 @@ void led_classdev_unregister(struct led_classdev *led_cdev)
}
EXPORT_SYMBOL_GPL(led_classdev_unregister);
-void led_blink_set(struct led_classdev *led_cdev,
- unsigned long *delay_on,
- unsigned long *delay_off)
-{
- del_timer_sync(&led_cdev->blink_timer);
-
- if (led_cdev->blink_set &&
- !led_cdev->blink_set(led_cdev, delay_on, delay_off))
- return;
-
- /* blink with 1 Hz as default if nothing specified */
- if (!*delay_on && !*delay_off)
- *delay_on = *delay_off = 500;
-
- led_set_software_blink(led_cdev, *delay_on, *delay_off);
-}
-EXPORT_SYMBOL(led_blink_set);
-
-void led_brightness_set(struct led_classdev *led_cdev,
- enum led_brightness brightness)
-{
- led_stop_software_blink(led_cdev);
- led_cdev->brightness_set(led_cdev, brightness);
-}
-EXPORT_SYMBOL(led_brightness_set);
-
static int __init leds_init(void)
{
leds_class = class_create(THIS_MODULE, "leds");
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index 016d19f5486f..d6860043f6f9 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -23,3 +23,73 @@ EXPORT_SYMBOL_GPL(leds_list_lock);
LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);
+
+static void led_stop_software_blink(struct led_classdev *led_cdev)
+{
+ /* deactivate previous settings */
+ del_timer_sync(&led_cdev->blink_timer);
+ led_cdev->blink_delay_on = 0;
+ led_cdev->blink_delay_off = 0;
+}
+
+static void led_set_software_blink(struct led_classdev *led_cdev,
+ unsigned long delay_on,
+ unsigned long delay_off)
+{
+ int current_brightness;
+
+ current_brightness = led_get_brightness(led_cdev);
+ if (current_brightness)
+ led_cdev->blink_brightness = current_brightness;
+ if (!led_cdev->blink_brightness)
+ led_cdev->blink_brightness = led_cdev->max_brightness;
+
+ if (led_get_trigger_data(led_cdev) &&
+ delay_on == led_cdev->blink_delay_on &&
+ delay_off == led_cdev->blink_delay_off)
+ return;
+
+ led_stop_software_blink(led_cdev);
+
+ led_cdev->blink_delay_on = delay_on;
+ led_cdev->blink_delay_off = delay_off;
+
+ /* never on - don't blink */
+ if (!delay_on)
+ return;
+
+ /* never off - just set to brightness */
+ if (!delay_off) {
+ led_set_brightness(led_cdev, led_cdev->blink_brightness);
+ return;
+ }
+
+ mod_timer(&led_cdev->blink_timer, jiffies + 1);
+}
+
+
+void led_blink_set(struct led_classdev *led_cdev,
+ unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ del_timer_sync(&led_cdev->blink_timer);
+
+ if (led_cdev->blink_set &&
+ !led_cdev->blink_set(led_cdev, delay_on, delay_off))
+ return;
+
+ /* blink with 1 Hz as default if nothing specified */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 500;
+
+ led_set_software_blink(led_cdev, *delay_on, *delay_off);
+}
+EXPORT_SYMBOL(led_blink_set);
+
+void led_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ led_stop_software_blink(led_cdev);
+ led_cdev->brightness_set(led_cdev, brightness);
+}
+EXPORT_SYMBOL(led_brightness_set);
diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c
index 4ca00624bd18..5b61aaf7ac0f 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/leds/leds-ams-delta.c b/drivers/leds/leds-ams-delta.c
deleted file mode 100644
index 07428357c83f..000000000000
--- a/drivers/leds/leds-ams-delta.c
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * LEDs driver for Amstrad Delta (E3)
- *
- * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/leds.h>
-#include <plat/board-ams-delta.h>
-
-/*
- * Our context
- */
-struct ams_delta_led {
- struct led_classdev cdev;
- u8 bitmask;
-};
-
-static void ams_delta_led_set(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct ams_delta_led *led_dev =
- container_of(led_cdev, struct ams_delta_led, cdev);
-
- if (value)
- ams_delta_latch1_write(led_dev->bitmask, led_dev->bitmask);
- else
- ams_delta_latch1_write(led_dev->bitmask, 0);
-}
-
-static struct ams_delta_led ams_delta_leds[] = {
- {
- .cdev = {
- .name = "ams-delta::camera",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_CAMERA,
- },
- {
- .cdev = {
- .name = "ams-delta::advert",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_ADVERT,
- },
- {
- .cdev = {
- .name = "ams-delta::email",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_EMAIL,
- },
- {
- .cdev = {
- .name = "ams-delta::handsfree",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_HANDSFREE,
- },
- {
- .cdev = {
- .name = "ams-delta::voicemail",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_VOICEMAIL,
- },
- {
- .cdev = {
- .name = "ams-delta::voice",
- .brightness_set = ams_delta_led_set,
- },
- .bitmask = AMS_DELTA_LATCH1_LED_VOICE,
- },
-};
-
-static int ams_delta_led_probe(struct platform_device *pdev)
-{
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++) {
- ams_delta_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME;
- ret = led_classdev_register(&pdev->dev,
- &ams_delta_leds[i].cdev);
- if (ret < 0)
- goto fail;
- }
-
- return 0;
-fail:
- while (--i >= 0)
- led_classdev_unregister(&ams_delta_leds[i].cdev);
- return ret;
-}
-
-static int ams_delta_led_remove(struct platform_device *pdev)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ams_delta_leds); i++)
- led_classdev_unregister(&ams_delta_leds[i].cdev);
-
- return 0;
-}
-
-static struct platform_driver ams_delta_led_driver = {
- .probe = ams_delta_led_probe,
- .remove = ams_delta_led_remove,
- .driver = {
- .name = "ams-delta-led",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(ams_delta_led_driver);
-
-MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
-MODULE_DESCRIPTION("Amstrad Delta LED driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:ams-delta-led");
diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c
index 7df74cb97e70..f4c470a3bc8d 100644
--- a/drivers/leds/leds-gpio.c
+++ b/drivers/leds/leds-gpio.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
@@ -20,8 +21,6 @@
#include <linux/workqueue.h>
#include <linux/module.h>
-#include <asm/gpio.h>
-
struct gpio_led_data {
struct led_classdev cdev;
unsigned gpio;
diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c
index e59c166a0ce2..968fd5fef4fc 100644
--- a/drivers/leds/leds-lm3530.c
+++ b/drivers/leds/leds-lm3530.c
@@ -26,7 +26,6 @@
#define LM3530_GEN_CONFIG 0x10
#define LM3530_ALS_CONFIG 0x20
#define LM3530_BRT_RAMP_RATE 0x30
-#define LM3530_ALS_ZONE_REG 0x40
#define LM3530_ALS_IMP_SELECT 0x41
#define LM3530_BRT_CTRL_REG 0xA0
#define LM3530_ALS_ZB0_REG 0x60
@@ -38,7 +37,7 @@
#define LM3530_ALS_Z2T_REG 0x72
#define LM3530_ALS_Z3T_REG 0x73
#define LM3530_ALS_Z4T_REG 0x74
-#define LM3530_REG_MAX 15
+#define LM3530_REG_MAX 14
/* General Control Register */
#define LM3530_EN_I2C_SHIFT (0)
@@ -80,6 +79,9 @@
#define LM3530_DEF_ZT_3 (0x33)
#define LM3530_DEF_ZT_4 (0x19)
+/* 7 bits are used for the brightness : LM3530_BRT_CTRL_REG */
+#define MAX_BRIGHTNESS (127)
+
struct lm3530_mode_map {
const char *mode;
enum lm3530_mode mode_val;
@@ -115,7 +117,6 @@ static const u8 lm3530_reg[LM3530_REG_MAX] = {
LM3530_GEN_CONFIG,
LM3530_ALS_CONFIG,
LM3530_BRT_RAMP_RATE,
- LM3530_ALS_ZONE_REG,
LM3530_ALS_IMP_SELECT,
LM3530_BRT_CTRL_REG,
LM3530_ALS_ZB0_REG,
@@ -152,27 +153,35 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
u8 reg_val[LM3530_REG_MAX];
u8 zones[LM3530_ALS_ZB_MAX];
u32 als_vmin, als_vmax, als_vstep;
- struct lm3530_platform_data *pltfm = drvdata->pdata;
+ struct lm3530_platform_data *pdata = drvdata->pdata;
struct i2c_client *client = drvdata->client;
+ struct lm3530_pwm_data *pwm = &pdata->pwm_data;
- gen_config = (pltfm->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
- ((pltfm->max_current & 7) << LM3530_MAX_CURR_SHIFT);
+ gen_config = (pdata->brt_ramp_law << LM3530_RAMP_LAW_SHIFT) |
+ ((pdata->max_current & 7) << LM3530_MAX_CURR_SHIFT);
- if (drvdata->mode == LM3530_BL_MODE_MANUAL ||
- drvdata->mode == LM3530_BL_MODE_ALS)
- gen_config |= (LM3530_ENABLE_I2C);
+ switch (drvdata->mode) {
+ case LM3530_BL_MODE_MANUAL:
+ case LM3530_BL_MODE_ALS:
+ gen_config |= LM3530_ENABLE_I2C;
+ break;
+ case LM3530_BL_MODE_PWM:
+ gen_config |= LM3530_ENABLE_PWM | LM3530_ENABLE_PWM_SIMPLE |
+ (pdata->pwm_pol_hi << LM3530_PWM_POL_SHIFT);
+ break;
+ }
if (drvdata->mode == LM3530_BL_MODE_ALS) {
- if (pltfm->als_vmax == 0) {
- pltfm->als_vmin = 0;
- pltfm->als_vmax = LM3530_ALS_WINDOW_mV;
+ if (pdata->als_vmax == 0) {
+ pdata->als_vmin = 0;
+ pdata->als_vmax = LM3530_ALS_WINDOW_mV;
}
- als_vmin = pltfm->als_vmin;
- als_vmax = pltfm->als_vmax;
+ als_vmin = pdata->als_vmin;
+ als_vmax = pdata->als_vmax;
if ((als_vmax - als_vmin) > LM3530_ALS_WINDOW_mV)
- pltfm->als_vmax = als_vmax =
+ pdata->als_vmax = als_vmax =
als_vmin + LM3530_ALS_WINDOW_mV;
/* n zone boundary makes n+1 zones */
@@ -184,44 +193,41 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
/ 1000;
als_config =
- (pltfm->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
+ (pdata->als_avrg_time << LM3530_ALS_AVG_TIME_SHIFT) |
(LM3530_ENABLE_ALS) |
- (pltfm->als_input_mode << LM3530_ALS_SEL_SHIFT);
+ (pdata->als_input_mode << LM3530_ALS_SEL_SHIFT);
als_imp_sel =
- (pltfm->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
- (pltfm->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
+ (pdata->als1_resistor_sel << LM3530_ALS1_IMP_SHIFT) |
+ (pdata->als2_resistor_sel << LM3530_ALS2_IMP_SHIFT);
}
- if (drvdata->mode == LM3530_BL_MODE_PWM)
- gen_config |= (LM3530_ENABLE_PWM) |
- (pltfm->pwm_pol_hi << LM3530_PWM_POL_SHIFT) |
- (LM3530_ENABLE_PWM_SIMPLE);
-
- brt_ramp = (pltfm->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
- (pltfm->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
+ brt_ramp = (pdata->brt_ramp_fall << LM3530_BRT_RAMP_FALL_SHIFT) |
+ (pdata->brt_ramp_rise << LM3530_BRT_RAMP_RISE_SHIFT);
if (drvdata->brightness)
brightness = drvdata->brightness;
else
- brightness = drvdata->brightness = pltfm->brt_val;
+ brightness = drvdata->brightness = pdata->brt_val;
+
+ if (brightness > drvdata->led_dev.max_brightness)
+ brightness = drvdata->led_dev.max_brightness;
reg_val[0] = gen_config; /* LM3530_GEN_CONFIG */
reg_val[1] = als_config; /* LM3530_ALS_CONFIG */
reg_val[2] = brt_ramp; /* LM3530_BRT_RAMP_RATE */
- reg_val[3] = 0x00; /* LM3530_ALS_ZONE_REG */
- reg_val[4] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
- reg_val[5] = brightness; /* LM3530_BRT_CTRL_REG */
- reg_val[6] = zones[0]; /* LM3530_ALS_ZB0_REG */
- reg_val[7] = zones[1]; /* LM3530_ALS_ZB1_REG */
- reg_val[8] = zones[2]; /* LM3530_ALS_ZB2_REG */
- reg_val[9] = zones[3]; /* LM3530_ALS_ZB3_REG */
- reg_val[10] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
- reg_val[11] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
- reg_val[12] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
- reg_val[13] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
- reg_val[14] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
+ reg_val[3] = als_imp_sel; /* LM3530_ALS_IMP_SELECT */
+ reg_val[4] = brightness; /* LM3530_BRT_CTRL_REG */
+ reg_val[5] = zones[0]; /* LM3530_ALS_ZB0_REG */
+ reg_val[6] = zones[1]; /* LM3530_ALS_ZB1_REG */
+ reg_val[7] = zones[2]; /* LM3530_ALS_ZB2_REG */
+ reg_val[8] = zones[3]; /* LM3530_ALS_ZB3_REG */
+ reg_val[9] = LM3530_DEF_ZT_0; /* LM3530_ALS_Z0T_REG */
+ reg_val[10] = LM3530_DEF_ZT_1; /* LM3530_ALS_Z1T_REG */
+ reg_val[11] = LM3530_DEF_ZT_2; /* LM3530_ALS_Z2T_REG */
+ reg_val[12] = LM3530_DEF_ZT_3; /* LM3530_ALS_Z3T_REG */
+ reg_val[13] = LM3530_DEF_ZT_4; /* LM3530_ALS_Z4T_REG */
if (!drvdata->enable) {
ret = regulator_enable(drvdata->regulator);
@@ -234,6 +240,15 @@ static int lm3530_init_registers(struct lm3530_data *drvdata)
}
for (i = 0; i < LM3530_REG_MAX; i++) {
+ /* do not update brightness register when pwm mode */
+ if (lm3530_reg[i] == LM3530_BRT_CTRL_REG &&
+ drvdata->mode == LM3530_BL_MODE_PWM) {
+ if (pwm->pwm_set_intensity)
+ pwm->pwm_set_intensity(reg_val[i],
+ drvdata->led_dev.max_brightness);
+ continue;
+ }
+
ret = i2c_smbus_write_byte_data(client,
lm3530_reg[i], reg_val[i]);
if (ret)
@@ -249,6 +264,9 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
int err;
struct lm3530_data *drvdata =
container_of(led_cdev, struct lm3530_data, led_dev);
+ struct lm3530_platform_data *pdata = drvdata->pdata;
+ struct lm3530_pwm_data *pwm = &pdata->pwm_data;
+ u8 max_brightness = led_cdev->max_brightness;
switch (drvdata->mode) {
case LM3530_BL_MODE_MANUAL:
@@ -264,12 +282,12 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
/* set the brightness in brightness control register*/
err = i2c_smbus_write_byte_data(drvdata->client,
- LM3530_BRT_CTRL_REG, brt_val / 2);
+ LM3530_BRT_CTRL_REG, brt_val);
if (err)
dev_err(&drvdata->client->dev,
"Unable to set brightness: %d\n", err);
else
- drvdata->brightness = brt_val / 2;
+ drvdata->brightness = brt_val;
if (brt_val == 0) {
err = regulator_disable(drvdata->regulator);
@@ -282,6 +300,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
case LM3530_BL_MODE_ALS:
break;
case LM3530_BL_MODE_PWM:
+ if (pwm->pwm_set_intensity)
+ pwm->pwm_set_intensity(brt_val, max_brightness);
break;
default:
break;
@@ -291,11 +311,11 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev,
static ssize_t lm3530_mode_get(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = container_of(
- dev->parent, struct i2c_client, dev);
- struct lm3530_data *drvdata = i2c_get_clientdata(client);
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lm3530_data *drvdata;
int i, len = 0;
+ drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
for (i = 0; i < ARRAY_SIZE(mode_map); i++)
if (drvdata->mode == mode_map[i].mode_val)
len += sprintf(buf + len, "[%s] ", mode_map[i].mode);
@@ -310,26 +330,26 @@ static ssize_t lm3530_mode_get(struct device *dev,
static ssize_t lm3530_mode_set(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
- int err;
- struct i2c_client *client = container_of(
- dev->parent, struct i2c_client, dev);
- struct lm3530_data *drvdata = i2c_get_clientdata(client);
- int mode;
+ struct led_classdev *led_cdev = dev_get_drvdata(dev);
+ struct lm3530_data *drvdata;
+ struct lm3530_pwm_data *pwm;
+ u8 max_brightness;
+ int mode, err;
+ drvdata = container_of(led_cdev, struct lm3530_data, led_dev);
+ pwm = &drvdata->pdata->pwm_data;
+ max_brightness = led_cdev->max_brightness;
mode = lm3530_get_mode_from_str(buf);
if (mode < 0) {
dev_err(dev, "Invalid mode\n");
return -EINVAL;
}
- if (mode == LM3530_BL_MODE_MANUAL)
- drvdata->mode = LM3530_BL_MODE_MANUAL;
- else if (mode == LM3530_BL_MODE_ALS)
- drvdata->mode = LM3530_BL_MODE_ALS;
- else if (mode == LM3530_BL_MODE_PWM) {
- dev_err(dev, "PWM mode not supported\n");
- return -EINVAL;
- }
+ drvdata->mode = mode;
+
+ /* set pwm to low if unnecessary */
+ if (mode != LM3530_BL_MODE_PWM && pwm->pwm_set_intensity)
+ pwm->pwm_set_intensity(0, max_brightness);
err = lm3530_init_registers(drvdata);
if (err) {
@@ -380,6 +400,7 @@ static int __devinit lm3530_probe(struct i2c_client *client,
drvdata->enable = false;
drvdata->led_dev.name = LM3530_LED_DEV;
drvdata->led_dev.brightness_set = lm3530_brightness_set;
+ drvdata->led_dev.max_brightness = MAX_BRIGHTNESS;
i2c_set_clientdata(client, drvdata);
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
index d62a7982a5e6..410a723b8691 100644
--- a/drivers/leds/leds-lp5521.c
+++ b/drivers/leds/leds-lp5521.c
@@ -81,18 +81,10 @@
#define LP5521_MASTER_ENABLE 0x40 /* Chip master enable */
#define LP5521_LOGARITHMIC_PWM 0x80 /* Logarithmic PWM adjustment */
#define LP5521_EXEC_RUN 0x2A
-
-/* Bits in CONFIG register */
-#define LP5521_PWM_HF 0x40 /* PWM: 0 = 256Hz, 1 = 558Hz */
-#define LP5521_PWRSAVE_EN 0x20 /* 1 = Power save mode */
-#define LP5521_CP_MODE_OFF 0 /* Charge pump (CP) off */
-#define LP5521_CP_MODE_BYPASS 8 /* CP forced to bypass mode */
-#define LP5521_CP_MODE_1X5 0x10 /* CP forced to 1.5x mode */
-#define LP5521_CP_MODE_AUTO 0x18 /* Automatic mode selection */
-#define LP5521_R_TO_BATT 4 /* R out: 0 = CP, 1 = Vbat */
-#define LP5521_CLK_SRC_EXT 0 /* Ext-clk source (CLK_32K) */
-#define LP5521_CLK_INT 1 /* Internal clock */
-#define LP5521_CLK_AUTO 2 /* Automatic clock selection */
+#define LP5521_ENABLE_DEFAULT \
+ (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM)
+#define LP5521_ENABLE_RUN_PROGRAM \
+ (LP5521_ENABLE_DEFAULT | LP5521_EXEC_RUN)
/* Status */
#define LP5521_EXT_CLK_USED 0x08
@@ -100,6 +92,9 @@
/* default R channel current register value */
#define LP5521_REG_R_CURR_DEFAULT 0xAF
+/* Pattern Mode */
+#define PATTERN_OFF 0
+
struct lp5521_engine {
int id;
u8 mode;
@@ -241,15 +236,16 @@ static int lp5521_configure(struct i2c_client *client)
{
struct lp5521_chip *chip = i2c_get_clientdata(client);
int ret;
+ u8 cfg;
lp5521_init_engine(chip);
/* Set all PWMs to direct control mode */
- ret = lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+ ret = lp5521_write(client, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
- /* Enable auto-powersave, set charge pump to auto, red to battery */
- ret |= lp5521_write(client, LP5521_REG_CONFIG,
- LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+ cfg = chip->pdata->update_config ?
+ : (LP5521_PWRSAVE_EN | LP5521_CP_MODE_AUTO | LP5521_R_TO_BATT);
+ ret |= lp5521_write(client, LP5521_REG_CONFIG, cfg);
/* Initialize all channels PWM to zero -> leds off */
ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
@@ -258,8 +254,7 @@ static int lp5521_configure(struct i2c_client *client)
/* Set engines are set to run state when OP_MODE enables engines */
ret |= lp5521_write(client, LP5521_REG_ENABLE,
- LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM |
- LP5521_EXEC_RUN);
+ LP5521_ENABLE_RUN_PROGRAM);
/* enable takes 500us. 1 - 2 ms leaves some margin */
usleep_range(1000, 2000);
@@ -310,8 +305,7 @@ static int lp5521_detect(struct i2c_client *client)
int ret;
u8 buf;
- ret = lp5521_write(client, LP5521_REG_ENABLE,
- LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM);
+ ret = lp5521_write(client, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
if (ret)
return ret;
/* enable takes 500us. 1 - 2 ms leaves some margin */
@@ -319,7 +313,7 @@ static int lp5521_detect(struct i2c_client *client)
ret = lp5521_read(client, LP5521_REG_ENABLE, &buf);
if (ret)
return ret;
- if (buf != (LP5521_MASTER_ENABLE | LP5521_LOGARITHMIC_PWM))
+ if (buf != LP5521_ENABLE_DEFAULT)
return -ENODEV;
return 0;
@@ -504,7 +498,7 @@ static ssize_t store_current(struct device *dev,
ssize_t ret;
unsigned long curr;
- if (strict_strtoul(buf, 0, &curr))
+ if (kstrtoul(buf, 0, &curr))
return -EINVAL;
if (curr > led->max_current)
@@ -536,6 +530,97 @@ static ssize_t lp5521_selftest(struct device *dev,
return sprintf(buf, "%s\n", ret ? "FAIL" : "OK");
}
+static void lp5521_clear_program_memory(struct i2c_client *cl)
+{
+ int i;
+ u8 rgb_mem[] = {
+ LP5521_REG_R_PROG_MEM,
+ LP5521_REG_G_PROG_MEM,
+ LP5521_REG_B_PROG_MEM,
+ };
+
+ for (i = 0; i < ARRAY_SIZE(rgb_mem); i++) {
+ lp5521_write(cl, rgb_mem[i], 0);
+ lp5521_write(cl, rgb_mem[i] + 1, 0);
+ }
+}
+
+static void lp5521_write_program_memory(struct i2c_client *cl,
+ u8 base, u8 *rgb, int size)
+{
+ int i;
+
+ if (!rgb || size <= 0)
+ return;
+
+ for (i = 0; i < size; i++)
+ lp5521_write(cl, base + i, *(rgb + i));
+
+ lp5521_write(cl, base + i, 0);
+ lp5521_write(cl, base + i + 1, 0);
+}
+
+static inline struct lp5521_led_pattern *lp5521_get_pattern
+ (struct lp5521_chip *chip, u8 offset)
+{
+ struct lp5521_led_pattern *ptn;
+ ptn = chip->pdata->patterns + (offset - 1);
+ return ptn;
+}
+
+static void lp5521_run_led_pattern(int mode, struct lp5521_chip *chip)
+{
+ struct lp5521_led_pattern *ptn;
+ struct i2c_client *cl = chip->client;
+ int num_patterns = chip->pdata->num_patterns;
+
+ if (mode > num_patterns || !(chip->pdata->patterns))
+ return;
+
+ if (mode == PATTERN_OFF) {
+ lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_DEFAULT);
+ usleep_range(1000, 2000);
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_DIRECT);
+ } else {
+ ptn = lp5521_get_pattern(chip, mode);
+ if (!ptn)
+ return;
+
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_LOAD);
+ usleep_range(1000, 2000);
+
+ lp5521_clear_program_memory(cl);
+
+ lp5521_write_program_memory(cl, LP5521_REG_R_PROG_MEM,
+ ptn->r, ptn->size_r);
+ lp5521_write_program_memory(cl, LP5521_REG_G_PROG_MEM,
+ ptn->g, ptn->size_g);
+ lp5521_write_program_memory(cl, LP5521_REG_B_PROG_MEM,
+ ptn->b, ptn->size_b);
+
+ lp5521_write(cl, LP5521_REG_OP_MODE, LP5521_CMD_RUN);
+ usleep_range(1000, 2000);
+ lp5521_write(cl, LP5521_REG_ENABLE, LP5521_ENABLE_RUN_PROGRAM);
+ }
+}
+
+static ssize_t store_led_pattern(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct lp5521_chip *chip = i2c_get_clientdata(to_i2c_client(dev));
+ unsigned long val;
+ int ret;
+
+ ret = strict_strtoul(buf, 16, &val);
+ if (ret)
+ return ret;
+
+ lp5521_run_led_pattern(val, chip);
+
+ return len;
+}
+
/* led class device attributes */
static DEVICE_ATTR(led_current, S_IRUGO | S_IWUSR, show_current, store_current);
static DEVICE_ATTR(max_current, S_IRUGO , show_max_current, NULL);
@@ -561,6 +646,7 @@ static DEVICE_ATTR(engine1_load, S_IWUSR, NULL, store_engine1_load);
static DEVICE_ATTR(engine2_load, S_IWUSR, NULL, store_engine2_load);
static DEVICE_ATTR(engine3_load, S_IWUSR, NULL, store_engine3_load);
static DEVICE_ATTR(selftest, S_IRUGO, lp5521_selftest, NULL);
+static DEVICE_ATTR(led_pattern, S_IWUSR, NULL, store_led_pattern);
static struct attribute *lp5521_attributes[] = {
&dev_attr_engine1_mode.attr,
@@ -570,6 +656,7 @@ static struct attribute *lp5521_attributes[] = {
&dev_attr_engine1_load.attr,
&dev_attr_engine2_load.attr,
&dev_attr_engine3_load.attr,
+ &dev_attr_led_pattern.attr,
NULL
};
@@ -620,10 +707,15 @@ static int __devinit lp5521_init_led(struct lp5521_led *led,
return -EINVAL;
}
- snprintf(name, sizeof(name), "%s:channel%d",
- pdata->label ?: client->name, chan);
led->cdev.brightness_set = lp5521_set_brightness;
- led->cdev.name = name;
+ if (pdata->led_config[chan].name) {
+ led->cdev.name = pdata->led_config[chan].name;
+ } else {
+ snprintf(name, sizeof(name), "%s:channel%d",
+ pdata->label ?: client->name, chan);
+ led->cdev.name = name;
+ }
+
res = led_classdev_register(dev, &led->cdev);
if (res < 0) {
dev_err(dev, "couldn't register led on channel %d\n", chan);
@@ -692,9 +784,9 @@ static int __devinit lp5521_probe(struct i2c_client *client,
* otherwise further access to the R G B channels in the
* LP5521_REG_ENABLE register will not have any effect - strange!
*/
- lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
+ ret = lp5521_read(client, LP5521_REG_R_CURRENT, &buf);
if (buf != LP5521_REG_R_CURR_DEFAULT) {
- dev_err(&client->dev, "error in reseting chip\n");
+ dev_err(&client->dev, "error in resetting chip\n");
goto fail2;
}
usleep_range(10000, 20000);
@@ -767,6 +859,7 @@ static int __devexit lp5521_remove(struct i2c_client *client)
struct lp5521_chip *chip = i2c_get_clientdata(client);
int i;
+ lp5521_run_led_pattern(PATTERN_OFF, chip);
lp5521_unregister_sysfs(client);
for (i = 0; i < chip->num_leds; i++) {
diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c
index 73e791ae7259..857a3e15f2dd 100644
--- a/drivers/leds/leds-lp5523.c
+++ b/drivers/leds/leds-lp5523.c
@@ -152,7 +152,7 @@ static inline struct lp5523_chip *led_to_lp5523(struct lp5523_led *led)
static int lp5523_set_mode(struct lp5523_engine *engine, u8 mode);
static int lp5523_set_engine_mode(struct lp5523_engine *engine, u8 mode);
-static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern);
+static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern);
static void lp5523_led_brightness_work(struct work_struct *work);
@@ -196,7 +196,7 @@ static int lp5523_configure(struct i2c_client *client)
u8 status;
/* one pattern per engine setting led mux start and stop addresses */
- u8 pattern[][LP5523_PROGRAM_LENGTH] = {
+ static const u8 pattern[][LP5523_PROGRAM_LENGTH] = {
{ 0x9c, 0x30, 0x9c, 0xb0, 0x9d, 0x80, 0xd8, 0x00, 0},
{ 0x9c, 0x40, 0x9c, 0xc0, 0x9d, 0x80, 0xd8, 0x00, 0},
{ 0x9c, 0x50, 0x9c, 0xd0, 0x9d, 0x80, 0xd8, 0x00, 0},
@@ -301,7 +301,7 @@ static int lp5523_load_mux(struct lp5523_engine *engine, u16 mux)
return ret;
}
-static int lp5523_load_program(struct lp5523_engine *engine, u8 *pattern)
+static int lp5523_load_program(struct lp5523_engine *engine, const u8 *pattern)
{
struct lp5523_chip *chip = engine_to_lp5523(engine);
struct i2c_client *client = chip->client;
diff --git a/drivers/leds/leds-pca9633.c b/drivers/leds/leds-pca9633.c
new file mode 100644
index 000000000000..d8926fd031aa
--- /dev/null
+++ b/drivers/leds/leds-pca9633.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2011 bct electronic GmbH
+ *
+ * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
+ *
+ * Based on leds-pca955x.c
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+/* LED select registers determine the source that drives LED outputs */
+#define PCA9633_LED_OFF 0x0 /* LED driver off */
+#define PCA9633_LED_ON 0x1 /* LED driver on */
+#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */
+#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */
+
+#define PCA9633_MODE1 0x00
+#define PCA9633_MODE2 0x01
+#define PCA9633_PWM_BASE 0x02
+#define PCA9633_LEDOUT 0x08
+
+static const struct i2c_device_id pca9633_id[] = {
+ { "pca9633", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pca9633_id);
+
+struct pca9633_led {
+ struct i2c_client *client;
+ struct work_struct work;
+ enum led_brightness brightness;
+ struct led_classdev led_cdev;
+ int led_num; /* 0 .. 3 potentially */
+ char name[32];
+};
+
+static void pca9633_led_work(struct work_struct *work)
+{
+ struct pca9633_led *pca9633 = container_of(work,
+ struct pca9633_led, work);
+ u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
+ int shift = 2 * pca9633->led_num;
+ u8 mask = 0x3 << shift;
+
+ switch (pca9633->brightness) {
+ case LED_FULL:
+ i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
+ (ledout & ~mask) | (PCA9633_LED_ON << shift));
+ break;
+ case LED_OFF:
+ i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
+ ledout & ~mask);
+ break;
+ default:
+ i2c_smbus_write_byte_data(pca9633->client,
+ PCA9633_PWM_BASE + pca9633->led_num,
+ pca9633->brightness);
+ i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
+ (ledout & ~mask) | (PCA9633_LED_PWM << shift));
+ break;
+ }
+}
+
+static void pca9633_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct pca9633_led *pca9633;
+
+ pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);
+
+ pca9633->brightness = value;
+
+ /*
+ * Must use workqueue for the actual I/O since I2C operations
+ * can sleep.
+ */
+ schedule_work(&pca9633->work);
+}
+
+static int __devinit pca9633_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pca9633_led *pca9633;
+ struct led_platform_data *pdata;
+ int i, err;
+
+ pdata = client->dev.platform_data;
+
+ if (pdata) {
+ if (pdata->num_leds <= 0 || pdata->num_leds > 4) {
+ dev_err(&client->dev, "board info must claim at most 4 LEDs");
+ return -EINVAL;
+ }
+ }
+
+ pca9633 = kcalloc(4, sizeof(*pca9633), GFP_KERNEL);
+ if (!pca9633)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, pca9633);
+
+ for (i = 0; i < 4; i++) {
+ pca9633[i].client = client;
+ pca9633[i].led_num = i;
+
+ /* Platform data can specify LED names and default triggers */
+ if (pdata && i < pdata->num_leds) {
+ if (pdata->leds[i].name)
+ snprintf(pca9633[i].name,
+ sizeof(pca9633[i].name), "pca9633:%s",
+ pdata->leds[i].name);
+ if (pdata->leds[i].default_trigger)
+ pca9633[i].led_cdev.default_trigger =
+ pdata->leds[i].default_trigger;
+ } else {
+ snprintf(pca9633[i].name, sizeof(pca9633[i].name),
+ "pca9633:%d", i);
+ }
+
+ pca9633[i].led_cdev.name = pca9633[i].name;
+ pca9633[i].led_cdev.brightness_set = pca9633_led_set;
+
+ INIT_WORK(&pca9633[i].work, pca9633_led_work);
+
+ err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
+ if (err < 0)
+ goto exit;
+ }
+
+ /* Disable LED all-call address and set normal mode */
+ i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);
+
+ /* Turn off LEDs */
+ i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);
+
+ return 0;
+
+exit:
+ while (i--) {
+ led_classdev_unregister(&pca9633[i].led_cdev);
+ cancel_work_sync(&pca9633[i].work);
+ }
+
+ kfree(pca9633);
+
+ return err;
+}
+
+static int __devexit pca9633_remove(struct i2c_client *client)
+{
+ struct pca9633_led *pca9633 = i2c_get_clientdata(client);
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ led_classdev_unregister(&pca9633[i].led_cdev);
+ cancel_work_sync(&pca9633[i].work);
+ }
+
+ kfree(pca9633);
+
+ return 0;
+}
+
+static struct i2c_driver pca9633_driver = {
+ .driver = {
+ .name = "leds-pca9633",
+ .owner = THIS_MODULE,
+ },
+ .probe = pca9633_probe,
+ .remove = __devexit_p(pca9633_remove),
+ .id_table = pca9633_id,
+};
+
+module_i2c_driver(pca9633_driver);
+
+MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
+MODULE_DESCRIPTION("PCA9633 LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/leds/leds-tca6507.c b/drivers/leds/leds-tca6507.c
index 133f89fb7071..6c1c14f31635 100644
--- a/drivers/leds/leds-tca6507.c
+++ b/drivers/leds/leds-tca6507.c
@@ -687,10 +687,9 @@ static int __devinit tca6507_probe(struct i2c_client *client,
NUM_LEDS);
return -ENODEV;
}
- err = -ENOMEM;
tca = kzalloc(sizeof(*tca), GFP_KERNEL);
if (!tca)
- goto exit;
+ return -ENOMEM;
tca->client = client;
INIT_WORK(&tca->work, tca6507_work);
@@ -724,11 +723,10 @@ static int __devinit tca6507_probe(struct i2c_client *client,
return 0;
exit:
- while (i--)
+ while (i--) {
if (tca->leds[i].led_cdev.name)
led_classdev_unregister(&tca->leds[i].led_cdev);
- cancel_work_sync(&tca->work);
- i2c_set_clientdata(client, NULL);
+ }
kfree(tca);
return err;
}
@@ -745,7 +743,6 @@ static int __devexit tca6507_remove(struct i2c_client *client)
}
tca6507_remove_gpio(tca);
cancel_work_sync(&tca->work);
- i2c_set_clientdata(client, NULL);
kfree(tca);
return 0;
diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c
index b6ef8f590764..87de8d9bcfad 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 c60d025044ee..fc71723cbc48 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 0ff92c208005..97cfc5ac9fd0 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 46c4e95f10d6..3b4a157714b1 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 971bc9582a5f..86511c570dd8 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 c9570fcf1cce..3725f088f17e 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 6cccd60c594e..22b8ce4191cc 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 aeb30d07d5a2..a00ee41f0573 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 647c6add2193..4d6a90a1372b 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 30e6195e19d4..04067e073aa9 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 749d174b0dc6..fc13d0f2663b 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 344273235124..a9430ed4f36c 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 43137b421f92..3c2be5193fd5 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 3c193504bb80..1cc4e4953d89 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 faa4741df6d3..10f122a3a856 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 046860c7a166..8b2e0dffe82e 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/bitmap.c b/drivers/md/bitmap.c
index 045e086144ad..3d0dfa7a89a2 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -26,6 +26,7 @@
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/buffer_head.h>
+#include <linux/seq_file.h>
#include "md.h"
#include "bitmap.h"
@@ -35,31 +36,6 @@ static inline char *bmname(struct bitmap *bitmap)
}
/*
- * just a placeholder - calls kmalloc for bitmap pages
- */
-static unsigned char *bitmap_alloc_page(struct bitmap *bitmap)
-{
- unsigned char *page;
-
- page = kzalloc(PAGE_SIZE, GFP_NOIO);
- if (!page)
- printk("%s: bitmap_alloc_page FAILED\n", bmname(bitmap));
- else
- pr_debug("%s: bitmap_alloc_page: allocated page at %p\n",
- bmname(bitmap), page);
- return page;
-}
-
-/*
- * for now just a placeholder -- just calls kfree for bitmap pages
- */
-static void bitmap_free_page(struct bitmap *bitmap, unsigned char *page)
-{
- pr_debug("%s: bitmap_free_page: free page %p\n", bmname(bitmap), page);
- kfree(page);
-}
-
-/*
* check a page and, if necessary, allocate it (or hijack it if the alloc fails)
*
* 1) check to see if this page is allocated, if it's not then try to alloc
@@ -96,7 +72,7 @@ __acquires(bitmap->lock)
/* this page has not been allocated yet */
spin_unlock_irq(&bitmap->lock);
- mappage = bitmap_alloc_page(bitmap);
+ mappage = kzalloc(PAGE_SIZE, GFP_NOIO);
spin_lock_irq(&bitmap->lock);
if (mappage == NULL) {
@@ -109,7 +85,7 @@ __acquires(bitmap->lock)
} else if (bitmap->bp[page].map ||
bitmap->bp[page].hijacked) {
/* somebody beat us to getting the page */
- bitmap_free_page(bitmap, mappage);
+ kfree(mappage);
return 0;
} else {
@@ -141,7 +117,7 @@ static void bitmap_checkfree(struct bitmap *bitmap, unsigned long page)
ptr = bitmap->bp[page].map;
bitmap->bp[page].map = NULL;
bitmap->missing_pages++;
- bitmap_free_page(bitmap, ptr);
+ kfree(ptr);
}
}
@@ -171,7 +147,7 @@ static struct page *read_sb_page(struct mddev *mddev, loff_t offset,
did_alloc = 1;
}
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (! test_bit(In_sync, &rdev->flags)
|| test_bit(Faulty, &rdev->flags))
continue;
@@ -445,18 +421,13 @@ out:
void bitmap_update_sb(struct bitmap *bitmap)
{
bitmap_super_t *sb;
- unsigned long flags;
if (!bitmap || !bitmap->mddev) /* no bitmap for this array */
return;
if (bitmap->mddev->bitmap_info.external)
return;
- spin_lock_irqsave(&bitmap->lock, flags);
- if (!bitmap->sb_page) { /* no superblock */
- spin_unlock_irqrestore(&bitmap->lock, flags);
+ if (!bitmap->sb_page) /* no superblock */
return;
- }
- spin_unlock_irqrestore(&bitmap->lock, flags);
sb = kmap_atomic(bitmap->sb_page);
sb->events = cpu_to_le64(bitmap->mddev->events);
if (bitmap->mddev->events < bitmap->events_cleared)
@@ -632,26 +603,28 @@ static int bitmap_read_sb(struct bitmap *bitmap)
/* keep the array size field of the bitmap superblock up to date */
sb->sync_size = cpu_to_le64(bitmap->mddev->resync_max_sectors);
- if (!bitmap->mddev->persistent)
- goto success;
-
- /*
- * if we have a persistent array superblock, compare the
- * bitmap's UUID and event counter to the mddev's
- */
- if (memcmp(sb->uuid, bitmap->mddev->uuid, 16)) {
- printk(KERN_INFO "%s: bitmap superblock UUID mismatch\n",
- bmname(bitmap));
- goto out;
- }
- events = le64_to_cpu(sb->events);
- if (events < bitmap->mddev->events) {
- printk(KERN_INFO "%s: bitmap file is out of date (%llu < %llu) "
- "-- forcing full recovery\n", bmname(bitmap), events,
- (unsigned long long) bitmap->mddev->events);
- sb->state |= cpu_to_le32(BITMAP_STALE);
+ if (bitmap->mddev->persistent) {
+ /*
+ * We have a persistent array superblock, so compare the
+ * bitmap's UUID and event counter to the mddev's
+ */
+ if (memcmp(sb->uuid, bitmap->mddev->uuid, 16)) {
+ printk(KERN_INFO
+ "%s: bitmap superblock UUID mismatch\n",
+ bmname(bitmap));
+ goto out;
+ }
+ events = le64_to_cpu(sb->events);
+ if (events < bitmap->mddev->events) {
+ printk(KERN_INFO
+ "%s: bitmap file is out of date (%llu < %llu) "
+ "-- forcing full recovery\n",
+ bmname(bitmap), events,
+ (unsigned long long) bitmap->mddev->events);
+ sb->state |= cpu_to_le32(BITMAP_STALE);
+ }
}
-success:
+
/* assign fields using values from superblock */
bitmap->mddev->bitmap_info.chunksize = chunksize;
bitmap->mddev->bitmap_info.daemon_sleep = daemon_sleep;
@@ -680,15 +653,10 @@ static int bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
enum bitmap_mask_op op)
{
bitmap_super_t *sb;
- unsigned long flags;
int old;
- spin_lock_irqsave(&bitmap->lock, flags);
- if (!bitmap->sb_page) { /* can't set the state */
- spin_unlock_irqrestore(&bitmap->lock, flags);
+ if (!bitmap->sb_page) /* can't set the state */
return 0;
- }
- spin_unlock_irqrestore(&bitmap->lock, flags);
sb = kmap_atomic(bitmap->sb_page);
old = le32_to_cpu(sb->state) & bits;
switch (op) {
@@ -870,7 +838,7 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
unsigned long bit;
struct page *page;
void *kaddr;
- unsigned long chunk = block >> CHUNK_BLOCK_SHIFT(bitmap);
+ unsigned long chunk = block >> bitmap->chunkshift;
if (!bitmap->filemap)
return;
@@ -1069,10 +1037,10 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
kunmap_atomic(paddr);
if (b) {
/* if the disk bit is set, set the memory bit */
- int needed = ((sector_t)(i+1) << (CHUNK_BLOCK_SHIFT(bitmap))
+ int needed = ((sector_t)(i+1) << bitmap->chunkshift
>= start);
bitmap_set_memory_bits(bitmap,
- (sector_t)i << CHUNK_BLOCK_SHIFT(bitmap),
+ (sector_t)i << bitmap->chunkshift,
needed);
bit_cnt++;
}
@@ -1116,7 +1084,7 @@ void bitmap_write_all(struct bitmap *bitmap)
static void bitmap_count_page(struct bitmap *bitmap, sector_t offset, int inc)
{
- sector_t chunk = offset >> CHUNK_BLOCK_SHIFT(bitmap);
+ sector_t chunk = offset >> bitmap->chunkshift;
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
bitmap->bp[page].count += inc;
bitmap_checkfree(bitmap, page);
@@ -1222,7 +1190,7 @@ void bitmap_daemon_work(struct mddev *mddev)
bitmap->allclean = 0;
}
bmc = bitmap_get_counter(bitmap,
- (sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
+ (sector_t)j << bitmap->chunkshift,
&blocks, 0);
if (!bmc)
j |= PAGE_COUNTER_MASK;
@@ -1231,7 +1199,7 @@ void bitmap_daemon_work(struct mddev *mddev)
/* we can clear the bit */
*bmc = 0;
bitmap_count_page(bitmap,
- (sector_t)j << CHUNK_BLOCK_SHIFT(bitmap),
+ (sector_t)j << bitmap->chunkshift,
-1);
/* clear the bit */
@@ -1285,7 +1253,7 @@ __acquires(bitmap->lock)
* The lock must have been taken with interrupts enabled.
* If !create, we don't release the lock.
*/
- sector_t chunk = offset >> CHUNK_BLOCK_SHIFT(bitmap);
+ sector_t chunk = offset >> bitmap->chunkshift;
unsigned long page = chunk >> PAGE_COUNTER_SHIFT;
unsigned long pageoff = (chunk & PAGE_COUNTER_MASK) << COUNTER_BYTE_SHIFT;
sector_t csize;
@@ -1295,10 +1263,10 @@ __acquires(bitmap->lock)
if (bitmap->bp[page].hijacked ||
bitmap->bp[page].map == NULL)
- csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap) +
+ csize = ((sector_t)1) << (bitmap->chunkshift +
PAGE_COUNTER_SHIFT - 1);
else
- csize = ((sector_t)1) << (CHUNK_BLOCK_SHIFT(bitmap));
+ csize = ((sector_t)1) << bitmap->chunkshift;
*blocks = csize - (offset & (csize - 1));
if (err < 0)
@@ -1424,7 +1392,7 @@ void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long secto
set_page_attr(bitmap,
filemap_get_page(
bitmap,
- offset >> CHUNK_BLOCK_SHIFT(bitmap)),
+ offset >> bitmap->chunkshift),
BITMAP_PAGE_PENDING);
bitmap->allclean = 0;
}
@@ -1512,7 +1480,7 @@ void bitmap_end_sync(struct bitmap *bitmap, sector_t offset, sector_t *blocks, i
else {
if (*bmc <= 2) {
set_page_attr(bitmap,
- filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap)),
+ filemap_get_page(bitmap, offset >> bitmap->chunkshift),
BITMAP_PAGE_PENDING);
bitmap->allclean = 0;
}
@@ -1559,7 +1527,7 @@ void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
bitmap->mddev->curr_resync_completed = sector;
set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
- sector &= ~((1ULL << CHUNK_BLOCK_SHIFT(bitmap)) - 1);
+ sector &= ~((1ULL << bitmap->chunkshift) - 1);
s = 0;
while (s < sector && s < bitmap->mddev->resync_max_sectors) {
bitmap_end_sync(bitmap, s, &blocks, 0);
@@ -1589,7 +1557,7 @@ static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int n
struct page *page;
*bmc = 2 | (needed ? NEEDED_MASK : 0);
bitmap_count_page(bitmap, offset, 1);
- page = filemap_get_page(bitmap, offset >> CHUNK_BLOCK_SHIFT(bitmap));
+ page = filemap_get_page(bitmap, offset >> bitmap->chunkshift);
set_page_attr(bitmap, page, BITMAP_PAGE_PENDING);
bitmap->allclean = 0;
}
@@ -1602,7 +1570,7 @@ void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
unsigned long chunk;
for (chunk = s; chunk <= e; chunk++) {
- sector_t sec = (sector_t)chunk << CHUNK_BLOCK_SHIFT(bitmap);
+ sector_t sec = (sector_t)chunk << bitmap->chunkshift;
bitmap_set_memory_bits(bitmap, sec, 1);
spin_lock_irq(&bitmap->lock);
bitmap_file_set_bit(bitmap, sec);
@@ -1759,11 +1727,12 @@ int bitmap_create(struct mddev *mddev)
goto error;
bitmap->daemon_lastrun = jiffies;
- bitmap->chunkshift = ffz(~mddev->bitmap_info.chunksize);
+ bitmap->chunkshift = (ffz(~mddev->bitmap_info.chunksize)
+ - BITMAP_BLOCK_SHIFT);
/* now that chunksize and chunkshift are set, we can use these macros */
- chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) >>
- CHUNK_BLOCK_SHIFT(bitmap);
+ chunks = (blocks + bitmap->chunkshift - 1) >>
+ bitmap->chunkshift;
pages = (chunks + PAGE_COUNTER_RATIO - 1) / PAGE_COUNTER_RATIO;
BUG_ON(!pages);
@@ -1836,6 +1805,33 @@ out:
}
EXPORT_SYMBOL_GPL(bitmap_load);
+void bitmap_status(struct seq_file *seq, struct bitmap *bitmap)
+{
+ unsigned long chunk_kb;
+ unsigned long flags;
+
+ if (!bitmap)
+ return;
+
+ spin_lock_irqsave(&bitmap->lock, flags);
+ chunk_kb = bitmap->mddev->bitmap_info.chunksize >> 10;
+ seq_printf(seq, "bitmap: %lu/%lu pages [%luKB], "
+ "%lu%s chunk",
+ bitmap->pages - bitmap->missing_pages,
+ bitmap->pages,
+ (bitmap->pages - bitmap->missing_pages)
+ << (PAGE_SHIFT - 10),
+ chunk_kb ? chunk_kb : bitmap->mddev->bitmap_info.chunksize,
+ chunk_kb ? "KB" : "B");
+ if (bitmap->file) {
+ seq_printf(seq, ", file: ");
+ seq_path(seq, &bitmap->file->f_path, " \t\n");
+ }
+
+ seq_printf(seq, "\n");
+ spin_unlock_irqrestore(&bitmap->lock, flags);
+}
+
static ssize_t
location_show(struct mddev *mddev, char *page)
{
@@ -1904,6 +1900,8 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
if (mddev->pers) {
mddev->pers->quiesce(mddev, 1);
rv = bitmap_create(mddev);
+ if (!rv)
+ rv = bitmap_load(mddev);
if (rv) {
bitmap_destroy(mddev);
mddev->bitmap_info.offset = 0;
diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h
index a15436dd9b3e..55ca5aec84e4 100644
--- a/drivers/md/bitmap.h
+++ b/drivers/md/bitmap.h
@@ -13,8 +13,6 @@
#define BITMAP_MAJOR_HI 4
#define BITMAP_MAJOR_HOSTENDIAN 3
-#define BITMAP_MINOR 39
-
/*
* in-memory bitmap:
*
@@ -101,21 +99,10 @@ typedef __u16 bitmap_counter_t;
/* same, except a mask value for more efficient bitops */
#define PAGE_COUNTER_MASK (PAGE_COUNTER_RATIO - 1)
-#define BITMAP_BLOCK_SIZE 512
#define BITMAP_BLOCK_SHIFT 9
/* how many blocks per chunk? (this is variable) */
#define CHUNK_BLOCK_RATIO(bitmap) ((bitmap)->mddev->bitmap_info.chunksize >> BITMAP_BLOCK_SHIFT)
-#define CHUNK_BLOCK_SHIFT(bitmap) ((bitmap)->chunkshift - BITMAP_BLOCK_SHIFT)
-#define CHUNK_BLOCK_MASK(bitmap) (CHUNK_BLOCK_RATIO(bitmap) - 1)
-
-/* when hijacked, the counters and bits represent even larger "chunks" */
-/* there will be 1024 chunks represented by each counter in the page pointers */
-#define PAGEPTR_BLOCK_RATIO(bitmap) \
- (CHUNK_BLOCK_RATIO(bitmap) << PAGE_COUNTER_SHIFT >> 1)
-#define PAGEPTR_BLOCK_SHIFT(bitmap) \
- (CHUNK_BLOCK_SHIFT(bitmap) + PAGE_COUNTER_SHIFT - 1)
-#define PAGEPTR_BLOCK_MASK(bitmap) (PAGEPTR_BLOCK_RATIO(bitmap) - 1)
#endif
@@ -181,12 +168,6 @@ struct bitmap_page {
unsigned int count:31;
};
-/* keep track of bitmap file pages that have pending writes on them */
-struct page_list {
- struct list_head list;
- struct page *page;
-};
-
/* the main bitmap structure - one per mddev */
struct bitmap {
struct bitmap_page *bp;
@@ -196,7 +177,7 @@ struct bitmap {
struct mddev *mddev; /* the md device that the bitmap is for */
/* bitmap chunksize -- how much data does each bit represent? */
- unsigned long chunkshift; /* chunksize = 2^chunkshift (for bitops) */
+ unsigned long chunkshift; /* chunksize = 2^(chunkshift+9) (for bitops) */
unsigned long chunks; /* total number of data chunks for the array */
__u64 events_cleared;
@@ -245,6 +226,7 @@ void bitmap_destroy(struct mddev *mddev);
void bitmap_print_sb(struct bitmap *bitmap);
void bitmap_update_sb(struct bitmap *bitmap);
+void bitmap_status(struct seq_file *seq, struct bitmap *bitmap);
int bitmap_setallbits(struct bitmap *bitmap);
void bitmap_write_all(struct bitmap *bitmap);
diff --git a/drivers/md/dm-bufio.c b/drivers/md/dm-bufio.c
index b6e58c7b6df5..cc06a1e52423 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 5c4c3a04e381..b142946a9e32 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 db6b51639cee..3f06df59fd82 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 f18375dcedd9..2dc22dddb2ae 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 042e71996569..aa70f7d43a1a 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 b280c433e4a0..ac49c01f1a44 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 1ce84ed0b765..a1a3e6df17b8 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 9728839f844a..3639eeab6042 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 3b52bb72bd1f..65ebaebf502b 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 801d92d237cf..922a3385eead 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 03a837aa5ce6..3941fae0de9f 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 787022c18187..b0ba52459ed7 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;
}
@@ -615,14 +617,14 @@ static int read_disk_sb(struct md_rdev *rdev, int size)
static void super_sync(struct mddev *mddev, struct md_rdev *rdev)
{
- struct md_rdev *r, *t;
+ struct md_rdev *r;
uint64_t failed_devices;
struct dm_raid_superblock *sb;
sb = page_address(rdev->sb_page);
failed_devices = le64_to_cpu(sb->failed_devices);
- rdev_for_each(r, t, mddev)
+ rdev_for_each(r, mddev)
if ((r->raid_disk >= 0) && test_bit(Faulty, &r->flags))
failed_devices |= (1ULL << r->raid_disk);
@@ -707,7 +709,7 @@ static int super_init_validation(struct mddev *mddev, struct md_rdev *rdev)
struct dm_raid_superblock *sb;
uint32_t new_devs = 0;
uint32_t rebuilds = 0;
- struct md_rdev *r, *t;
+ struct md_rdev *r;
struct dm_raid_superblock *sb2;
sb = page_address(rdev->sb_page);
@@ -750,7 +752,7 @@ static int super_init_validation(struct mddev *mddev, struct md_rdev *rdev)
* case the In_sync bit will /not/ be set and
* recovery_cp must be MaxSector.
*/
- rdev_for_each(r, t, mddev) {
+ rdev_for_each(r, mddev) {
if (!test_bit(In_sync, &r->flags)) {
DMINFO("Device %d specified for rebuild: "
"Clearing superblock", r->raid_disk);
@@ -782,7 +784,7 @@ static int super_init_validation(struct mddev *mddev, struct md_rdev *rdev)
* Now we set the Faulty bit for those devices that are
* recorded in the superblock as failed.
*/
- rdev_for_each(r, t, mddev) {
+ rdev_for_each(r, mddev) {
if (!r->sb_page)
continue;
sb2 = page_address(r->sb_page);
@@ -855,11 +857,27 @@ 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;
- struct md_rdev *rdev, *freshest, *tmp;
+ 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, tmp, mddev) {
+ rdev_for_each(rdev, mddev) {
if (!rdev->meta_bdev)
continue;
@@ -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;
}
@@ -888,7 +937,7 @@ static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
if (super_validate(mddev, freshest))
return -EINVAL;
- rdev_for_each(rdev, tmp, mddev)
+ rdev_for_each(rdev, mddev)
if ((rdev != freshest) && super_validate(mddev, rdev))
return -EINVAL;
@@ -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 9bfd057be686..d039de8322f0 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 27f1d423b76c..6ab1192cdd5f 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 59883bd78214..9df8f6bd6418 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 3d80cf0c152d..35c94ff24ad5 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 63cc54289aff..2e227fbf1622 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 237571af77fd..737d38865b69 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 859c16896877..ed4725e67c96 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 c3087575fef0..213ae32a0fc4 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 000000000000..fa365d39b612
--- /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 b89c548ec3f8..e24143cc2040 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/faulty.c b/drivers/md/faulty.c
index feb2c3c7bb44..45135f69509c 100644
--- a/drivers/md/faulty.c
+++ b/drivers/md/faulty.c
@@ -315,7 +315,7 @@ static int run(struct mddev *mddev)
}
conf->nfaults = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
conf->rdev = rdev;
md_set_array_sectors(mddev, faulty_size(mddev, 0, 0));
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 627456542fb3..b0fcc7d02adb 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -68,10 +68,19 @@ static int linear_mergeable_bvec(struct request_queue *q,
struct dev_info *dev0;
unsigned long maxsectors, bio_sectors = bvm->bi_size >> 9;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
+ int maxbytes = biovec->bv_len;
+ struct request_queue *subq;
rcu_read_lock();
dev0 = which_dev(mddev, sector);
maxsectors = dev0->end_sector - sector;
+ subq = bdev_get_queue(dev0->rdev->bdev);
+ if (subq->merge_bvec_fn) {
+ bvm->bi_bdev = dev0->rdev->bdev;
+ bvm->bi_sector -= dev0->end_sector - dev0->rdev->sectors;
+ maxbytes = min(maxbytes, subq->merge_bvec_fn(subq, bvm,
+ biovec));
+ }
rcu_read_unlock();
if (maxsectors < bio_sectors)
@@ -80,12 +89,12 @@ static int linear_mergeable_bvec(struct request_queue *q,
maxsectors -= bio_sectors;
if (maxsectors <= (PAGE_SIZE >> 9 ) && bio_sectors == 0)
- return biovec->bv_len;
- /* The bytes available at this offset could be really big,
- * so we cap at 2^31 to avoid overflow */
- if (maxsectors > (1 << (31-9)))
- return 1<<31;
- return maxsectors << 9;
+ return maxbytes;
+
+ if (maxsectors > (maxbytes >> 9))
+ return maxbytes;
+ else
+ return maxsectors << 9;
}
static int linear_congested(void *data, int bits)
@@ -138,7 +147,7 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
cnt = 0;
conf->array_sectors = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
int j = rdev->raid_disk;
struct dev_info *disk = conf->disks + j;
sector_t sectors;
@@ -158,15 +167,6 @@ static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks)
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must never risk
- * violating it, so limit max_segments to 1 lying within
- * a single page.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
conf->array_sectors += rdev->sectors;
cnt++;
diff --git a/drivers/md/md.c b/drivers/md/md.c
index ce88755baf4a..b572e1e386ce 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -439,7 +439,7 @@ static void submit_flushes(struct work_struct *ws)
INIT_WORK(&mddev->flush_work, md_submit_flush_data);
atomic_set(&mddev->flush_pending, 1);
rcu_read_lock();
- list_for_each_entry_rcu(rdev, &mddev->disks, same_set)
+ rdev_for_each_rcu(rdev, mddev)
if (rdev->raid_disk >= 0 &&
!test_bit(Faulty, &rdev->flags)) {
/* Take two references, one is dropped
@@ -749,7 +749,7 @@ static struct md_rdev * find_rdev_nr(struct mddev *mddev, int nr)
{
struct md_rdev *rdev;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->desc_nr == nr)
return rdev;
@@ -760,7 +760,7 @@ static struct md_rdev * find_rdev(struct mddev * mddev, dev_t dev)
{
struct md_rdev *rdev;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->bdev->bd_dev == dev)
return rdev;
@@ -1342,7 +1342,7 @@ static void super_90_sync(struct mddev *mddev, struct md_rdev *rdev)
sb->state |= (1<<MD_SB_BITMAP_PRESENT);
sb->disks[0].state = (1<<MD_DISK_REMOVED);
- list_for_each_entry(rdev2, &mddev->disks, same_set) {
+ rdev_for_each(rdev2, mddev) {
mdp_disk_t *d;
int desc_nr;
int is_active = test_bit(In_sync, &rdev2->flags);
@@ -1805,18 +1805,18 @@ retry:
| BB_LEN(internal_bb));
*bbp++ = cpu_to_le64(store_bb);
}
+ bb->changed = 0;
if (read_seqretry(&bb->lock, seq))
goto retry;
bb->sector = (rdev->sb_start +
(int)le32_to_cpu(sb->bblog_offset));
bb->size = le16_to_cpu(sb->bblog_size);
- bb->changed = 0;
}
}
max_dev = 0;
- list_for_each_entry(rdev2, &mddev->disks, same_set)
+ rdev_for_each(rdev2, mddev)
if (rdev2->desc_nr+1 > max_dev)
max_dev = rdev2->desc_nr+1;
@@ -1833,7 +1833,7 @@ retry:
for (i=0; i<max_dev;i++)
sb->dev_roles[i] = cpu_to_le16(0xfffe);
- list_for_each_entry(rdev2, &mddev->disks, same_set) {
+ rdev_for_each(rdev2, mddev) {
i = rdev2->desc_nr;
if (test_bit(Faulty, &rdev2->flags))
sb->dev_roles[i] = cpu_to_le16(0xfffe);
@@ -1948,7 +1948,7 @@ int md_integrity_register(struct mddev *mddev)
return 0; /* nothing to do */
if (!mddev->gendisk || blk_get_integrity(mddev->gendisk))
return 0; /* shouldn't register, or already is */
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
/* skip spares and non-functional disks */
if (test_bit(Faulty, &rdev->flags))
continue;
@@ -2175,7 +2175,7 @@ static void export_array(struct mddev *mddev)
{
struct md_rdev *rdev, *tmp;
- rdev_for_each(rdev, tmp, mddev) {
+ rdev_for_each_safe(rdev, tmp, mddev) {
if (!rdev->mddev) {
MD_BUG();
continue;
@@ -2307,11 +2307,11 @@ static void md_print_devices(void)
bitmap_print_sb(mddev->bitmap);
else
printk("%s: ", mdname(mddev));
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
printk("<%s>", bdevname(rdev->bdev,b));
printk("\n");
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
print_rdev(rdev, mddev->major_version);
}
printk("md: **********************************\n");
@@ -2328,7 +2328,7 @@ static void sync_sbs(struct mddev * mddev, int nospares)
* with the rest of the array)
*/
struct md_rdev *rdev;
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->sb_events == mddev->events ||
(nospares &&
rdev->raid_disk < 0 &&
@@ -2351,7 +2351,7 @@ static void md_update_sb(struct mddev * mddev, int force_change)
repeat:
/* First make sure individual recovery_offsets are correct */
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->raid_disk >= 0 &&
mddev->delta_disks >= 0 &&
!test_bit(In_sync, &rdev->flags) &&
@@ -2364,8 +2364,9 @@ repeat:
clear_bit(MD_CHANGE_DEVS, &mddev->flags);
if (!mddev->external) {
clear_bit(MD_CHANGE_PENDING, &mddev->flags);
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->badblocks.changed) {
+ rdev->badblocks.changed = 0;
md_ack_all_badblocks(&rdev->badblocks);
md_error(mddev, rdev);
}
@@ -2430,7 +2431,7 @@ repeat:
mddev->events --;
}
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->badblocks.changed)
any_badblocks_changed++;
if (test_bit(Faulty, &rdev->flags))
@@ -2444,7 +2445,7 @@ repeat:
mdname(mddev), mddev->in_sync);
bitmap_update_sb(mddev->bitmap);
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
char b[BDEVNAME_SIZE];
if (rdev->sb_loaded != 1)
@@ -2493,7 +2494,7 @@ repeat:
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (test_and_clear_bit(FaultRecorded, &rdev->flags))
clear_bit(Blocked, &rdev->flags);
@@ -2896,7 +2897,7 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
struct md_rdev *rdev2;
mddev_lock(mddev);
- list_for_each_entry(rdev2, &mddev->disks, same_set)
+ rdev_for_each(rdev2, mddev)
if (rdev->bdev == rdev2->bdev &&
rdev != rdev2 &&
overlaps(rdev->data_offset, rdev->sectors,
@@ -3193,7 +3194,7 @@ static void analyze_sbs(struct mddev * mddev)
char b[BDEVNAME_SIZE];
freshest = NULL;
- rdev_for_each(rdev, tmp, mddev)
+ rdev_for_each_safe(rdev, tmp, mddev)
switch (super_types[mddev->major_version].
load_super(rdev, freshest, mddev->minor_version)) {
case 1:
@@ -3214,7 +3215,7 @@ static void analyze_sbs(struct mddev * mddev)
validate_super(mddev, freshest);
i = 0;
- rdev_for_each(rdev, tmp, mddev) {
+ rdev_for_each_safe(rdev, tmp, mddev) {
if (mddev->max_disks &&
(rdev->desc_nr >= mddev->max_disks ||
i > mddev->max_disks)) {
@@ -3403,7 +3404,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
return -EINVAL;
}
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
rdev->new_raid_disk = rdev->raid_disk;
/* ->takeover must set new_* and/or delta_disks
@@ -3456,7 +3457,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
mddev->safemode = 0;
}
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->raid_disk < 0)
continue;
if (rdev->new_raid_disk >= mddev->raid_disks)
@@ -3465,7 +3466,7 @@ level_store(struct mddev *mddev, const char *buf, size_t len)
continue;
sysfs_unlink_rdev(mddev, rdev);
}
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->raid_disk < 0)
continue;
if (rdev->new_raid_disk == rdev->raid_disk)
@@ -4796,7 +4797,7 @@ int md_run(struct mddev *mddev)
* the only valid external interface is through the md
* device.
*/
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (test_bit(Faulty, &rdev->flags))
continue;
sync_blockdev(rdev->bdev);
@@ -4867,8 +4868,8 @@ int md_run(struct mddev *mddev)
struct md_rdev *rdev2;
int warned = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set)
- list_for_each_entry(rdev2, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev)
+ rdev_for_each(rdev2, mddev) {
if (rdev < rdev2 &&
rdev->bdev->bd_contains ==
rdev2->bdev->bd_contains) {
@@ -4945,7 +4946,7 @@ int md_run(struct mddev *mddev)
mddev->in_sync = 1;
smp_wmb();
mddev->ready = 1;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0)
if (sysfs_link_rdev(mddev, rdev))
/* failure here is OK */;
@@ -5073,6 +5074,7 @@ static void md_clean(struct mddev *mddev)
mddev->changed = 0;
mddev->degraded = 0;
mddev->safemode = 0;
+ mddev->merge_check_needed = 0;
mddev->bitmap_info.offset = 0;
mddev->bitmap_info.default_offset = 0;
mddev->bitmap_info.chunksize = 0;
@@ -5175,7 +5177,7 @@ static int do_md_stop(struct mddev * mddev, int mode, int is_open)
/* tell userspace to handle 'inactive' */
sysfs_notify_dirent_safe(mddev->sysfs_state);
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0)
sysfs_unlink_rdev(mddev, rdev);
@@ -5226,7 +5228,7 @@ static void autorun_array(struct mddev *mddev)
printk(KERN_INFO "md: running: ");
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
char b[BDEVNAME_SIZE];
printk("<%s>", bdevname(rdev->bdev,b));
}
@@ -5356,7 +5358,7 @@ static int get_array_info(struct mddev * mddev, void __user * arg)
struct md_rdev *rdev;
nr=working=insync=failed=spare=0;
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
nr++;
if (test_bit(Faulty, &rdev->flags))
failed++;
@@ -5923,7 +5925,7 @@ static int update_size(struct mddev *mddev, sector_t num_sectors)
* grow, and re-add.
*/
return -EBUSY;
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
sector_t avail = rdev->sectors;
if (fit && (num_sectors == 0 || num_sectors > avail))
@@ -6724,7 +6726,6 @@ static int md_seq_show(struct seq_file *seq, void *v)
struct mddev *mddev = v;
sector_t sectors;
struct md_rdev *rdev;
- struct bitmap *bitmap;
if (v == (void*)1) {
struct md_personality *pers;
@@ -6758,7 +6759,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
}
sectors = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
char b[BDEVNAME_SIZE];
seq_printf(seq, " %s[%d]",
bdevname(rdev->bdev,b), rdev->desc_nr);
@@ -6812,27 +6813,7 @@ static int md_seq_show(struct seq_file *seq, void *v)
} else
seq_printf(seq, "\n ");
- if ((bitmap = mddev->bitmap)) {
- unsigned long chunk_kb;
- unsigned long flags;
- spin_lock_irqsave(&bitmap->lock, flags);
- chunk_kb = mddev->bitmap_info.chunksize >> 10;
- seq_printf(seq, "bitmap: %lu/%lu pages [%luKB], "
- "%lu%s chunk",
- bitmap->pages - bitmap->missing_pages,
- bitmap->pages,
- (bitmap->pages - bitmap->missing_pages)
- << (PAGE_SHIFT - 10),
- chunk_kb ? chunk_kb : mddev->bitmap_info.chunksize,
- chunk_kb ? "KB" : "B");
- if (bitmap->file) {
- seq_printf(seq, ", file: ");
- seq_path(seq, &bitmap->file->f_path, " \t\n");
- }
-
- seq_printf(seq, "\n");
- spin_unlock_irqrestore(&bitmap->lock, flags);
- }
+ bitmap_status(seq, mddev->bitmap);
seq_printf(seq, "\n");
}
@@ -7170,7 +7151,7 @@ void md_do_sync(struct mddev *mddev)
max_sectors = mddev->dev_sectors;
j = MaxSector;
rcu_read_lock();
- list_for_each_entry_rcu(rdev, &mddev->disks, same_set)
+ rdev_for_each_rcu(rdev, mddev)
if (rdev->raid_disk >= 0 &&
!test_bit(Faulty, &rdev->flags) &&
!test_bit(In_sync, &rdev->flags) &&
@@ -7342,7 +7323,7 @@ void md_do_sync(struct mddev *mddev)
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
mddev->curr_resync = MaxSector;
rcu_read_lock();
- list_for_each_entry_rcu(rdev, &mddev->disks, same_set)
+ rdev_for_each_rcu(rdev, mddev)
if (rdev->raid_disk >= 0 &&
mddev->delta_disks >= 0 &&
!test_bit(Faulty, &rdev->flags) &&
@@ -7388,7 +7369,7 @@ static int remove_and_add_spares(struct mddev *mddev)
mddev->curr_resync_completed = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0 &&
!test_bit(Blocked, &rdev->flags) &&
(test_bit(Faulty, &rdev->flags) ||
@@ -7406,7 +7387,7 @@ static int remove_and_add_spares(struct mddev *mddev)
"degraded");
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (rdev->raid_disk >= 0 &&
!test_bit(In_sync, &rdev->flags) &&
!test_bit(Faulty, &rdev->flags))
@@ -7451,7 +7432,7 @@ static void reap_sync_thread(struct mddev *mddev)
* do the superblock for an incrementally recovered device
* written out.
*/
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (!mddev->degraded ||
test_bit(In_sync, &rdev->flags))
rdev->saved_raid_disk = -1;
@@ -7529,7 +7510,7 @@ void md_check_recovery(struct mddev *mddev)
* failed devices.
*/
struct md_rdev *rdev;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0 &&
!test_bit(Blocked, &rdev->flags) &&
test_bit(Faulty, &rdev->flags) &&
@@ -8040,7 +8021,7 @@ void md_ack_all_badblocks(struct badblocks *bb)
return;
write_seqlock_irq(&bb->lock);
- if (bb->changed == 0) {
+ if (bb->changed == 0 && bb->unacked_exist) {
u64 *p = bb->page;
int i;
for (i = 0; i < bb->count ; i++) {
@@ -8157,30 +8138,23 @@ static int md_notify_reboot(struct notifier_block *this,
struct mddev *mddev;
int need_delay = 0;
- if ((code == SYS_DOWN) || (code == SYS_HALT) || (code == SYS_POWER_OFF)) {
-
- printk(KERN_INFO "md: stopping all md devices.\n");
-
- for_each_mddev(mddev, tmp) {
- if (mddev_trylock(mddev)) {
- /* Force a switch to readonly even array
- * appears to still be in use. Hence
- * the '100'.
- */
- md_set_readonly(mddev, 100);
- mddev_unlock(mddev);
- }
- need_delay = 1;
+ for_each_mddev(mddev, tmp) {
+ if (mddev_trylock(mddev)) {
+ __md_stop_writes(mddev);
+ mddev->safemode = 2;
+ mddev_unlock(mddev);
}
- /*
- * certain more exotic SCSI devices are known to be
- * volatile wrt too early system reboots. While the
- * right place to handle this issue is the given
- * driver, we do want to have a safe RAID driver ...
- */
- if (need_delay)
- mdelay(1000*1);
+ need_delay = 1;
}
+ /*
+ * certain more exotic SCSI devices are known to be
+ * volatile wrt too early system reboots. While the
+ * right place to handle this issue is the given
+ * driver, we do want to have a safe RAID driver ...
+ */
+ if (need_delay)
+ mdelay(1000*1);
+
return NOTIFY_DONE;
}
diff --git a/drivers/md/md.h b/drivers/md/md.h
index 44c63dfeeb2b..1c2063ccf48e 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -128,6 +128,10 @@ struct md_rdev {
enum flag_bits {
Faulty, /* device is known to have a fault */
In_sync, /* device is in_sync with rest of array */
+ Unmerged, /* device is being added to array and should
+ * be considerred for bvec_merge_fn but not
+ * yet for actual IO
+ */
WriteMostly, /* Avoid reading if at all possible */
AutoDetected, /* added by auto-detect */
Blocked, /* An error occurred but has not yet
@@ -345,6 +349,10 @@ struct mddev {
int degraded; /* whether md should consider
* adding a spare
*/
+ int merge_check_needed; /* at least one
+ * member device
+ * has a
+ * merge_bvec_fn */
atomic_t recovery_active; /* blocks scheduled, but not written */
wait_queue_head_t recovery_wait;
@@ -519,7 +527,10 @@ static inline void sysfs_unlink_rdev(struct mddev *mddev, struct md_rdev *rdev)
/*
* iterates through the 'same array disks' ringlist
*/
-#define rdev_for_each(rdev, tmp, mddev) \
+#define rdev_for_each(rdev, mddev) \
+ list_for_each_entry(rdev, &((mddev)->disks), same_set)
+
+#define rdev_for_each_safe(rdev, tmp, mddev) \
list_for_each_entry_safe(rdev, tmp, &((mddev)->disks), same_set)
#define rdev_for_each_rcu(rdev, mddev) \
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index a222f516660e..9339e67fcc79 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -428,7 +428,7 @@ static int multipath_run (struct mddev *mddev)
}
working_disks = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
disk_idx = rdev->raid_disk;
if (disk_idx < 0 ||
disk_idx >= mddev->raid_disks)
diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h
index d279c768f8f1..5709bfeab1e8 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 023fbc2d389e..aa71e2359a07 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 bd1e7ffbe26c..d12b2cc51f1a 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 df2494c06cdc..ff3beed6ad2d 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/md/raid0.c b/drivers/md/raid0.c
index 7294bd115e34..6f31f5596e01 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -91,7 +91,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
if (!conf)
return -ENOMEM;
- list_for_each_entry(rdev1, &mddev->disks, same_set) {
+ rdev_for_each(rdev1, mddev) {
pr_debug("md/raid0:%s: looking at %s\n",
mdname(mddev),
bdevname(rdev1->bdev, b));
@@ -102,7 +102,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
sector_div(sectors, mddev->chunk_sectors);
rdev1->sectors = sectors * mddev->chunk_sectors;
- list_for_each_entry(rdev2, &mddev->disks, same_set) {
+ rdev_for_each(rdev2, mddev) {
pr_debug("md/raid0:%s: comparing %s(%llu)"
" with %s(%llu)\n",
mdname(mddev),
@@ -157,7 +157,7 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
smallest = NULL;
dev = conf->devlist;
err = -EINVAL;
- list_for_each_entry(rdev1, &mddev->disks, same_set) {
+ rdev_for_each(rdev1, mddev) {
int j = rdev1->raid_disk;
if (mddev->level == 10) {
@@ -188,16 +188,10 @@ static int create_strip_zones(struct mddev *mddev, struct r0conf **private_conf)
disk_stack_limits(mddev->gendisk, rdev1->bdev,
rdev1->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must never risk
- * violating it, so limit ->max_segments to 1, lying within
- * a single page.
- */
- if (rdev1->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
+ if (rdev1->bdev->bd_disk->queue->merge_bvec_fn)
+ conf->has_merge_bvec = 1;
+
if (!smallest || (rdev1->sectors < smallest->sectors))
smallest = rdev1;
cnt++;
@@ -290,8 +284,64 @@ abort:
return err;
}
+/* Find the zone which holds a particular offset
+ * Update *sectorp to be an offset in that zone
+ */
+static struct strip_zone *find_zone(struct r0conf *conf,
+ sector_t *sectorp)
+{
+ int i;
+ struct strip_zone *z = conf->strip_zone;
+ sector_t sector = *sectorp;
+
+ for (i = 0; i < conf->nr_strip_zones; i++)
+ if (sector < z[i].zone_end) {
+ if (i)
+ *sectorp = sector - z[i-1].zone_end;
+ return z + i;
+ }
+ BUG();
+}
+
+/*
+ * remaps the bio to the target device. we separate two flows.
+ * power 2 flow and a general flow for the sake of perfromance
+*/
+static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone,
+ sector_t sector, sector_t *sector_offset)
+{
+ unsigned int sect_in_chunk;
+ sector_t chunk;
+ struct r0conf *conf = mddev->private;
+ int raid_disks = conf->strip_zone[0].nb_dev;
+ unsigned int chunk_sects = mddev->chunk_sectors;
+
+ if (is_power_of_2(chunk_sects)) {
+ int chunksect_bits = ffz(~chunk_sects);
+ /* find the sector offset inside the chunk */
+ sect_in_chunk = sector & (chunk_sects - 1);
+ sector >>= chunksect_bits;
+ /* chunk in zone */
+ chunk = *sector_offset;
+ /* quotient is the chunk in real device*/
+ sector_div(chunk, zone->nb_dev << chunksect_bits);
+ } else{
+ sect_in_chunk = sector_div(sector, chunk_sects);
+ chunk = *sector_offset;
+ sector_div(chunk, chunk_sects * zone->nb_dev);
+ }
+ /*
+ * position the bio over the real device
+ * real sector = chunk in device + starting of zone
+ * + the position in the chunk
+ */
+ *sector_offset = (chunk * chunk_sects) + sect_in_chunk;
+ return conf->devlist[(zone - conf->strip_zone)*raid_disks
+ + sector_div(sector, zone->nb_dev)];
+}
+
/**
- * raid0_mergeable_bvec -- tell bio layer if a two requests can be merged
+ * raid0_mergeable_bvec -- tell bio layer if two requests can be merged
* @q: request queue
* @bvm: properties of new bio
* @biovec: the request that could be merged to it.
@@ -303,10 +353,15 @@ static int raid0_mergeable_bvec(struct request_queue *q,
struct bio_vec *biovec)
{
struct mddev *mddev = q->queuedata;
+ struct r0conf *conf = mddev->private;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
+ sector_t sector_offset = sector;
int max;
unsigned int chunk_sectors = mddev->chunk_sectors;
unsigned int bio_sectors = bvm->bi_size >> 9;
+ struct strip_zone *zone;
+ struct md_rdev *rdev;
+ struct request_queue *subq;
if (is_power_of_2(chunk_sectors))
max = (chunk_sectors - ((sector & (chunk_sectors-1))
@@ -314,10 +369,27 @@ static int raid0_mergeable_bvec(struct request_queue *q,
else
max = (chunk_sectors - (sector_div(sector, chunk_sectors)
+ bio_sectors)) << 9;
- if (max < 0) max = 0; /* bio_add cannot handle a negative return */
+ if (max < 0)
+ max = 0; /* bio_add cannot handle a negative return */
if (max <= biovec->bv_len && bio_sectors == 0)
return biovec->bv_len;
- else
+ if (max < biovec->bv_len)
+ /* too small already, no need to check further */
+ return max;
+ if (!conf->has_merge_bvec)
+ return max;
+
+ /* May need to check subordinate device */
+ sector = sector_offset;
+ zone = find_zone(mddev->private, &sector_offset);
+ rdev = map_sector(mddev, zone, sector, &sector_offset);
+ subq = bdev_get_queue(rdev->bdev);
+ if (subq->merge_bvec_fn) {
+ bvm->bi_bdev = rdev->bdev;
+ bvm->bi_sector = sector_offset + zone->dev_start +
+ rdev->data_offset;
+ return min(max, subq->merge_bvec_fn(subq, bvm, biovec));
+ } else
return max;
}
@@ -329,7 +401,7 @@ static sector_t raid0_size(struct mddev *mddev, sector_t sectors, int raid_disks
WARN_ONCE(sectors || raid_disks,
"%s does not support generic reshape\n", __func__);
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
array_sectors += rdev->sectors;
return array_sectors;
@@ -397,62 +469,6 @@ static int raid0_stop(struct mddev *mddev)
return 0;
}
-/* Find the zone which holds a particular offset
- * Update *sectorp to be an offset in that zone
- */
-static struct strip_zone *find_zone(struct r0conf *conf,
- sector_t *sectorp)
-{
- int i;
- struct strip_zone *z = conf->strip_zone;
- sector_t sector = *sectorp;
-
- for (i = 0; i < conf->nr_strip_zones; i++)
- if (sector < z[i].zone_end) {
- if (i)
- *sectorp = sector - z[i-1].zone_end;
- return z + i;
- }
- BUG();
-}
-
-/*
- * remaps the bio to the target device. we separate two flows.
- * power 2 flow and a general flow for the sake of perfromance
-*/
-static struct md_rdev *map_sector(struct mddev *mddev, struct strip_zone *zone,
- sector_t sector, sector_t *sector_offset)
-{
- unsigned int sect_in_chunk;
- sector_t chunk;
- struct r0conf *conf = mddev->private;
- int raid_disks = conf->strip_zone[0].nb_dev;
- unsigned int chunk_sects = mddev->chunk_sectors;
-
- if (is_power_of_2(chunk_sects)) {
- int chunksect_bits = ffz(~chunk_sects);
- /* find the sector offset inside the chunk */
- sect_in_chunk = sector & (chunk_sects - 1);
- sector >>= chunksect_bits;
- /* chunk in zone */
- chunk = *sector_offset;
- /* quotient is the chunk in real device*/
- sector_div(chunk, zone->nb_dev << chunksect_bits);
- } else{
- sect_in_chunk = sector_div(sector, chunk_sects);
- chunk = *sector_offset;
- sector_div(chunk, chunk_sects * zone->nb_dev);
- }
- /*
- * position the bio over the real device
- * real sector = chunk in device + starting of zone
- * + the position in the chunk
- */
- *sector_offset = (chunk * chunk_sects) + sect_in_chunk;
- return conf->devlist[(zone - conf->strip_zone)*raid_disks
- + sector_div(sector, zone->nb_dev)];
-}
-
/*
* Is io distribute over 1 or more chunks ?
*/
@@ -505,7 +521,7 @@ static void raid0_make_request(struct mddev *mddev, struct bio *bio)
}
sector_offset = bio->bi_sector;
- zone = find_zone(mddev->private, &sector_offset);
+ zone = find_zone(mddev->private, &sector_offset);
tmp_dev = map_sector(mddev, zone, bio->bi_sector,
&sector_offset);
bio->bi_bdev = tmp_dev->bdev;
@@ -543,7 +559,7 @@ static void *raid0_takeover_raid45(struct mddev *mddev)
return ERR_PTR(-EINVAL);
}
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
/* check slot number for a disk */
if (rdev->raid_disk == mddev->raid_disks-1) {
printk(KERN_ERR "md/raid0:%s: raid5 must have missing parity disk!\n",
diff --git a/drivers/md/raid0.h b/drivers/md/raid0.h
index 0884bba8df4c..05539d9c97f0 100644
--- a/drivers/md/raid0.h
+++ b/drivers/md/raid0.h
@@ -4,13 +4,16 @@
struct strip_zone {
sector_t zone_end; /* Start of the next zone (in sectors) */
sector_t dev_start; /* Zone offset in real dev (in sectors) */
- int nb_dev; /* # of devices attached to the zone */
+ int nb_dev; /* # of devices attached to the zone */
};
struct r0conf {
- struct strip_zone *strip_zone;
- struct md_rdev **devlist; /* lists of rdevs, pointed to by strip_zone->dev */
- int nr_strip_zones;
+ struct strip_zone *strip_zone;
+ struct md_rdev **devlist; /* lists of rdevs, pointed to
+ * by strip_zone->dev */
+ int nr_strip_zones;
+ int has_merge_bvec; /* at least one member has
+ * a merge_bvec_fn */
};
#endif
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index a0b225eb4ac4..4a40a200d769 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -523,6 +523,7 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
rdev = rcu_dereference(conf->mirrors[disk].rdev);
if (r1_bio->bios[disk] == IO_BLOCKED
|| rdev == NULL
+ || test_bit(Unmerged, &rdev->flags)
|| test_bit(Faulty, &rdev->flags))
continue;
if (!test_bit(In_sync, &rdev->flags) &&
@@ -614,6 +615,39 @@ static int read_balance(struct r1conf *conf, struct r1bio *r1_bio, int *max_sect
return best_disk;
}
+static int raid1_mergeable_bvec(struct request_queue *q,
+ struct bvec_merge_data *bvm,
+ struct bio_vec *biovec)
+{
+ struct mddev *mddev = q->queuedata;
+ struct r1conf *conf = mddev->private;
+ sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
+ int max = biovec->bv_len;
+
+ if (mddev->merge_check_needed) {
+ int disk;
+ rcu_read_lock();
+ for (disk = 0; disk < conf->raid_disks * 2; disk++) {
+ struct md_rdev *rdev = rcu_dereference(
+ conf->mirrors[disk].rdev);
+ if (rdev && !test_bit(Faulty, &rdev->flags)) {
+ struct request_queue *q =
+ bdev_get_queue(rdev->bdev);
+ if (q->merge_bvec_fn) {
+ bvm->bi_sector = sector +
+ rdev->data_offset;
+ bvm->bi_bdev = rdev->bdev;
+ max = min(max, q->merge_bvec_fn(
+ q, bvm, biovec));
+ }
+ }
+ }
+ rcu_read_unlock();
+ }
+ return max;
+
+}
+
int md_raid1_congested(struct mddev *mddev, int bits)
{
struct r1conf *conf = mddev->private;
@@ -737,9 +771,22 @@ static void wait_barrier(struct r1conf *conf)
spin_lock_irq(&conf->resync_lock);
if (conf->barrier) {
conf->nr_waiting++;
- wait_event_lock_irq(conf->wait_barrier, !conf->barrier,
+ /* Wait for the barrier to drop.
+ * However if there are already pending
+ * requests (preventing the barrier from
+ * rising completely), and the
+ * pre-process bio queue isn't empty,
+ * then don't wait, as we need to empty
+ * that queue to get the nr_pending
+ * count down.
+ */
+ wait_event_lock_irq(conf->wait_barrier,
+ !conf->barrier ||
+ (conf->nr_pending &&
+ current->bio_list &&
+ !bio_list_empty(current->bio_list)),
conf->resync_lock,
- );
+ );
conf->nr_waiting--;
}
conf->nr_pending++;
@@ -1002,7 +1049,8 @@ read_again:
break;
}
r1_bio->bios[i] = NULL;
- if (!rdev || test_bit(Faulty, &rdev->flags)) {
+ if (!rdev || test_bit(Faulty, &rdev->flags)
+ || test_bit(Unmerged, &rdev->flags)) {
if (i < conf->raid_disks)
set_bit(R1BIO_Degraded, &r1_bio->state);
continue;
@@ -1322,6 +1370,7 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
struct mirror_info *p;
int first = 0;
int last = conf->raid_disks - 1;
+ struct request_queue *q = bdev_get_queue(rdev->bdev);
if (mddev->recovery_disabled == conf->recovery_disabled)
return -EBUSY;
@@ -1329,23 +1378,17 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
if (rdev->raid_disk >= 0)
first = last = rdev->raid_disk;
+ if (q->merge_bvec_fn) {
+ set_bit(Unmerged, &rdev->flags);
+ mddev->merge_check_needed = 1;
+ }
+
for (mirror = first; mirror <= last; mirror++) {
p = conf->mirrors+mirror;
if (!p->rdev) {
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must
- * never risk violating it, so limit
- * ->max_segments to one lying with a single
- * page, as a one page request is never in
- * violation.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
p->head_position = 0;
rdev->raid_disk = mirror;
@@ -1370,6 +1413,19 @@ static int raid1_add_disk(struct mddev *mddev, struct md_rdev *rdev)
break;
}
}
+ if (err == 0 && test_bit(Unmerged, &rdev->flags)) {
+ /* Some requests might not have seen this new
+ * merge_bvec_fn. We must wait for them to complete
+ * before merging the device fully.
+ * First we make sure any code which has tested
+ * our function has submitted the request, then
+ * we wait for all outstanding requests to complete.
+ */
+ synchronize_sched();
+ raise_barrier(conf);
+ lower_barrier(conf);
+ clear_bit(Unmerged, &rdev->flags);
+ }
md_integrity_add_rdev(rdev, mddev);
print_conf(conf);
return err;
@@ -2491,7 +2547,7 @@ static struct r1conf *setup_conf(struct mddev *mddev)
err = -EINVAL;
spin_lock_init(&conf->device_lock);
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
int disk_idx = rdev->raid_disk;
if (disk_idx >= mddev->raid_disks
|| disk_idx < 0)
@@ -2609,20 +2665,11 @@ static int run(struct mddev *mddev)
if (IS_ERR(conf))
return PTR_ERR(conf);
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
if (!mddev->gendisk)
continue;
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must never risk
- * violating it, so limit ->max_segments to 1 lying within
- * a single page, as a one page request is never in violation.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
}
mddev->degraded = 0;
@@ -2656,6 +2703,7 @@ static int run(struct mddev *mddev)
if (mddev->queue) {
mddev->queue->backing_dev_info.congested_fn = raid1_congested;
mddev->queue->backing_dev_info.congested_data = mddev;
+ blk_queue_merge_bvec(mddev->queue, raid1_mergeable_bvec);
}
return md_integrity_register(mddev);
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 58c44d6453a0..3540316886f2 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -586,25 +586,68 @@ static sector_t raid10_find_virt(struct r10conf *conf, sector_t sector, int dev)
* @biovec: the request that could be merged to it.
*
* Return amount of bytes we can accept at this offset
- * If near_copies == raid_disk, there are no striping issues,
- * but in that case, the function isn't called at all.
+ * This requires checking for end-of-chunk if near_copies != raid_disks,
+ * and for subordinate merge_bvec_fns if merge_check_needed.
*/
static int raid10_mergeable_bvec(struct request_queue *q,
struct bvec_merge_data *bvm,
struct bio_vec *biovec)
{
struct mddev *mddev = q->queuedata;
+ struct r10conf *conf = mddev->private;
sector_t sector = bvm->bi_sector + get_start_sect(bvm->bi_bdev);
int max;
unsigned int chunk_sectors = mddev->chunk_sectors;
unsigned int bio_sectors = bvm->bi_size >> 9;
- max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + bio_sectors)) << 9;
- if (max < 0) max = 0; /* bio_add cannot handle a negative return */
- if (max <= biovec->bv_len && bio_sectors == 0)
- return biovec->bv_len;
- else
- return max;
+ if (conf->near_copies < conf->raid_disks) {
+ max = (chunk_sectors - ((sector & (chunk_sectors - 1))
+ + bio_sectors)) << 9;
+ if (max < 0)
+ /* bio_add cannot handle a negative return */
+ max = 0;
+ if (max <= biovec->bv_len && bio_sectors == 0)
+ return biovec->bv_len;
+ } else
+ max = biovec->bv_len;
+
+ if (mddev->merge_check_needed) {
+ struct r10bio r10_bio;
+ int s;
+ r10_bio.sector = sector;
+ raid10_find_phys(conf, &r10_bio);
+ rcu_read_lock();
+ for (s = 0; s < conf->copies; s++) {
+ int disk = r10_bio.devs[s].devnum;
+ struct md_rdev *rdev = rcu_dereference(
+ conf->mirrors[disk].rdev);
+ if (rdev && !test_bit(Faulty, &rdev->flags)) {
+ struct request_queue *q =
+ bdev_get_queue(rdev->bdev);
+ if (q->merge_bvec_fn) {
+ bvm->bi_sector = r10_bio.devs[s].addr
+ + rdev->data_offset;
+ bvm->bi_bdev = rdev->bdev;
+ max = min(max, q->merge_bvec_fn(
+ q, bvm, biovec));
+ }
+ }
+ rdev = rcu_dereference(conf->mirrors[disk].replacement);
+ if (rdev && !test_bit(Faulty, &rdev->flags)) {
+ struct request_queue *q =
+ bdev_get_queue(rdev->bdev);
+ if (q->merge_bvec_fn) {
+ bvm->bi_sector = r10_bio.devs[s].addr
+ + rdev->data_offset;
+ bvm->bi_bdev = rdev->bdev;
+ max = min(max, q->merge_bvec_fn(
+ q, bvm, biovec));
+ }
+ }
+ }
+ rcu_read_unlock();
+ }
+ return max;
}
/*
@@ -668,11 +711,12 @@ retry:
disk = r10_bio->devs[slot].devnum;
rdev = rcu_dereference(conf->mirrors[disk].replacement);
if (rdev == NULL || test_bit(Faulty, &rdev->flags) ||
+ test_bit(Unmerged, &rdev->flags) ||
r10_bio->devs[slot].addr + sectors > rdev->recovery_offset)
rdev = rcu_dereference(conf->mirrors[disk].rdev);
- if (rdev == NULL)
- continue;
- if (test_bit(Faulty, &rdev->flags))
+ if (rdev == NULL ||
+ test_bit(Faulty, &rdev->flags) ||
+ test_bit(Unmerged, &rdev->flags))
continue;
if (!test_bit(In_sync, &rdev->flags) &&
r10_bio->devs[slot].addr + sectors > rdev->recovery_offset)
@@ -863,9 +907,22 @@ static void wait_barrier(struct r10conf *conf)
spin_lock_irq(&conf->resync_lock);
if (conf->barrier) {
conf->nr_waiting++;
- wait_event_lock_irq(conf->wait_barrier, !conf->barrier,
+ /* Wait for the barrier to drop.
+ * However if there are already pending
+ * requests (preventing the barrier from
+ * rising completely), and the
+ * pre-process bio queue isn't empty,
+ * then don't wait, as we need to empty
+ * that queue to get the nr_pending
+ * count down.
+ */
+ wait_event_lock_irq(conf->wait_barrier,
+ !conf->barrier ||
+ (conf->nr_pending &&
+ current->bio_list &&
+ !bio_list_empty(current->bio_list)),
conf->resync_lock,
- );
+ );
conf->nr_waiting--;
}
conf->nr_pending++;
@@ -1121,12 +1178,14 @@ retry_write:
blocked_rdev = rrdev;
break;
}
- if (rrdev && test_bit(Faulty, &rrdev->flags))
+ if (rrdev && (test_bit(Faulty, &rrdev->flags)
+ || test_bit(Unmerged, &rrdev->flags)))
rrdev = NULL;
r10_bio->devs[i].bio = NULL;
r10_bio->devs[i].repl_bio = NULL;
- if (!rdev || test_bit(Faulty, &rdev->flags)) {
+ if (!rdev || test_bit(Faulty, &rdev->flags) ||
+ test_bit(Unmerged, &rdev->flags)) {
set_bit(R10BIO_Degraded, &r10_bio->state);
continue;
}
@@ -1477,18 +1536,24 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
int mirror;
int first = 0;
int last = conf->raid_disks - 1;
+ struct request_queue *q = bdev_get_queue(rdev->bdev);
if (mddev->recovery_cp < MaxSector)
/* only hot-add to in-sync arrays, as recovery is
* very different from resync
*/
return -EBUSY;
- if (!enough(conf, -1))
+ if (rdev->saved_raid_disk < 0 && !enough(conf, -1))
return -EINVAL;
if (rdev->raid_disk >= 0)
first = last = rdev->raid_disk;
+ if (q->merge_bvec_fn) {
+ set_bit(Unmerged, &rdev->flags);
+ mddev->merge_check_needed = 1;
+ }
+
if (rdev->saved_raid_disk >= first &&
conf->mirrors[rdev->saved_raid_disk].rdev == NULL)
mirror = rdev->saved_raid_disk;
@@ -1508,11 +1573,6 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
err = 0;
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
conf->fullsync = 1;
rcu_assign_pointer(p->replacement, rdev);
break;
@@ -1520,17 +1580,6 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must
- * never risk violating it, so limit
- * ->max_segments to one lying with a single
- * page, as a one page request is never in
- * violation.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
p->head_position = 0;
p->recovery_disabled = mddev->recovery_disabled - 1;
@@ -1541,7 +1590,19 @@ static int raid10_add_disk(struct mddev *mddev, struct md_rdev *rdev)
rcu_assign_pointer(p->rdev, rdev);
break;
}
-
+ if (err == 0 && test_bit(Unmerged, &rdev->flags)) {
+ /* Some requests might not have seen this new
+ * merge_bvec_fn. We must wait for them to complete
+ * before merging the device fully.
+ * First we make sure any code which has tested
+ * our function has submitted the request, then
+ * we wait for all outstanding requests to complete.
+ */
+ synchronize_sched();
+ raise_barrier(conf, 0);
+ lower_barrier(conf);
+ clear_bit(Unmerged, &rdev->flags);
+ }
md_integrity_add_rdev(rdev, mddev);
print_conf(conf);
return err;
@@ -1682,10 +1743,8 @@ static void end_sync_write(struct bio *bio, int error)
d = find_bio_disk(conf, r10_bio, bio, &slot, &repl);
if (repl)
rdev = conf->mirrors[d].replacement;
- if (!rdev) {
- smp_mb();
+ else
rdev = conf->mirrors[d].rdev;
- }
if (!uptodate) {
if (repl)
@@ -2087,6 +2146,7 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
d = r10_bio->devs[sl].devnum;
rdev = rcu_dereference(conf->mirrors[d].rdev);
if (rdev &&
+ !test_bit(Unmerged, &rdev->flags) &&
test_bit(In_sync, &rdev->flags) &&
is_badblock(rdev, r10_bio->devs[sl].addr + sect, s,
&first_bad, &bad_sectors) == 0) {
@@ -2140,6 +2200,7 @@ static void fix_read_error(struct r10conf *conf, struct mddev *mddev, struct r10
d = r10_bio->devs[sl].devnum;
rdev = rcu_dereference(conf->mirrors[d].rdev);
if (!rdev ||
+ test_bit(Unmerged, &rdev->flags) ||
!test_bit(In_sync, &rdev->flags))
continue;
@@ -3242,7 +3303,7 @@ static int run(struct mddev *mddev)
blk_queue_io_opt(mddev->queue, chunk_size *
(conf->raid_disks / conf->near_copies));
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
disk_idx = rdev->raid_disk;
if (disk_idx >= conf->raid_disks
@@ -3262,15 +3323,6 @@ static int run(struct mddev *mddev)
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
- /* as we don't honour merge_bvec_fn, we must never risk
- * violating it, so limit max_segments to 1 lying
- * within a single page.
- */
- if (rdev->bdev->bd_disk->queue->merge_bvec_fn) {
- blk_queue_max_segments(mddev->queue, 1);
- blk_queue_segment_boundary(mddev->queue,
- PAGE_CACHE_SIZE - 1);
- }
disk->head_position = 0;
}
@@ -3334,8 +3386,7 @@ static int run(struct mddev *mddev)
mddev->queue->backing_dev_info.ra_pages = 2* stripe;
}
- if (conf->near_copies < conf->raid_disks)
- blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
+ blk_queue_merge_bvec(mddev->queue, raid10_mergeable_bvec);
if (md_integrity_register(mddev))
goto out_free_conf;
@@ -3385,6 +3436,43 @@ static void raid10_quiesce(struct mddev *mddev, int state)
}
}
+static int raid10_resize(struct mddev *mddev, sector_t sectors)
+{
+ /* Resize of 'far' arrays is not supported.
+ * For 'near' and 'offset' arrays we can set the
+ * number of sectors used to be an appropriate multiple
+ * of the chunk size.
+ * For 'offset', this is far_copies*chunksize.
+ * For 'near' the multiplier is the LCM of
+ * near_copies and raid_disks.
+ * So if far_copies > 1 && !far_offset, fail.
+ * Else find LCM(raid_disks, near_copy)*far_copies and
+ * multiply by chunk_size. Then round to this number.
+ * This is mostly done by raid10_size()
+ */
+ struct r10conf *conf = mddev->private;
+ sector_t oldsize, size;
+
+ if (conf->far_copies > 1 && !conf->far_offset)
+ return -EINVAL;
+
+ oldsize = raid10_size(mddev, 0, 0);
+ size = raid10_size(mddev, sectors, 0);
+ md_set_array_sectors(mddev, size);
+ if (mddev->array_sectors > size)
+ return -EINVAL;
+ set_capacity(mddev->gendisk, mddev->array_sectors);
+ revalidate_disk(mddev->gendisk);
+ if (sectors > mddev->dev_sectors &&
+ mddev->recovery_cp > oldsize) {
+ mddev->recovery_cp = oldsize;
+ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
+ }
+ mddev->dev_sectors = sectors;
+ mddev->resync_max_sectors = size;
+ return 0;
+}
+
static void *raid10_takeover_raid0(struct mddev *mddev)
{
struct md_rdev *rdev;
@@ -3408,7 +3496,7 @@ static void *raid10_takeover_raid0(struct mddev *mddev)
conf = setup_conf(mddev);
if (!IS_ERR(conf)) {
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->raid_disk >= 0)
rdev->new_raid_disk = rdev->raid_disk * 2;
conf->barrier = 1;
@@ -3454,6 +3542,7 @@ static struct md_personality raid10_personality =
.sync_request = sync_request,
.quiesce = raid10_quiesce,
.size = raid10_size,
+ .resize = raid10_resize,
.takeover = raid10_takeover,
};
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 360f2b98f62b..23ac880bba9a 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -208,11 +208,10 @@ static void __release_stripe(struct r5conf *conf, struct stripe_head *sh)
md_wakeup_thread(conf->mddev->thread);
} else {
BUG_ON(stripe_operations_active(sh));
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
- atomic_dec(&conf->preread_active_stripes);
- if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD)
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ if (atomic_dec_return(&conf->preread_active_stripes)
+ < IO_THRESHOLD)
md_wakeup_thread(conf->mddev->thread);
- }
atomic_dec(&conf->active_stripes);
if (!test_bit(STRIPE_EXPANDING, &sh->state)) {
list_add_tail(&sh->lru, &conf->inactive_list);
@@ -4843,7 +4842,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
pr_debug("raid456: run(%s) called.\n", mdname(mddev));
- list_for_each_entry(rdev, &mddev->disks, same_set) {
+ rdev_for_each(rdev, mddev) {
raid_disk = rdev->raid_disk;
if (raid_disk >= max_disks
|| raid_disk < 0)
@@ -5178,7 +5177,7 @@ static int run(struct mddev *mddev)
blk_queue_io_opt(mddev->queue, chunk_size *
(conf->raid_disks - conf->max_degraded));
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
disk_stack_limits(mddev->gendisk, rdev->bdev,
rdev->data_offset << 9);
}
@@ -5362,7 +5361,7 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev)
if (mddev->recovery_disabled == conf->recovery_disabled)
return -EBUSY;
- if (has_failed(conf))
+ if (rdev->saved_raid_disk < 0 && has_failed(conf))
/* no point adding a device */
return -EINVAL;
@@ -5501,7 +5500,7 @@ static int raid5_start_reshape(struct mddev *mddev)
if (!check_stripe_cache(mddev))
return -ENOSPC;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (!test_bit(In_sync, &rdev->flags)
&& !test_bit(Faulty, &rdev->flags))
spares++;
@@ -5547,16 +5546,14 @@ static int raid5_start_reshape(struct mddev *mddev)
* such devices during the reshape and confusion could result.
*/
if (mddev->delta_disks >= 0) {
- int added_devices = 0;
- list_for_each_entry(rdev, &mddev->disks, same_set)
+ rdev_for_each(rdev, mddev)
if (rdev->raid_disk < 0 &&
!test_bit(Faulty, &rdev->flags)) {
if (raid5_add_disk(mddev, rdev) == 0) {
if (rdev->raid_disk
- >= conf->previous_raid_disks) {
+ >= conf->previous_raid_disks)
set_bit(In_sync, &rdev->flags);
- added_devices++;
- } else
+ else
rdev->recovery_offset = 0;
if (sysfs_link_rdev(mddev, rdev))
@@ -5566,7 +5563,6 @@ static int raid5_start_reshape(struct mddev *mddev)
&& !test_bit(Faulty, &rdev->flags)) {
/* This is a spare that was manually added */
set_bit(In_sync, &rdev->flags);
- added_devices++;
}
/* When a reshape changes the number of devices,
@@ -5592,6 +5588,7 @@ static int raid5_start_reshape(struct mddev *mddev)
spin_lock_irq(&conf->device_lock);
mddev->raid_disks = conf->raid_disks = conf->previous_raid_disks;
conf->reshape_progress = MaxSector;
+ mddev->reshape_position = MaxSector;
spin_unlock_irq(&conf->device_lock);
return -EAGAIN;
}
diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
index 8295854ab94b..f80407eb8998 100644
--- a/drivers/media/common/tuners/Makefile
+++ b/drivers/media/common/tuners/Makefile
@@ -29,5 +29,5 @@ obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o
obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o
obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c
index cb2c98fbad1b..ba84936aafd6 100644
--- a/drivers/media/common/tuners/max2165.c
+++ b/drivers/media/common/tuners/max2165.c
@@ -168,7 +168,7 @@ int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction)
int i;
if (0 == divisor)
- return -1;
+ return -EINVAL;
q = dividend / divisor;
remainder = dividend - q * divisor;
@@ -194,10 +194,13 @@ static int max2165_set_rf(struct max2165_priv *priv, u32 freq)
u8 tf_ntch;
u32 t;
u32 quotient, fraction;
+ int ret;
/* Set PLL divider according to RF frequency */
- fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
- &quotient, &fraction);
+ ret = fixpt_div32(freq / 1000, priv->config->osc_clk * 1000,
+ &quotient, &fraction);
+ if (ret != 0)
+ return ret;
/* 20-bit fraction */
fraction >>= 12;
diff --git a/drivers/media/common/tuners/mt2063.c b/drivers/media/common/tuners/mt2063.c
index c89af3cd5eba..0ed9091ff48e 100644
--- a/drivers/media/common/tuners/mt2063.c
+++ b/drivers/media/common/tuners/mt2063.c
@@ -350,7 +350,7 @@ static int MT2063_Sleep(struct dvb_frontend *fe)
/*
* ToDo: Add code here to implement a OS blocking
*/
- msleep(10);
+ msleep(100);
return 0;
}
@@ -2226,7 +2226,7 @@ static struct dvb_tuner_ops mt2063_ops = {
.info = {
.name = "MT2063 Silicon Tuner",
.frequency_min = 45000000,
- .frequency_max = 850000000,
+ .frequency_max = 865000000,
.frequency_step = 0,
},
diff --git a/drivers/media/common/tuners/mt2063.h b/drivers/media/common/tuners/mt2063.h
index 62d0e8ec4e99..3f5cfd93713f 100644
--- a/drivers/media/common/tuners/mt2063.h
+++ b/drivers/media/common/tuners/mt2063.h
@@ -23,10 +23,6 @@ static inline struct dvb_frontend *mt2063_attach(struct dvb_frontend *fe,
return NULL;
}
-int mt2063_setTune(struct dvb_frontend *fe, u32 f_in,
- u32 bw_in,
- enum MTTune_atv_standard tv_type);
-
/* FIXME: Should use the standard DVB attachment interfaces */
unsigned int tuner_MT2063_SoftwareShutdown(struct dvb_frontend *fe);
unsigned int tuner_MT2063_ClearPowerMaskBits(struct dvb_frontend *fe);
diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c
index e13683bab6b3..2da4440c16ee 100644
--- a/drivers/media/common/tuners/tuner-types.c
+++ b/drivers/media/common/tuners/tuner-types.c
@@ -1868,6 +1868,10 @@ struct tunertype tuners[] = {
.params = tuner_tena_tnf_5337_params,
.count = ARRAY_SIZE(tuner_tena_tnf_5337_params),
},
+ [TUNER_XC5000C] = { /* Xceive 5000C */
+ .name = "Xceive 5000C tuner",
+ /* see xc5000.c for details */
+ },
};
EXPORT_SYMBOL(tuners);
diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
index 296df05b8cda..7f98984e4fad 100644
--- a/drivers/media/common/tuners/xc5000.c
+++ b/drivers/media/common/tuners/xc5000.c
@@ -49,9 +49,6 @@ static LIST_HEAD(hybrid_tuner_instance_list);
#define dprintk(level, fmt, arg...) if (debug >= level) \
printk(KERN_INFO "%s: " fmt, "xc5000", ## arg)
-#define XC5000_DEFAULT_FIRMWARE "dvb-fe-xc5000-1.6.114.fw"
-#define XC5000_DEFAULT_FIRMWARE_SIZE 12401
-
struct xc5000_priv {
struct tuner_i2c_props i2c_props;
struct list_head hybrid_tuner_instance_list;
@@ -62,6 +59,8 @@ struct xc5000_priv {
u8 video_standard;
u8 rf_mode;
u8 radio_input;
+
+ int chip_id;
};
/* Misc Defines */
@@ -204,6 +203,33 @@ static struct XC_TV_STANDARD XC5000_Standard[MAX_TV_STANDARD] = {
{"FM Radio-INPUT1_MONO", 0x0278, 0x9002}
};
+
+struct xc5000_fw_cfg {
+ char *name;
+ u16 size;
+};
+
+static const struct xc5000_fw_cfg xc5000a_1_6_114 = {
+ .name = "dvb-fe-xc5000-1.6.114.fw",
+ .size = 12401,
+};
+
+static const struct xc5000_fw_cfg xc5000c_41_024_5_31875 = {
+ .name = "dvb-fe-xc5000c-41.024.5-31875.fw",
+ .size = 16503,
+};
+
+static inline const struct xc5000_fw_cfg *xc5000_assign_firmware(int chip_id)
+{
+ switch (chip_id) {
+ default:
+ case XC5000A:
+ return &xc5000a_1_6_114;
+ case XC5000C:
+ return &xc5000c_41_024_5_31875;
+ }
+}
+
static int xc_load_fw_and_init_tuner(struct dvb_frontend *fe);
static int xc5000_is_firmware_loaded(struct dvb_frontend *fe);
static int xc5000_readreg(struct xc5000_priv *priv, u16 reg, u16 *val);
@@ -552,12 +578,14 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
struct xc5000_priv *priv = fe->tuner_priv;
const struct firmware *fw;
int ret;
+ const struct xc5000_fw_cfg *desired_fw =
+ xc5000_assign_firmware(priv->chip_id);
/* request the firmware, this will block and timeout */
printk(KERN_INFO "xc5000: waiting for firmware upload (%s)...\n",
- XC5000_DEFAULT_FIRMWARE);
+ desired_fw->name);
- ret = request_firmware(&fw, XC5000_DEFAULT_FIRMWARE,
+ ret = request_firmware(&fw, desired_fw->name,
priv->i2c_props.adap->dev.parent);
if (ret) {
printk(KERN_ERR "xc5000: Upload failed. (file not found?)\n");
@@ -569,7 +597,7 @@ static int xc5000_fwupload(struct dvb_frontend *fe)
ret = XC_RESULT_SUCCESS;
}
- if (fw->size != XC5000_DEFAULT_FIRMWARE_SIZE) {
+ if (fw->size != desired_fw->size) {
printk(KERN_ERR "xc5000: firmware incorrect size\n");
ret = XC_RESULT_RESET_FAILURE;
} else {
@@ -1139,6 +1167,13 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
if (priv->radio_input == 0)
priv->radio_input = cfg->radio_input;
+ /* don't override chip id if it's already been set
+ unless explicitly specified */
+ if ((priv->chip_id == 0) || (cfg->chip_id))
+ /* use default chip id if none specified, set to 0 so
+ it can be overridden if this is a hybrid driver */
+ priv->chip_id = (cfg->chip_id) ? cfg->chip_id : 0;
+
/* Check if firmware has been loaded. It is possible that another
instance of the driver has loaded the firmware.
*/
diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h
index e2957451b532..3396f8e02b40 100644
--- a/drivers/media/common/tuners/xc5000.h
+++ b/drivers/media/common/tuners/xc5000.h
@@ -27,10 +27,15 @@
struct dvb_frontend;
struct i2c_adapter;
+#define XC5000A 1
+#define XC5000C 2
+
struct xc5000_config {
u8 i2c_address;
u32 if_khz;
u8 radio_input;
+
+ int chip_id;
};
/* xc5000 callback command */
diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c
index ce4f85849e7b..d88c4aa7d24d 100644
--- a/drivers/media/dvb/ddbridge/ddbridge-core.c
+++ b/drivers/media/dvb/ddbridge/ddbridge-core.c
@@ -578,6 +578,7 @@ static int demod_attach_drxk(struct ddb_input *input)
struct drxk_config config;
memset(&config, 0, sizeof(config));
+ config.microcode_name = "drxk_a3.mc";
config.adr = 0x29 + (input->nr & 1);
fe = input->fe = dvb_attach(drxk_attach, &config, i2c);
diff --git a/drivers/media/dvb/ddbridge/ddbridge.h b/drivers/media/dvb/ddbridge/ddbridge.h
index 6d14893218f4..8b1b41d2a52d 100644
--- a/drivers/media/dvb/ddbridge/ddbridge.h
+++ b/drivers/media/dvb/ddbridge/ddbridge.h
@@ -32,8 +32,6 @@
#include <asm/dma.h>
#include <linux/dvb/frontend.h>
#include <linux/dvb/ca.h>
-#include <linux/dvb/video.h>
-#include <linux/dvb/audio.h>
#include <linux/socket.h>
#include "dmxdev.h"
diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
index e4b5c03ae516..73970cd97af1 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/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index fbbe545a74cb..4555baa383b2 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -655,6 +655,8 @@ restart:
dprintk("%s: Retune requested, FESTATE_RETUNE\n", __func__);
re_tune = true;
fepriv->state = FESTATE_TUNED;
+ } else {
+ re_tune = false;
}
if (fe->ops.tune)
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 9f203c6767a6..63bf45679f98 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -361,6 +361,14 @@ config DVB_USB_EC168
help
Say Y here to support the E3C EC168 DVB-T USB2.0 receiver.
+config DVB_USB_AZ6007
+ tristate "AzureWave 6007 and clones DVB-T/C USB2.0 support"
+ depends on DVB_USB
+ select DVB_DRXK if !DVB_FE_CUSTOMISE
+ select MEDIA_TUNER_MT2063 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support theAfatech AF9005 based DVB-T/DVB-C receivers.
+
config DVB_USB_AZ6027
tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support"
depends on DVB_USB
@@ -378,6 +386,7 @@ config DVB_USB_LME2510
select DVB_IX2505V if !DVB_FE_CUSTOMISE
select DVB_STV0299 if !DVB_FE_CUSTOMISE
select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_M88RS2000 if !DVB_FE_CUSTOMISE
help
Say Y here to support the LME DM04/QQBOX DVB-S USB2.0 .
@@ -403,3 +412,13 @@ config DVB_USB_MXL111SF
select VIDEO_TVEEPROM
help
Say Y here to support the MxL111SF USB2.0 DTV receiver.
+
+config DVB_USB_RTL28XXU
+ tristate "Realtek RTL28xxU DVB USB support"
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_RTL2830
+ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Realtek RTL28xxU DVB USB receiver.
diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
index 26c8b9e57050..b76acb5387e6 100644
--- a/drivers/media/dvb/dvb-usb/Makefile
+++ b/drivers/media/dvb/dvb-usb/Makefile
@@ -54,7 +54,6 @@ obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o
dvb-usb-opera-objs = opera1.o
obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o
-
dvb-usb-af9005-objs = af9005.o af9005-fe.o
obj-$(CONFIG_DVB_USB_AF9005) += dvb-usb-af9005.o
@@ -88,6 +87,9 @@ obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o
dvb-usb-ec168-objs = ec168.o
obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o
+dvb-usb-az6007-objs = az6007.o
+obj-$(CONFIG_DVB_USB_AZ6007) += dvb-usb-az6007.o
+
dvb-usb-az6027-objs = az6027.o
obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o
@@ -105,8 +107,12 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += dvb-usb-mxl111sf.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-demod.o
obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o
-ccflags-y += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+dvb-usb-rtl28xxu-objs = rtl28xxu.o
+obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o
+
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/
# due to tuner-xc3028
-ccflags-y += -Idrivers/media/common/tuners
-EXTRA_CFLAGS += -Idrivers/media/dvb/ttpci
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/ttpci
diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
index 282a43d648df..7e70ea50ef26 100644
--- a/drivers/media/dvb/dvb-usb/af9015.c
+++ b/drivers/media/dvb/dvb-usb/af9015.c
@@ -1164,6 +1164,41 @@ static int af9015_af9013_sleep(struct dvb_frontend *fe)
return ret;
}
+/* override tuner callbacks for resource locking */
+static int af9015_tuner_init(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct af9015_state *priv = adap->dev->priv;
+
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex))
+ return -EAGAIN;
+
+ ret = priv->tuner_init[adap->id](fe);
+
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
+}
+
+/* override tuner callbacks for resource locking */
+static int af9015_tuner_sleep(struct dvb_frontend *fe)
+{
+ int ret;
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct af9015_state *priv = adap->dev->priv;
+
+ if (mutex_lock_interruptible(&adap->dev->usb_mutex))
+ return -EAGAIN;
+
+ ret = priv->tuner_sleep[adap->id](fe);
+
+ mutex_unlock(&adap->dev->usb_mutex);
+
+ return ret;
+}
+
+
static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap)
{
int ret;
@@ -1283,6 +1318,7 @@ static struct mxl5007t_config af9015_mxl5007t_config = {
static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
{
int ret;
+ struct af9015_state *state = adap->dev->priv;
deb_info("%s:\n", __func__);
switch (af9015_af9013_config[adap->id].tuner) {
@@ -1340,6 +1376,19 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
err("Unknown tuner id:%d",
af9015_af9013_config[adap->id].tuner);
}
+
+ if (adap->fe_adap[0].fe->ops.tuner_ops.init) {
+ state->tuner_init[adap->id] =
+ adap->fe_adap[0].fe->ops.tuner_ops.init;
+ adap->fe_adap[0].fe->ops.tuner_ops.init = af9015_tuner_init;
+ }
+
+ if (adap->fe_adap[0].fe->ops.tuner_ops.sleep) {
+ state->tuner_sleep[adap->id] =
+ adap->fe_adap[0].fe->ops.tuner_ops.sleep;
+ adap->fe_adap[0].fe->ops.tuner_ops.sleep = af9015_tuner_sleep;
+ }
+
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
index f619063fa72f..2f68419e899b 100644
--- a/drivers/media/dvb/dvb-usb/af9015.h
+++ b/drivers/media/dvb/dvb-usb/af9015.h
@@ -108,6 +108,8 @@ struct af9015_state {
int (*read_status[2]) (struct dvb_frontend *fe, fe_status_t *status);
int (*init[2]) (struct dvb_frontend *fe);
int (*sleep[2]) (struct dvb_frontend *fe);
+ int (*tuner_init[2]) (struct dvb_frontend *fe);
+ int (*tuner_sleep[2]) (struct dvb_frontend *fe);
};
struct af9015_config {
diff --git a/drivers/media/dvb/dvb-usb/anysee.c b/drivers/media/dvb/dvb-usb/anysee.c
index cf0c318d6989..03c28655af1b 100644
--- a/drivers/media/dvb/dvb-usb/anysee.c
+++ b/drivers/media/dvb/dvb-usb/anysee.c
@@ -58,7 +58,7 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
u8 *rbuf, u8 rlen)
{
struct anysee_state *state = d->priv;
- int act_len, ret;
+ int act_len, ret, i;
u8 buf[64];
memcpy(&buf[0], sbuf, slen);
@@ -73,26 +73,52 @@ static int anysee_ctrl_msg(struct dvb_usb_device *d, u8 *sbuf, u8 slen,
/* We need receive one message more after dvb_usb_generic_rw due
to weird transaction flow, which is 1 x send + 2 x receive. */
ret = dvb_usb_generic_rw(d, buf, sizeof(buf), buf, sizeof(buf), 0);
- if (!ret) {
+ if (ret)
+ goto error_unlock;
+
+ /* TODO FIXME: dvb_usb_generic_rw() fails rarely with error code -32
+ * (EPIPE, Broken pipe). Function supports currently msleep() as a
+ * parameter but I would not like to use it, since according to
+ * Documentation/timers/timers-howto.txt it should not be used such
+ * short, under < 20ms, sleeps. Repeating failed message would be
+ * better choice as not to add unwanted delays...
+ * Fixing that correctly is one of those or both;
+ * 1) use repeat if possible
+ * 2) add suitable delay
+ */
+
+ /* get answer, retry few times if error returned */
+ for (i = 0; i < 3; i++) {
/* receive 2nd answer */
ret = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev,
d->props.generic_bulk_ctrl_endpoint), buf, sizeof(buf),
&act_len, 2000);
- if (ret)
- err("%s: recv bulk message failed: %d", __func__, ret);
- else {
+
+ if (ret) {
+ deb_info("%s: recv bulk message failed: %d",
+ __func__, ret);
+ } else {
deb_xfer("<<< ");
debug_dump(buf, rlen, deb_xfer);
if (buf[63] != 0x4f)
deb_info("%s: cmd failed\n", __func__);
+
+ break;
}
}
+ if (ret) {
+ /* all retries failed, it is fatal */
+ err("%s: recv bulk message failed: %d", __func__, ret);
+ goto error_unlock;
+ }
+
/* read request, copy returned data to return buf */
- if (!ret && rbuf && rlen)
+ if (rbuf && rlen)
memcpy(rbuf, buf, rlen);
+error_unlock:
mutex_unlock(&anysee_usb_mutex);
return ret;
diff --git a/drivers/media/dvb/dvb-usb/az6007.c b/drivers/media/dvb/dvb-usb/az6007.c
new file mode 100644
index 000000000000..4008b9c50fbd
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/az6007.c
@@ -0,0 +1,957 @@
+/*
+ * Driver for AzureWave 6007 DVB-C/T USB2.0 and clones
+ *
+ * Copyright (c) Henry Wang <Henry.wang@AzureWave.com>
+ *
+ * This driver was made publicly available by Terratec, at:
+ * http://linux.terratec.de/files/TERRATEC_H7/20110323_TERRATEC_H7_Linux.tar.gz
+ * The original driver's license is GPL, as declared with MODULE_LICENSE()
+ *
+ * Copyright (c) 2010-2011 Mauro Carvalho Chehab <mchehab@redhat.com>
+ * Driver modified by in order to work with upstream drxk driver, and
+ * tons of bugs got fixed.
+ *
+ * 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 under version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "drxk.h"
+#include "mt2063.h"
+#include "dvb_ca_en50221.h"
+
+#define DVB_USB_LOG_PREFIX "az6007"
+#include "dvb-usb.h"
+
+/* debug */
+int dvb_usb_az6007_debug;
+module_param_named(debug, dvb_usb_az6007_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))."
+ DVB_USB_DEBUG_STATUS);
+
+#define deb_info(args...) dprintk(dvb_usb_az6007_debug, 0x01, args)
+#define deb_xfer(args...) dprintk(dvb_usb_az6007_debug, 0x02, args)
+#define deb_rc(args...) dprintk(dvb_usb_az6007_debug, 0x04, args)
+#define deb_fe(args...) dprintk(dvb_usb_az6007_debug, 0x08, args)
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+/* Known requests (Cypress FX2 firmware + az6007 "private" ones*/
+
+#define FX2_OED 0xb5
+#define AZ6007_READ_DATA 0xb7
+#define AZ6007_I2C_RD 0xb9
+#define AZ6007_POWER 0xbc
+#define AZ6007_I2C_WR 0xbd
+#define FX2_SCON1 0xc0
+#define AZ6007_TS_THROUGH 0xc7
+#define AZ6007_READ_IR 0xb4
+
+struct az6007_device_state {
+ struct mutex mutex;
+ struct mutex ca_mutex;
+ struct dvb_ca_en50221 ca;
+ unsigned warm:1;
+ int (*gate_ctrl) (struct dvb_frontend *, int);
+ unsigned char data[4096];
+};
+
+static struct drxk_config terratec_h7_drxk = {
+ .adr = 0x29,
+ .parallel_ts = true,
+ .dynamic_clk = true,
+ .single_master = true,
+ .enable_merr_cfg = true,
+ .no_i2c_bridge = false,
+ .chunk_size = 64,
+ .mpeg_out_clk_strength = 0x02,
+ .microcode_name = "dvb-usb-terratec-h7-drxk.fw",
+};
+
+static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct dvb_usb_adapter *adap = fe->sec_priv;
+ struct az6007_device_state *st;
+ int status = 0;
+
+ deb_info("%s: %s\n", __func__, enable ? "enable" : "disable");
+
+ if (!adap)
+ return -EINVAL;
+
+ st = adap->dev->priv;
+
+ if (!st)
+ return -EINVAL;
+
+ if (enable)
+ status = st->gate_ctrl(fe, 1);
+ else
+ status = st->gate_ctrl(fe, 0);
+
+ return status;
+}
+
+static struct mt2063_config az6007_mt2063_config = {
+ .tuner_address = 0x60,
+ .refclock = 36125000,
+};
+
+static int __az6007_read(struct usb_device *udev, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ int ret;
+
+ ret = usb_control_msg(udev,
+ usb_rcvctrlpipe(udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_IN,
+ value, index, b, blen, 5000);
+ if (ret < 0) {
+ warn("usb read operation failed. (%d)", ret);
+ return -EIO;
+ }
+
+ deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
+ index);
+ debug_dump(b, blen, deb_xfer);
+
+ return ret;
+}
+
+static int az6007_read(struct dvb_usb_device *d, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ ret = __az6007_read(d->udev, req, value, index, b, blen);
+
+ mutex_unlock(&st->mutex);
+
+ return ret;
+}
+
+static int __az6007_write(struct usb_device *udev, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ int ret;
+
+ deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value,
+ index);
+ debug_dump(b, blen, deb_xfer);
+
+ if (blen > 64) {
+ err("az6007: tried to write %d bytes, but I2C max size is 64 bytes\n",
+ blen);
+ return -EOPNOTSUPP;
+ }
+
+ ret = usb_control_msg(udev,
+ usb_sndctrlpipe(udev, 0),
+ req,
+ USB_TYPE_VENDOR | USB_DIR_OUT,
+ value, index, b, blen, 5000);
+ if (ret != blen) {
+ err("usb write operation failed. (%d)", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int az6007_write(struct dvb_usb_device *d, u8 req, u16 value,
+ u16 index, u8 *b, int blen)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ ret = __az6007_write(d->udev, req, value, index, b, blen);
+
+ mutex_unlock(&st->mutex);
+
+ return ret;
+}
+
+static int az6007_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
+{
+ struct dvb_usb_device *d = adap->dev;
+
+ deb_info("%s: %s", __func__, onoff ? "enable" : "disable");
+
+ return az6007_write(d, 0xbc, onoff, 0, NULL, 0);
+}
+
+/* remote control stuff (does not work with my box) */
+static int az6007_rc_query(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *st = d->priv;
+ unsigned code = 0;
+
+ az6007_read(d, AZ6007_READ_IR, 0, 0, st->data, 10);
+
+ if (st->data[1] == 0x44)
+ return 0;
+
+ if ((st->data[1] ^ st->data[2]) == 0xff)
+ code = st->data[1];
+ else
+ code = st->data[1] << 8 | st->data[2];
+
+ if ((st->data[3] ^ st->data[4]) == 0xff)
+ code = code << 8 | st->data[3];
+ else
+ code = code << 16 | st->data[3] << 8 | st->data[4];
+
+ rc_keydown(d->rc_dev, code, st->data[5]);
+
+ return 0;
+}
+
+static int az6007_ci_read_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC1;
+ value = address;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EINVAL;
+ } else {
+ ret = b[0];
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_write_attribute_mem(struct dvb_ca_en50221 *ca,
+ int slot,
+ int address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ deb_info("%s %d", __func__, slot);
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC2;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6007_write(d, req, value1, index, NULL, blen);
+ if (ret != 0)
+ warn("usb out operation failed. (%d)", ret);
+
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_read_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC3;
+ value = address;
+ index = 0;
+ blen = 2;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EINVAL;
+ } else {
+ if (b[0] == 0)
+ warn("Read CI IO error");
+
+ ret = b[1];
+ deb_info("read cam data = %x from 0x%x", b[1], value);
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_write_cam_control(struct dvb_ca_en50221 *ca,
+ int slot,
+ u8 address,
+ u8 value)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value1;
+ u16 index;
+ int blen;
+
+ if (slot != 0)
+ return -EINVAL;
+
+ mutex_lock(&state->ca_mutex);
+ req = 0xC4;
+ value1 = address;
+ index = value;
+ blen = 0;
+
+ ret = az6007_write(d, req, value1, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+
+ req = 0xC8;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else{
+ ret = b[0];
+ }
+ kfree(b);
+ return ret;
+}
+
+static int az6007_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret, i;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC6;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+ msleep(500);
+ req = 0xC6;
+ value = 0;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+ for (i = 0; i < 15; i++) {
+ msleep(100);
+
+ if (CI_CamReady(ca, slot)) {
+ deb_info("CAM Ready");
+ break;
+ }
+ }
+ msleep(5000);
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
+{
+ return 0;
+}
+
+static int az6007_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+
+ deb_info("%s", __func__);
+ mutex_lock(&state->ca_mutex);
+ req = 0xC7;
+ value = 1;
+ index = 0;
+ blen = 0;
+
+ ret = az6007_write(d, req, value, index, NULL, blen);
+ if (ret != 0) {
+ warn("usb out operation failed. (%d)", ret);
+ goto failed;
+ }
+
+failed:
+ mutex_unlock(&state->ca_mutex);
+ return ret;
+}
+
+static int az6007_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
+{
+ struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+ int ret;
+ u8 req;
+ u16 value;
+ u16 index;
+ int blen;
+ u8 *b;
+
+ b = kmalloc(12, GFP_KERNEL);
+ if (!b)
+ return -ENOMEM;
+ mutex_lock(&state->ca_mutex);
+
+ req = 0xC5;
+ value = 0;
+ index = 0;
+ blen = 1;
+
+ ret = az6007_read(d, req, value, index, b, blen);
+ if (ret < 0) {
+ warn("usb in operation failed. (%d)", ret);
+ ret = -EIO;
+ } else
+ ret = 0;
+
+ if (!ret && b[0] == 1) {
+ ret = DVB_CA_EN50221_POLL_CAM_PRESENT |
+ DVB_CA_EN50221_POLL_CAM_READY;
+ }
+
+ mutex_unlock(&state->ca_mutex);
+ kfree(b);
+ return ret;
+}
+
+
+static void az6007_ci_uninit(struct dvb_usb_device *d)
+{
+ struct az6007_device_state *state;
+
+ deb_info("%s", __func__);
+
+ if (NULL == d)
+ return;
+
+ state = (struct az6007_device_state *)d->priv;
+ if (NULL == state)
+ return;
+
+ if (NULL == state->ca.data)
+ return;
+
+ dvb_ca_en50221_release(&state->ca);
+
+ memset(&state->ca, 0, sizeof(state->ca));
+}
+
+
+static int az6007_ci_init(struct dvb_usb_adapter *a)
+{
+ struct dvb_usb_device *d = a->dev;
+ struct az6007_device_state *state = (struct az6007_device_state *)d->priv;
+ int ret;
+
+ deb_info("%s", __func__);
+
+ mutex_init(&state->ca_mutex);
+
+ state->ca.owner = THIS_MODULE;
+ state->ca.read_attribute_mem = az6007_ci_read_attribute_mem;
+ state->ca.write_attribute_mem = az6007_ci_write_attribute_mem;
+ state->ca.read_cam_control = az6007_ci_read_cam_control;
+ state->ca.write_cam_control = az6007_ci_write_cam_control;
+ state->ca.slot_reset = az6007_ci_slot_reset;
+ state->ca.slot_shutdown = az6007_ci_slot_shutdown;
+ state->ca.slot_ts_enable = az6007_ci_slot_ts_enable;
+ state->ca.poll_slot_status = az6007_ci_poll_slot_status;
+ state->ca.data = d;
+
+ ret = dvb_ca_en50221_init(&a->dvb_adap,
+ &state->ca,
+ 0, /* flags */
+ 1);/* n_slots */
+ if (ret != 0) {
+ err("Cannot initialize CI: Error %d.", ret);
+ memset(&state->ca, 0, sizeof(state->ca));
+ return ret;
+ }
+
+ deb_info("CI initialized.");
+
+ return 0;
+}
+
+static int az6007_read_mac_addr(struct dvb_usb_device *d, u8 mac[6])
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ ret = az6007_read(d, AZ6007_READ_DATA, 6, 0, st->data, 6);
+ memcpy(mac, st->data, sizeof(mac));
+
+ if (ret > 0)
+ deb_info("%s: mac is %02x:%02x:%02x:%02x:%02x:%02x\n",
+ __func__, mac[0], mac[1], mac[2],
+ mac[3], mac[4], mac[5]);
+
+ return ret;
+}
+
+static int az6007_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ struct az6007_device_state *st = adap->dev->priv;
+
+ deb_info("attaching demod drxk");
+
+ adap->fe_adap[0].fe = dvb_attach(drxk_attach, &terratec_h7_drxk,
+ &adap->dev->i2c_adap);
+ if (!adap->fe_adap[0].fe)
+ return -EINVAL;
+
+ adap->fe_adap[0].fe->sec_priv = adap;
+ st->gate_ctrl = adap->fe_adap[0].fe->ops.i2c_gate_ctrl;
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl = drxk_gate_ctrl;
+
+ az6007_ci_init(adap);
+
+ return 0;
+}
+
+static int az6007_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ deb_info("attaching tuner mt2063");
+
+ /* Attach mt2063 to DVB-C frontend */
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 1);
+ if (!dvb_attach(mt2063_attach, adap->fe_adap[0].fe,
+ &az6007_mt2063_config,
+ &adap->dev->i2c_adap))
+ return -EINVAL;
+
+ if (adap->fe_adap[0].fe->ops.i2c_gate_ctrl)
+ adap->fe_adap[0].fe->ops.i2c_gate_ctrl(adap->fe_adap[0].fe, 0);
+
+ return 0;
+}
+
+int az6007_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ struct az6007_device_state *st = d->priv;
+ int ret;
+
+ deb_info("%s()\n", __func__);
+
+ if (!st->warm) {
+ mutex_init(&st->mutex);
+
+ ret = az6007_write(d, AZ6007_POWER, 0, 2, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(60);
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(100);
+ ret = az6007_write(d, AZ6007_POWER, 1, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(20);
+ ret = az6007_write(d, AZ6007_POWER, 1, 4, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ msleep(400);
+ ret = az6007_write(d, FX2_SCON1, 0, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(150);
+ ret = az6007_write(d, FX2_SCON1, 1, 3, NULL, 0);
+ if (ret < 0)
+ return ret;
+ msleep(430);
+ ret = az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ st->warm = true;
+
+ return 0;
+ }
+
+ if (!onoff)
+ return 0;
+
+ az6007_write(d, AZ6007_POWER, 0, 0, NULL, 0);
+ az6007_write(d, AZ6007_TS_THROUGH, 0, 0, NULL, 0);
+
+ return 0;
+}
+
+/* I2C */
+static int az6007_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
+ int num)
+{
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct az6007_device_state *st = d->priv;
+ int i, j, len;
+ int ret = 0;
+ u16 index;
+ u16 value;
+ int length;
+ u8 req, addr;
+
+ if (mutex_lock_interruptible(&st->mutex) < 0)
+ return -EAGAIN;
+
+ for (i = 0; i < num; i++) {
+ addr = msgs[i].addr << 1;
+ if (((i + 1) < num)
+ && (msgs[i].len == 1)
+ && (!msgs[i].flags & I2C_M_RD)
+ && (msgs[i + 1].flags & I2C_M_RD)
+ && (msgs[i].addr == msgs[i + 1].addr)) {
+ /*
+ * A write + read xfer for the same address, where
+ * the first xfer has just 1 byte length.
+ * Need to join both into one operation
+ */
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_DEBUG
+ "az6007 I2C xfer write+read addr=0x%x len=%d/%d: ",
+ addr, msgs[i].len, msgs[i + 1].len);
+ req = AZ6007_I2C_RD;
+ index = msgs[i].buf[0];
+ value = addr | (1 << 8);
+ length = 6 + msgs[i + 1].len;
+ len = msgs[i + 1].len;
+ ret = __az6007_read(d->udev, req, value, index,
+ st->data, length);
+ if (ret >= len) {
+ for (j = 0; j < len; j++) {
+ msgs[i + 1].buf[j] = st->data[j + 5];
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT
+ "0x%02x ",
+ msgs[i + 1].buf[j]);
+ }
+ } else
+ ret = -EIO;
+ i++;
+ } else if (!(msgs[i].flags & I2C_M_RD)) {
+ /* write bytes */
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_DEBUG
+ "az6007 I2C xfer write addr=0x%x len=%d: ",
+ addr, msgs[i].len);
+ req = AZ6007_I2C_WR;
+ index = msgs[i].buf[0];
+ value = addr | (1 << 8);
+ length = msgs[i].len - 1;
+ len = msgs[i].len - 1;
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT "(0x%02x) ", msgs[i].buf[0]);
+ for (j = 0; j < len; j++) {
+ st->data[j] = msgs[i].buf[j + 1];
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT "0x%02x ",
+ st->data[j]);
+ }
+ ret = __az6007_write(d->udev, req, value, index,
+ st->data, length);
+ } else {
+ /* read bytes */
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_DEBUG
+ "az6007 I2C xfer read addr=0x%x len=%d: ",
+ addr, msgs[i].len);
+ req = AZ6007_I2C_RD;
+ index = msgs[i].buf[0];
+ value = addr;
+ length = msgs[i].len + 6;
+ len = msgs[i].len;
+ ret = __az6007_read(d->udev, req, value, index,
+ st->data, length);
+ for (j = 0; j < len; j++) {
+ msgs[i].buf[j] = st->data[j + 5];
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT
+ "0x%02x ", st->data[j + 5]);
+ }
+ }
+ if (dvb_usb_az6007_debug & 2)
+ printk(KERN_CONT "\n");
+ if (ret < 0)
+ goto err;
+ }
+err:
+ mutex_unlock(&st->mutex);
+
+ if (ret < 0) {
+ info("%s ERROR: %i", __func__, ret);
+ return ret;
+ }
+ return num;
+}
+
+static u32 az6007_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm az6007_i2c_algo = {
+ .master_xfer = az6007_i2c_xfer,
+ .functionality = az6007_i2c_func,
+};
+
+int az6007_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc, int *cold)
+{
+ int ret;
+ u8 *mac;
+
+ mac = kmalloc(6, GFP_ATOMIC);
+ if (!mac)
+ return -ENOMEM;
+
+ /* Try to read the mac address */
+ ret = __az6007_read(udev, AZ6007_READ_DATA, 6, 0, mac, 6);
+ if (ret == 6)
+ *cold = 0;
+ else
+ *cold = 1;
+
+ kfree(mac);
+
+ if (*cold) {
+ __az6007_write(udev, 0x09, 1, 0, NULL, 0);
+ __az6007_write(udev, 0x00, 0, 0, NULL, 0);
+ __az6007_write(udev, 0x00, 0, 0, NULL, 0);
+ }
+
+ deb_info("Device is on %s state\n", *cold ? "warm" : "cold");
+ return 0;
+}
+
+static struct dvb_usb_device_properties az6007_properties;
+
+static void az6007_usb_disconnect(struct usb_interface *intf)
+{
+ struct dvb_usb_device *d = usb_get_intfdata(intf);
+ az6007_ci_uninit(d);
+ dvb_usb_device_exit(intf);
+}
+
+static int az6007_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ return dvb_usb_device_init(intf, &az6007_properties,
+ THIS_MODULE, NULL, adapter_nr);
+}
+
+static struct usb_device_id az6007_usb_table[] = {
+ {USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_6007)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7)},
+ {USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_H7_2)},
+ {0},
+};
+
+MODULE_DEVICE_TABLE(usb, az6007_usb_table);
+
+static struct dvb_usb_device_properties az6007_properties = {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-terratec-h7-az6007.fw",
+ .no_reconnect = 1,
+ .size_of_priv = sizeof(struct az6007_device_state),
+ .identify_state = az6007_identify_state,
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {{
+ .streaming_ctrl = az6007_streaming_ctrl,
+ .tuner_attach = az6007_tuner_attach,
+ .frontend_attach = az6007_frontend_attach,
+
+ /* parameter for the MPEG2-data transfer */
+ .stream = {
+ .type = USB_BULK,
+ .count = 10,
+ .endpoint = 0x02,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+ } }
+ } },
+ .power_ctrl = az6007_power_ctrl,
+ .read_mac_address = az6007_read_mac_addr,
+
+ .rc.core = {
+ .rc_interval = 400,
+ .rc_codes = RC_MAP_NEC_TERRATEC_CINERGY_XS,
+ .module_name = "az6007",
+ .rc_query = az6007_rc_query,
+ .allowed_protos = RC_TYPE_NEC,
+ },
+ .i2c_algo = &az6007_i2c_algo,
+
+ .num_device_descs = 2,
+ .devices = {
+ { .name = "AzureWave DTV StarBox DVB-T/C USB2.0 (az6007)",
+ .cold_ids = { &az6007_usb_table[0], NULL },
+ .warm_ids = { NULL },
+ },
+ { .name = "TerraTec DTV StarBox DVB-T/C USB2.0 (az6007)",
+ .cold_ids = { &az6007_usb_table[1], &az6007_usb_table[2], NULL },
+ .warm_ids = { NULL },
+ },
+ { NULL },
+ }
+};
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver az6007_usb_driver = {
+ .name = "dvb_usb_az6007",
+ .probe = az6007_usb_probe,
+ .disconnect = az6007_usb_disconnect,
+ .id_table = az6007_usb_table,
+};
+
+/* module stuff */
+static int __init az6007_usb_module_init(void)
+{
+ int result;
+ deb_info("az6007 usb module init\n");
+
+ result = usb_register(&az6007_usb_driver);
+ if (result) {
+ err("usb_register failed. (%d)", result);
+ return result;
+ }
+
+ return 0;
+}
+
+static void __exit az6007_usb_module_exit(void)
+{
+ /* deregister this driver from the USB subsystem */
+ deb_info("az6007 usb module exit\n");
+ usb_deregister(&az6007_usb_driver);
+}
+
+module_init(az6007_usb_module_init);
+module_exit(az6007_usb_module_exit);
+
+MODULE_AUTHOR("Henry Wang <Henry.wang@AzureWave.com>");
+MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>");
+MODULE_DESCRIPTION("Driver for AzureWave 6007 DVB-C/T USB2.0 and clones");
+MODULE_VERSION("1.1");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
index 070e82aa53f5..02290c60f72f 100644
--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
+++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
@@ -677,11 +677,9 @@ static void dib0700_rc_urb_completion(struct urb *purb)
u8 toggle;
deb_info("%s()\n", __func__);
- if (d == NULL)
- return;
-
if (d->rc_dev == NULL) {
/* This will occur if disable_rc_polling=1 */
+ kfree(purb->transfer_buffer);
usb_free_urb(purb);
return;
}
@@ -690,6 +688,7 @@ static void dib0700_rc_urb_completion(struct urb *purb)
if (purb->status < 0) {
deb_info("discontinuing polling\n");
+ kfree(purb->transfer_buffer);
usb_free_urb(purb);
return;
}
@@ -784,8 +783,11 @@ int dib0700_rc_setup(struct dvb_usb_device *d)
dib0700_rc_urb_completion, d);
ret = usb_submit_urb(purb, GFP_ATOMIC);
- if (ret)
+ if (ret) {
err("rc submit urb failed\n");
+ kfree(purb->transfer_buffer);
+ usb_free_urb(purb);
+ }
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index d390ddaa5a53..397d8f232731 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -51,6 +51,7 @@
#define USB_VID_PINNACLE 0x2304
#define USB_VID_PCTV 0x2013
#define USB_VID_PIXELVIEW 0x1554
+#define USB_VID_REALTEK 0x0bda
#define USB_VID_TECHNOTREND 0x0b48
#define USB_VID_TERRATEC 0x0ccd
#define USB_VID_TELESTAR 0x10b9
@@ -80,6 +81,7 @@
#define USB_PID_ANSONIC_DVBT_USB 0x6000
#define USB_PID_ANYSEE 0x861f
#define USB_PID_AZUREWAVE_AD_TU700 0x3237
+#define USB_PID_AZUREWAVE_6007 0x0ccd
#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
@@ -125,6 +127,8 @@
#define USB_PID_E3C_EC168_3 0xfffb
#define USB_PID_E3C_EC168_4 0x1001
#define USB_PID_E3C_EC168_5 0x1002
+#define USB_PID_FREECOM_DVBT 0x0160
+#define USB_PID_FREECOM_DVBT_2 0x0161
#define USB_PID_UNIWILL_STK7700P 0x6003
#define USB_PID_GENIUS_TVGO_DVB_T03 0x4012
#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
@@ -226,6 +230,8 @@
#define USB_PID_TERRATEC_CINERGY_T_EXPRESS 0x0062
#define USB_PID_TERRATEC_CINERGY_T_XXS 0x0078
#define USB_PID_TERRATEC_CINERGY_T_XXS_2 0x00ab
+#define USB_PID_TERRATEC_H7 0x10b4
+#define USB_PID_TERRATEC_H7_2 0x10a3
#define USB_PID_TERRATEC_T3 0x10a0
#define USB_PID_TERRATEC_T5 0x10a1
#define USB_PID_PINNACLE_EXPRESSCARD_320CX 0x022e
@@ -249,6 +255,8 @@
#define USB_PID_PCTV_400E 0x020f
#define USB_PID_PCTV_450E 0x0222
#define USB_PID_PCTV_452E 0x021f
+#define USB_PID_REALTEK_RTL2831U 0x2831
+#define USB_PID_REALTEK_RTL2832U 0x2832
#define USB_PID_TECHNOTREND_CONNECT_S2_3600 0x3007
#define USB_PID_TECHNOTREND_CONNECT_S2_3650_CI 0x300a
#define USB_PID_NEBULA_DIGITV 0x0201
diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c
index 9f01cd7a6e3f..3b7b102f20ae 100644
--- a/drivers/media/dvb/dvb-usb/it913x.c
+++ b/drivers/media/dvb/dvb-usb/it913x.c
@@ -64,6 +64,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct it913x_state {
u8 id;
struct ite_config it913x_config;
+ u8 pid_filter_onoff;
};
struct ite_config it913x_config;
@@ -259,15 +260,16 @@ static u32 it913x_query(struct usb_device *udev, u8 pro)
static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
+ struct it913x_state *st = adap->dev->priv;
struct usb_device *udev = adap->dev->udev;
int ret;
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
+
deb_info(1, "PID_C (%02x)", onoff);
- ret = it913x_wr_reg(udev, pro, PID_EN, onoff);
+ ret = it913x_wr_reg(udev, pro, PID_EN, st->pid_filter_onoff);
mutex_unlock(&adap->dev->i2c_mutex);
return ret;
@@ -276,12 +278,13 @@ static int it913x_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
static int it913x_pid_filter(struct dvb_usb_adapter *adap,
int index, u16 pid, int onoff)
{
+ struct it913x_state *st = adap->dev->priv;
struct usb_device *udev = adap->dev->udev;
int ret;
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
+
deb_info(1, "PID_F (%02x)", onoff);
ret = it913x_wr_reg(udev, pro, PID_LSB, (u8)(pid & 0xff));
@@ -292,6 +295,13 @@ static int it913x_pid_filter(struct dvb_usb_adapter *adap,
ret |= it913x_wr_reg(udev, pro, PID_INX, (u8)(index & 0x1f));
+ if (udev->speed == USB_SPEED_HIGH && pid == 0x2000) {
+ ret |= it913x_wr_reg(udev, pro, PID_EN, !onoff);
+ st->pid_filter_onoff = !onoff;
+ } else
+ st->pid_filter_onoff =
+ adap->fe_adap[adap->active_fe].pid_filtering;
+
mutex_unlock(&adap->dev->i2c_mutex);
return 0;
}
@@ -316,8 +326,8 @@ static int it913x_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
int ret;
u32 reg;
u8 pro;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+
+ mutex_lock(&d->i2c_mutex);
debug_data_snipet(1, "Message out", msg[0].buf);
deb_info(2, "num of messages %d address %02x", num, msg[0].addr);
@@ -358,8 +368,7 @@ static int it913x_rc_query(struct dvb_usb_device *d)
int ret;
u32 key;
/* Avoid conflict with frontends*/
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&d->i2c_mutex);
ret = it913x_io(d->udev, READ_LONG, PRO_LINK, CMD_IR_GET,
0, 0, &ibuf[0], sizeof(ibuf));
@@ -388,19 +397,12 @@ static int ite_firmware_select(struct usb_device *udev,
{
int sw;
/* auto switch */
- if (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_ITETECH_IT9135)
- sw = IT9135_V1_FW;
- else if (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_ITETECH_IT9135_9005)
+ if (le16_to_cpu(udev->descriptor.idVendor) == USB_VID_KWORLD_2)
+ sw = IT9137_FW;
+ else if (it913x_config.chip_ver == 1)
sw = IT9135_V1_FW;
- else if (le16_to_cpu(udev->descriptor.idProduct) ==
- USB_PID_ITETECH_IT9135_9006) {
+ else
sw = IT9135_V2_FW;
- if (it913x_config.tuner_id_0 == 0)
- it913x_config.tuner_id_0 = IT9135_60;
- } else
- sw = IT9137_FW;
/* force switch */
if (dvb_usb_it913x_firmware != IT9135_AUTO)
@@ -410,41 +412,103 @@ static int ite_firmware_select(struct usb_device *udev,
case IT9135_V1_FW:
it913x_config.firmware_ver = 1;
it913x_config.adc_x2 = 1;
+ it913x_config.read_slevel = false;
props->firmware = fw_it9135_v1;
break;
case IT9135_V2_FW:
it913x_config.firmware_ver = 1;
it913x_config.adc_x2 = 1;
+ it913x_config.read_slevel = false;
props->firmware = fw_it9135_v2;
+ switch (it913x_config.tuner_id_0) {
+ case IT9135_61:
+ case IT9135_62:
+ break;
+ default:
+ info("Unknown tuner ID applying default 0x60");
+ case IT9135_60:
+ it913x_config.tuner_id_0 = IT9135_60;
+ }
break;
case IT9137_FW:
default:
it913x_config.firmware_ver = 0;
it913x_config.adc_x2 = 0;
+ it913x_config.read_slevel = true;
props->firmware = fw_it9137;
}
return 0;
}
+static void it913x_select_remote(struct usb_device *udev,
+ struct dvb_usb_device_properties *props)
+{
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case USB_PID_ITETECH_IT9135_9005:
+ props->rc.core.rc_codes = RC_MAP_IT913X_V2;
+ return;
+ default:
+ props->rc.core.rc_codes = RC_MAP_IT913X_V1;
+ }
+ return;
+}
+
#define TS_MPEG_PKT_SIZE 188
#define EP_LOW 21
#define TS_BUFFER_SIZE_PID (EP_LOW*TS_MPEG_PKT_SIZE)
#define EP_HIGH 348
#define TS_BUFFER_SIZE_MAX (EP_HIGH*TS_MPEG_PKT_SIZE)
-static int it913x_identify_state(struct usb_device *udev,
- struct dvb_usb_device_properties *props,
- struct dvb_usb_device_description **desc,
- int *cold)
+static int it913x_select_config(struct usb_device *udev,
+ struct dvb_usb_device_properties *props)
{
- int ret = 0, firm_no;
- u8 reg, remote;
+ int ret = 0, reg;
+ bool proprietary_ir = false;
- firm_no = it913x_return_status(udev);
+ if (it913x_config.chip_ver == 0x02
+ && it913x_config.chip_type == 0x9135)
+ reg = it913x_read_reg(udev, 0x461d);
+ else
+ reg = it913x_read_reg(udev, 0x461b);
- /* checnk for dual mode */
- it913x_config.dual_mode = it913x_read_reg(udev, 0x49c5);
+ if (reg < 0)
+ return reg;
+
+ if (reg == 0) {
+ it913x_config.dual_mode = 0;
+ it913x_config.tuner_id_0 = IT9135_38;
+ proprietary_ir = true;
+ } else {
+ /* TS mode */
+ reg = it913x_read_reg(udev, 0x49c5);
+ if (reg < 0)
+ return reg;
+ it913x_config.dual_mode = reg;
+
+ /* IR mode type */
+ reg = it913x_read_reg(udev, 0x49ac);
+ if (reg < 0)
+ return reg;
+ if (reg == 5) {
+ info("Remote propriety (raw) mode");
+ proprietary_ir = true;
+ } else if (reg == 1) {
+ info("Remote HID mode NOT SUPPORTED");
+ proprietary_ir = false;
+ props->rc.core.rc_codes = NULL;
+ } else
+ props->rc.core.rc_codes = NULL;
+
+ /* Tuner_id */
+ reg = it913x_read_reg(udev, 0x49d0);
+ if (reg < 0)
+ return reg;
+ it913x_config.tuner_id_0 = reg;
+ }
+
+ if (proprietary_ir)
+ it913x_select_remote(udev, props);
if (udev->speed != USB_SPEED_HIGH) {
props->adapter[0].fe[0].pid_filter_count = 5;
@@ -459,17 +523,6 @@ static int it913x_identify_state(struct usb_device *udev,
if(props->adapter[0].fe[0].pid_filter_count == 5)
props->adapter[0].fe[0].pid_filter_count = 31;
- /* TODO different remotes */
- remote = it913x_read_reg(udev, 0x49ac); /* Remote */
- if (remote == 0)
- props->rc.core.rc_codes = NULL;
-
- /* TODO at the moment tuner_id is always assigned to 0x38 */
- it913x_config.tuner_id_0 = it913x_read_reg(udev, 0x49d0);
-
- info("Dual mode=%x Remote=%x Tuner Type=%x", it913x_config.dual_mode
- , remote, it913x_config.tuner_id_0);
-
/* Select Stream Buffer Size and pid filter option*/
if (pid_filter) {
props->adapter[0].fe[0].stream.u.bulk.buffersize =
@@ -490,8 +543,29 @@ static int it913x_identify_state(struct usb_device *udev,
} else
props->num_adapters = 1;
+ info("Dual mode=%x Tuner Type=%x", it913x_config.dual_mode,
+ it913x_config.tuner_id_0);
+
ret = ite_firmware_select(udev, props);
+ return ret;
+}
+
+static int it913x_identify_state(struct usb_device *udev,
+ struct dvb_usb_device_properties *props,
+ struct dvb_usb_device_description **desc,
+ int *cold)
+{
+ int ret = 0, firm_no;
+ u8 reg;
+
+ firm_no = it913x_return_status(udev);
+
+ /* Read and select config */
+ ret = it913x_select_config(udev, props);
+ if (ret < 0)
+ return ret;
+
if (firm_no > 0) {
*cold = 0;
return 0;
@@ -538,18 +612,22 @@ static int it913x_identify_state(struct usb_device *udev,
static int it913x_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
+ struct it913x_state *st = adap->dev->priv;
int ret = 0;
u8 pro = (adap->id == 0) ? DEV_0_DMOD : DEV_1_DMOD;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
deb_info(1, "STM (%02x)", onoff);
- if (!onoff)
+ if (!onoff) {
+ mutex_lock(&adap->dev->i2c_mutex);
+
ret = it913x_wr_reg(adap->dev->udev, pro, PID_RST, 0x1);
+ mutex_unlock(&adap->dev->i2c_mutex);
+ st->pid_filter_onoff =
+ adap->fe_adap[adap->active_fe].pid_filtering;
- mutex_unlock(&adap->dev->i2c_mutex);
+ }
return ret;
}
@@ -789,7 +867,7 @@ static struct dvb_usb_device_properties it913x_properties = {
.rc_query = it913x_rc_query,
.rc_interval = IT913X_POLL,
.allowed_protos = RC_TYPE_NEC,
- .rc_codes = RC_MAP_MSI_DIGIVOX_III,
+ .rc_codes = RC_MAP_IT913X_V1,
},
.i2c_algo = &it913x_i2c_algo,
.num_device_descs = 5,
@@ -823,5 +901,5 @@ module_usb_driver(it913x_driver);
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
MODULE_DESCRIPTION("it913x USB 2 Driver");
-MODULE_VERSION("1.22");
+MODULE_VERSION("1.27");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c
index 291f6b110399..5dde06d066ff 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.c
+++ b/drivers/media/dvb/dvb-usb/lmedm04.c
@@ -77,6 +77,7 @@
#include "stv0299.h"
#include "dvb-pll.h"
#include "z0194a.h"
+#include "m88rs2000.h"
@@ -104,7 +105,7 @@ MODULE_PARM_DESC(firmware, "set default firmware 0=Sharp7395 1=LG");
static int pid_filter;
module_param_named(pid, pid_filter, int, 0644);
-MODULE_PARM_DESC(pid, "set default 0=on 1=off");
+MODULE_PARM_DESC(pid, "set default 0=default 1=off 2=on");
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
@@ -113,6 +114,7 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
#define TUNER_LG 0x1
#define TUNER_S7395 0x2
#define TUNER_S0194 0x3
+#define TUNER_RS2000 0x4
struct lme2510_state {
u8 id;
@@ -121,6 +123,8 @@ struct lme2510_state {
u8 signal_level;
u8 signal_sn;
u8 time_key;
+ u8 last_key;
+ u8 key_timeout;
u8 i2c_talk_onoff;
u8 i2c_gate;
u8 i2c_tuner_gate_w;
@@ -128,6 +132,7 @@ struct lme2510_state {
u8 i2c_tuner_addr;
u8 stream_on;
u8 pid_size;
+ u8 pid_off;
void *buffer;
struct urb *lme_urb;
void *usb_buffer;
@@ -178,14 +183,8 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
/* the read/write capped at 64 */
memcpy(buff, wbuf, (wlen < 64) ? wlen : 64);
- ret |= usb_clear_halt(d->udev, usb_sndbulkpipe(d->udev, 0x01));
-
ret |= lme2510_bulk_write(d->udev, buff, wlen , 0x01);
- msleep(10);
-
- ret |= usb_clear_halt(d->udev, usb_rcvbulkpipe(d->udev, 0x01));
-
ret |= lme2510_bulk_read(d->udev, buff, (rlen < 64) ?
rlen : 64 , 0x01);
@@ -199,9 +198,14 @@ static int lme2510_usb_talk(struct dvb_usb_device *d,
static int lme2510_stream_restart(struct dvb_usb_device *d)
{
- static u8 stream_on[] = LME_ST_ON_W;
+ struct lme2510_state *st = d->priv;
+ u8 all_pids[] = LME_ALL_PIDS;
+ u8 stream_on[] = LME_ST_ON_W;
int ret;
- u8 rbuff[10];
+ u8 rbuff[1];
+ if (st->pid_off)
+ ret = lme2510_usb_talk(d, all_pids, sizeof(all_pids),
+ rbuff, sizeof(rbuff));
/*Restart Stream Command*/
ret = lme2510_usb_talk(d, stream_on, sizeof(stream_on),
rbuff, sizeof(rbuff));
@@ -308,6 +312,14 @@ static void lme2510_int_response(struct urb *lme_urb)
((ibuf[2] & 0x01) << 0x03);
}
break;
+ case TUNER_RS2000:
+ if (ibuf[2] > 0)
+ st->signal_lock = 0xff;
+ else
+ st->signal_lock = 0xf0;
+ st->signal_level = ibuf[4];
+ st->signal_sn = ibuf[5];
+ st->time_key = ibuf[7];
default:
break;
}
@@ -359,19 +371,20 @@ static int lme2510_int_read(struct dvb_usb_adapter *adap)
static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct lme2510_state *st = adap->dev->priv;
- static u8 clear_pid_reg[] = LME_CLEAR_PID;
+ static u8 clear_pid_reg[] = LME_ALL_PIDS;
static u8 rbuf[1];
int ret;
deb_info(1, "PID Clearing Filter");
- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex);
- if (ret < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
- if (!onoff)
+ if (!onoff) {
ret |= lme2510_usb_talk(adap->dev, clear_pid_reg,
sizeof(clear_pid_reg), rbuf, sizeof(rbuf));
+ st->pid_off = true;
+ } else
+ st->pid_off = false;
st->pid_size = 0;
@@ -389,11 +402,9 @@ static int lme2510_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid,
pid, index, onoff);
if (onoff) {
- ret = mutex_lock_interruptible(&adap->dev->i2c_mutex);
- if (ret < 0)
- return -EAGAIN;
- ret |= lme2510_enable_pid(adap->dev, index, pid);
- mutex_unlock(&adap->dev->i2c_mutex);
+ mutex_lock(&adap->dev->i2c_mutex);
+ ret |= lme2510_enable_pid(adap->dev, index, pid);
+ mutex_unlock(&adap->dev->i2c_mutex);
}
@@ -425,9 +436,6 @@ static int lme2510_msg(struct dvb_usb_device *d,
int ret = 0;
struct lme2510_state *st = d->priv;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
-
if (st->i2c_talk_onoff == 1) {
ret = lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
@@ -456,8 +464,6 @@ static int lme2510_msg(struct dvb_usb_device *d,
st->i2c_talk_onoff = 0;
}
}
- if ((wbuf[3] != 0x6) & (wbuf[3] != 0x5))
- msleep(5);
}
break;
case TUNER_S0194:
@@ -472,10 +478,12 @@ static int lme2510_msg(struct dvb_usb_device *d,
}
}
break;
+ case TUNER_RS2000:
default:
break;
}
} else {
+ /* TODO rewrite this section */
switch (st->tuner_config) {
case TUNER_LG:
switch (wbuf[3]) {
@@ -559,6 +567,24 @@ static int lme2510_msg(struct dvb_usb_device *d,
break;
}
break;
+ case TUNER_RS2000:
+ switch (wbuf[3]) {
+ case 0x8c:
+ rbuf[0] = 0x55;
+ rbuf[1] = 0xff;
+ if (st->last_key == st->time_key) {
+ st->key_timeout++;
+ if (st->key_timeout > 5)
+ rbuf[1] = 0;
+ } else
+ st->key_timeout = 0;
+ st->last_key = st->time_key;
+ break;
+ default:
+ lme2510_usb_talk(d, wbuf, wlen, rbuf, rlen);
+ st->i2c_talk_onoff = 1;
+ break;
+ }
default:
break;
}
@@ -568,8 +594,6 @@ static int lme2510_msg(struct dvb_usb_device *d,
}
- mutex_unlock(&d->i2c_mutex);
-
return ret;
}
@@ -584,6 +608,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
u16 len;
u8 gate = st->i2c_gate;
+ mutex_lock(&d->i2c_mutex);
+
if (gate == 0)
gate = 5;
@@ -622,6 +648,7 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
if (lme2510_msg(d, obuf, len, ibuf, 64) < 0) {
deb_info(1, "i2c transfer failed.");
+ mutex_unlock(&d->i2c_mutex);
return -EAGAIN;
}
@@ -634,6 +661,8 @@ static int lme2510_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
}
}
}
+
+ mutex_unlock(&d->i2c_mutex);
return i;
}
@@ -653,7 +682,7 @@ static int lme2510_identify_state(struct usb_device *udev,
struct dvb_usb_device_description **desc,
int *cold)
{
- if (pid_filter > 0)
+ if (pid_filter != 2)
props->adapter[0].fe[0].caps &=
~DVB_USB_ADAP_NEED_PID_FILTERING;
*cold = 0;
@@ -663,7 +692,7 @@ static int lme2510_identify_state(struct usb_device *udev,
static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
{
struct lme2510_state *st = adap->dev->priv;
- static u8 clear_reg_3[] = LME_CLEAR_PID;
+ static u8 clear_reg_3[] = LME_ALL_PIDS;
static u8 rbuf[1];
int ret = 0, rlen = sizeof(rbuf);
@@ -675,8 +704,7 @@ static int lme2510_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
else {
deb_info(1, "STM Steam Off");
/* mutex is here only to avoid collision with I2C */
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
ret = lme2510_usb_talk(adap->dev, clear_reg_3,
sizeof(clear_reg_3), rbuf, rlen);
@@ -781,16 +809,18 @@ static int lme_firmware_switch(struct usb_device *udev, int cold)
const char fw_c_s7395[] = "dvb-usb-lme2510c-s7395.fw";
const char fw_c_lg[] = "dvb-usb-lme2510c-lg.fw";
const char fw_c_s0194[] = "dvb-usb-lme2510c-s0194.fw";
+ const char fw_c_rs2000[] = "dvb-usb-lme2510c-rs2000.fw";
const char fw_lg[] = "dvb-usb-lme2510-lg.fw";
const char fw_s0194[] = "dvb-usb-lme2510-s0194.fw";
const char *fw_lme;
- int ret, cold_fw;
+ int ret = 0, cold_fw;
cold = (cold > 0) ? (cold & 1) : 0;
cold_fw = !cold;
- if (le16_to_cpu(udev->descriptor.idProduct) == 0x1122) {
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case 0x1122:
switch (dvb_usb_lme2510_firmware) {
default:
dvb_usb_lme2510_firmware = TUNER_S0194;
@@ -813,7 +843,8 @@ static int lme_firmware_switch(struct usb_device *udev, int cold)
cold_fw = 0;
break;
}
- } else {
+ break;
+ case 0x1120:
switch (dvb_usb_lme2510_firmware) {
default:
dvb_usb_lme2510_firmware = TUNER_S7395;
@@ -842,8 +873,17 @@ static int lme_firmware_switch(struct usb_device *udev, int cold)
cold_fw = 0;
break;
}
+ break;
+ case 0x22f0:
+ fw_lme = fw_c_rs2000;
+ ret = request_firmware(&fw, fw_lme, &udev->dev);
+ dvb_usb_lme2510_firmware = TUNER_RS2000;
+ break;
+ default:
+ fw_lme = fw_c_s7395;
}
+
if (cold_fw) {
info("FRM Loading %s file", fw_lme);
ret = lme2510_download_firmware(udev, fw);
@@ -906,6 +946,29 @@ static struct stv0299_config sharp_z0194_config = {
.set_symbol_rate = sharp_z0194a_set_symbol_rate,
};
+static int dm04_rs2000_set_ts_param(struct dvb_frontend *fe,
+ int caller)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct dvb_usb_device *d = adap->dev;
+ struct lme2510_state *st = d->priv;
+
+ mutex_lock(&d->i2c_mutex);
+ if ((st->i2c_talk_onoff == 1) && (st->stream_on & 1)) {
+ st->i2c_talk_onoff = 0;
+ lme2510_stream_restart(d);
+ }
+ mutex_unlock(&d->i2c_mutex);
+
+ return 0;
+}
+
+static struct m88rs2000_config m88rs2000_config = {
+ .demod_addr = 0xd0,
+ .tuner_addr = 0xc0,
+ .set_ts_params = dm04_rs2000_set_ts_param,
+};
+
static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
fe_sec_voltage_t voltage)
{
@@ -915,8 +978,7 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
static u8 rbuf[1];
int ret = 0, len = 3, rlen = 1;
- if (mutex_lock_interruptible(&adap->dev->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&adap->dev->i2c_mutex);
switch (voltage) {
case SEC_VOLTAGE_18:
@@ -937,12 +999,31 @@ static int dm04_lme2510_set_voltage(struct dvb_frontend *fe,
return (ret < 0) ? -ENODEV : 0;
}
+static int dm04_rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct lme2510_state *st = adap->dev->priv;
+
+ *strength = (u16)((u32)st->signal_level * 0xffff / 0x7f);
+ return 0;
+}
+
+static int dm04_rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ struct dvb_usb_adapter *adap = fe->dvb->priv;
+ struct lme2510_state *st = adap->dev->priv;
+
+ *snr = (u16)((u32)st->signal_sn * 0xffff / 0xff);
+ return 0;
+}
+
static int lme_name(struct dvb_usb_adapter *adap)
{
struct lme2510_state *st = adap->dev->priv;
const char *desc = adap->dev->desc->name;
char *fe_name[] = {"", " LG TDQY-P001F", " SHARP:BS2F7HZ7395",
- " SHARP:BS2F7HZ0194"};
+ " SHARP:BS2F7HZ0194", " RS2000"};
char *name = adap->fe_adap[0].fe->ops.info.name;
strlcpy(name, desc, 128);
@@ -958,60 +1039,82 @@ static int dm04_lme2510_frontend_attach(struct dvb_usb_adapter *adap)
int ret = 0;
st->i2c_talk_onoff = 1;
+ switch (le16_to_cpu(adap->dev->udev->descriptor.idProduct)) {
+ case 0x1122:
+ case 0x1120:
+ st->i2c_gate = 4;
+ adap->fe_adap[0].fe = dvb_attach(tda10086_attach,
+ &tda10086_config, &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe) {
+ info("TUN Found Frontend TDA10086");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 4;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_LG;
+ if (dvb_usb_lme2510_firmware != TUNER_LG) {
+ dvb_usb_lme2510_firmware = TUNER_LG;
+ ret = lme_firmware_switch(adap->dev->udev, 1);
+ }
+ break;
+ }
- st->i2c_gate = 4;
- adap->fe_adap[0].fe = dvb_attach(tda10086_attach, &tda10086_config,
- &adap->dev->i2c_adap);
-
- if (adap->fe_adap[0].fe) {
- info("TUN Found Frontend TDA10086");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 4;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_LG;
- if (dvb_usb_lme2510_firmware != TUNER_LG) {
- dvb_usb_lme2510_firmware = TUNER_LG;
- ret = lme_firmware_switch(adap->dev->udev, 1);
+ st->i2c_gate = 4;
+ adap->fe_adap[0].fe = dvb_attach(stv0299_attach,
+ &sharp_z0194_config, &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe) {
+ info("FE Found Stv0299");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_S0194;
+ if (dvb_usb_lme2510_firmware != TUNER_S0194) {
+ dvb_usb_lme2510_firmware = TUNER_S0194;
+ ret = lme_firmware_switch(adap->dev->udev, 1);
+ }
+ break;
}
- goto end;
- }
- st->i2c_gate = 4;
- adap->fe_adap[0].fe = dvb_attach(stv0299_attach, &sharp_z0194_config,
+ st->i2c_gate = 5;
+ adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
&adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe) {
- info("FE Found Stv0299");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_S0194;
- if (dvb_usb_lme2510_firmware != TUNER_S0194) {
- dvb_usb_lme2510_firmware = TUNER_S0194;
- ret = lme_firmware_switch(adap->dev->udev, 1);
+
+ if (adap->fe_adap[0].fe) {
+ info("FE Found Stv0288");
+ st->i2c_tuner_gate_w = 4;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_S7395;
+ if (dvb_usb_lme2510_firmware != TUNER_S7395) {
+ dvb_usb_lme2510_firmware = TUNER_S7395;
+ ret = lme_firmware_switch(adap->dev->udev, 1);
+ }
+ break;
}
- goto end;
- }
+ case 0x22f0:
+ st->i2c_gate = 5;
+ adap->fe_adap[0].fe = dvb_attach(m88rs2000_attach,
+ &m88rs2000_config, &adap->dev->i2c_adap);
- st->i2c_gate = 5;
- adap->fe_adap[0].fe = dvb_attach(stv0288_attach, &lme_config,
- &adap->dev->i2c_adap);
- if (adap->fe_adap[0].fe) {
- info("FE Found Stv0288");
- st->i2c_tuner_gate_w = 4;
- st->i2c_tuner_gate_r = 5;
- st->i2c_tuner_addr = 0xc0;
- st->tuner_config = TUNER_S7395;
- if (dvb_usb_lme2510_firmware != TUNER_S7395) {
- dvb_usb_lme2510_firmware = TUNER_S7395;
- ret = lme_firmware_switch(adap->dev->udev, 1);
+ if (adap->fe_adap[0].fe) {
+ info("FE Found M88RS2000");
+ st->i2c_tuner_gate_w = 5;
+ st->i2c_tuner_gate_r = 5;
+ st->i2c_tuner_addr = 0xc0;
+ st->tuner_config = TUNER_RS2000;
+ adap->fe_adap[0].fe->ops.read_signal_strength =
+ dm04_rs2000_read_signal_strength;
+ adap->fe_adap[0].fe->ops.read_snr =
+ dm04_rs2000_read_snr;
}
- } else {
- info("DM04 Not Supported");
- return -ENODEV;
+ break;
}
+ if (adap->fe_adap[0].fe == NULL) {
+ info("DM04/QQBOX Not Powered up or not Supported");
+ return -ENODEV;
+ }
-end: if (ret) {
+ if (ret) {
if (adap->fe_adap[0].fe) {
dvb_frontend_detach(adap->fe_adap[0].fe);
adap->fe_adap[0].fe = NULL;
@@ -1028,7 +1131,7 @@ end: if (ret) {
static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
{
struct lme2510_state *st = adap->dev->priv;
- char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA"};
+ char *tun_msg[] = {"", "TDA8263", "IX2505V", "DVB_PLL_OPERA", "RS2000"};
int ret = 0;
switch (st->tuner_config) {
@@ -1047,6 +1150,9 @@ static int dm04_lme2510_tuner(struct dvb_usb_adapter *adap)
&adap->dev->i2c_adap, DVB_PLL_OPERA1))
ret = st->tuner_config;
break;
+ case TUNER_RS2000:
+ ret = st->tuner_config;
+ break;
default:
break;
}
@@ -1075,10 +1181,9 @@ static int lme2510_powerup(struct dvb_usb_device *d, int onoff)
static u8 lnb_on[] = LNB_ON;
static u8 lnb_off[] = LNB_OFF;
static u8 rbuf[1];
- int ret, len = 3, rlen = 1;
+ int ret = 0, len = 3, rlen = 1;
- if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
- return -EAGAIN;
+ mutex_lock(&d->i2c_mutex);
if (onoff)
ret = lme2510_usb_talk(d, lnb_on, len, rbuf, rlen);
@@ -1136,6 +1241,7 @@ static int lme2510_probe(struct usb_interface *intf,
static struct usb_device_id lme2510_table[] = {
{ USB_DEVICE(0x3344, 0x1122) }, /* LME2510 */
{ USB_DEVICE(0x3344, 0x1120) }, /* LME2510C */
+ { USB_DEVICE(0x3344, 0x22f0) }, /* LME2510C RS2000 */
{} /* Terminating entry */
};
@@ -1153,7 +1259,7 @@ static struct dvb_usb_device_properties lme2510_properties = {
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.streaming_ctrl = lme2510_streaming_ctrl,
- .pid_filter_count = 15,
+ .pid_filter_count = 32,
.pid_filter = lme2510_pid_filter,
.pid_filter_ctrl = lme2510_pid_filter_ctrl,
.frontend_attach = dm04_lme2510_frontend_attach,
@@ -1204,7 +1310,7 @@ static struct dvb_usb_device_properties lme2510c_properties = {
DVB_USB_ADAP_NEED_PID_FILTERING|
DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF,
.streaming_ctrl = lme2510_streaming_ctrl,
- .pid_filter_count = 15,
+ .pid_filter_count = 32,
.pid_filter = lme2510_pid_filter,
.pid_filter_ctrl = lme2510_pid_filter_ctrl,
.frontend_attach = dm04_lme2510_frontend_attach,
@@ -1234,11 +1340,14 @@ static struct dvb_usb_device_properties lme2510c_properties = {
.identify_state = lme2510_identify_state,
.i2c_algo = &lme2510_i2c_algo,
.generic_bulk_ctrl_endpoint = 0,
- .num_device_descs = 1,
+ .num_device_descs = 2,
.devices = {
{ "DM04_LME2510C_DVB-S",
{ &lme2510_table[1], NULL },
},
+ { "DM04_LME2510C_DVB-S RS2000",
+ { &lme2510_table[2], NULL },
+ },
}
};
@@ -1295,5 +1404,5 @@ module_usb_driver(lme2510_driver);
MODULE_AUTHOR("Malcolm Priestley <tvboxspy@gmail.com>");
MODULE_DESCRIPTION("LME2510(C) DVB-S USB2.0");
-MODULE_VERSION("1.91");
+MODULE_VERSION("1.99");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/lmedm04.h b/drivers/media/dvb/dvb-usb/lmedm04.h
index ab21e2ef53fa..e9c207205c2f 100644
--- a/drivers/media/dvb/dvb-usb/lmedm04.h
+++ b/drivers/media/dvb/dvb-usb/lmedm04.h
@@ -41,6 +41,7 @@
#define LME_ST_ON_W {0x06, 0x00}
#define LME_CLEAR_PID {0x03, 0x02, 0x20, 0xa0}
#define LME_ZERO_PID {0x03, 0x06, 0x00, 0x00, 0x01, 0x00, 0x20, 0x9c}
+#define LME_ALL_PIDS {0x03, 0x06, 0x00, 0xff, 0x01, 0x1f, 0x20, 0x81}
/* LNB Voltage
* 07 XX XX
diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c
index 38ef0253d3b5..81305de2fea5 100644
--- a/drivers/media/dvb/dvb-usb/mxl111sf.c
+++ b/drivers/media/dvb/dvb-usb/mxl111sf.c
@@ -351,15 +351,13 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff)
adap_state->ep6_clockphase,
0, 0);
mxl_fail(ret);
+#if 0
} else {
ret = mxl111sf_disable_656_port(state);
mxl_fail(ret);
+#endif
}
- mxl111sf_read_reg(state, 0x12, &tmp);
- tmp &= ~0x04;
- mxl111sf_write_reg(state, 0x12, tmp);
-
return ret;
}
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c
new file mode 100644
index 000000000000..8f4736a10fc8
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c
@@ -0,0 +1,982 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtl28xxu.h"
+
+#include "rtl2830.h"
+
+#include "qt1010.h"
+#include "mt2060.h"
+#include "mxl5005s.h"
+
+/* debug */
+static int dvb_usb_rtl28xxu_debug;
+module_param_named(debug, dvb_usb_rtl28xxu_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int rtl28xxu_ctrl_msg(struct dvb_usb_device *d, struct rtl28xxu_req *req)
+{
+ int ret;
+ unsigned int pipe;
+ u8 requesttype;
+ u8 *buf;
+
+ buf = kmalloc(req->size, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ if (req->index & CMD_WR_FLAG) {
+ /* write */
+ memcpy(buf, req->data, req->size);
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
+ pipe = usb_sndctrlpipe(d->udev, 0);
+ } else {
+ /* read */
+ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
+ pipe = usb_rcvctrlpipe(d->udev, 0);
+ }
+
+ ret = usb_control_msg(d->udev, pipe, 0, requesttype, req->value,
+ req->index, buf, req->size, 1000);
+ if (ret > 0)
+ ret = 0;
+
+ deb_dump(0, requesttype, req->value, req->index, buf, req->size,
+ deb_xfer);
+
+ /* read request, copy returned data to return buf */
+ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
+ memcpy(req->data, buf, req->size);
+
+ kfree(buf);
+
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831_wr_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_WR;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_WR;
+ else
+ req.index = CMD_IR_WR;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_rd_regs(struct dvb_usb_device *d, u16 reg, u8 *val, int len)
+{
+ struct rtl28xxu_req req;
+
+ if (reg < 0x3000)
+ req.index = CMD_USB_RD;
+ else if (reg < 0x4000)
+ req.index = CMD_SYS_RD;
+ else
+ req.index = CMD_IR_RD;
+
+ req.value = reg;
+ req.size = len;
+ req.data = val;
+
+ return rtl28xxu_ctrl_msg(d, &req);
+}
+
+static int rtl2831_wr_reg(struct dvb_usb_device *d, u16 reg, u8 val)
+{
+ return rtl2831_wr_regs(d, reg, &val, 1);
+}
+
+static int rtl2831_rd_reg(struct dvb_usb_device *d, u16 reg, u8 *val)
+{
+ return rtl2831_rd_regs(d, reg, val, 1);
+}
+
+/* I2C */
+static int rtl28xxu_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
+ int num)
+{
+ int ret;
+ struct dvb_usb_device *d = i2c_get_adapdata(adap);
+ struct rtl28xxu_priv *priv = d->priv;
+ struct rtl28xxu_req req;
+
+ /*
+ * It is not known which are real I2C bus xfer limits, but testing
+ * with RTL2831U + MT2060 gives max RD 24 and max WR 22 bytes.
+ * TODO: find out RTL2832U lens
+ */
+
+ /*
+ * I2C adapter logic looks rather complicated due to fact it handles
+ * three different access methods. Those methods are;
+ * 1) integrated demod access
+ * 2) old I2C access
+ * 3) new I2C access
+ *
+ * Used method is selected in order 1, 2, 3. Method 3 can handle all
+ * requests but there is two reasons why not use it always;
+ * 1) It is most expensive, usually two USB messages are needed
+ * 2) At least RTL2831U does not support it
+ *
+ * Method 3 is needed in case of I2C write+read (typical register read)
+ * where write is more than one byte.
+ */
+
+ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
+ return -EAGAIN;
+
+ if (num == 2 && !(msg[0].flags & I2C_M_RD) &&
+ (msg[1].flags & I2C_M_RD)) {
+ if (msg[0].len > 24 || msg[1].len > 24) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_DEMOD_RD | priv->page;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else if (msg[0].len < 2) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_RD;
+ req.size = msg[1].len;
+ req.data = &msg[1].buf[0];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ if (ret)
+ goto err_mutex_unlock;
+
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_RD;
+ req.size = msg[1].len;
+ req.data = msg[1].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) {
+ if (msg[0].len > 22) {
+ /* TODO: check msg[0].len max */
+ ret = -EOPNOTSUPP;
+ goto err_mutex_unlock;
+ } else if (msg[0].addr == 0x10) {
+ /* method 1 - integrated demod */
+ if (msg[0].buf[0] == 0x00) {
+ /* save demod page for later demod access */
+ priv->page = msg[0].buf[1];
+ ret = 0;
+ } else {
+ req.value = (msg[0].buf[0] << 8) |
+ (msg[0].addr << 1);
+ req.index = CMD_DEMOD_WR | priv->page;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else if (msg[0].len < 23) {
+ /* method 2 - old I2C */
+ req.value = (msg[0].buf[0] << 8) | (msg[0].addr << 1);
+ req.index = CMD_I2C_WR;
+ req.size = msg[0].len-1;
+ req.data = &msg[0].buf[1];
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ } else {
+ /* method 3 - new I2C */
+ req.value = (msg[0].addr << 1);
+ req.index = CMD_I2C_DA_WR;
+ req.size = msg[0].len;
+ req.data = msg[0].buf;
+ ret = rtl28xxu_ctrl_msg(d, &req);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+
+err_mutex_unlock:
+ mutex_unlock(&d->i2c_mutex);
+
+ return ret ? ret : num;
+}
+
+static u32 rtl28xxu_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm rtl28xxu_i2c_algo = {
+ .master_xfer = rtl28xxu_i2c_xfer,
+ .functionality = rtl28xxu_i2c_func,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mt2060_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .if_dvbt = 36150000,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_qt1010_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 1,
+ .if_dvbt = 36125000,
+ .vtop = 0x20,
+ .krf = 0x04,
+ .agc_targ_val = 0x2d,
+};
+
+static struct rtl2830_config rtl28xxu_rtl2830_mxl5005s_config = {
+ .i2c_addr = 0x10, /* 0x20 */
+ .xtal = 28800000,
+ .ts_mode = 0,
+ .spec_inv = 0,
+ .if_dvbt = 4570000,
+ .vtop = 0x3f,
+ .krf = 0x04,
+ .agc_targ_val = 0x3e,
+};
+
+static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ u8 buf[1];
+ struct rtl2830_config *rtl2830_config;
+ /* open RTL2831U/RTL2830 I2C gate */
+ struct rtl28xxu_req req_gate = { 0x0120, 0x0011, 0x0001, "\x08" };
+ /* for MT2060 tuner probe */
+ struct rtl28xxu_req req_mt2060 = { 0x00c0, CMD_I2C_RD, 1, buf };
+ /* for QT1010 tuner probe */
+ struct rtl28xxu_req req_qt1010 = { 0x0fc4, CMD_I2C_RD, 1, buf };
+
+ deb_info("%s:\n", __func__);
+
+ /*
+ * RTL2831U GPIOs
+ * =========================================================
+ * GPIO0 | tuner#0 | 0 off | 1 on | MXL5005S (?)
+ * GPIO2 | LED | 0 off | 1 on |
+ * GPIO4 | tuner#1 | 0 on | 1 off | MT2060
+ */
+
+ /* GPIO direction */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
+ if (ret)
+ goto err;
+
+ /* check QT1010 ID(?) register; reg=0f val=2c */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_qt1010);
+ if (ret == 0 && buf[0] == 0x2c) {
+ priv->tuner = TUNER_RTL2830_QT1010;
+ rtl2830_config = &rtl28xxu_rtl2830_qt1010_config;
+ deb_info("%s: QT1010\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: QT1010 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate);
+ if (ret)
+ goto err;
+
+ /* check MT2060 ID register; reg=00 val=63 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_mt2060);
+ if (ret == 0 && buf[0] == 0x63) {
+ priv->tuner = TUNER_RTL2830_MT2060;
+ rtl2830_config = &rtl28xxu_rtl2830_mt2060_config;
+ deb_info("%s: MT2060\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: MT2060 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* assume MXL5005S */
+ ret = 0;
+ priv->tuner = TUNER_RTL2830_MXL5005S;
+ rtl2830_config = &rtl28xxu_rtl2830_mxl5005s_config;
+ deb_info("%s: MXL5005S\n", __func__);
+ goto found;
+
+found:
+ /* attach demodulator */
+ adap->fe_adap[0].fe = dvb_attach(rtl2830_attach, rtl2830_config,
+ &adap->dev->i2c_adap);
+ if (adap->fe_adap[0].fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_frontend_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ u8 buf[1];
+ /* open RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_open = {0x0120, 0x0011, 0x0001, "\x18"};
+ /* close RTL2832U/RTL2832 I2C gate */
+ struct rtl28xxu_req req_gate_close = {0x0120, 0x0011, 0x0001, "\x10"};
+ /* for FC2580 tuner probe */
+ struct rtl28xxu_req req_fc2580 = {0x01ac, CMD_I2C_RD, 1, buf};
+
+ deb_info("%s:\n", __func__);
+
+ /* GPIO direction */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_DIR, 0x0a);
+ if (ret)
+ goto err;
+
+ /* enable as output GPIO0, GPIO2, GPIO4 */
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_EN, 0x15);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_reg(adap->dev, SYS_DEMOD_CTL, 0xe8);
+ if (ret)
+ goto err;
+
+ /*
+ * Probe used tuner. We need to know used tuner before demod attach
+ * since there is some demod params needed to set according to tuner.
+ */
+
+ /* open demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_open);
+ if (ret)
+ goto err;
+
+ /* check FC2580 ID register; reg=01 val=56 */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_fc2580);
+ if (ret == 0 && buf[0] == 0x56) {
+ priv->tuner = TUNER_RTL2832_FC2580;
+ deb_info("%s: FC2580\n", __func__);
+ goto found;
+ } else {
+ deb_info("%s: FC2580 probe failed=%d - %02x\n",
+ __func__, ret, buf[0]);
+ }
+
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
+ if (ret)
+ goto err;
+
+ /* tuner not found */
+ ret = -ENODEV;
+ goto err;
+
+found:
+ /* close demod I2C gate */
+ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate_close);
+ if (ret)
+ goto err;
+
+ /* attach demodulator */
+ /* TODO: */
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct qt1010_config rtl28xxu_qt1010_config = {
+ .i2c_address = 0x62, /* 0xc4 */
+};
+
+static struct mt2060_config rtl28xxu_mt2060_config = {
+ .i2c_address = 0x60, /* 0xc0 */
+ .clock_out = 0,
+};
+
+static struct mxl5005s_config rtl28xxu_mxl5005s_config = {
+ .i2c_address = 0x63, /* 0xc6 */
+ .if_freq = IF_FREQ_4570000HZ,
+ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
+ .agc_mode = MXL_SINGLE_AGC,
+ .tracking_filter = MXL_TF_C_H,
+ .rssi_enable = MXL_RSSI_ENABLE,
+ .cap_select = MXL_CAP_SEL_ENABLE,
+ .div_out = MXL_DIV_OUT_4,
+ .clock_out = MXL_CLOCK_OUT_DISABLE,
+ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
+ .top = MXL5005S_TOP_25P2,
+ .mod_mode = MXL_DIGITAL_MODE,
+ .if_mode = MXL_ZERO_IF,
+ .AgcMasterByte = 0x00,
+};
+
+static int rtl2831u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ struct i2c_adapter *rtl2830_tuner_i2c;
+ struct dvb_frontend *fe;
+
+ deb_info("%s:\n", __func__);
+
+ /* use rtl2830 driver I2C adapter, for more info see rtl2830 driver */
+ rtl2830_tuner_i2c = rtl2830_get_tuner_i2c_adapter(adap->fe_adap[0].fe);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2830_QT1010:
+ fe = dvb_attach(qt1010_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_qt1010_config);
+ break;
+ case TUNER_RTL2830_MT2060:
+ fe = dvb_attach(mt2060_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_mt2060_config,
+ 1220);
+ break;
+ case TUNER_RTL2830_MXL5005S:
+ fe = dvb_attach(mxl5005s_attach, adap->fe_adap[0].fe,
+ rtl2830_tuner_i2c, &rtl28xxu_mxl5005s_config);
+ break;
+ default:
+ fe = NULL;
+ err("unknown tuner=%d", priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
+{
+ int ret;
+ struct rtl28xxu_priv *priv = adap->dev->priv;
+ struct dvb_frontend *fe;
+
+ deb_info("%s:\n", __func__);
+
+ switch (priv->tuner) {
+ case TUNER_RTL2832_FC2580:
+ /* TODO: */
+ fe = NULL;
+ break;
+ default:
+ fe = NULL;
+ err("unknown tuner=%d", priv->tuner);
+ }
+
+ if (fe == NULL) {
+ ret = -ENODEV;
+ goto err;
+ }
+
+ return 0;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_streaming_ctrl(struct dvb_usb_adapter *adap , int onoff)
+{
+ int ret;
+ u8 buf[2], gpio;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ ret = rtl2831_rd_reg(adap->dev, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ if (onoff) {
+ buf[0] = 0x00;
+ buf[1] = 0x00;
+ gpio |= 0x04; /* LED on */
+ } else {
+ buf[0] = 0x10; /* stall EPA */
+ buf[1] = 0x02; /* reset EPA */
+ gpio &= (~0x04); /* LED off */
+ }
+
+ ret = rtl2831_wr_reg(adap->dev, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(adap->dev, USB_EPA_CTL, buf, 2);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl28xxu_power_ctrl(struct dvb_usb_device *d, int onoff)
+{
+ int ret;
+ u8 gpio, sys0;
+
+ deb_info("%s: onoff=%d\n", __func__, onoff);
+
+ /* demod adc */
+ ret = rtl2831_rd_reg(d, SYS_SYS0, &sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, read GPIOs */
+ ret = rtl2831_rd_reg(d, SYS_GPIO_OUT_VAL, &gpio);
+ if (ret)
+ goto err;
+
+ deb_info("%s: RD SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
+
+ if (onoff) {
+ gpio |= 0x01; /* GPIO0 = 1 */
+ gpio &= (~0x10); /* GPIO4 = 0 */
+ sys0 = sys0 & 0x0f;
+ sys0 |= 0xe0;
+ } else {
+ gpio &= (~0x01); /* GPIO0 = 0 */
+ gpio |= 0x10; /* GPIO4 = 1 */
+ sys0 = sys0 & (~0xc0);
+ }
+
+ deb_info("%s: WR SYS0=%02x GPIO_OUT_VAL=%02x\n", __func__, sys0, gpio);
+
+ /* demod adc */
+ ret = rtl2831_wr_reg(d, SYS_SYS0, sys0);
+ if (ret)
+ goto err;
+
+ /* tuner power, write GPIOs */
+ ret = rtl2831_wr_reg(d, SYS_GPIO_OUT_VAL, gpio);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2831u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[5];
+ u32 rc_code;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { 0x3033, 0x80 },
+ { 0x3020, 0x43 },
+ { 0x3021, 0x16 },
+ { 0x3022, 0x16 },
+ { 0x3023, 0x5a },
+ { 0x3024, 0x2d },
+ { 0x3025, 0x16 },
+ { 0x3026, 0x01 },
+ { 0x3028, 0xb0 },
+ { 0x3029, 0x04 },
+ { 0x302c, 0x88 },
+ { 0x302e, 0x13 },
+ { 0x3030, 0xdf },
+ { 0x3031, 0x05 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_regs(d, SYS_IRRC_RP, buf, 5);
+ if (ret)
+ goto err;
+
+ if (buf[4] & 0x01) {
+ if (buf[2] == (u8) ~buf[3]) {
+ if (buf[0] == (u8) ~buf[1]) {
+ /* NEC standard (16 bit) */
+ rc_code = buf[0] << 8 | buf[2];
+ } else {
+ /* NEC extended (24 bit) */
+ rc_code = buf[0] << 16 |
+ buf[1] << 8 | buf[2];
+ }
+ } else {
+ /* NEC full (32 bit) */
+ rc_code = buf[0] << 24 | buf[1] << 16 |
+ buf[2] << 8 | buf[3];
+ }
+
+ rc_keydown(d->rc_dev, rc_code, 0);
+
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+
+ /* repeated intentionally to avoid extra keypress */
+ ret = rtl2831_wr_reg(d, SYS_IRRC_SR, 1);
+ if (ret)
+ goto err;
+ }
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static int rtl2832u_rc_query(struct dvb_usb_device *d)
+{
+ int ret, i;
+ struct rtl28xxu_priv *priv = d->priv;
+ u8 buf[128];
+ int len;
+ struct rtl28xxu_reg_val rc_nec_tab[] = {
+ { IR_RX_CTRL, 0x20 },
+ { IR_RX_BUF_CTRL, 0x80 },
+ { IR_RX_IF, 0xff },
+ { IR_RX_IE, 0xff },
+ { IR_MAX_DURATION0, 0xd0 },
+ { IR_MAX_DURATION1, 0x07 },
+ { IR_IDLE_LEN0, 0xc0 },
+ { IR_IDLE_LEN1, 0x00 },
+ { IR_GLITCH_LEN, 0x03 },
+ { IR_RX_CLK, 0x09 },
+ { IR_RX_CFG, 0x1c },
+ { IR_MAX_H_TOL_LEN, 0x1e },
+ { IR_MAX_L_TOL_LEN, 0x1e },
+ { IR_RX_CTRL, 0x80 },
+ };
+
+ /* init remote controller */
+ if (!priv->rc_active) {
+ for (i = 0; i < ARRAY_SIZE(rc_nec_tab); i++) {
+ ret = rtl2831_wr_reg(d, rc_nec_tab[i].reg,
+ rc_nec_tab[i].val);
+ if (ret)
+ goto err;
+ }
+ priv->rc_active = true;
+ }
+
+ ret = rtl2831_rd_reg(d, IR_RX_IF, &buf[0]);
+ if (ret)
+ goto err;
+
+ if (buf[0] != 0x83)
+ goto exit;
+
+ ret = rtl2831_rd_reg(d, IR_RX_BC, &buf[0]);
+ if (ret)
+ goto err;
+
+ len = buf[0];
+ ret = rtl2831_rd_regs(d, IR_RX_BUF, buf, len);
+
+ /* TODO: pass raw IR to Kernel IR decoder */
+
+ ret = rtl2831_wr_reg(d, IR_RX_IF, 0x03);
+ ret = rtl2831_wr_reg(d, IR_RX_BUF_CTRL, 0x80);
+ ret = rtl2831_wr_reg(d, IR_RX_CTRL, 0x80);
+
+exit:
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+enum rtl28xxu_usb_table_entry {
+ RTL2831U_0BDA_2831,
+ RTL2831U_14AA_0160,
+ RTL2831U_14AA_0161,
+};
+
+static struct usb_device_id rtl28xxu_table[] = {
+ /* RTL2831U */
+ [RTL2831U_0BDA_2831] = {
+ USB_DEVICE(USB_VID_REALTEK, USB_PID_REALTEK_RTL2831U)},
+ [RTL2831U_14AA_0160] = {
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT)},
+ [RTL2831U_14AA_0161] = {
+ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_FREECOM_DVBT_2)},
+
+ /* RTL2832U */
+ {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, rtl28xxu_table);
+
+static struct dvb_usb_device_properties rtl28xxu_properties[] = {
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = rtl2831u_frontend_attach,
+ .tuner_attach = rtl2831u_tuner_attach,
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 8*512,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = rtl28xxu_power_ctrl,
+
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "rtl28xxu",
+ .rc_query = rtl2831u_rc_query,
+ .rc_interval = 400,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+
+ .i2c_algo = &rtl28xxu_i2c_algo,
+
+ .num_device_descs = 2,
+ .devices = {
+ {
+ .name = "Realtek RTL2831U reference design",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2831U_0BDA_2831],
+ },
+ },
+ {
+ .name = "Freecom USB2.0 DVB-T",
+ .warm_ids = {
+ &rtl28xxu_table[RTL2831U_14AA_0160],
+ &rtl28xxu_table[RTL2831U_14AA_0161],
+ },
+ },
+ }
+ },
+ {
+ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
+
+ .usb_ctrl = DEVICE_SPECIFIC,
+ .no_reconnect = 1,
+
+ .size_of_priv = sizeof(struct rtl28xxu_priv),
+
+ .num_adapters = 1,
+ .adapter = {
+ {
+ .num_frontends = 1,
+ .fe = {
+ {
+ .frontend_attach = rtl2832u_frontend_attach,
+ .tuner_attach = rtl2832u_tuner_attach,
+ .streaming_ctrl = rtl28xxu_streaming_ctrl,
+ .stream = {
+ .type = USB_BULK,
+ .count = 6,
+ .endpoint = 0x81,
+ .u = {
+ .bulk = {
+ .buffersize = 8*512,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+
+ .power_ctrl = rtl28xxu_power_ctrl,
+
+ .rc.core = {
+ .protocol = RC_TYPE_NEC,
+ .module_name = "rtl28xxu",
+ .rc_query = rtl2832u_rc_query,
+ .rc_interval = 400,
+ .allowed_protos = RC_TYPE_NEC,
+ .rc_codes = RC_MAP_EMPTY,
+ },
+
+ .i2c_algo = &rtl28xxu_i2c_algo,
+
+ .num_device_descs = 0, /* disabled as no support for RTL2832 */
+ .devices = {
+ {
+ .name = "Realtek RTL2832U reference design",
+ },
+ }
+ },
+
+};
+
+static int rtl28xxu_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ int ret, i;
+ int properties_count = ARRAY_SIZE(rtl28xxu_properties);
+ struct dvb_usb_device *d;
+
+ deb_info("%s: interface=%d\n", __func__,
+ intf->cur_altsetting->desc.bInterfaceNumber);
+
+ if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+ return 0;
+
+ for (i = 0; i < properties_count; i++) {
+ ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i],
+ THIS_MODULE, &d, adapter_nr);
+ if (ret == 0 || ret != -ENODEV)
+ break;
+ }
+
+ if (ret)
+ goto err;
+
+ /* init USB endpoints */
+ ret = rtl2831_wr_reg(d, USB_SYSCTL_0, 0x09);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(d, USB_EPA_MAXPKT, "\x00\x02\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ ret = rtl2831_wr_regs(d, USB_EPA_FIFO_CFG, "\x14\x00\x00\x00", 4);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ deb_info("%s: failed=%d\n", __func__, ret);
+ return ret;
+}
+
+static struct usb_driver rtl28xxu_driver = {
+ .name = "dvb_usb_rtl28xxu",
+ .probe = rtl28xxu_probe,
+ .disconnect = dvb_usb_device_exit,
+ .id_table = rtl28xxu_table,
+};
+
+/* module stuff */
+static int __init rtl28xxu_module_init(void)
+{
+ int ret;
+
+ deb_info("%s:\n", __func__);
+
+ ret = usb_register(&rtl28xxu_driver);
+ if (ret)
+ err("usb_register failed=%d", ret);
+
+ return ret;
+}
+
+static void __exit rtl28xxu_module_exit(void)
+{
+ deb_info("%s:\n", __func__);
+
+ /* deregister this driver from the USB subsystem */
+ usb_deregister(&rtl28xxu_driver);
+}
+
+module_init(rtl28xxu_module_init);
+module_exit(rtl28xxu_module_exit);
+
+MODULE_DESCRIPTION("Realtek RTL28xxU DVB USB driver");
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.h b/drivers/media/dvb/dvb-usb/rtl28xxu.h
new file mode 100644
index 000000000000..90f3bb4f4c0e
--- /dev/null
+++ b/drivers/media/dvb/dvb-usb/rtl28xxu.h
@@ -0,0 +1,264 @@
+/*
+ * Realtek RTL28xxU DVB USB driver
+ *
+ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL28XXU_H
+#define RTL28XXU_H
+
+#define DVB_USB_LOG_PREFIX "rtl28xxu"
+#include "dvb-usb.h"
+
+#define deb_info(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x01, args)
+#define deb_rc(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x02, args)
+#define deb_xfer(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x04, args)
+#define deb_reg(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x08, args)
+#define deb_i2c(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x10, args)
+#define deb_fw(args...) dprintk(dvb_usb_rtl28xxu_debug, 0x20, args)
+
+#define deb_dump(r, t, v, i, b, l, func) { \
+ int loop_; \
+ func("%02x %02x %02x %02x %02x %02x %02x %02x", \
+ t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
+ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
+ func(" >>> "); \
+ else \
+ func(" <<< "); \
+ for (loop_ = 0; loop_ < l; loop_++) \
+ func("%02x ", b[loop_]); \
+ func("\n");\
+}
+
+/*
+ * USB commands
+ * (usb_control_msg() index parameter)
+ */
+
+#define DEMOD 0x0000
+#define USB 0x0100
+#define SYS 0x0200
+#define I2C 0x0300
+#define I2C_DA 0x0600
+
+#define CMD_WR_FLAG 0x0010
+#define CMD_DEMOD_RD 0x0000
+#define CMD_DEMOD_WR 0x0010
+#define CMD_USB_RD 0x0100
+#define CMD_USB_WR 0x0110
+#define CMD_SYS_RD 0x0200
+#define CMD_IR_RD 0x0201
+#define CMD_IR_WR 0x0211
+#define CMD_SYS_WR 0x0210
+#define CMD_I2C_RD 0x0300
+#define CMD_I2C_WR 0x0310
+#define CMD_I2C_DA_RD 0x0600
+#define CMD_I2C_DA_WR 0x0610
+
+
+struct rtl28xxu_priv {
+ u8 chip_id;
+ u8 tuner;
+ u8 page; /* integrated demod active register page */
+ bool rc_active;
+};
+
+enum rtl28xxu_chip_id {
+ CHIP_ID_NONE,
+ CHIP_ID_RTL2831U,
+ CHIP_ID_RTL2832U,
+};
+
+enum rtl28xxu_tuner {
+ TUNER_NONE,
+
+ TUNER_RTL2830_QT1010,
+ TUNER_RTL2830_MT2060,
+ TUNER_RTL2830_MXL5005S,
+
+ TUNER_RTL2832_MT2266,
+ TUNER_RTL2832_FC2580,
+ TUNER_RTL2832_MT2063,
+ TUNER_RTL2832_MAX3543,
+ TUNER_RTL2832_TUA9001,
+ TUNER_RTL2832_MXL5007T,
+ TUNER_RTL2832_FC0012,
+ TUNER_RTL2832_E4000,
+ TUNER_RTL2832_TDA18272,
+ TUNER_RTL2832_FC0013,
+};
+
+struct rtl28xxu_req {
+ u16 value;
+ u16 index;
+ u16 size;
+ u8 *data;
+};
+
+struct rtl28xxu_reg_val {
+ u16 reg;
+ u8 val;
+};
+
+/*
+ * memory map
+ *
+ * 0x0000 DEMOD : demodulator
+ * 0x2000 USB : SIE, USB endpoint, debug, DMA
+ * 0x3000 SYS : system
+ * 0xfc00 RC : remote controller (not RTL2831U)
+ */
+
+/*
+ * USB registers
+ */
+/* SIE Control Registers */
+#define USB_SYSCTL 0x2000 /* USB system control */
+#define USB_SYSCTL_0 0x2000 /* USB system control */
+#define USB_SYSCTL_1 0x2001 /* USB system control */
+#define USB_SYSCTL_2 0x2002 /* USB system control */
+#define USB_SYSCTL_3 0x2003 /* USB system control */
+#define USB_IRQSTAT 0x2008 /* SIE interrupt status */
+#define USB_IRQEN 0x200C /* SIE interrupt enable */
+#define USB_CTRL 0x2010 /* USB control */
+#define USB_STAT 0x2014 /* USB status */
+#define USB_DEVADDR 0x2018 /* USB device address */
+#define USB_TEST 0x201C /* USB test mode */
+#define USB_FRAME_NUMBER 0x2020 /* frame number */
+#define USB_FIFO_ADDR 0x2028 /* address of SIE FIFO RAM */
+#define USB_FIFO_CMD 0x202A /* SIE FIFO RAM access command */
+#define USB_FIFO_DATA 0x2030 /* SIE FIFO RAM data */
+/* Endpoint Registers */
+#define EP0_SETUPA 0x20F8 /* EP 0 setup packet lower byte */
+#define EP0_SETUPB 0x20FC /* EP 0 setup packet higher byte */
+#define USB_EP0_CFG 0x2104 /* EP 0 configure */
+#define USB_EP0_CTL 0x2108 /* EP 0 control */
+#define USB_EP0_STAT 0x210C /* EP 0 status */
+#define USB_EP0_IRQSTAT 0x2110 /* EP 0 interrupt status */
+#define USB_EP0_IRQEN 0x2114 /* EP 0 interrupt enable */
+#define USB_EP0_MAXPKT 0x2118 /* EP 0 max packet size */
+#define USB_EP0_BC 0x2120 /* EP 0 FIFO byte counter */
+#define USB_EPA_CFG 0x2144 /* EP A configure */
+#define USB_EPA_CFG_0 0x2144 /* EP A configure */
+#define USB_EPA_CFG_1 0x2145 /* EP A configure */
+#define USB_EPA_CFG_2 0x2146 /* EP A configure */
+#define USB_EPA_CFG_3 0x2147 /* EP A configure */
+#define USB_EPA_CTL 0x2148 /* EP A control */
+#define USB_EPA_CTL_0 0x2148 /* EP A control */
+#define USB_EPA_CTL_1 0x2149 /* EP A control */
+#define USB_EPA_CTL_2 0x214A /* EP A control */
+#define USB_EPA_CTL_3 0x214B /* EP A control */
+#define USB_EPA_STAT 0x214C /* EP A status */
+#define USB_EPA_IRQSTAT 0x2150 /* EP A interrupt status */
+#define USB_EPA_IRQEN 0x2154 /* EP A interrupt enable */
+#define USB_EPA_MAXPKT 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_0 0x2158 /* EP A max packet size */
+#define USB_EPA_MAXPKT_1 0x2159 /* EP A max packet size */
+#define USB_EPA_MAXPKT_2 0x215A /* EP A max packet size */
+#define USB_EPA_MAXPKT_3 0x215B /* EP A max packet size */
+#define USB_EPA_FIFO_CFG 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_0 0x2160 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_1 0x2161 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_2 0x2162 /* EP A FIFO configure */
+#define USB_EPA_FIFO_CFG_3 0x2163 /* EP A FIFO configure */
+/* Debug Registers */
+#define USB_PHYTSTDIS 0x2F04 /* PHY test disable */
+#define USB_TOUT_VAL 0x2F08 /* USB time-out time */
+#define USB_VDRCTRL 0x2F10 /* UTMI vendor signal control */
+#define USB_VSTAIN 0x2F14 /* UTMI vendor signal status in */
+#define USB_VLOADM 0x2F18 /* UTMI load vendor signal status in */
+#define USB_VSTAOUT 0x2F1C /* UTMI vendor signal status out */
+#define USB_UTMI_TST 0x2F80 /* UTMI test */
+#define USB_UTMI_STATUS 0x2F84 /* UTMI status */
+#define USB_TSTCTL 0x2F88 /* test control */
+#define USB_TSTCTL2 0x2F8C /* test control 2 */
+#define USB_PID_FORCE 0x2F90 /* force PID */
+#define USB_PKTERR_CNT 0x2F94 /* packet error counter */
+#define USB_RXERR_CNT 0x2F98 /* RX error counter */
+#define USB_MEM_BIST 0x2F9C /* MEM BIST test */
+#define USB_SLBBIST 0x2FA0 /* self-loop-back BIST */
+#define USB_CNTTEST 0x2FA4 /* counter test */
+#define USB_PHYTST 0x2FC0 /* USB PHY test */
+#define USB_DBGIDX 0x2FF0 /* select individual block debug signal */
+#define USB_DBGMUX 0x2FF4 /* debug signal module mux */
+
+/*
+ * SYS registers
+ */
+/* demod control registers */
+#define SYS_SYS0 0x3000 /* include DEMOD_CTL, GPO, GPI, GPOE */
+#define SYS_DEMOD_CTL 0x3000 /* control register for DVB-T demodulator */
+/* GPIO registers */
+#define SYS_GPIO_OUT_VAL 0x3001 /* output value of GPIO */
+#define SYS_GPIO_IN_VAL 0x3002 /* input value of GPIO */
+#define SYS_GPIO_OUT_EN 0x3003 /* output enable of GPIO */
+#define SYS_SYS1 0x3004 /* include GPD, SYSINTE, SYSINTS, GP_CFG0 */
+#define SYS_GPIO_DIR 0x3004 /* direction control for GPIO */
+#define SYS_SYSINTE 0x3005 /* system interrupt enable */
+#define SYS_SYSINTS 0x3006 /* system interrupt status */
+#define SYS_GPIO_CFG0 0x3007 /* PAD configuration for GPIO0-GPIO3 */
+#define SYS_SYS2 0x3008 /* include GP_CFG1 and 3 reserved bytes */
+#define SYS_GPIO_CFG1 0x3008 /* PAD configuration for GPIO4 */
+#define SYS_DEMOD_CTL1 0x300B
+
+/* IrDA registers */
+#define SYS_IRRC_PSR 0x3020 /* IR protocol selection */
+#define SYS_IRRC_PER 0x3024 /* IR protocol extension */
+#define SYS_IRRC_SF 0x3028 /* IR sampling frequency */
+#define SYS_IRRC_DPIR 0x302C /* IR data package interval */
+#define SYS_IRRC_CR 0x3030 /* IR control */
+#define SYS_IRRC_RP 0x3034 /* IR read port */
+#define SYS_IRRC_SR 0x3038 /* IR status */
+/* I2C master registers */
+#define SYS_I2CCR 0x3040 /* I2C clock */
+#define SYS_I2CMCR 0x3044 /* I2C master control */
+#define SYS_I2CMSTR 0x3048 /* I2C master SCL timing */
+#define SYS_I2CMSR 0x304C /* I2C master status */
+#define SYS_I2CMFR 0x3050 /* I2C master FIFO */
+
+/*
+ * IR registers
+ */
+#define IR_RX_BUF 0xFC00
+#define IR_RX_IE 0xFD00
+#define IR_RX_IF 0xFD01
+#define IR_RX_CTRL 0xFD02
+#define IR_RX_CFG 0xFD03
+#define IR_MAX_DURATION0 0xFD04
+#define IR_MAX_DURATION1 0xFD05
+#define IR_IDLE_LEN0 0xFD06
+#define IR_IDLE_LEN1 0xFD07
+#define IR_GLITCH_LEN 0xFD08
+#define IR_RX_BUF_CTRL 0xFD09
+#define IR_RX_BUF_DATA 0xFD0A
+#define IR_RX_BC 0xFD0B
+#define IR_RX_CLK 0xFD0C
+#define IR_RX_C_COUNT_L 0xFD0D
+#define IR_RX_C_COUNT_H 0xFD0E
+#define IR_SUSPEND_CTRL 0xFD10
+#define IR_ERR_TOL_CTRL 0xFD11
+#define IR_UNIT_LEN 0xFD12
+#define IR_ERR_TOL_LEN 0xFD13
+#define IR_MAX_H_TOL_LEN 0xFD14
+#define IR_MAX_L_TOL_LEN 0xFD15
+#define IR_MASK_CTRL 0xFD16
+#define IR_MASK_DATA 0xFD17
+#define IR_RES_MASK_ADDR 0xFD18
+#define IR_RES_MASK_T_LEN 0xFD19
+
+#endif
diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c
index 864b6274c729..e24ec539a5fd 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/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index ebb5ed7a7783..21246707fbfb 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -425,6 +425,13 @@ config DVB_CXD2820R
help
Say Y when you want to support this frontend.
+config DVB_RTL2830
+ tristate "Realtek RTL2830 DVB-T"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ Say Y when you want to support this frontend.
+
comment "DVB-C (cable) frontends"
depends on DVB_CORE
@@ -698,6 +705,14 @@ config DVB_IT913X_FE
A DVB-T tuner module.
Say Y when you want to support this frontend.
+config DVB_M88RS2000
+ tristate "M88RS2000 DVB-S demodulator and tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module.
+ Say Y when you want to support this frontend.
+
comment "Tools to develop new frontends"
config DVB_DUMMY_FE
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 00a20636df62..86fa808bf589 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -2,8 +2,8 @@
# Makefile for the kernel DVB frontend device drivers.
#
-ccflags-y += -Idrivers/media/dvb/dvb-core/
-ccflags-y += -Idrivers/media/common/tuners/
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core/
+ccflags-y += -I$(srctree)/drivers/media/common/tuners/
stb0899-objs = stb0899_drv.o stb0899_algo.o
stv0900-objs = stv0900_core.o stv0900_sw.o
@@ -96,4 +96,6 @@ obj-$(CONFIG_DVB_TDA18271C2DD) += tda18271c2dd.o
obj-$(CONFIG_DVB_IT913X_FE) += it913x-fe.o
obj-$(CONFIG_DVB_A8293) += a8293.o
obj-$(CONFIG_DVB_TDA10071) += tda10071.o
+obj-$(CONFIG_DVB_RTL2830) += rtl2830.o
+obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o
diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
index 2b248c12f404..55b6390198e3 100644
--- a/drivers/media/dvb/frontends/au8522_decoder.c
+++ b/drivers/media/dvb/frontends/au8522_decoder.c
@@ -839,15 +839,4 @@ static struct i2c_driver au8522_driver = {
.id_table = au8522_id,
};
-static __init int init_au8522(void)
-{
- return i2c_add_driver(&au8522_driver);
-}
-
-static __exit void exit_au8522(void)
-{
- i2c_del_driver(&au8522_driver);
-}
-
-module_init(init_au8522);
-module_exit(exit_au8522);
+module_i2c_driver(au8522_driver);
diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c
index c688b95df486..25f650934c73 100644
--- a/drivers/media/dvb/frontends/au8522_dig.c
+++ b/drivers/media/dvb/frontends/au8522_dig.c
@@ -588,11 +588,6 @@ static int au8522_set_frontend(struct dvb_frontend *fe)
(state->current_modulation == c->modulation))
return 0;
- au8522_enable_modulation(fe, c->modulation);
-
- /* Allow the demod to settle */
- msleep(100);
-
if (fe->ops.tuner_ops.set_params) {
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
@@ -604,6 +599,11 @@ static int au8522_set_frontend(struct dvb_frontend *fe)
if (ret < 0)
return ret;
+ /* Allow the tuner to settle */
+ msleep(100);
+
+ au8522_enable_modulation(fe, c->modulation);
+
state->current_frequency = c->frequency;
return 0;
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index faba82485086..edc8eafc5c09 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -502,10 +502,26 @@ static int cx22702_read_signal_strength(struct dvb_frontend *fe,
u16 *signal_strength)
{
struct cx22702_state *state = fe->demodulator_priv;
+ u8 reg23;
- u16 rs_ber;
- rs_ber = cx22702_readreg(state, 0x23);
- *signal_strength = (rs_ber << 8) | rs_ber;
+ /*
+ * Experience suggests that the strength signal register works as
+ * follows:
+ * - In the absence of signal, value is 0xff.
+ * - In the presence of a weak signal, bit 7 is set, not sure what
+ * the lower 7 bits mean.
+ * - In the presence of a strong signal, the register holds a 7-bit
+ * value (bit 7 is cleared), with greater values standing for
+ * weaker signals.
+ */
+ reg23 = cx22702_readreg(state, 0x23);
+ if (reg23 & 0x80) {
+ *signal_strength = 0;
+ } else {
+ reg23 = ~reg23 & 0x7f;
+ /* Scale to 16 bit */
+ *signal_strength = (reg23 << 9) | (reg23 << 2) | (reg23 >> 5);
+ }
return 0;
}
diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c
index 224d81e85091..d9fe60b4be48 100644
--- a/drivers/media/dvb/frontends/dib0090.c
+++ b/drivers/media/dvb/frontends/dib0090.c
@@ -519,7 +519,7 @@ static int dib0090_fw_identify(struct dvb_frontend *fe)
return 0;
identification_error:
- return -EIO;;
+ return -EIO;
}
static void dib0090_reset_digital(struct dvb_frontend *fe, const struct dib0090_config *cfg)
diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c
index 863ef3cfab9f..80848b4c15d4 100644
--- a/drivers/media/dvb/frontends/dib9000.c
+++ b/drivers/media/dvb/frontends/dib9000.c
@@ -33,7 +33,7 @@ struct i2c_device {
/* lock */
#define DIB_LOCK struct mutex
-#define DibAcquireLock(lock) do { if (mutex_lock_interruptible(lock) < 0) dprintk("could not get the lock"); } while (0)
+#define DibAcquireLock(lock) mutex_lock_interruptible(lock)
#define DibReleaseLock(lock) mutex_unlock(lock)
#define DibInitLock(lock) mutex_init(lock)
#define DibFreeLock(lock)
@@ -446,7 +446,10 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1
if (!state->platform.risc.fw_is_running)
return -EIO;
- DibAcquireLock(&state->platform.risc.mem_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
dib9000_risc_mem_setup(state, cmd | 0x80);
dib9000_risc_mem_read_chunks(state, b, len);
DibReleaseLock(&state->platform.risc.mem_lock);
@@ -459,7 +462,10 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8
if (!state->platform.risc.fw_is_running)
return -EIO;
- DibAcquireLock(&state->platform.risc.mem_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
dib9000_risc_mem_setup(state, cmd);
dib9000_risc_mem_write_chunks(state, b, m->size);
DibReleaseLock(&state->platform.risc.mem_lock);
@@ -531,7 +537,10 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data,
if (!state->platform.risc.fw_is_running)
return -EINVAL;
- DibAcquireLock(&state->platform.risc.mbx_if_lock);
+ if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
tmp = MAX_MAILBOX_TRY;
do {
size = dib9000_read_word_attr(state, 1043, attr) & 0xff;
@@ -593,7 +602,10 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id,
if (!state->platform.risc.fw_is_running)
return 0;
- DibAcquireLock(&state->platform.risc.mbx_if_lock);
+ if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
if (risc_id == 1)
mc_base = 16;
else
@@ -701,7 +713,10 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr)
if (!state->platform.risc.fw_is_running)
return -1;
- DibAcquireLock(&state->platform.risc.mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return -1;
+ }
if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */
ret = dib9000_mbx_fetch_to_cache(state, attr);
@@ -1178,7 +1193,10 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe)
struct dibDVBTChannel *ch;
int ret = 0;
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
ret = -EIO;
goto error;
@@ -1660,7 +1678,10 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2
p[12] = 0;
}
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
dib9000_risc_mem_write(state, FE_MM_W_COMPONENT_ACCESS, p);
@@ -1768,7 +1789,10 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff)
return 0;
}
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
val = dib9000_read_word(state, 294 + 1) & 0xffef;
val |= (onoff & 0x1) << 4;
@@ -1800,7 +1824,10 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff)
return 0;
}
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff);
ret = dib9000_write_word(state, 300 + 1 + id,
onoff ? (1 << 13) | pid : 0);
@@ -1848,7 +1875,10 @@ static int dib9000_sleep(struct dvb_frontend *fe)
u8 index_frontend;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
ret = state->fe[index_frontend]->ops.sleep(state->fe[index_frontend]);
if (ret < 0)
@@ -1874,8 +1904,12 @@ static int dib9000_get_frontend(struct dvb_frontend *fe)
fe_status_t stat;
int ret = 0;
- if (state->get_frontend_internal == 0)
- DibAcquireLock(&state->demod_lock);
+ if (state->get_frontend_internal == 0) {
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ }
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_status(state->fe[index_frontend], &stat);
@@ -1978,7 +2012,10 @@ static int dib9000_set_frontend(struct dvb_frontend *fe)
}
state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
fe->dtv_property_cache.delivery_system = SYS_DVBT;
@@ -2138,7 +2175,10 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat)
u8 index_frontend;
u16 lock = 0, lock_slave = 0;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
lock_slave |= dib9000_read_lock(state->fe[index_frontend]);
@@ -2168,8 +2208,15 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber)
u16 *c;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ ret = -EINTR;
+ goto error;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
@@ -2196,7 +2243,10 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
u16 val;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
*strength = 0;
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++) {
state->fe[index_frontend]->ops.read_signal_strength(state->fe[index_frontend], &val);
@@ -2206,8 +2256,13 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength)
*strength += val;
}
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ ret = -EINTR;
+ goto error;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
@@ -2232,9 +2287,14 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe)
u32 n, s, exp;
u16 val;
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
- if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0)
- return -EIO;
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ return 0;
+ }
+ if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
+ return 0;
+ }
dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2);
DibReleaseLock(&state->platform.risc.mem_mbx_lock);
@@ -2266,7 +2326,10 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr)
u8 index_frontend;
u32 snr_master;
- DibAcquireLock(&state->demod_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
snr_master = dib9000_get_snr(fe);
for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (state->fe[index_frontend] != NULL); index_frontend++)
snr_master += dib9000_get_snr(state->fe[index_frontend]);
@@ -2288,9 +2351,17 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc)
u16 *c = (u16 *)state->i2c_read_buffer;
int ret = 0;
- DibAcquireLock(&state->demod_lock);
- DibAcquireLock(&state->platform.risc.mem_mbx_lock);
+ if (DibAcquireLock(&state->demod_lock) < 0) {
+ dprintk("could not get the lock");
+ return -EINTR;
+ }
+ if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) {
+ dprintk("could not get the lock");
+ ret = -EINTR;
+ goto error;
+ }
if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) {
+ DibReleaseLock(&state->platform.risc.mem_mbx_lock);
ret = -EIO;
goto error;
}
diff --git a/drivers/media/dvb/frontends/drxd_hard.c b/drivers/media/dvb/frontends/drxd_hard.c
index 7bf39cda83c5..f380eb43e9d5 100644
--- a/drivers/media/dvb/frontends/drxd_hard.c
+++ b/drivers/media/dvb/frontends/drxd_hard.c
@@ -101,9 +101,9 @@ struct SCfgAgc {
struct SNoiseCal {
int cpOpt;
- u16 cpNexpOfs;
- u16 tdCal2k;
- u16 tdCal8k;
+ short cpNexpOfs;
+ short tdCal2k;
+ short tdCal8k;
};
enum app_env {
diff --git a/drivers/media/dvb/frontends/drxk.h b/drivers/media/dvb/frontends/drxk.h
index 020981844a86..9d64e4fea066 100644
--- a/drivers/media/dvb/frontends/drxk.h
+++ b/drivers/media/dvb/frontends/drxk.h
@@ -7,15 +7,19 @@
/**
* struct drxk_config - Configure the initial parameters for DRX-K
*
- * adr: I2C Address of the DRX-K
- * parallel_ts: true means that the device uses parallel TS,
+ * @adr: I2C Address of the DRX-K
+ * @parallel_ts: True means that the device uses parallel TS,
* Serial otherwise.
- * single_master: Device is on the single master mode
- * no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
- * antenna_gpio: GPIO bit used to control the antenna
- * antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
+ * @dynamic_clk: True means that the clock will be dynamically
+ * adjusted. Static clock otherwise.
+ * @enable_merr_cfg: Enable SIO_PDR_PERR_CFG/SIO_PDR_MVAL_CFG.
+ * @single_master: Device is on the single master mode
+ * @no_i2c_bridge: Don't switch the I2C bridge to talk with tuner
+ * @antenna_gpio: GPIO bit used to control the antenna
+ * @antenna_dvbt: GPIO bit for changing antenna to DVB-C. A value of 1
* means that 1=DVBC, 0 = DVBT. Zero means the opposite.
- * microcode_name: Name of the firmware file with the microcode
+ * @mpeg_out_clk_strength: DRXK Mpeg output clock drive strength.
+ * @microcode_name: Name of the firmware file with the microcode
*
* On the *_gpio vars, bit 0 is UIO-1, bit 1 is UIO-2 and bit 2 is
* UIO-3.
@@ -25,11 +29,14 @@ struct drxk_config {
bool single_master;
bool no_i2c_bridge;
bool parallel_ts;
+ bool dynamic_clk;
+ bool enable_merr_cfg;
bool antenna_dvbt;
u16 antenna_gpio;
- int chunk_size;
+ u8 mpeg_out_clk_strength;
+ int chunk_size;
const char *microcode_name;
};
diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c
index 5ab53795bd7a..36d11756492f 100644
--- a/drivers/media/dvb/frontends/drxk_hard.c
+++ b/drivers/media/dvb/frontends/drxk_hard.c
@@ -90,10 +90,6 @@ bool IsA1WithRomCode(struct drxk_state *state)
#define DRXK_MPEG_PARALLEL_OUTPUT_PIN_DRIVE_STRENGTH (0x03)
#endif
-#ifndef DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH
-#define DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH (0x06)
-#endif
-
#define DEFAULT_DRXK_MPEG_LOCK_TIMEOUT 700
#define DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT 500
@@ -649,9 +645,6 @@ static int init_state(struct drxk_state *state)
u32 ulQual83 = DEFAULT_MER_83;
u32 ulQual93 = DEFAULT_MER_93;
- u32 ulDVBTStaticTSClock = 1;
- u32 ulDVBCStaticTSClock = 1;
-
u32 ulMpegLockTimeOut = DEFAULT_DRXK_MPEG_LOCK_TIMEOUT;
u32 ulDemodLockTimeOut = DEFAULT_DRXK_DEMOD_LOCK_TIMEOUT;
@@ -661,7 +654,6 @@ static int init_state(struct drxk_state *state)
u32 ulGPIOCfg = 0x0113;
u32 ulInvertTSClock = 0;
u32 ulTSDataStrength = DRXK_MPEG_SERIAL_OUTPUT_PIN_DRIVE_STRENGTH;
- u32 ulTSClockkStrength = DRXK_MPEG_OUTPUT_CLK_DRIVE_STRENGTH;
u32 ulDVBTBitrate = 50000000;
u32 ulDVBCBitrate = DRXK_QAM_SYMBOLRATE_MAX * 8;
@@ -814,8 +806,7 @@ static int init_state(struct drxk_state *state)
state->m_invertSTR = false; /* If TRUE; invert STR signals */
state->m_invertVAL = false; /* If TRUE; invert VAL signals */
state->m_invertCLK = (ulInvertTSClock != 0); /* If TRUE; invert CLK signals */
- state->m_DVBTStaticCLK = (ulDVBTStaticTSClock != 0);
- state->m_DVBCStaticCLK = (ulDVBCStaticTSClock != 0);
+
/* If TRUE; static MPEG clockrate will be used;
otherwise clockrate will adapt to the bitrate of the TS */
@@ -823,7 +814,6 @@ static int init_state(struct drxk_state *state)
state->m_DVBCBitrate = ulDVBCBitrate;
state->m_TSDataStrength = (ulTSDataStrength & 0x07);
- state->m_TSClockkStrength = (ulTSClockkStrength & 0x07);
/* Maximum bitrate in b/s in case static clockrate is selected */
state->m_mpegTsStaticBitrate = 19392658;
@@ -1188,6 +1178,7 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
int status = -1;
u16 sioPdrMclkCfg = 0;
u16 sioPdrMdxCfg = 0;
+ u16 err_cfg = 0;
dprintk(1, ": mpeg %s, %s mode\n",
mpegEnable ? "enable" : "disable",
@@ -1253,12 +1244,17 @@ static int MPEGTSConfigurePins(struct drxk_state *state, bool mpegEnable)
status = write16(state, SIO_PDR_MSTRT_CFG__A, sioPdrMdxCfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MERR_CFG__A, 0x0000); /* Disable */
+
+ if (state->enable_merr_cfg)
+ err_cfg = sioPdrMdxCfg;
+
+ status = write16(state, SIO_PDR_MERR_CFG__A, err_cfg);
if (status < 0)
goto error;
- status = write16(state, SIO_PDR_MVAL_CFG__A, 0x0000); /* Disable */
+ status = write16(state, SIO_PDR_MVAL_CFG__A, err_cfg);
if (status < 0)
goto error;
+
if (state->m_enableParallel == true) {
/* paralel -> enable MD1 to MD7 */
status = write16(state, SIO_PDR_MD1_CFG__A, sioPdrMdxCfg);
@@ -6069,9 +6065,7 @@ static int init_drxk(struct drxk_state *state)
if (status < 0)
goto error;
- if (!state->microcode_name)
- load_microcode(state, "drxk_a3.mc");
- else
+ if (state->microcode_name)
load_microcode(state, state->microcode_name);
/* disable token-ring bus through OFDM block for possible ucode upload */
@@ -6322,15 +6316,12 @@ static int drxk_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_t
switch (p->delivery_system) {
case SYS_DVBC_ANNEX_A:
case SYS_DVBC_ANNEX_C:
+ case SYS_DVBT:
sets->min_delay_ms = 3000;
sets->max_drift = 0;
sets->step_size = 0;
return 0;
default:
- /*
- * For DVB-T, let it use the default DVB core way, that is:
- * fepriv->step_size = fe->ops.info.frequency_stepsize * 2
- */
return -EINVAL;
}
}
@@ -6390,6 +6381,21 @@ struct dvb_frontend *drxk_attach(const struct drxk_config *config,
state->antenna_gpio = config->antenna_gpio;
state->antenna_dvbt = config->antenna_dvbt;
state->m_ChunkSize = config->chunk_size;
+ state->enable_merr_cfg = config->enable_merr_cfg;
+
+ if (config->dynamic_clk) {
+ state->m_DVBTStaticCLK = 0;
+ state->m_DVBCStaticCLK = 0;
+ } else {
+ state->m_DVBTStaticCLK = 1;
+ state->m_DVBCStaticCLK = 1;
+ }
+
+
+ if (config->mpeg_out_clk_strength)
+ state->m_TSClockkStrength = config->mpeg_out_clk_strength & 0x07;
+ else
+ state->m_TSClockkStrength = 0x06;
if (config->parallel_ts)
state->m_enableParallel = true;
diff --git a/drivers/media/dvb/frontends/drxk_hard.h b/drivers/media/dvb/frontends/drxk_hard.h
index 3a58b73eb9b9..4bbf841de83a 100644
--- a/drivers/media/dvb/frontends/drxk_hard.h
+++ b/drivers/media/dvb/frontends/drxk_hard.h
@@ -332,6 +332,7 @@ struct drxk_state {
u16 UIO_mask; /* Bits used by UIO */
+ bool enable_merr_cfg;
bool single_master;
bool no_i2c_bridge;
bool antenna_dvbt;
diff --git a/drivers/media/dvb/frontends/it913x-fe-priv.h b/drivers/media/dvb/frontends/it913x-fe-priv.h
index 93b086ea7e1c..eb6fd8aebdb3 100644
--- a/drivers/media/dvb/frontends/it913x-fe-priv.h
+++ b/drivers/media/dvb/frontends/it913x-fe-priv.h
@@ -201,6 +201,11 @@ fe_modulation_t fe_con[] = {
QAM_64,
};
+enum {
+ PRIORITY_HIGH = 0, /* High-priority stream */
+ PRIORITY_LOW, /* Low-priority stream */
+};
+
/* Standard demodulator functions */
static struct it913xset set_solo_fe[] = {
{PRO_LINK, GPIOH5_EN, {0x01}, 0x01},
diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c
index ccc36bf2deb4..84df03c29179 100644
--- a/drivers/media/dvb/frontends/it913x-fe.c
+++ b/drivers/media/dvb/frontends/it913x-fe.c
@@ -57,6 +57,7 @@ struct it913x_fe_state {
u32 frequency;
fe_modulation_t constellation;
fe_transmit_mode_t transmission_mode;
+ u8 priority;
u32 crystalFrequency;
u32 adcFrequency;
u8 tuner_type;
@@ -500,19 +501,87 @@ static int it913x_fe_read_status(struct dvb_frontend *fe, fe_status_t *status)
return 0;
}
+/* FEC values based on fe_code_rate_t non supported values 0*/
+int it913x_qpsk_pval[] = {0, -93, -91, -90, 0, -89, -88};
+int it913x_16qam_pval[] = {0, -87, -85, -84, 0, -83, -82};
+int it913x_64qam_pval[] = {0, -82, -80, -78, 0, -77, -76};
+
+static int it913x_get_signal_strength(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *p = &fe->dtv_property_cache;
+ struct it913x_fe_state *state = fe->demodulator_priv;
+ u8 code_rate;
+ int ret, temp;
+ u8 lna_gain_os;
+
+ ret = it913x_read_reg_u8(state, VAR_P_INBAND);
+ if (ret < 0)
+ return ret;
+
+ /* VHF/UHF gain offset */
+ if (state->frequency < 300000000)
+ lna_gain_os = 7;
+ else
+ lna_gain_os = 14;
+
+ temp = (ret - 100) - lna_gain_os;
+
+ if (state->priority == PRIORITY_HIGH)
+ code_rate = p->code_rate_HP;
+ else
+ code_rate = p->code_rate_LP;
+
+ if (code_rate >= ARRAY_SIZE(it913x_qpsk_pval))
+ return -EINVAL;
+
+ deb_info("Reg VAR_P_INBAND:%d Calc Offset Value:%d", ret, temp);
+
+ /* Apply FEC offset values*/
+ switch (p->modulation) {
+ case QPSK:
+ temp -= it913x_qpsk_pval[code_rate];
+ break;
+ case QAM_16:
+ temp -= it913x_16qam_pval[code_rate];
+ break;
+ case QAM_64:
+ temp -= it913x_64qam_pval[code_rate];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (temp < -15)
+ ret = 0;
+ else if ((-15 <= temp) && (temp < 0))
+ ret = (2 * (temp + 15)) / 3;
+ else if ((0 <= temp) && (temp < 20))
+ ret = 4 * temp + 10;
+ else if ((20 <= temp) && (temp < 35))
+ ret = (2 * (temp - 20)) / 3 + 90;
+ else if (temp >= 35)
+ ret = 100;
+
+ deb_info("Signal Strength :%d", ret);
+
+ return ret;
+}
+
static int it913x_fe_read_signal_strength(struct dvb_frontend *fe,
u16 *strength)
{
struct it913x_fe_state *state = fe->demodulator_priv;
- int ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
- /*SIGNAL_LEVEL always returns 100%! so using FE_HAS_SIGNAL as switch*/
- if (state->it913x_status & FE_HAS_SIGNAL)
- ret = (ret * 0xff) / 0x64;
- else
- ret = 0x0;
- ret |= ret << 0x8;
- *strength = ret;
- return 0;
+ int ret = 0;
+ if (state->config->read_slevel) {
+ if (state->it913x_status & FE_HAS_SIGNAL)
+ ret = it913x_read_reg_u8(state, SIGNAL_LEVEL);
+ } else
+ ret = it913x_get_signal_strength(fe);
+
+ if (ret >= 0)
+ *strength = (u16)((u32)ret * 0xffff / 0x64);
+
+ return (ret < 0) ? -ENODEV : 0;
}
static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr)
@@ -606,6 +675,8 @@ static int it913x_fe_get_frontend(struct dvb_frontend *fe)
if (reg[2] < 4)
p->hierarchy = fe_hi[reg[2]];
+ state->priority = reg[5];
+
p->code_rate_HP = (reg[6] < 6) ? fe_code[reg[6]] : FEC_NONE;
p->code_rate_LP = (reg[7] < 6) ? fe_code[reg[7]] : FEC_NONE;
@@ -972,5 +1043,5 @@ static struct dvb_frontend_ops it913x_fe_ofdm_ops = {
MODULE_DESCRIPTION("it913x Frontend and it9137 tuner");
MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
-MODULE_VERSION("1.13");
+MODULE_VERSION("1.15");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/it913x-fe.h b/drivers/media/dvb/frontends/it913x-fe.h
index c4a908e354e0..07fa4594c12b 100644
--- a/drivers/media/dvb/frontends/it913x-fe.h
+++ b/drivers/media/dvb/frontends/it913x-fe.h
@@ -34,6 +34,8 @@ struct ite_config {
u8 tuner_id_1;
u8 dual_mode;
u8 adf;
+ /* option to read SIGNAL_LEVEL */
+ u8 read_slevel;
};
#if defined(CONFIG_DVB_IT913X_FE) || (defined(CONFIG_DVB_IT913X_FE_MODULE) && \
@@ -168,6 +170,8 @@ static inline struct dvb_frontend *it913x_fe_attach(
#define EST_SIGNAL_LEVEL 0x004a
#define FREE_BAND 0x004b
#define SUSPEND_FLAG 0x004c
+#define VAR_P_INBAND 0x00f7
+
/* Build in tuner types */
#define IT9137 0x38
#define IT9135_38 0x38
diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c
index c990d35a13dc..e046622df0e4 100644
--- a/drivers/media/dvb/frontends/lgdt330x.c
+++ b/drivers/media/dvb/frontends/lgdt330x.c
@@ -104,8 +104,8 @@ static int i2c_write_demod_bytes (struct lgdt330x_state* state,
* then reads the data returned for (len) bytes.
*/
-static u8 i2c_read_demod_bytes (struct lgdt330x_state* state,
- enum I2C_REG reg, u8* buf, int len)
+static int i2c_read_demod_bytes(struct lgdt330x_state *state,
+ enum I2C_REG reg, u8 *buf, int len)
{
u8 wr [] = { reg };
struct i2c_msg msg [] = {
@@ -118,6 +118,8 @@ static u8 i2c_read_demod_bytes (struct lgdt330x_state* state,
ret = i2c_transfer(state->i2c, msg, 2);
if (ret != 2) {
printk(KERN_WARNING "lgdt330x: %s: addr 0x%02x select 0x%02x error (ret == %i)\n", __func__, state->config->demod_address, reg, ret);
+ if (ret >= 0)
+ ret = -EIO;
} else {
ret = 0;
}
diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c
new file mode 100644
index 000000000000..045ee5a6f7ae
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88rs2000.c
@@ -0,0 +1,904 @@
+/*
+ Driver for M88RS2000 demodulator and tuner
+
+ Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ Beta Driver
+
+ Include various calculation code from DS3000 driver.
+ Copyright (C) 2009 Konstantin Dimitrov.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+
+#include "dvb_frontend.h"
+#include "m88rs2000.h"
+
+struct m88rs2000_state {
+ struct i2c_adapter *i2c;
+ const struct m88rs2000_config *config;
+ struct dvb_frontend frontend;
+ u8 no_lock_count;
+ u32 tuner_frequency;
+ u32 symbol_rate;
+ fe_code_rate_t fec_inner;
+ u8 tuner_level;
+ int errmode;
+};
+
+static int m88rs2000_debug;
+
+module_param_named(debug, m88rs2000_debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info (or-able)).");
+
+#define dprintk(level, args...) do { \
+ if (level & m88rs2000_debug) \
+ printk(KERN_DEBUG "m88rs2000-fe: " args); \
+} while (0)
+
+#define deb_info(args...) dprintk(0x01, args)
+#define info(format, arg...) \
+ printk(KERN_INFO "m88rs2000-fe: " format "\n" , ## arg)
+
+static int m88rs2000_writereg(struct m88rs2000_state *state, u8 tuner,
+ u8 reg, u8 data)
+{
+ int ret;
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
+ state->config->demod_addr;
+ u8 buf[] = { reg, data };
+ struct i2c_msg msg = {
+ .addr = addr,
+ .flags = 0,
+ .buf = buf,
+ .len = 2
+ };
+
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ deb_info("%s: writereg error (reg == 0x%02x, val == 0x%02x, "
+ "ret == %i)\n", __func__, reg, data, ret);
+
+ return (ret != 1) ? -EREMOTEIO : 0;
+}
+
+static int m88rs2000_demod_write(struct m88rs2000_state *state, u8 reg, u8 data)
+{
+ return m88rs2000_writereg(state, 1, reg, data);
+}
+
+static int m88rs2000_tuner_write(struct m88rs2000_state *state, u8 reg, u8 data)
+{
+ m88rs2000_demod_write(state, 0x81, 0x84);
+ udelay(10);
+ return m88rs2000_writereg(state, 0, reg, data);
+
+}
+
+static int m88rs2000_write(struct dvb_frontend *fe, const u8 buf[], int len)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return m88rs2000_writereg(state, 1, buf[0], buf[1]);
+}
+
+static u8 m88rs2000_readreg(struct m88rs2000_state *state, u8 tuner, u8 reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ u8 addr = (tuner == 0) ? state->config->tuner_addr :
+ state->config->demod_addr;
+ struct i2c_msg msg[] = {
+ {
+ .addr = addr,
+ .flags = 0,
+ .buf = b0,
+ .len = 1
+ }, {
+ .addr = addr,
+ .flags = I2C_M_RD,
+ .buf = b1,
+ .len = 1
+ }
+ };
+
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2)
+ deb_info("%s: readreg error (reg == 0x%02x, ret == %i)\n",
+ __func__, reg, ret);
+
+ return b1[0];
+}
+
+static u8 m88rs2000_demod_read(struct m88rs2000_state *state, u8 reg)
+{
+ return m88rs2000_readreg(state, 1, reg);
+}
+
+static u8 m88rs2000_tuner_read(struct m88rs2000_state *state, u8 reg)
+{
+ m88rs2000_demod_write(state, 0x81, 0x85);
+ udelay(10);
+ return m88rs2000_readreg(state, 0, reg);
+}
+
+static int m88rs2000_set_symbolrate(struct dvb_frontend *fe, u32 srate)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u32 temp;
+ u8 b[3];
+
+ if ((srate < 1000000) || (srate > 45000000))
+ return -EINVAL;
+
+ temp = srate / 1000;
+ temp *= 11831;
+ temp /= 68;
+ temp -= 3;
+
+ b[0] = (u8) (temp >> 16) & 0xff;
+ b[1] = (u8) (temp >> 8) & 0xff;
+ b[2] = (u8) temp & 0xff;
+ ret = m88rs2000_demod_write(state, 0x93, b[2]);
+ ret |= m88rs2000_demod_write(state, 0x94, b[1]);
+ ret |= m88rs2000_demod_write(state, 0x95, b[0]);
+
+ deb_info("m88rs2000: m88rs2000_set_symbolrate\n");
+ return ret;
+}
+
+static int m88rs2000_send_diseqc_msg(struct dvb_frontend *fe,
+ struct dvb_diseqc_master_cmd *m)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ int i;
+ u8 reg;
+ deb_info("%s\n", __func__);
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg = m88rs2000_demod_read(state, 0xb2);
+ reg &= 0x3f;
+ m88rs2000_demod_write(state, 0xb2, reg);
+ for (i = 0; i < m->msg_len; i++)
+ m88rs2000_demod_write(state, 0xb3 + i, m->msg[i]);
+
+ reg = m88rs2000_demod_read(state, 0xb1);
+ reg &= 0x87;
+ reg |= ((m->msg_len - 1) << 3) | 0x07;
+ reg &= 0x7f;
+ m88rs2000_demod_write(state, 0xb1, reg);
+
+ for (i = 0; i < 15; i++) {
+ if ((m88rs2000_demod_read(state, 0xb1) & 0x40) == 0x0)
+ break;
+ msleep(20);
+ }
+
+ reg = m88rs2000_demod_read(state, 0xb1);
+ if ((reg & 0x40) > 0x0) {
+ reg &= 0x7f;
+ reg |= 0x40;
+ m88rs2000_demod_write(state, 0xb1, reg);
+ }
+
+ reg = m88rs2000_demod_read(state, 0xb2);
+ reg &= 0x3f;
+ reg |= 0x80;
+ m88rs2000_demod_write(state, 0xb2, reg);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+
+ return 0;
+}
+
+static int m88rs2000_send_diseqc_burst(struct dvb_frontend *fe,
+ fe_sec_mini_cmd_t burst)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ deb_info("%s\n", __func__);
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ msleep(50);
+ reg0 = m88rs2000_demod_read(state, 0xb1);
+ reg1 = m88rs2000_demod_read(state, 0xb2);
+ /* TODO complete this section */
+ m88rs2000_demod_write(state, 0xb2, reg1);
+ m88rs2000_demod_write(state, 0xb1, reg0);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+ return 0;
+}
+
+static int m88rs2000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg0, reg1;
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg0 = m88rs2000_demod_read(state, 0xb1);
+ reg1 = m88rs2000_demod_read(state, 0xb2);
+
+ reg1 &= 0x3f;
+
+ switch (tone) {
+ case SEC_TONE_ON:
+ reg0 |= 0x4;
+ reg0 &= 0xbc;
+ break;
+ case SEC_TONE_OFF:
+ reg1 |= 0x80;
+ break;
+ default:
+ break;
+ }
+ m88rs2000_demod_write(state, 0xb2, reg1);
+ m88rs2000_demod_write(state, 0xb1, reg0);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+ return 0;
+}
+
+struct inittab {
+ u8 cmd;
+ u8 reg;
+ u8 val;
+};
+
+struct inittab m88rs2000_setup[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {WRITE_DELAY, 0x19, 0x00},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {DEMOD_WRITE, 0x81, 0xc1},
+ {TUNER_WRITE, 0x42, 0x73},
+ {TUNER_WRITE, 0x05, 0x07},
+ {TUNER_WRITE, 0x20, 0x27},
+ {TUNER_WRITE, 0x07, 0x02},
+ {TUNER_WRITE, 0x11, 0xff},
+ {TUNER_WRITE, 0x60, 0xf9},
+ {TUNER_WRITE, 0x08, 0x01},
+ {TUNER_WRITE, 0x00, 0x41},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {DEMOD_WRITE, 0x86, 0xc6},
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xf0, 0x22},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0xb0, 0x45},
+ {DEMOD_WRITE, 0xb2, 0x01}, /* set voltage pin always set 1*/
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab m88rs2000_shutdown[] = {
+ {DEMOD_WRITE, 0x9a, 0x30},
+ {DEMOD_WRITE, 0xb0, 0x00},
+ {DEMOD_WRITE, 0xf1, 0x89},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {TUNER_WRITE, 0x00, 0x40},
+ {DEMOD_WRITE, 0x81, 0x81},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab tuner_reset[] = {
+ {TUNER_WRITE, 0x42, 0x73},
+ {TUNER_WRITE, 0x05, 0x07},
+ {TUNER_WRITE, 0x20, 0x27},
+ {TUNER_WRITE, 0x07, 0x02},
+ {TUNER_WRITE, 0x11, 0xff},
+ {TUNER_WRITE, 0x60, 0xf9},
+ {TUNER_WRITE, 0x08, 0x01},
+ {TUNER_WRITE, 0x00, 0x41},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab fe_reset[] = {
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0xf1, 0xbf},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x20, 0x81},
+ {DEMOD_WRITE, 0x21, 0x80},
+ {DEMOD_WRITE, 0x10, 0x33},
+ {DEMOD_WRITE, 0x11, 0x44},
+ {DEMOD_WRITE, 0x12, 0x07},
+ {DEMOD_WRITE, 0x18, 0x20},
+ {DEMOD_WRITE, 0x28, 0x04},
+ {DEMOD_WRITE, 0x29, 0x8e},
+ {DEMOD_WRITE, 0x3b, 0xff},
+ {DEMOD_WRITE, 0x32, 0x10},
+ {DEMOD_WRITE, 0x33, 0x02},
+ {DEMOD_WRITE, 0x34, 0x30},
+ {DEMOD_WRITE, 0x35, 0xff},
+ {DEMOD_WRITE, 0x38, 0x50},
+ {DEMOD_WRITE, 0x39, 0x68},
+ {DEMOD_WRITE, 0x3c, 0x7f},
+ {DEMOD_WRITE, 0x3d, 0x0f},
+ {DEMOD_WRITE, 0x45, 0x20},
+ {DEMOD_WRITE, 0x46, 0x24},
+ {DEMOD_WRITE, 0x47, 0x7c},
+ {DEMOD_WRITE, 0x48, 0x16},
+ {DEMOD_WRITE, 0x49, 0x04},
+ {DEMOD_WRITE, 0x4a, 0x01},
+ {DEMOD_WRITE, 0x4b, 0x78},
+ {DEMOD_WRITE, 0X4d, 0xd2},
+ {DEMOD_WRITE, 0x4e, 0x6d},
+ {DEMOD_WRITE, 0x50, 0x30},
+ {DEMOD_WRITE, 0x51, 0x30},
+ {DEMOD_WRITE, 0x54, 0x7b},
+ {DEMOD_WRITE, 0x56, 0x09},
+ {DEMOD_WRITE, 0x58, 0x59},
+ {DEMOD_WRITE, 0x59, 0x37},
+ {DEMOD_WRITE, 0x63, 0xfa},
+ {0xff, 0xaa, 0xff}
+};
+
+struct inittab fe_trigger[] = {
+ {DEMOD_WRITE, 0x97, 0x04},
+ {DEMOD_WRITE, 0x99, 0x77},
+ {DEMOD_WRITE, 0x9b, 0x64},
+ {DEMOD_WRITE, 0x9e, 0x00},
+ {DEMOD_WRITE, 0x9f, 0xf8},
+ {DEMOD_WRITE, 0xa0, 0x20},
+ {DEMOD_WRITE, 0xa1, 0xe0},
+ {DEMOD_WRITE, 0xa3, 0x38},
+ {DEMOD_WRITE, 0x98, 0xff},
+ {DEMOD_WRITE, 0xc0, 0x0f},
+ {DEMOD_WRITE, 0x89, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {WRITE_DELAY, 0x0a, 0x00},
+ {DEMOD_WRITE, 0x00, 0x01},
+ {DEMOD_WRITE, 0x00, 0x00},
+ {DEMOD_WRITE, 0x9a, 0xb0},
+ {0xff, 0xaa, 0xff}
+};
+
+static int m88rs2000_tab_set(struct m88rs2000_state *state,
+ struct inittab *tab)
+{
+ int ret = 0;
+ u8 i;
+ if (tab == NULL)
+ return -EINVAL;
+
+ for (i = 0; i < 255; i++) {
+ switch (tab[i].cmd) {
+ case 0x01:
+ ret = m88rs2000_demod_write(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x02:
+ ret = m88rs2000_tuner_write(state, tab[i].reg,
+ tab[i].val);
+ break;
+ case 0x10:
+ if (tab[i].reg > 0)
+ mdelay(tab[i].reg);
+ break;
+ case 0xff:
+ if (tab[i].reg == 0xaa && tab[i].val == 0xff)
+ return 0;
+ case 0x00:
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (ret < 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt)
+{
+ deb_info("%s: %s\n", __func__,
+ volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
+ volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
+
+ return 0;
+}
+
+static int m88rs2000_startup(struct m88rs2000_state *state)
+{
+ int ret = 0;
+ u8 reg;
+
+ reg = m88rs2000_tuner_read(state, 0x00);
+ if ((reg & 0x40) == 0)
+ ret = -ENODEV;
+
+ return ret;
+}
+
+static int m88rs2000_init(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+
+ deb_info("m88rs2000: init chip\n");
+ /* Setup frontend from shutdown/cold */
+ ret = m88rs2000_tab_set(state, m88rs2000_setup);
+
+ return ret;
+}
+
+static int m88rs2000_sleep(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ /* Shutdown the frondend */
+ ret = m88rs2000_tab_set(state, m88rs2000_shutdown);
+ return ret;
+}
+
+static int m88rs2000_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ u8 reg = m88rs2000_demod_read(state, 0x8c);
+
+ *status = 0;
+
+ if ((reg & 0x7) == 0x7) {
+ *status = FE_HAS_CARRIER | FE_HAS_SIGNAL | FE_HAS_VITERBI
+ | FE_HAS_LOCK;
+ if (state->config->set_ts_params)
+ state->config->set_ts_params(fe, CALL_IS_READ);
+ }
+ return 0;
+}
+
+/* Extact code for these unknown but lmedm04 driver uses interupt callbacks */
+
+static int m88rs2000_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ deb_info("m88rs2000_read_ber %d\n", *ber);
+ *ber = 0;
+ return 0;
+}
+
+static int m88rs2000_read_signal_strength(struct dvb_frontend *fe,
+ u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static int m88rs2000_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ deb_info("m88rs2000_read_snr %d\n", *snr);
+ *snr = 0;
+ return 0;
+}
+
+static int m88rs2000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ deb_info("m88rs2000_read_ber %d\n", *ucblocks);
+ *ucblocks = 0;
+ return 0;
+}
+
+static int m88rs2000_tuner_gate_ctrl(struct m88rs2000_state *state, u8 offset)
+{
+ int ret;
+ ret = m88rs2000_tuner_write(state, 0x51, 0x1f - offset);
+ ret |= m88rs2000_tuner_write(state, 0x51, 0x1f);
+ ret |= m88rs2000_tuner_write(state, 0x50, offset);
+ ret |= m88rs2000_tuner_write(state, 0x50, 0x00);
+ msleep(20);
+ return ret;
+}
+
+static int m88rs2000_set_tuner_rf(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int reg;
+ reg = m88rs2000_tuner_read(state, 0x3d);
+ reg &= 0x7f;
+ if (reg < 0x16)
+ reg = 0xa1;
+ else if (reg == 0x16)
+ reg = 0x99;
+ else
+ reg = 0xf9;
+
+ m88rs2000_tuner_write(state, 0x60, reg);
+ reg = m88rs2000_tuner_gate_ctrl(state, 0x08);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ return reg;
+}
+
+static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ int ret;
+ u32 frequency = c->frequency;
+ s32 offset_khz;
+ s32 tmp;
+ u32 symbol_rate = (c->symbol_rate / 1000);
+ u32 f3db, gdiv28;
+ u16 value, ndiv, lpf_coeff;
+ u8 lpf_mxdiv, mlpf_max, mlpf_min, nlpf;
+ u8 lo = 0x01, div4 = 0x0;
+
+ /* Reset Tuner */
+ ret = m88rs2000_tab_set(state, tuner_reset);
+
+ /* Calculate frequency divider */
+ if (frequency < 1060000) {
+ lo |= 0x10;
+ div4 = 0x1;
+ ndiv = (frequency * 14 * 4) / FE_CRYSTAL_KHZ;
+ } else
+ ndiv = (frequency * 14 * 2) / FE_CRYSTAL_KHZ;
+ ndiv = ndiv + ndiv % 2;
+ ndiv = ndiv - 1024;
+
+ ret = m88rs2000_tuner_write(state, 0x10, 0x80 | lo);
+
+ /* Set frequency divider */
+ ret |= m88rs2000_tuner_write(state, 0x01, (ndiv >> 8) & 0xf);
+ ret |= m88rs2000_tuner_write(state, 0x02, ndiv & 0xff);
+
+ ret |= m88rs2000_tuner_write(state, 0x03, 0x06);
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x10);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Tuner Frequency Range */
+ ret = m88rs2000_tuner_write(state, 0x10, lo);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x08);
+
+ /* Tuner RF */
+ ret |= m88rs2000_set_tuner_rf(fe);
+
+ gdiv28 = (FE_CRYSTAL_KHZ / 1000 * 1694 + 500) / 1000;
+ ret |= m88rs2000_tuner_write(state, 0x04, gdiv28 & 0xff);
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
+ if (ret < 0)
+ return -ENODEV;
+
+ value = m88rs2000_tuner_read(state, 0x26);
+
+ f3db = (symbol_rate * 135) / 200 + 2000;
+ f3db += FREQ_OFFSET_LOW_SYM_RATE;
+ if (f3db < 7000)
+ f3db = 7000;
+ if (f3db > 40000)
+ f3db = 40000;
+
+ gdiv28 = gdiv28 * 207 / (value * 2 + 151);
+ mlpf_max = gdiv28 * 135 / 100;
+ mlpf_min = gdiv28 * 78 / 100;
+ if (mlpf_max > 63)
+ mlpf_max = 63;
+
+ lpf_coeff = 2766;
+
+ nlpf = (f3db * gdiv28 * 2 / lpf_coeff /
+ (FE_CRYSTAL_KHZ / 1000) + 1) / 2;
+ if (nlpf > 23)
+ nlpf = 23;
+ if (nlpf < 1)
+ nlpf = 1;
+
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+
+ if (lpf_mxdiv < mlpf_min) {
+ nlpf++;
+ lpf_mxdiv = (nlpf * (FE_CRYSTAL_KHZ / 1000)
+ * lpf_coeff * 2 / f3db + 1) / 2;
+ }
+
+ if (lpf_mxdiv > mlpf_max)
+ lpf_mxdiv = mlpf_max;
+
+ ret = m88rs2000_tuner_write(state, 0x04, lpf_mxdiv);
+ ret |= m88rs2000_tuner_write(state, 0x06, nlpf);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x04);
+
+ ret |= m88rs2000_tuner_gate_ctrl(state, 0x01);
+
+ msleep(80);
+ /* calculate offset assuming 96000kHz*/
+ offset_khz = (ndiv - ndiv % 2 + 1024) * FE_CRYSTAL_KHZ
+ / 14 / (div4 + 1) / 2;
+
+ offset_khz -= frequency;
+
+ tmp = offset_khz;
+ tmp *= 65536;
+
+ tmp = (2 * tmp + 96000) / (2 * 96000);
+ if (tmp < 0)
+ tmp += 65536;
+
+ *offset = tmp & 0xffff;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret < 0) ? -EINVAL : 0;
+}
+
+static int m88rs2000_set_fec(struct m88rs2000_state *state,
+ fe_code_rate_t fec)
+{
+ int ret;
+ u16 fec_set;
+ switch (fec) {
+ /* This is not confirmed kept for reference */
+/* case FEC_1_2:
+ fec_set = 0x88;
+ break;
+ case FEC_2_3:
+ fec_set = 0x68;
+ break;
+ case FEC_3_4:
+ fec_set = 0x48;
+ break;
+ case FEC_5_6:
+ fec_set = 0x28;
+ break;
+ case FEC_7_8:
+ fec_set = 0x18;
+ break; */
+ case FEC_AUTO:
+ default:
+ fec_set = 0x08;
+ }
+ ret = m88rs2000_demod_write(state, 0x76, fec_set);
+
+ return 0;
+}
+
+
+static fe_code_rate_t m88rs2000_get_fec(struct m88rs2000_state *state)
+{
+ u8 reg;
+ m88rs2000_demod_write(state, 0x9a, 0x30);
+ reg = m88rs2000_demod_read(state, 0x76);
+ m88rs2000_demod_write(state, 0x9a, 0xb0);
+
+ switch (reg) {
+ case 0x88:
+ return FEC_1_2;
+ case 0x68:
+ return FEC_2_3;
+ case 0x48:
+ return FEC_3_4;
+ case 0x28:
+ return FEC_5_6;
+ case 0x18:
+ return FEC_7_8;
+ case 0x08:
+ default:
+ break;
+ }
+
+ return FEC_AUTO;
+}
+
+static int m88rs2000_set_frontend(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ fe_status_t status;
+ int i, ret;
+ u16 offset = 0;
+ u8 reg;
+
+ state->no_lock_count = 0;
+
+ if (c->delivery_system != SYS_DVBS) {
+ deb_info("%s: unsupported delivery "
+ "system selected (%d)\n",
+ __func__, c->delivery_system);
+ return -EOPNOTSUPP;
+ }
+
+ /* Set Tuner */
+ ret = m88rs2000_set_tuner(fe, &offset);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = m88rs2000_demod_write(state, 0x9a, 0x30);
+ /* Unknown usually 0xc6 sometimes 0xc1 */
+ reg = m88rs2000_demod_read(state, 0x86);
+ ret |= m88rs2000_demod_write(state, 0x86, reg);
+ /* Offset lower nibble always 0 */
+ ret |= m88rs2000_demod_write(state, 0x9c, (offset >> 8));
+ ret |= m88rs2000_demod_write(state, 0x9d, offset & 0xf0);
+
+
+ /* Reset Demod */
+ ret = m88rs2000_tab_set(state, fe_reset);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Unknown */
+ reg = m88rs2000_demod_read(state, 0x70);
+ ret = m88rs2000_demod_write(state, 0x70, reg);
+
+ /* Set FEC */
+ ret |= m88rs2000_set_fec(state, c->fec_inner);
+ ret |= m88rs2000_demod_write(state, 0x85, 0x1);
+ ret |= m88rs2000_demod_write(state, 0x8a, 0xbf);
+ ret |= m88rs2000_demod_write(state, 0x8d, 0x1e);
+ ret |= m88rs2000_demod_write(state, 0x90, 0xf1);
+ ret |= m88rs2000_demod_write(state, 0x91, 0x08);
+
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set Symbol Rate */
+ ret = m88rs2000_set_symbolrate(fe, c->symbol_rate);
+ if (ret < 0)
+ return -ENODEV;
+
+ /* Set up Demod */
+ ret = m88rs2000_tab_set(state, fe_trigger);
+ if (ret < 0)
+ return -ENODEV;
+
+ for (i = 0; i < 25; i++) {
+ u8 reg = m88rs2000_demod_read(state, 0x8c);
+ if ((reg & 0x7) == 0x7) {
+ status = FE_HAS_LOCK;
+ break;
+ }
+ state->no_lock_count++;
+ if (state->no_lock_count > 15) {
+ reg = m88rs2000_demod_read(state, 0x70);
+ reg ^= 0x4;
+ m88rs2000_demod_write(state, 0x70, reg);
+ state->no_lock_count = 0;
+ }
+ if (state->no_lock_count == 20)
+ m88rs2000_set_tuner_rf(fe);
+ msleep(20);
+ }
+
+ if (status & FE_HAS_LOCK) {
+ state->fec_inner = m88rs2000_get_fec(state);
+ /* Uknown suspect SNR level */
+ reg = m88rs2000_demod_read(state, 0x65);
+ }
+
+ state->tuner_frequency = c->frequency;
+ state->symbol_rate = c->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_get_frontend(struct dvb_frontend *fe)
+{
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ c->fec_inner = state->fec_inner;
+ c->frequency = state->tuner_frequency;
+ c->symbol_rate = state->symbol_rate;
+ return 0;
+}
+
+static int m88rs2000_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+
+ if (enable)
+ m88rs2000_demod_write(state, 0x81, 0x84);
+ else
+ m88rs2000_demod_write(state, 0x81, 0x81);
+ udelay(10);
+ return 0;
+}
+
+static void m88rs2000_release(struct dvb_frontend *fe)
+{
+ struct m88rs2000_state *state = fe->demodulator_priv;
+ kfree(state);
+}
+
+static struct dvb_frontend_ops m88rs2000_ops = {
+ .delsys = { SYS_DVBS },
+ .info = {
+ .name = "M88RS2000 DVB-S",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 1000, /* kHz for QPSK frontends */
+ .frequency_tolerance = 5000,
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .symbol_rate_tolerance = 500, /* ppm */
+ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 |
+ FE_CAN_QPSK |
+ FE_CAN_FEC_AUTO
+ },
+
+ .release = m88rs2000_release,
+ .init = m88rs2000_init,
+ .sleep = m88rs2000_sleep,
+ .write = m88rs2000_write,
+ .i2c_gate_ctrl = m88rs2000_i2c_gate_ctrl,
+ .read_status = m88rs2000_read_status,
+ .read_ber = m88rs2000_read_ber,
+ .read_signal_strength = m88rs2000_read_signal_strength,
+ .read_snr = m88rs2000_read_snr,
+ .read_ucblocks = m88rs2000_read_ucblocks,
+ .diseqc_send_master_cmd = m88rs2000_send_diseqc_msg,
+ .diseqc_send_burst = m88rs2000_send_diseqc_burst,
+ .set_tone = m88rs2000_set_tone,
+ .set_voltage = m88rs2000_set_voltage,
+
+ .set_frontend = m88rs2000_set_frontend,
+ .get_frontend = m88rs2000_get_frontend,
+};
+
+struct dvb_frontend *m88rs2000_attach(const struct m88rs2000_config *config,
+ struct i2c_adapter *i2c)
+{
+ struct m88rs2000_state *state = NULL;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct m88rs2000_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+ state->tuner_frequency = 0;
+ state->symbol_rate = 0;
+ state->fec_inner = 0;
+
+ if (m88rs2000_startup(state) < 0)
+ goto error;
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &m88rs2000_ops,
+ sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+
+error:
+ kfree(state);
+
+ return NULL;
+}
+EXPORT_SYMBOL(m88rs2000_attach);
+
+MODULE_DESCRIPTION("M88RS2000 DVB-S Demodulator driver");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.13");
+
diff --git a/drivers/media/dvb/frontends/m88rs2000.h b/drivers/media/dvb/frontends/m88rs2000.h
new file mode 100644
index 000000000000..59acdb696873
--- /dev/null
+++ b/drivers/media/dvb/frontends/m88rs2000.h
@@ -0,0 +1,66 @@
+/*
+ Driver for M88RS2000 demodulator
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef M88RS2000_H
+#define M88RS2000_H
+
+#include <linux/dvb/frontend.h>
+#include "dvb_frontend.h"
+
+struct m88rs2000_config {
+ /* Demodulator i2c address */
+ u8 demod_addr;
+ /* Tuner address */
+ u8 tuner_addr;
+
+ u8 *inittab;
+
+ /* minimum delay before retuning */
+ int min_delay_ms;
+
+ int (*set_ts_params)(struct dvb_frontend *, int);
+};
+
+enum {
+ CALL_IS_SET_FRONTEND = 0x0,
+ CALL_IS_READ,
+};
+
+#if defined(CONFIG_DVB_M88RS2000) || (defined(CONFIG_DVB_M88RS2000_MODULE) && \
+ defined(MODULE))
+extern struct dvb_frontend *m88rs2000_attach(
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend *m88rs2000_attach(
+ const struct m88rs2000_config *config, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+#endif /* CONFIG_DVB_M88RS2000 */
+
+#define FE_CRYSTAL_KHZ 27000
+#define FREQ_OFFSET_LOW_SYM_RATE 3000
+
+enum {
+ DEMOD_WRITE = 0x1,
+ TUNER_WRITE,
+ WRITE_DELAY = 0x10,
+};
+#endif /* M88RS2000_H */
diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c
new file mode 100644
index 000000000000..45196c5b0736
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830.c
@@ -0,0 +1,562 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+/*
+ * Driver implements own I2C-adapter for tuner I2C access. That's since chip
+ * have unusual I2C-gate control which closes gate automatically after each
+ * I2C transfer. Using own I2C adapter we can workaround that.
+ */
+
+#include "rtl2830_priv.h"
+
+int rtl2830_debug;
+module_param_named(debug, rtl2830_debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+/* write multiple hardware registers */
+static int rtl2830_wr(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ u8 buf[1+len];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1+len,
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ memcpy(&buf[1], val, len);
+
+ ret = i2c_transfer(priv->i2c, msg, 1);
+ if (ret == 1) {
+ ret = 0;
+ } else {
+ warn("i2c wr failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* read multiple hardware registers */
+static int rtl2830_rd(struct rtl2830_priv *priv, u8 reg, u8 *val, int len)
+{
+ int ret;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = priv->cfg.i2c_addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg,
+ }, {
+ .addr = priv->cfg.i2c_addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = val,
+ }
+ };
+
+ ret = i2c_transfer(priv->i2c, msg, 2);
+ if (ret == 2) {
+ ret = 0;
+ } else {
+ warn("i2c rd failed=%d reg=%02x len=%d", ret, reg, len);
+ ret = -EREMOTEIO;
+ }
+ return ret;
+}
+
+/* write multiple registers */
+static int rtl2830_wr_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_wr(priv, reg2, val, len);
+}
+
+/* read multiple registers */
+static int rtl2830_rd_regs(struct rtl2830_priv *priv, u16 reg, u8 *val, int len)
+{
+ int ret;
+ u8 reg2 = (reg >> 0) & 0xff;
+ u8 page = (reg >> 8) & 0xff;
+
+ /* switch bank if needed */
+ if (page != priv->page) {
+ ret = rtl2830_wr(priv, 0x00, &page, 1);
+ if (ret)
+ return ret;
+
+ priv->page = page;
+ }
+
+ return rtl2830_rd(priv, reg2, val, len);
+}
+
+#if 0 /* currently not used */
+/* write single register */
+static int rtl2830_wr_reg(struct rtl2830_priv *priv, u16 reg, u8 val)
+{
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+#endif
+
+/* read single register */
+static int rtl2830_rd_reg(struct rtl2830_priv *priv, u16 reg, u8 *val)
+{
+ return rtl2830_rd_regs(priv, reg, val, 1);
+}
+
+/* write single register with mask */
+int rtl2830_wr_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 val, u8 mask)
+{
+ int ret;
+ u8 tmp;
+
+ /* no need for read if whole reg is written */
+ if (mask != 0xff) {
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ tmp &= ~mask;
+ val |= tmp;
+ }
+
+ return rtl2830_wr_regs(priv, reg, &val, 1);
+}
+
+/* read single register with mask */
+int rtl2830_rd_reg_mask(struct rtl2830_priv *priv, u16 reg, u8 *val, u8 mask)
+{
+ int ret, i;
+ u8 tmp;
+
+ ret = rtl2830_rd_regs(priv, reg, &tmp, 1);
+ if (ret)
+ return ret;
+
+ tmp &= mask;
+
+ /* find position of the first bit */
+ for (i = 0; i < 8; i++) {
+ if ((mask >> i) & 0x01)
+ break;
+ }
+ *val = tmp >> i;
+
+ return 0;
+}
+
+static int rtl2830_init(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret, i;
+ u64 num;
+ u8 buf[3], tmp;
+ u32 if_ctl;
+ struct rtl2830_reg_val_mask tab[] = {
+ { 0x00d, 0x01, 0x03 },
+ { 0x00d, 0x10, 0x10 },
+ { 0x104, 0x00, 0x1e },
+ { 0x105, 0x80, 0x80 },
+ { 0x110, 0x02, 0x03 },
+ { 0x110, 0x08, 0x0c },
+ { 0x17b, 0x00, 0x40 },
+ { 0x17d, 0x05, 0x0f },
+ { 0x17d, 0x50, 0xf0 },
+ { 0x18c, 0x08, 0x0f },
+ { 0x18d, 0x00, 0xc0 },
+ { 0x188, 0x05, 0x0f },
+ { 0x189, 0x00, 0xfc },
+ { 0x2d5, 0x02, 0x02 },
+ { 0x2f1, 0x02, 0x06 },
+ { 0x2f1, 0x20, 0xf8 },
+ { 0x16d, 0x00, 0x01 },
+ { 0x1a6, 0x00, 0x80 },
+ { 0x106, priv->cfg.vtop, 0x3f },
+ { 0x107, priv->cfg.krf, 0x3f },
+ { 0x112, 0x28, 0xff },
+ { 0x103, priv->cfg.agc_targ_val, 0xff },
+ { 0x00a, 0x02, 0x07 },
+ { 0x140, 0x0c, 0x3c },
+ { 0x140, 0x40, 0xc0 },
+ { 0x15b, 0x05, 0x07 },
+ { 0x15b, 0x28, 0x38 },
+ { 0x15c, 0x05, 0x07 },
+ { 0x15c, 0x28, 0x38 },
+ { 0x115, priv->cfg.spec_inv, 0x01 },
+ { 0x16f, 0x01, 0x07 },
+ { 0x170, 0x18, 0x38 },
+ { 0x172, 0x0f, 0x0f },
+ { 0x173, 0x08, 0x38 },
+ { 0x175, 0x01, 0x07 },
+ { 0x176, 0x00, 0xc0 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(tab); i++) {
+ ret = rtl2830_wr_reg_mask(priv, tab[i].reg, tab[i].val,
+ tab[i].mask);
+ if (ret)
+ goto err;
+ }
+
+ ret = rtl2830_wr_regs(priv, 0x18f, "\x28\x00", 2);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x195,
+ "\x04\x06\x0a\x12\x0a\x12\x1e\x28", 8);
+ if (ret)
+ goto err;
+
+ num = priv->cfg.if_dvbt % priv->cfg.xtal;
+ num *= 0x400000;
+ num = div_u64(num, priv->cfg.xtal);
+ num = -num;
+ if_ctl = num & 0x3fffff;
+ dbg("%s: if_ctl=%08x", __func__, if_ctl);
+
+ ret = rtl2830_rd_reg_mask(priv, 0x119, &tmp, 0xc0); /* b[7:6] */
+ if (ret)
+ goto err;
+
+ buf[0] = tmp << 6;
+ buf[0] = (if_ctl >> 16) & 0x3f;
+ buf[1] = (if_ctl >> 8) & 0xff;
+ buf[2] = (if_ctl >> 0) & 0xff;
+
+ ret = rtl2830_wr_regs(priv, 0x119, buf, 3);
+ if (ret)
+ goto err;
+
+ /* TODO: spec init */
+
+ /* soft reset */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x04, 0x04);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x00, 0x04);
+ if (ret)
+ goto err;
+
+ priv->sleeping = false;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_sleep(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ priv->sleeping = true;
+ return 0;
+}
+
+int rtl2830_get_tune_settings(struct dvb_frontend *fe,
+ struct dvb_frontend_tune_settings *s)
+{
+ s->min_delay_ms = 500;
+ s->step_size = fe->ops.info.frequency_stepsize * 2;
+ s->max_drift = (fe->ops.info.frequency_stepsize * 2) + 1;
+
+ return 0;
+}
+
+static int rtl2830_set_frontend(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
+ int ret, i;
+ static u8 bw_params1[3][34] = {
+ {
+ 0x1f, 0xf0, 0x1f, 0xf0, 0x1f, 0xfa, 0x00, 0x17, 0x00, 0x41,
+ 0x00, 0x64, 0x00, 0x67, 0x00, 0x38, 0x1f, 0xde, 0x1f, 0x7a,
+ 0x1f, 0x47, 0x1f, 0x7c, 0x00, 0x30, 0x01, 0x4b, 0x02, 0x82,
+ 0x03, 0x73, 0x03, 0xcf, /* 6 MHz */
+ }, {
+ 0x1f, 0xfa, 0x1f, 0xda, 0x1f, 0xc1, 0x1f, 0xb3, 0x1f, 0xca,
+ 0x00, 0x07, 0x00, 0x4d, 0x00, 0x6d, 0x00, 0x40, 0x1f, 0xca,
+ 0x1f, 0x4d, 0x1f, 0x2a, 0x1f, 0xb2, 0x00, 0xec, 0x02, 0x7e,
+ 0x03, 0xd0, 0x04, 0x53, /* 7 MHz */
+ }, {
+ 0x00, 0x10, 0x00, 0x0e, 0x1f, 0xf7, 0x1f, 0xc9, 0x1f, 0xa0,
+ 0x1f, 0xa6, 0x1f, 0xec, 0x00, 0x4e, 0x00, 0x7d, 0x00, 0x3a,
+ 0x1f, 0x98, 0x1f, 0x10, 0x1f, 0x40, 0x00, 0x75, 0x02, 0x5f,
+ 0x04, 0x24, 0x04, 0xdb, /* 8 MHz */
+ },
+ };
+ static u8 bw_params2[3][6] = {
+ {0xc3, 0x0c, 0x44, 0x33, 0x33, 0x30,}, /* 6 MHz */
+ {0xb8, 0xe3, 0x93, 0x99, 0x99, 0x98,}, /* 7 MHz */
+ {0xae, 0xba, 0xf3, 0x26, 0x66, 0x64,}, /* 8 MHz */
+ };
+
+
+ dbg("%s: frequency=%d bandwidth_hz=%d inversion=%d", __func__,
+ c->frequency, c->bandwidth_hz, c->inversion);
+
+ /* program tuner */
+ if (fe->ops.tuner_ops.set_params)
+ fe->ops.tuner_ops.set_params(fe);
+
+ switch (c->bandwidth_hz) {
+ case 6000000:
+ i = 0;
+ break;
+ case 7000000:
+ i = 1;
+ break;
+ case 8000000:
+ i = 2;
+ break;
+ default:
+ dbg("invalid bandwidth");
+ return -EINVAL;
+ }
+
+ ret = rtl2830_wr_reg_mask(priv, 0x008, i << 1, 0x06);
+ if (ret)
+ goto err;
+
+ /* 1/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x11c, &bw_params1[i][0], 17);
+ if (ret)
+ goto err;
+
+ /* 2/2 split I2C write */
+ ret = rtl2830_wr_regs(priv, 0x12d, &bw_params1[i][17], 17);
+ if (ret)
+ goto err;
+
+ ret = rtl2830_wr_regs(priv, 0x19d, bw_params2[i], 6);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ int ret;
+ u8 tmp;
+ *status = 0;
+
+ if (priv->sleeping)
+ return 0;
+
+ ret = rtl2830_rd_reg_mask(priv, 0x351, &tmp, 0x78); /* [6:3] */
+ if (ret)
+ goto err;
+
+ if (tmp == 11) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK;
+ } else if (tmp == 10) {
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER |
+ FE_HAS_VITERBI;
+ }
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr)
+{
+ *snr = 0;
+ return 0;
+}
+
+static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber)
+{
+ *ber = 0;
+ return 0;
+}
+
+static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+{
+ *ucblocks = 0;
+ return 0;
+}
+
+static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
+{
+ *strength = 0;
+ return 0;
+}
+
+static struct dvb_frontend_ops rtl2830_ops;
+
+static u32 rtl2830_tuner_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int rtl2830_tuner_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msg[], int num)
+{
+ struct rtl2830_priv *priv = i2c_get_adapdata(i2c_adap);
+ int ret;
+
+ /* open i2c-gate */
+ ret = rtl2830_wr_reg_mask(priv, 0x101, 0x08, 0x08);
+ if (ret)
+ goto err;
+
+ ret = i2c_transfer(priv->i2c, msg, num);
+ if (ret < 0)
+ warn("tuner i2c failed=%d", ret);
+
+ return ret;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ return ret;
+}
+
+static struct i2c_algorithm rtl2830_tuner_i2c_algo = {
+ .master_xfer = rtl2830_tuner_i2c_xfer,
+ .functionality = rtl2830_tuner_i2c_func,
+};
+
+struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+ return &priv->tuner_i2c_adapter;
+}
+EXPORT_SYMBOL(rtl2830_get_tuner_i2c_adapter);
+
+static void rtl2830_release(struct dvb_frontend *fe)
+{
+ struct rtl2830_priv *priv = fe->demodulator_priv;
+
+ i2c_del_adapter(&priv->tuner_i2c_adapter);
+ kfree(priv);
+}
+
+struct dvb_frontend *rtl2830_attach(const struct rtl2830_config *cfg,
+ struct i2c_adapter *i2c)
+{
+ struct rtl2830_priv *priv = NULL;
+ int ret = 0;
+ u8 tmp;
+
+ /* allocate memory for the internal state */
+ priv = kzalloc(sizeof(struct rtl2830_priv), GFP_KERNEL);
+ if (priv == NULL)
+ goto err;
+
+ /* setup the priv */
+ priv->i2c = i2c;
+ memcpy(&priv->cfg, cfg, sizeof(struct rtl2830_config));
+
+ /* check if the demod is there */
+ ret = rtl2830_rd_reg(priv, 0x000, &tmp);
+ if (ret)
+ goto err;
+
+ /* create dvb_frontend */
+ memcpy(&priv->fe.ops, &rtl2830_ops, sizeof(struct dvb_frontend_ops));
+ priv->fe.demodulator_priv = priv;
+
+ /* create tuner i2c adapter */
+ strlcpy(priv->tuner_i2c_adapter.name, "RTL2830 tuner I2C adapter",
+ sizeof(priv->tuner_i2c_adapter.name));
+ priv->tuner_i2c_adapter.algo = &rtl2830_tuner_i2c_algo;
+ priv->tuner_i2c_adapter.algo_data = NULL;
+ i2c_set_adapdata(&priv->tuner_i2c_adapter, priv);
+ if (i2c_add_adapter(&priv->tuner_i2c_adapter) < 0) {
+ err("tuner I2C bus could not be initialized");
+ goto err;
+ }
+
+ priv->sleeping = true;
+
+ return &priv->fe;
+err:
+ dbg("%s: failed=%d", __func__, ret);
+ kfree(priv);
+ return NULL;
+}
+EXPORT_SYMBOL(rtl2830_attach);
+
+static struct dvb_frontend_ops rtl2830_ops = {
+ .delsys = { SYS_DVBT },
+ .info = {
+ .name = "Realtek RTL2830 (DVB-T)",
+ .caps = FE_CAN_FEC_1_2 |
+ FE_CAN_FEC_2_3 |
+ FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 |
+ FE_CAN_FEC_7_8 |
+ FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK |
+ FE_CAN_QAM_16 |
+ FE_CAN_QAM_64 |
+ FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_HIERARCHY_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_MUTE_TS
+ },
+
+ .release = rtl2830_release,
+
+ .init = rtl2830_init,
+ .sleep = rtl2830_sleep,
+
+ .get_tune_settings = rtl2830_get_tune_settings,
+
+ .set_frontend = rtl2830_set_frontend,
+
+ .read_status = rtl2830_read_status,
+ .read_snr = rtl2830_read_snr,
+ .read_ber = rtl2830_read_ber,
+ .read_ucblocks = rtl2830_read_ucblocks,
+ .read_signal_strength = rtl2830_read_signal_strength,
+};
+
+MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
+MODULE_DESCRIPTION("Realtek RTL2830 DVB-T demodulator driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/rtl2830.h b/drivers/media/dvb/frontends/rtl2830.h
new file mode 100644
index 000000000000..1c6ee91749c2
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830.h
@@ -0,0 +1,97 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_H
+#define RTL2830_H
+
+#include <linux/dvb/frontend.h>
+
+struct rtl2830_config {
+ /*
+ * Demodulator I2C address.
+ */
+ u8 i2c_addr;
+
+ /*
+ * Xtal frequency.
+ * Hz
+ * 4000000, 16000000, 25000000, 28800000
+ */
+ u32 xtal;
+
+ /*
+ * TS output mode.
+ */
+ u8 ts_mode;
+
+ /*
+ * Spectrum inversion.
+ */
+ bool spec_inv;
+
+ /*
+ * IFs for all used modes.
+ * Hz
+ * 4570000, 4571429, 36000000, 36125000, 36166667, 44000000
+ */
+ u32 if_dvbt;
+
+ /*
+ */
+ u8 vtop;
+
+ /*
+ */
+ u8 krf;
+
+ /*
+ */
+ u8 agc_targ_val;
+};
+
+#if defined(CONFIG_DVB_RTL2830) || \
+ (defined(CONFIG_DVB_RTL2830_MODULE) && defined(MODULE))
+extern struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+);
+
+extern struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+);
+#else
+static inline struct dvb_frontend *rtl2830_attach(
+ const struct rtl2830_config *config,
+ struct i2c_adapter *i2c
+)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+}
+
+static inline struct i2c_adapter *rtl2830_get_tuner_i2c_adapter(
+ struct dvb_frontend *fe
+)
+{
+ return NULL;
+}
+#endif
+
+#endif /* RTL2830_H */
diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h
new file mode 100644
index 000000000000..4a464761b5b8
--- /dev/null
+++ b/drivers/media/dvb/frontends/rtl2830_priv.h
@@ -0,0 +1,57 @@
+/*
+ * Realtek RTL2830 DVB-T demodulator driver
+ *
+ * Copyright (C) 2011 Antti Palosaari <crope@iki.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTL2830_PRIV_H
+#define RTL2830_PRIV_H
+
+#include "dvb_frontend.h"
+#include "rtl2830.h"
+
+#define LOG_PREFIX "rtl2830"
+
+#undef dbg
+#define dbg(f, arg...) \
+ if (rtl2830_debug) \
+ printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef err
+#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg)
+#undef info
+#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg)
+#undef warn
+#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg)
+
+struct rtl2830_priv {
+ struct i2c_adapter *i2c;
+ struct dvb_frontend fe;
+ struct rtl2830_config cfg;
+ struct i2c_adapter tuner_i2c_adapter;
+
+ bool sleeping;
+
+ u8 page; /* active register page */
+};
+
+struct rtl2830_reg_val_mask {
+ u16 reg;
+ u8 val;
+ u8 mask;
+};
+
+#endif /* RTL2830_PRIV_H */
diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c
index 38565beafe23..dd08f4ac64a8 100644
--- a/drivers/media/dvb/frontends/stb0899_drv.c
+++ b/drivers/media/dvb/frontends/stb0899_drv.c
@@ -67,7 +67,7 @@ static const struct stb0899_tab stb0899_cn_tab[] = {
* Crude linear extrapolation below -84.8dBm and above -8.0dBm.
*/
static const struct stb0899_tab stb0899_dvbsrf_tab[] = {
- { -950, -128 },
+ { -750, -128 },
{ -748, -94 },
{ -745, -92 },
{ -735, -90 },
@@ -131,7 +131,7 @@ static const struct stb0899_tab stb0899_dvbs2rf_tab[] = {
{ -730, 13645 },
{ -750, 13909 },
{ -766, 14153 },
- { -999, 16383 }
+ { -950, 16383 }
};
/* DVB-S2 Es/N0 quant in dB/100 vs read value * 100*/
@@ -964,6 +964,7 @@ static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
int val;
u32 reg;
+ *strength = 0;
switch (state->delsys) {
case SYS_DVBS:
case SYS_DSS:
@@ -983,11 +984,11 @@ static int stb0899_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
break;
case SYS_DVBS2:
if (internal->lock) {
- reg = STB0899_READ_S2REG(STB0899_DEMOD, IF_AGC_GAIN);
+ reg = STB0899_READ_S2REG(STB0899_S2DEMOD, IF_AGC_GAIN);
val = STB0899_GETFIELD(IF_AGC_GAIN, reg);
*strength = stb0899_table_lookup(stb0899_dvbs2rf_tab, ARRAY_SIZE(stb0899_dvbs2rf_tab) - 1, val);
- *strength += 750;
+ *strength += 950;
dprintk(state->verbose, FE_DEBUG, 1, "IF_AGC_GAIN = 0x%04x, C = %d * 0.1 dBm",
val & 0x3fff, *strength);
}
@@ -1009,6 +1010,7 @@ static int stb0899_read_snr(struct dvb_frontend *fe, u16 *snr)
u8 buf[2];
u32 reg;
+ *snr = 0;
reg = stb0899_read_reg(state, STB0899_VSTATUS);
switch (state->delsys) {
case SYS_DVBS:
@@ -1071,7 +1073,7 @@ static int stb0899_read_status(struct dvb_frontend *fe, enum fe_status *status)
reg = stb0899_read_reg(state, STB0899_VSTATUS);
if (STB0899_GETFIELD(VSTATUS_LOCKEDVIT, reg)) {
dprintk(state->verbose, FE_DEBUG, 1, "--------> FE_HAS_CARRIER | FE_HAS_LOCK");
- *status |= FE_HAS_CARRIER | FE_HAS_LOCK;
+ *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_LOCK;
reg = stb0899_read_reg(state, STB0899_PLPARM);
if (STB0899_GETFIELD(VITCURPUN, reg)) {
diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c
index fb5548a82208..632b25156e4c 100644
--- a/drivers/media/dvb/frontends/stv0288.c
+++ b/drivers/media/dvb/frontends/stv0288.c
@@ -506,7 +506,7 @@ static int stv0288_set_frontend(struct dvb_frontend *fe)
tda[1] = (unsigned char)tm;
stv0288_writeregI(state, 0x2b, tda[1]);
stv0288_writeregI(state, 0x2c, tda[2]);
- udelay(30);
+ msleep(30);
}
state->tuner_frequency = c->frequency;
state->fec_inner = FEC_AUTO;
diff --git a/drivers/media/dvb/frontends/tda10071.c b/drivers/media/dvb/frontends/tda10071.c
index a99205026751..c21bc92d2811 100644
--- a/drivers/media/dvb/frontends/tda10071.c
+++ b/drivers/media/dvb/frontends/tda10071.c
@@ -1215,7 +1215,7 @@ error:
EXPORT_SYMBOL(tda10071_attach);
static struct dvb_frontend_ops tda10071_ops = {
- .delsys = { SYS_DVBT, SYS_DVBT2 },
+ .delsys = { SYS_DVBS, SYS_DVBS2 },
.info = {
.name = "NXP TDA10071",
.frequency_min = 950000,
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index 8418c02bcefe..7539a5d71029 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -216,6 +216,7 @@ static int demod_attach_drxk(struct ngene_channel *chan,
struct drxk_config config;
memset(&config, 0, sizeof(config));
+ config.microcode_name = "drxk_a3.mc";
config.adr = 0x29 + (chan->number ^ 2);
chan->fe = dvb_attach(drxk_attach, &config, i2c);
diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c
index b81df5fafe26..15b35c4725f1 100644
--- a/drivers/media/dvb/pt1/pt1.c
+++ b/drivers/media/dvb/pt1/pt1.c
@@ -28,6 +28,7 @@
#include <linux/pci.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/ratelimit.h>
#include "dvbdev.h"
#include "dvb_demux.h"
@@ -77,6 +78,8 @@ struct pt1 {
struct pt1_adapter *adaps[PT1_NR_ADAPS];
struct pt1_table *tables;
struct task_struct *kthread;
+ int table_index;
+ int buf_index;
struct mutex lock;
int power;
@@ -90,12 +93,12 @@ struct pt1_adapter {
u8 *buf;
int upacket_count;
int packet_count;
+ int st_count;
struct dvb_adapter adap;
struct dvb_demux demux;
int users;
struct dmxdev dmxdev;
- struct dvb_net net;
struct dvb_frontend *fe;
int (*orig_set_voltage)(struct dvb_frontend *fe,
fe_sec_voltage_t voltage);
@@ -119,7 +122,7 @@ static u32 pt1_read_reg(struct pt1 *pt1, int reg)
return readl(pt1->regs + reg * 4);
}
-static int pt1_nr_tables = 64;
+static int pt1_nr_tables = 8;
module_param_named(nr_tables, pt1_nr_tables, int, 0);
static void pt1_increment_table_count(struct pt1 *pt1)
@@ -264,6 +267,7 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
struct pt1_adapter *adap;
int offset;
u8 *buf;
+ int sc;
if (!page->upackets[PT1_NR_UPACKETS - 1])
return 0;
@@ -280,6 +284,16 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
else if (!adap->upacket_count)
continue;
+ if (upacket >> 24 & 1)
+ printk_ratelimited(KERN_INFO "earth-pt1: device "
+ "buffer overflowing. table[%d] buf[%d]\n",
+ pt1->table_index, pt1->buf_index);
+ sc = upacket >> 26 & 0x7;
+ if (adap->st_count != -1 && sc != ((adap->st_count + 1) & 0x7))
+ printk_ratelimited(KERN_INFO "earth-pt1: data loss"
+ " in streamID(adapter)[%d]\n", index);
+ adap->st_count = sc;
+
buf = adap->buf;
offset = adap->packet_count * 188 + adap->upacket_count * 3;
buf[offset] = upacket >> 16;
@@ -303,30 +317,25 @@ static int pt1_filter(struct pt1 *pt1, struct pt1_buffer_page *page)
static int pt1_thread(void *data)
{
struct pt1 *pt1;
- int table_index;
- int buf_index;
struct pt1_buffer_page *page;
pt1 = data;
set_freezable();
- table_index = 0;
- buf_index = 0;
-
while (!kthread_should_stop()) {
try_to_freeze();
- page = pt1->tables[table_index].bufs[buf_index].page;
+ page = pt1->tables[pt1->table_index].bufs[pt1->buf_index].page;
if (!pt1_filter(pt1, page)) {
schedule_timeout_interruptible((HZ + 999) / 1000);
continue;
}
- if (++buf_index >= PT1_NR_BUFS) {
+ if (++pt1->buf_index >= PT1_NR_BUFS) {
pt1_increment_table_count(pt1);
- buf_index = 0;
- if (++table_index >= pt1_nr_tables)
- table_index = 0;
+ pt1->buf_index = 0;
+ if (++pt1->table_index >= pt1_nr_tables)
+ pt1->table_index = 0;
}
}
@@ -477,21 +486,60 @@ err:
return ret;
}
+static int pt1_start_polling(struct pt1 *pt1)
+{
+ int ret = 0;
+
+ mutex_lock(&pt1->lock);
+ if (!pt1->kthread) {
+ pt1->kthread = kthread_run(pt1_thread, pt1, "earth-pt1");
+ if (IS_ERR(pt1->kthread)) {
+ ret = PTR_ERR(pt1->kthread);
+ pt1->kthread = NULL;
+ }
+ }
+ mutex_unlock(&pt1->lock);
+ return ret;
+}
+
static int pt1_start_feed(struct dvb_demux_feed *feed)
{
struct pt1_adapter *adap;
adap = container_of(feed->demux, struct pt1_adapter, demux);
- if (!adap->users++)
+ if (!adap->users++) {
+ int ret;
+
+ ret = pt1_start_polling(adap->pt1);
+ if (ret)
+ return ret;
pt1_set_stream(adap->pt1, adap->index, 1);
+ }
return 0;
}
+static void pt1_stop_polling(struct pt1 *pt1)
+{
+ int i, count;
+
+ mutex_lock(&pt1->lock);
+ for (i = 0, count = 0; i < PT1_NR_ADAPS; i++)
+ count += pt1->adaps[i]->users;
+
+ if (count == 0 && pt1->kthread) {
+ kthread_stop(pt1->kthread);
+ pt1->kthread = NULL;
+ }
+ mutex_unlock(&pt1->lock);
+}
+
static int pt1_stop_feed(struct dvb_demux_feed *feed)
{
struct pt1_adapter *adap;
adap = container_of(feed->demux, struct pt1_adapter, demux);
- if (!--adap->users)
+ if (!--adap->users) {
pt1_set_stream(adap->pt1, adap->index, 0);
+ pt1_stop_polling(adap->pt1);
+ }
return 0;
}
@@ -575,7 +623,6 @@ static int pt1_wakeup(struct dvb_frontend *fe)
static void pt1_free_adapter(struct pt1_adapter *adap)
{
- dvb_net_release(&adap->net);
adap->demux.dmx.close(&adap->demux.dmx);
dvb_dmxdev_release(&adap->dmxdev);
dvb_dmx_release(&adap->demux);
@@ -616,6 +663,7 @@ pt1_alloc_adapter(struct pt1 *pt1)
adap->buf = buf;
adap->upacket_count = 0;
adap->packet_count = 0;
+ adap->st_count = -1;
dvb_adap = &adap->adap;
dvb_adap->priv = adap;
@@ -644,8 +692,6 @@ pt1_alloc_adapter(struct pt1 *pt1)
if (ret < 0)
goto err_dmx_release;
- dvb_net_init(dvb_adap, &adap->net, &demux->dmx);
-
return adap;
err_dmx_release:
@@ -1020,7 +1066,8 @@ static void __devexit pt1_remove(struct pci_dev *pdev)
pt1 = pci_get_drvdata(pdev);
regs = pt1->regs;
- kthread_stop(pt1->kthread);
+ if (pt1->kthread)
+ kthread_stop(pt1->kthread);
pt1_cleanup_tables(pt1);
pt1_cleanup_frontends(pt1);
pt1_disable_ram(pt1);
@@ -1043,7 +1090,6 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
void __iomem *regs;
struct pt1 *pt1;
struct i2c_adapter *i2c_adap;
- struct task_struct *kthread;
ret = pci_enable_device(pdev);
if (ret < 0)
@@ -1139,17 +1185,8 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if (ret < 0)
goto err_pt1_cleanup_frontends;
- kthread = kthread_run(pt1_thread, pt1, "pt1");
- if (IS_ERR(kthread)) {
- ret = PTR_ERR(kthread);
- goto err_pt1_cleanup_tables;
- }
-
- pt1->kthread = kthread;
return 0;
-err_pt1_cleanup_tables:
- pt1_cleanup_tables(pt1);
err_pt1_cleanup_frontends:
pt1_cleanup_frontends(pt1);
err_pt1_disable_ram:
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 6ecbcf614878..4bd8bd56befc 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 7b42ace419d9..f6b52d549430 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>
@@ -312,7 +311,7 @@ static void __exit media_devnode_exit(void)
unregister_chrdev_region(media_dev_t, MEDIA_NUM_DEVICES);
}
-module_init(media_devnode_init)
+subsys_initcall(media_devnode_init);
module_exit(media_devnode_exit)
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index e954781c90bf..8db2d7f4b52a 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -43,7 +43,7 @@ config USB_DSBR
config RADIO_MAXIRADIO
tristate "Guillemot MAXI Radio FM 2000 radio"
- depends on VIDEO_V4L2 && PCI
+ depends on VIDEO_V4L2 && PCI && SND
---help---
Choose Y here if you have this radio card. This card may also be
found as Gemtek PCI FM.
@@ -80,6 +80,16 @@ config RADIO_SI4713
To compile this driver as a module, choose M here: the
module will be called radio-si4713.
+config USB_KEENE
+ tristate "Keene FM Transmitter USB support"
+ depends on USB && VIDEO_V4L2
+ ---help---
+ Say Y here if you want to connect this type of FM transmitter
+ to your computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called radio-keene.
+
config RADIO_TEA5764
tristate "TEA5764 I2C FM radio support"
depends on I2C && VIDEO_V4L2
@@ -167,6 +177,10 @@ menuconfig V4L_RADIO_ISA_DRIVERS
if V4L_RADIO_ISA_DRIVERS
+config RADIO_ISA
+ depends on ISA
+ tristate
+
config RADIO_CADET
tristate "ADS Cadet AM/FM Tuner"
depends on ISA && VIDEO_V4L2
@@ -174,20 +188,13 @@ config RADIO_CADET
Choose Y here if you have one of these AM/FM radio cards, and then
fill in the port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
-
- Further documentation on this driver can be found on the WWW at
- <http://linux.blackhawke.net/cadet/>.
-
To compile this driver as a module, choose M here: the
module will be called radio-cadet.
config RADIO_RTRACK
tristate "AIMSlab RadioTrack (aka RadioReveal) support"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
@@ -201,11 +208,7 @@ config RADIO_RTRACK
You must also pass the module a suitable io parameter, 0x248 has
been reported to be used by these cards.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>. More information is
- contained in the file
+ More information is contained in the file
<file:Documentation/video4linux/radiotrack.txt>.
To compile this driver as a module, choose M here: the
@@ -214,7 +217,7 @@ config RADIO_RTRACK
config RADIO_RTRACK_PORT
hex "RadioTrack i/o port (0x20f or 0x30f)"
depends on RADIO_RTRACK=y
- default "20f"
+ default "30f"
help
Enter either 0x30f or 0x20f here. The card default is 0x30f, if you
haven't changed the jumper setting on the card.
@@ -222,14 +225,14 @@ config RADIO_RTRACK_PORT
config RADIO_RTRACK2
tristate "AIMSlab RadioTrack II support"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have this FM radio card, and then fill in the
port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-rtrack2.
@@ -245,15 +248,11 @@ config RADIO_RTRACK2_PORT
config RADIO_AZTECH
tristate "Aztech/Packard Bell Radio"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
-
To compile this driver as a module, choose M here: the
module will be called radio-aztech.
@@ -269,6 +268,7 @@ config RADIO_AZTECH_PORT
config RADIO_GEMTEK
tristate "GemTek Radio card (or compatible) support"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have this FM radio card, and then fill in the
I/O port address and settings below. The following cards either have
@@ -278,23 +278,21 @@ config RADIO_GEMTEK
- Typhoon Radio card (some models)
- Hama Radio card
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
-
To compile this driver as a module, choose M here: the
module will be called radio-gemtek.
config RADIO_GEMTEK_PORT
- hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0c24c or 0x28c)"
+ hex "Fixed I/O port (0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c)"
depends on RADIO_GEMTEK=y
default "34c"
help
- Enter either 0x20c, 0x30c, 0x24c or 0x34c here. The card default is
- 0x34c, if you haven't changed the jumper setting on the card. On
- Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
+ Enter either 0x20c, 0x30c, 0x24c, 0x34c, 0x248 or 0x28c here. The
+ card default is 0x34c, if you haven't changed the jumper setting
+ on the card.
+
+ On Sound Vision 16 Gold PnP with FM Radio (ESS1869+FM Gemtek), the I/O
port is 0x20c, 0x248 or 0x28c.
+
If automatic I/O port probing is enabled this port will be used only
in case of automatic probing failure, ie. as a fallback.
@@ -318,11 +316,6 @@ config RADIO_MIROPCM20
sound card driver "Miro miroSOUND PCM1pro/PCM12/PCM20radio" as this
is required for the radio-miropcm20.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
-
To compile this driver as a module, choose M here: the
module will be called radio-miropcm20.
@@ -332,11 +325,6 @@ config RADIO_SF16FMI
---help---
Choose Y here if you have one of these FM radio cards.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
-
To compile this driver as a module, choose M here: the
module will be called radio-sf16fmi.
@@ -346,50 +334,35 @@ config RADIO_SF16FMR2
---help---
Choose Y here if you have one of these FM radio cards.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found on the WWW at
- <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
-
To compile this driver as a module, choose M here: the
module will be called radio-sf16fmr2.
config RADIO_TERRATEC
tristate "TerraTec ActiveRadio ISA Standalone"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
- Choose Y here if you have this FM radio card, and then fill in the
- port address below. (TODO)
+ Choose Y here if you have this FM radio card.
- Note: This driver is in its early stages. Right now volume and
- frequency control and muting works at least for me, but
- unfortunately I have not found anybody who wants to use this card
- with Linux. So if it is this what YOU are trying to do right now,
- PLEASE DROP ME A NOTE!! Rolf Offermanns <rolf@offermanns.de>.
-
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-terratec.
-config RADIO_TERRATEC_PORT
- hex "Terratec i/o port (normally 0x590)"
- depends on RADIO_TERRATEC=y
- default "590"
- help
- Fill in the I/O port of your TerraTec FM radio card. If unsure, go
- with the default.
-
config RADIO_TRUST
tristate "Trust FM radio card"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
help
This is a driver for the Trust FM radio cards. Say Y if you have
such a card and want to use it under Linux.
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
+
To compile this driver as a module, choose M here: the
module will be called radio-trust.
@@ -404,14 +377,14 @@ config RADIO_TRUST_PORT
config RADIO_TYPHOON
tristate "Typhoon Radio (a.k.a. EcoRadio)"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address and the frequency used for muting below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-typhoon.
@@ -438,14 +411,14 @@ config RADIO_TYPHOON_MUTEFREQ
config RADIO_ZOLTRIX
tristate "Zoltrix Radio"
depends on ISA && VIDEO_V4L2
+ select RADIO_ISA
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
- In order to control your radio card, you will need to use programs
- that are compatible with the Video For Linux API. Information on
- this API and pointers to "v4l" programs may be found at
- <file:Documentation/video4linux/API.html>.
+ Note: this driver hasn't been tested since a long time due to lack
+ of hardware. If you have this hardware, then please contact the
+ linux-media mailinglist.
To compile this driver as a module, choose M here: the
module will be called radio-zoltrix.
diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile
index 390daf94d847..ca8c7d134b95 100644
--- a/drivers/media/radio/Makefile
+++ b/drivers/media/radio/Makefile
@@ -2,6 +2,7 @@
# Makefile for the kernel character device drivers.
#
+obj-$(CONFIG_RADIO_ISA) += radio-isa.o
obj-$(CONFIG_RADIO_AZTECH) += radio-aztech.o
obj-$(CONFIG_RADIO_RTRACK2) += radio-rtrack2.o
obj-$(CONFIG_RADIO_SF16FMI) += radio-sf16fmi.o
@@ -20,6 +21,7 @@ obj-$(CONFIG_RADIO_MIROPCM20) += radio-miropcm20.o
obj-$(CONFIG_USB_DSBR) += dsbr100.o
obj-$(CONFIG_RADIO_SI470X) += si470x/
obj-$(CONFIG_USB_MR800) += radio-mr800.o
+obj-$(CONFIG_USB_KEENE) += radio-keene.o
obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o
obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o
obj-$(CONFIG_RADIO_TEF6862) += tef6862.o
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index 1c3f8440a55c..98e0c8c20312 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -1,16 +1,13 @@
-/* radiotrack (radioreveal) driver for Linux radio support
- * (c) 1997 M. Kirkwood
+/*
+ * AimsLab RadioTrack (aka RadioVeveal) driver
+ *
+ * Copyright 1997 M. Kirkwood
+ *
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
- * History:
- * 1999-02-24 Russell Kroll <rkroll@exploits.org>
- * Fine tuning/VIDEO_TUNER_LOW
- * Frequency range expanded to start at 87 MHz
- *
- * TODO: Allow for more than one of these foolish entities :-)
- *
* Notes on the hardware (reverse engineered from other peoples'
* reverse engineering of AIMS' code :-)
*
@@ -26,6 +23,7 @@
* wait(a_wee_while);
* out(port, stop_changing_the_volume);
*
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include <linux/module.h> /* Modules */
@@ -34,401 +32,179 @@
#include <linux/delay.h> /* msleep */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include "radio-isa.h"
-MODULE_AUTHOR("M.Kirkwood");
+MODULE_AUTHOR("M. Kirkwood");
MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("1.0.0");
#ifndef CONFIG_RADIO_RTRACK_PORT
#define CONFIG_RADIO_RTRACK_PORT -1
#endif
-static int io = CONFIG_RADIO_RTRACK_PORT;
-static int radio_nr = -1;
+#define RTRACK_MAX 2
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
-module_param(radio_nr, int, 0);
+static int io[RTRACK_MAX] = { [0] = CONFIG_RADIO_RTRACK_PORT,
+ [1 ... (RTRACK_MAX - 1)] = -1 };
+static int radio_nr[RTRACK_MAX] = { [0 ... (RTRACK_MAX - 1)] = -1 };
-struct rtrack
-{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int port;
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+struct rtrack {
+ struct radio_isa_card isa;
int curvol;
- unsigned long curfreq;
- int muted;
- int io;
- struct mutex lock;
};
-static struct rtrack rtrack_card;
-
-/* local things */
-
-static void rt_decvol(struct rtrack *rt)
-{
- outb(0x58, rt->io); /* volume down + sigstr + on */
- msleep(100);
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
-}
-
-static void rt_incvol(struct rtrack *rt)
-{
- outb(0x98, rt->io); /* volume up + sigstr + on */
- msleep(100);
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
-}
-
-static void rt_mute(struct rtrack *rt)
-{
- rt->muted = 1;
- mutex_lock(&rt->lock);
- outb(0xd0, rt->io); /* volume steady, off */
- mutex_unlock(&rt->lock);
-}
-
-static int rt_setvol(struct rtrack *rt, int vol)
+static struct radio_isa_card *rtrack_alloc(void)
{
- int i;
-
- mutex_lock(&rt->lock);
-
- if (vol == rt->curvol) { /* requested volume = current */
- if (rt->muted) { /* user is unmuting the card */
- rt->muted = 0;
- outb(0xd8, rt->io); /* enable card */
- }
- mutex_unlock(&rt->lock);
- return 0;
- }
-
- if (vol == 0) { /* volume = 0 means mute the card */
- outb(0x48, rt->io); /* volume down but still "on" */
- msleep(2000); /* make sure it's totally down */
- outb(0xd0, rt->io); /* volume steady, off */
- rt->curvol = 0; /* track the volume state! */
- mutex_unlock(&rt->lock);
- return 0;
- }
+ struct rtrack *rt = kzalloc(sizeof(struct rtrack), GFP_KERNEL);
- rt->muted = 0;
- if (vol > rt->curvol)
- for (i = rt->curvol; i < vol; i++)
- rt_incvol(rt);
- else
- for (i = rt->curvol; i > vol; i--)
- rt_decvol(rt);
-
- rt->curvol = vol;
- mutex_unlock(&rt->lock);
- return 0;
+ if (rt)
+ rt->curvol = 0xff;
+ return rt ? &rt->isa : NULL;
}
-/* the 128+64 on these outb's is to keep the volume stable while tuning
- * without them, the volume _will_ creep up with each frequency change
- * and bit 4 (+16) is to keep the signal strength meter enabled
+/* The 128+64 on these outb's is to keep the volume stable while tuning.
+ * Without them, the volume _will_ creep up with each frequency change
+ * and bit 4 (+16) is to keep the signal strength meter enabled.
*/
-static void send_0_byte(struct rtrack *rt)
+static void send_0_byte(struct radio_isa_card *isa, int on)
{
- if (rt->curvol == 0 || rt->muted) {
- outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */
- outb_p(128+64+16+2+1, rt->io); /* clock */
- }
- else {
- outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */
- outb_p(128+64+16+8+2+1, rt->io); /* clock */
- }
+ outb_p(128+64+16+on+1, isa->io); /* wr-enable + data low */
+ outb_p(128+64+16+on+2+1, isa->io); /* clock */
msleep(1);
}
-static void send_1_byte(struct rtrack *rt)
+static void send_1_byte(struct radio_isa_card *isa, int on)
{
- if (rt->curvol == 0 || rt->muted) {
- outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */
- outb_p(128+64+16+4+2+1, rt->io); /* clock */
- }
- else {
- outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */
- outb_p(128+64+16+8+4+2+1, rt->io); /* clock */
- }
-
+ outb_p(128+64+16+on+4+1, isa->io); /* wr-enable+data high */
+ outb_p(128+64+16+on+4+2+1, isa->io); /* clock */
msleep(1);
}
-static int rt_setfreq(struct rtrack *rt, unsigned long freq)
+static int rtrack_s_frequency(struct radio_isa_card *isa, u32 freq)
{
+ int on = v4l2_ctrl_g_ctrl(isa->mute) ? 0 : 8;
int i;
- mutex_lock(&rt->lock); /* Stop other ops interfering */
-
- rt->curfreq = freq;
-
- /* now uses VIDEO_TUNER_LOW for fine tuning */
-
freq += 171200; /* Add 10.7 MHz IF */
freq /= 800; /* Convert to 50 kHz units */
- send_0_byte(rt); /* 0: LSB of frequency */
+ send_0_byte(isa, on); /* 0: LSB of frequency */
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
if (freq & (1 << i))
- send_1_byte(rt);
+ send_1_byte(isa, on);
else
- send_0_byte(rt);
-
- send_0_byte(rt); /* 14: test bit - always 0 */
- send_0_byte(rt); /* 15: test bit - always 0 */
-
- send_0_byte(rt); /* 16: band data 0 - always 0 */
- send_0_byte(rt); /* 17: band data 1 - always 0 */
- send_0_byte(rt); /* 18: band data 2 - always 0 */
- send_0_byte(rt); /* 19: time base - always 0 */
-
- send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */
- send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */
- send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */
- send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */
-
- if (rt->curvol == 0 || rt->muted)
- outb(0xd0, rt->io); /* volume steady + sigstr */
- else
- outb(0xd8, rt->io); /* volume steady + sigstr + on */
-
- mutex_unlock(&rt->lock);
-
- return 0;
-}
-
-static int rt_getsigstr(struct rtrack *rt)
-{
- int sig = 1;
-
- mutex_lock(&rt->lock);
- if (inb(rt->io) & 2) /* bit set = no signal present */
- sig = 0;
- mutex_unlock(&rt->lock);
- return sig;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
- strlcpy(v->card, "RadioTrack", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct rtrack *rt = video_drvdata(file);
+ send_0_byte(isa, on);
- if (v->index > 0)
- return -EINVAL;
+ send_0_byte(isa, on); /* 14: test bit - always 0 */
+ send_0_byte(isa, on); /* 15: test bit - always 0 */
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff * rt_getsigstr(rt);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct rtrack *rt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- rt_setfreq(rt, f->frequency);
- return 0;
-}
+ send_0_byte(isa, on); /* 16: band data 0 - always 0 */
+ send_0_byte(isa, on); /* 17: band data 1 - always 0 */
+ send_0_byte(isa, on); /* 18: band data 2 - always 0 */
+ send_0_byte(isa, on); /* 19: time base - always 0 */
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct rtrack *rt = video_drvdata(file);
+ send_0_byte(isa, on); /* 20: spacing (0 = 25 kHz) */
+ send_1_byte(isa, on); /* 21: spacing (1 = 25 kHz) */
+ send_0_byte(isa, on); /* 22: spacing (0 = 25 kHz) */
+ send_1_byte(isa, on); /* 23: AM/FM (FM = 1, always) */
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
+ outb(0xd0 + on, isa->io); /* volume steady + sigstr */
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static u32 rtrack_g_signal(struct radio_isa_card *isa)
{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
- }
- return -EINVAL;
+ /* bit set = no signal present */
+ return 0xffff * !(inb(isa->io) & 2);
}
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int rtrack_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- struct rtrack *rt = video_drvdata(file);
+ struct rtrack *rt = container_of(isa, struct rtrack, isa);
+ int curvol = rt->curvol;
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = rt->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = rt->curvol;
+ if (mute) {
+ outb(0xd0, isa->io); /* volume steady + sigstr + off */
return 0;
}
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- rt_mute(rt);
- else
- rt_setvol(rt, rt->curvol);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- rt_setvol(rt, ctrl->value);
- return 0;
+ if (vol == 0) { /* volume = 0 means mute the card */
+ outb(0x48, isa->io); /* volume down but still "on" */
+ msleep(curvol * 3); /* make sure it's totally down */
+ } else if (curvol < vol) {
+ outb(0x98, isa->io); /* volume up + sigstr + on */
+ for (; curvol < vol; curvol++)
+ udelay(3000);
+ } else if (curvol > vol) {
+ outb(0x58, isa->io); /* volume down + sigstr + on */
+ for (; curvol > vol; curvol--)
+ udelay(3000);
}
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
+ outb(0xd8, isa->io); /* volume steady + sigstr + on */
+ rt->curvol = vol;
return 0;
}
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+/* Mute card - prevents noisy bootups */
+static int rtrack_initialize(struct radio_isa_card *isa)
{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
+ /* this ensures that the volume is all the way up */
+ outb(0x90, isa->io); /* volume up but still "on" */
+ msleep(3000); /* make sure it's totally up */
+ outb(0xc0, isa->io); /* steady volume, mute card */
return 0;
}
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations rtrack_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops rtrack_ops = {
+ .alloc = rtrack_alloc,
+ .init = rtrack_initialize,
+ .s_mute_volume = rtrack_s_mute_volume,
+ .s_frequency = rtrack_s_frequency,
+ .g_signal = rtrack_g_signal,
};
-static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int rtrack_ioports[] = { 0x20f, 0x30f };
+
+static struct radio_isa_driver rtrack_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-aimslab",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = rtrack_ioports,
+ .num_of_io_ports = ARRAY_SIZE(rtrack_ioports),
+ .region_size = 2,
+ .card = "AIMSlab RadioTrack/RadioReveal",
+ .ops = &rtrack_ops,
+ .has_stereo = true,
+ .max_volume = 0xff,
};
static int __init rtrack_init(void)
{
- struct rtrack *rt = &rtrack_card;
- struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
- rt->io = io;
-
- if (rt->io == -1) {
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
- return -EINVAL;
- }
-
- if (!request_region(rt->io, 2, "rtrack")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(rt->io, 2);
- v4l2_err(v4l2_dev, "could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
- rt->vdev.v4l2_dev = v4l2_dev;
- rt->vdev.fops = &rtrack_fops;
- rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
- rt->vdev.release = video_device_release_empty;
- video_set_drvdata(&rt->vdev, rt);
-
- /* Set up the I/O locking */
-
- mutex_init(&rt->lock);
-
- /* mute card - prevents noisy bootups */
-
- /* this ensures that the volume is all the way down */
- outb(0x48, rt->io); /* volume down but still "on" */
- msleep(2000); /* make sure it's totally down */
- outb(0xc0, rt->io); /* steady volume, mute card */
-
- if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(&rt->v4l2_dev);
- release_region(rt->io, 2);
- return -EINVAL;
- }
- v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
-
- return 0;
+ return isa_register_driver(&rtrack_driver.driver, RTRACK_MAX);
}
static void __exit rtrack_exit(void)
{
- struct rtrack *rt = &rtrack_card;
-
- video_unregister_device(&rt->vdev);
- v4l2_device_unregister(&rt->v4l2_dev);
- release_region(rt->io, 2);
+ isa_unregister_driver(&rtrack_driver.driver);
}
module_init(rtrack_init);
module_exit(rtrack_exit);
-
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index eed7b0840734..177bcbd7a7c1 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -1,5 +1,7 @@
-/* radio-aztech.c - Aztech radio card driver for Linux 2.2
+/*
+ * radio-aztech.c - Aztech radio card driver
*
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@xs4all.nl>
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
* Adapted to support the Video for Linux API by
* Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
@@ -10,19 +12,7 @@
* Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
* William McGrath (wmcgrath@twilight.vtc.vsc.edu)
*
- * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
- * along with more information on the card itself.
- *
- * History:
- * 1999-02-24 Russell Kroll <rkroll@exploits.org>
- * Fine tuning/VIDEO_TUNER_LOW
- * Range expanded to 87-108 MHz (from 87.9-107.8)
- *
- * Notable changes from the original source:
- * - includes stripped down to the essentials
- * - for loops used as delays replaced with udelay()
- * - #defines removed, changed to static values
- * - tuning structure changed - no more character arrays, other changes
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include <linux/module.h> /* Modules */
@@ -31,126 +21,72 @@
#include <linux/delay.h> /* udelay */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include "radio-isa.h"
MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Aztech radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("1.0.0");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
-
#ifndef CONFIG_RADIO_AZTECH_PORT
#define CONFIG_RADIO_AZTECH_PORT -1
#endif
-static int io = CONFIG_RADIO_AZTECH_PORT;
-static int radio_nr = -1;
-static int radio_wait_time = 1000;
+#define AZTECH_MAX 2
-module_param(io, int, 0);
-module_param(radio_nr, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
+static int io[AZTECH_MAX] = { [0] = CONFIG_RADIO_AZTECH_PORT,
+ [1 ... (AZTECH_MAX - 1)] = -1 };
+static int radio_nr[AZTECH_MAX] = { [0 ... (AZTECH_MAX - 1)] = -1 };
+static const int radio_wait_time = 1000;
-struct aztech
-{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Aztech card (0x350 or 0x358)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
+
+struct aztech {
+ struct radio_isa_card isa;
int curvol;
- unsigned long curfreq;
- int stereo;
- struct mutex lock;
};
-static struct aztech aztech_card;
-
-static int volconvert(int level)
-{
- level >>= 14; /* Map 16bits down to 2 bit */
- level &= 3;
-
- /* convert to card-friendly values */
- switch (level) {
- case 0:
- return 0;
- case 1:
- return 1;
- case 2:
- return 4;
- case 3:
- return 5;
- }
- return 0; /* Quieten gcc */
-}
-
static void send_0_byte(struct aztech *az)
{
udelay(radio_wait_time);
- outb_p(2 + volconvert(az->curvol), az->io);
- outb_p(64 + 2 + volconvert(az->curvol), az->io);
+ outb_p(2 + az->curvol, az->isa.io);
+ outb_p(64 + 2 + az->curvol, az->isa.io);
}
static void send_1_byte(struct aztech *az)
{
- udelay (radio_wait_time);
- outb_p(128 + 2 + volconvert(az->curvol), az->io);
- outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io);
-}
-
-static int az_setvol(struct aztech *az, int vol)
-{
- mutex_lock(&az->lock);
- outb(volconvert(vol), az->io);
- mutex_unlock(&az->lock);
- return 0;
-}
-
-/* thanks to Michael Dwyer for giving me a dose of clues in
- * the signal strength department..
- *
- * This card has a stereo bit - bit 0 set = mono, not set = stereo
- * It also has a "signal" bit - bit 1 set = bad signal, not set = good
- *
- */
-
-static int az_getsigstr(struct aztech *az)
-{
- int sig = 1;
-
- mutex_lock(&az->lock);
- if (inb(az->io) & 2) /* bit set = no signal present */
- sig = 0;
- mutex_unlock(&az->lock);
- return sig;
+ udelay(radio_wait_time);
+ outb_p(128 + 2 + az->curvol, az->isa.io);
+ outb_p(128 + 64 + 2 + az->curvol, az->isa.io);
}
-static int az_getstereo(struct aztech *az)
+static struct radio_isa_card *aztech_alloc(void)
{
- int stereo = 1;
+ struct aztech *az = kzalloc(sizeof(*az), GFP_KERNEL);
- mutex_lock(&az->lock);
- if (inb(az->io) & 1) /* bit set = mono */
- stereo = 0;
- mutex_unlock(&az->lock);
- return stereo;
+ return az ? &az->isa : NULL;
}
-static int az_setfreq(struct aztech *az, unsigned long frequency)
+static int aztech_s_frequency(struct radio_isa_card *isa, u32 freq)
{
+ struct aztech *az = container_of(isa, struct aztech, isa);
int i;
- mutex_lock(&az->lock);
-
- az->curfreq = frequency;
- frequency += 171200; /* Add 10.7 MHz IF */
- frequency /= 800; /* Convert to 50 kHz units */
+ freq += 171200; /* Add 10.7 MHz IF */
+ freq /= 800; /* Convert to 50 kHz units */
send_0_byte(az); /* 0: LSB of frequency */
for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
- if (frequency & (1 << i))
+ if (freq & (1 << i))
send_1_byte(az);
else
send_0_byte(az);
@@ -158,7 +94,7 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
send_0_byte(az); /* 14: test bit - always 0 */
send_0_byte(az); /* 15: test bit - always 0 */
send_0_byte(az); /* 16: band data 0 - always 0 */
- if (az->stereo) /* 17: stereo (1 to enable) */
+ if (isa->stereo) /* 17: stereo (1 to enable) */
send_1_byte(az);
else
send_0_byte(az);
@@ -173,225 +109,77 @@ static int az_setfreq(struct aztech *az, unsigned long frequency)
/* latch frequency */
udelay(radio_wait_time);
- outb_p(128 + 64 + volconvert(az->curvol), az->io);
-
- mutex_unlock(&az->lock);
-
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
- strlcpy(v->card, "Aztech Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct aztech *az = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
-
- v->rangelow = 87 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (az_getstereo(az))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * az_getsigstr(az);
-
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
+ outb_p(128 + 64 + az->curvol, az->isa.io);
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
return 0;
}
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+/* thanks to Michael Dwyer for giving me a dose of clues in
+ * the signal strength department..
+ *
+ * This card has a stereo bit - bit 0 set = mono, not set = stereo
+ */
+static u32 aztech_g_rxsubchans(struct radio_isa_card *isa)
{
- return a->index ? -EINVAL : 0;
+ if (inb(isa->io) & 1)
+ return V4L2_TUNER_SUB_MONO;
+ return V4L2_TUNER_SUB_STEREO;
}
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
+static int aztech_s_stereo(struct radio_isa_card *isa, bool stereo)
{
- struct aztech *az = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- az_setfreq(az, f->frequency);
- return 0;
+ return aztech_s_frequency(isa, isa->freq);
}
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
+static int aztech_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- struct aztech *az = video_drvdata(file);
+ struct aztech *az = container_of(isa, struct aztech, isa);
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = az->curfreq;
+ if (mute)
+ vol = 0;
+ az->curvol = (vol & 1) + ((vol & 2) << 1);
+ outb(az->curvol, isa->io);
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct aztech *az = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (az->curvol == 0)
- ctrl->value = 1;
- else
- ctrl->value = 0;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = az->curvol * 6554;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct aztech *az = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- az_setvol(az, 0);
- else
- az_setvol(az, az->curvol);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- az_setvol(az, ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static const struct v4l2_file_operations aztech_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops aztech_ops = {
+ .alloc = aztech_alloc,
+ .s_mute_volume = aztech_s_mute_volume,
+ .s_frequency = aztech_s_frequency,
+ .s_stereo = aztech_s_stereo,
+ .g_rxsubchans = aztech_g_rxsubchans,
};
-static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int aztech_ioports[] = { 0x350, 0x358 };
+
+static struct radio_isa_driver aztech_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-aztech",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = aztech_ioports,
+ .num_of_io_ports = ARRAY_SIZE(aztech_ioports),
+ .region_size = 2,
+ .card = "Aztech Radio",
+ .ops = &aztech_ops,
+ .has_stereo = true,
+ .max_volume = 3,
};
static int __init aztech_init(void)
{
- struct aztech *az = &aztech_card;
- struct v4l2_device *v4l2_dev = &az->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
- az->io = io;
-
- if (az->io == -1) {
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n");
- return -EINVAL;
- }
-
- if (!request_region(az->io, 2, "aztech")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(az->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- mutex_init(&az->lock);
- strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
- az->vdev.v4l2_dev = v4l2_dev;
- az->vdev.fops = &aztech_fops;
- az->vdev.ioctl_ops = &aztech_ioctl_ops;
- az->vdev.release = video_device_release_empty;
- video_set_drvdata(&az->vdev, az);
- /* mute card - prevents noisy bootups */
- outb(0, az->io);
-
- if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(az->io, 2);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
- return 0;
+ return isa_register_driver(&aztech_driver.driver, AZTECH_MAX);
}
static void __exit aztech_exit(void)
{
- struct aztech *az = &aztech_card;
-
- video_unregister_device(&az->vdev);
- v4l2_device_unregister(&az->v4l2_dev);
- release_region(az->io, 2);
+ isa_unregister_driver(&aztech_driver.driver);
}
module_init(aztech_init);
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 36ce0611c037..2e639ce6f256 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -1,4 +1,7 @@
-/* GemTek radio card driver for Linux (C) 1998 Jonas Munsin <jmunsin@iki.fi>
+/*
+ * GemTek radio card driver
+ *
+ * Copyright 1998 Jonas Munsin <jmunsin@iki.fi>
*
* GemTek hasn't released any specs on the card, so the protocol had to
* be reverse engineered with dosemu.
@@ -11,9 +14,12 @@
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
- * TODO: Allow for more than one of these foolish entities :-)
- *
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Note: this card seems to swap the left and right audio channels!
+ *
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include <linux/module.h> /* Modules */
@@ -23,8 +29,10 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
+#include "radio-isa.h"
/*
* Module info.
@@ -33,7 +41,7 @@
MODULE_AUTHOR("Jonas Munsin, Pekka Seppänen <pexu@kapsi.fi>");
MODULE_DESCRIPTION("A driver for the GemTek Radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.4");
+MODULE_VERSION("1.0.0");
/*
* Module params.
@@ -46,45 +54,29 @@ MODULE_VERSION("0.0.4");
#define CONFIG_RADIO_GEMTEK_PROBE 1
#endif
-static int io = CONFIG_RADIO_GEMTEK_PORT;
-static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
-static bool hardmute;
-static bool shutdown = 1;
-static bool keepmuted = 1;
-static bool initmute = 1;
-static int radio_nr = -1;
+#define GEMTEK_MAX 4
-module_param(io, int, 0444);
-MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
- "probing is disabled or fails. The most common I/O ports are: 0x20c "
- "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
- "work for the combined sound/radiocard).");
+static bool probe = CONFIG_RADIO_GEMTEK_PROBE;
+static bool hardmute;
+static int io[GEMTEK_MAX] = { [0] = CONFIG_RADIO_GEMTEK_PORT,
+ [1 ... (GEMTEK_MAX - 1)] = -1 };
+static int radio_nr[GEMTEK_MAX] = { [0 ... (GEMTEK_MAX - 1)] = -1 };
module_param(probe, bool, 0444);
-MODULE_PARM_DESC(probe, "Enable automatic device probing. Note: only the most "
- "common I/O ports used by the card are probed.");
+MODULE_PARM_DESC(probe, "Enable automatic device probing.");
module_param(hardmute, bool, 0644);
-MODULE_PARM_DESC(hardmute, "Enable `hard muting' by shutting down PLL, may "
+MODULE_PARM_DESC(hardmute, "Enable 'hard muting' by shutting down PLL, may "
"reduce static noise.");
-module_param(shutdown, bool, 0644);
-MODULE_PARM_DESC(shutdown, "Enable shutting down PLL and muting line when "
- "module is unloaded.");
-
-module_param(keepmuted, bool, 0644);
-MODULE_PARM_DESC(keepmuted, "Keep card muted even when frequency is changed.");
-
-module_param(initmute, bool, 0444);
-MODULE_PARM_DESC(initmute, "Mute card when module is loaded.");
-
-module_param(radio_nr, int, 0444);
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "Force I/O ports for the GemTek Radio card if automatic "
+ "probing is disabled or fails. The most common I/O ports are: 0x20c "
+ "0x30c, 0x24c or 0x34c (0x20c, 0x248 and 0x28c have been reported to "
+ "work for the combined sound/radiocard).");
-/*
- * Functions for controlling the card.
- */
-#define GEMTEK_LOWFREQ (87*16000)
-#define GEMTEK_HIGHFREQ (108*16000)
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
/*
* Frequency calculation constants. Intermediate frequency 10.52 MHz (nominal
@@ -108,18 +100,11 @@ module_param(radio_nr, int, 0444);
#define LONG_DELAY 75 /* usec */
struct gemtek {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- struct mutex lock;
- unsigned long lastfreq;
- int muted;
- int verified;
- int io;
+ struct radio_isa_card isa;
+ bool muted;
u32 bu2614data;
};
-static struct gemtek gemtek_card;
-
#define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */
#define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */
#define BU2614_VOID_BITS 4 /* unused */
@@ -166,31 +151,24 @@ static struct gemtek gemtek_card;
*/
static void gemtek_bu2614_transmit(struct gemtek *gt)
{
+ struct radio_isa_card *isa = &gt->isa;
int i, bit, q, mute;
- mutex_lock(&gt->lock);
-
mute = gt->muted ? GEMTEK_MT : 0x00;
- outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
- udelay(SHORT_DELAY);
- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
+ outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, isa->io);
udelay(LONG_DELAY);
for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {
bit = (q & 1) ? GEMTEK_DA : 0;
- outb_p(mute | GEMTEK_CE | bit, gt->io);
+ outb_p(mute | GEMTEK_CE | bit, isa->io);
udelay(SHORT_DELAY);
- outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io);
+ outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY);
}
- outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
+ outb_p(mute | GEMTEK_DA | GEMTEK_CK, isa->io);
udelay(SHORT_DELAY);
- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
- udelay(LONG_DELAY);
-
- mutex_unlock(&gt->lock);
}
/*
@@ -198,21 +176,27 @@ static void gemtek_bu2614_transmit(struct gemtek *gt)
*/
static unsigned long gemtek_convfreq(unsigned long freq)
{
- return ((freq<<FSCALE) + IF_OFFSET + REF_FREQ/2) / REF_FREQ;
+ return ((freq << FSCALE) + IF_OFFSET + REF_FREQ / 2) / REF_FREQ;
+}
+
+static struct radio_isa_card *gemtek_alloc(void)
+{
+ struct gemtek *gt = kzalloc(sizeof(*gt), GFP_KERNEL);
+
+ if (gt)
+ gt->muted = true;
+ return gt ? &gt->isa : NULL;
}
/*
* Set FM-frequency.
*/
-static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
+static int gemtek_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- if (keepmuted && hardmute && gt->muted)
- return;
+ struct gemtek *gt = container_of(isa, struct gemtek, isa);
- freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);
-
- gt->lastfreq = freq;
- gt->muted = 0;
+ if (hardmute && gt->muted)
+ return 0;
gemtek_bu2614_set(gt, BU2614_PORT, 0);
gemtek_bu2614_set(gt, BU2614_FMES, 0);
@@ -220,23 +204,25 @@ static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
gemtek_bu2614_set(gt, BU2614_SWAL, 0);
gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */
gemtek_bu2614_set(gt, BU2614_TEST, 0);
-
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));
-
gemtek_bu2614_transmit(gt);
+ return 0;
}
/*
* Set mute flag.
*/
-static void gemtek_mute(struct gemtek *gt)
+static int gemtek_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
+ struct gemtek *gt = container_of(isa, struct gemtek, isa);
int i;
- gt->muted = 1;
-
+ gt->muted = mute;
if (hardmute) {
+ if (!mute)
+ return gemtek_s_frequency(isa, isa->freq);
+
/* Turn off PLL, disable data output */
gemtek_bu2614_set(gt, BU2614_PORT, 0);
gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */
@@ -247,367 +233,85 @@ static void gemtek_mute(struct gemtek *gt)
gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);
gemtek_bu2614_set(gt, BU2614_FREQ, 0);
gemtek_bu2614_transmit(gt);
- return;
+ return 0;
}
- mutex_lock(&gt->lock);
-
/* Read bus contents (CE, CK and DA). */
- i = inb_p(gt->io);
+ i = inb_p(isa->io);
/* Write it back with mute flag set. */
- outb_p((i >> 5) | GEMTEK_MT, gt->io);
+ outb_p((i >> 5) | (mute ? GEMTEK_MT : 0), isa->io);
udelay(SHORT_DELAY);
-
- mutex_unlock(&gt->lock);
-}
-
-/*
- * Unset mute flag.
- */
-static void gemtek_unmute(struct gemtek *gt)
-{
- int i;
-
- gt->muted = 0;
- if (hardmute) {
- /* Turn PLL back on. */
- gemtek_setfreq(gt, gt->lastfreq);
- return;
- }
- mutex_lock(&gt->lock);
-
- i = inb_p(gt->io);
- outb_p(i >> 5, gt->io);
- udelay(SHORT_DELAY);
-
- mutex_unlock(&gt->lock);
+ return 0;
}
-/*
- * Get signal strength (= stereo status).
- */
-static inline int gemtek_getsigstr(struct gemtek *gt)
+static u32 gemtek_g_rxsubchans(struct radio_isa_card *isa)
{
- int sig;
-
- mutex_lock(&gt->lock);
- sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;
- mutex_unlock(&gt->lock);
- return sig;
+ if (inb_p(isa->io) & GEMTEK_NS)
+ return V4L2_TUNER_SUB_MONO;
+ return V4L2_TUNER_SUB_STEREO;
}
/*
* Check if requested card acts like GemTek Radio card.
*/
-static int gemtek_verify(struct gemtek *gt, int port)
+static bool gemtek_probe(struct radio_isa_card *isa, int io)
{
int i, q;
- if (gt->verified == port)
- return 1;
-
- mutex_lock(&gt->lock);
-
- q = inb_p(port); /* Read bus contents before probing. */
+ q = inb_p(io); /* Read bus contents before probing. */
/* Try to turn on CE, CK and DA respectively and check if card responds
properly. */
for (i = 0; i < 3; ++i) {
- outb_p(1 << i, port);
+ outb_p(1 << i, io);
udelay(SHORT_DELAY);
- if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) {
- mutex_unlock(&gt->lock);
- return 0;
- }
+ if ((inb_p(io) & ~GEMTEK_NS) != (0x17 | (1 << (i + 5))))
+ return false;
}
- outb_p(q >> 5, port); /* Write bus contents back. */
+ outb_p(q >> 5, io); /* Write bus contents back. */
udelay(SHORT_DELAY);
-
- mutex_unlock(&gt->lock);
- gt->verified = port;
-
- return 1;
-}
-
-/*
- * Automatic probing for card.
- */
-static int gemtek_probe(struct gemtek *gt)
-{
- struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
- int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
- int i;
-
- if (!probe) {
- v4l2_info(v4l2_dev, "Automatic device probing disabled.\n");
- return -1;
- }
-
- v4l2_info(v4l2_dev, "Automatic device probing enabled.\n");
-
- for (i = 0; i < ARRAY_SIZE(ioports); ++i) {
- v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]);
-
- if (!request_region(ioports[i], 1, "gemtek-probe")) {
- v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n",
- ioports[i]);
- continue;
- }
-
- if (gemtek_verify(gt, ioports[i])) {
- v4l2_info(v4l2_dev, "Card found from I/O port "
- "0x%x!\n", ioports[i]);
-
- release_region(ioports[i], 1);
- gt->io = ioports[i];
- return gt->io;
- }
-
- release_region(ioports[i], 1);
- }
-
- v4l2_err(v4l2_dev, "Automatic probing failed!\n");
- return -1;
+ return true;
}
-/*
- * Video 4 Linux stuff.
- */
-
-static const struct v4l2_file_operations gemtek_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops gemtek_ops = {
+ .alloc = gemtek_alloc,
+ .probe = gemtek_probe,
+ .s_mute_volume = gemtek_s_mute_volume,
+ .s_frequency = gemtek_s_frequency,
+ .g_rxsubchans = gemtek_g_rxsubchans,
};
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
- strlcpy(v->card, "GemTek", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
-{
- struct gemtek *gt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = GEMTEK_LOWFREQ;
- v->rangehigh = GEMTEK_HIGHFREQ;
- v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
- v->signal = 0xffff * gemtek_getsigstr(gt);
- if (v->signal) {
- v->audmode = V4L2_TUNER_MODE_STEREO;
- v->rxsubchans = V4L2_TUNER_SUB_STEREO;
- } else {
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- }
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
-{
- return (v->index != 0) ? -EINVAL : 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct gemtek *gt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = gt->lastfreq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct gemtek *gt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- gemtek_setfreq(gt, f->frequency);
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
- default:
- return -EINVAL;
- }
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct gemtek *gt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = gt->muted;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct gemtek *gt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- gemtek_mute(gt);
- else
- gemtek_unmute(gt);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return (i != 0) ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
-{
- return (a->index != 0) ? -EINVAL : 0;
-}
-
-static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl
+static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
+
+static struct radio_isa_driver gemtek_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-gemtek",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = gemtek_ioports,
+ .num_of_io_ports = ARRAY_SIZE(gemtek_ioports),
+ .region_size = 1,
+ .card = "GemTek Radio",
+ .ops = &gemtek_ops,
+ .has_stereo = true,
};
-/*
- * Initialization / cleanup related stuff.
- */
-
static int __init gemtek_init(void)
{
- struct gemtek *gt = &gemtek_card;
- struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));
-
- v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n");
-
- mutex_init(&gt->lock);
-
- gt->verified = -1;
- gt->io = io;
- gemtek_probe(gt);
- if (gt->io) {
- if (!request_region(gt->io, 1, "gemtek")) {
- v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io);
- return -EBUSY;
- }
-
- if (!gemtek_verify(gt, gt->io))
- v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "
- "respond properly, check your "
- "configuration.\n", gt->io);
- else
- v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io);
- } else if (probe) {
- v4l2_err(v4l2_dev, "Automatic probing failed and no "
- "fixed I/O port defined.\n");
- return -ENODEV;
- } else {
- v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "
- "I/O port defined.");
- return -EINVAL;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- release_region(gt->io, 1);
- return res;
- }
-
- strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));
- gt->vdev.v4l2_dev = v4l2_dev;
- gt->vdev.fops = &gemtek_fops;
- gt->vdev.ioctl_ops = &gemtek_ioctl_ops;
- gt->vdev.release = video_device_release_empty;
- video_set_drvdata(&gt->vdev, gt);
-
- /* Set defaults */
- gt->lastfreq = GEMTEK_LOWFREQ;
- gt->bu2614data = 0;
-
- if (initmute)
- gemtek_mute(gt);
-
- if (video_register_device(&gt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(gt->io, 1);
- return -EBUSY;
- }
-
- return 0;
+ gemtek_driver.probe = probe;
+ return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX);
}
-/*
- * Module cleanup
- */
static void __exit gemtek_exit(void)
{
- struct gemtek *gt = &gemtek_card;
- struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
-
- if (shutdown) {
- hardmute = 1; /* Turn off PLL */
- gemtek_mute(gt);
- } else {
- v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n");
- }
-
- video_unregister_device(&gt->vdev);
- v4l2_device_unregister(&gt->v4l2_dev);
- release_region(gt->io, 1);
+ hardmute = 1; /* Turn off PLL */
+ isa_unregister_driver(&gemtek_driver.driver);
}
module_init(gemtek_init);
diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c
new file mode 100644
index 000000000000..06f906351fad
--- /dev/null
+++ b/drivers/media/radio/radio-isa.c
@@ -0,0 +1,340 @@
+/*
+ * Framework for ISA radio drivers.
+ * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
+ * to concentrate on the actual hardware operation.
+ *
+ * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.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/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+
+#include "radio-isa.h"
+
+MODULE_AUTHOR("Hans Verkuil");
+MODULE_DESCRIPTION("A framework for ISA radio drivers.");
+MODULE_LICENSE("GPL");
+
+#define FREQ_LOW (87U * 16000U)
+#define FREQ_HIGH (108U * 16000U)
+
+static int radio_isa_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+
+ strlcpy(v->driver, isa->drv->driver.driver.name, sizeof(v->driver));
+ strlcpy(v->card, isa->drv->card, sizeof(v->card));
+ snprintf(v->bus_info, sizeof(v->bus_info), "ISA:%s", isa->v4l2_dev.name);
+
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ v->device_caps = v->capabilities | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int radio_isa_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+ const struct radio_isa_ops *ops = isa->drv->ops;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = FREQ_LOW;
+ v->rangehigh = FREQ_HIGH;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ if (isa->drv->has_stereo)
+ v->capability |= V4L2_TUNER_CAP_STEREO;
+
+ if (ops->g_rxsubchans)
+ v->rxsubchans = ops->g_rxsubchans(isa);
+ else
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->audmode = isa->stereo ? V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO;
+ if (ops->g_signal)
+ v->signal = ops->g_signal(isa);
+ else
+ v->signal = (v->rxsubchans & V4L2_TUNER_SUB_STEREO) ?
+ 0xffff : 0;
+ return 0;
+}
+
+static int radio_isa_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+ const struct radio_isa_ops *ops = isa->drv->ops;
+
+ if (v->index)
+ return -EINVAL;
+ if (ops->s_stereo) {
+ isa->stereo = (v->audmode == V4L2_TUNER_MODE_STEREO);
+ return ops->s_stereo(isa, isa->stereo);
+ }
+ return 0;
+}
+
+static int radio_isa_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+ int res;
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+ f->frequency = clamp(f->frequency, FREQ_LOW, FREQ_HIGH);
+ res = isa->drv->ops->s_frequency(isa, f->frequency);
+ if (res == 0)
+ isa->freq = f->frequency;
+ return res;
+}
+
+static int radio_isa_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = isa->freq;
+ return 0;
+}
+
+static int radio_isa_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct radio_isa_card *isa =
+ container_of(ctrl->handler, struct radio_isa_card, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ return isa->drv->ops->s_mute_volume(isa, ctrl->val,
+ isa->volume ? isa->volume->val : 0);
+ }
+ return -EINVAL;
+}
+
+static int radio_isa_log_status(struct file *file, void *priv)
+{
+ struct radio_isa_card *isa = video_drvdata(file);
+
+ v4l2_info(&isa->v4l2_dev, "I/O Port = 0x%03x\n", isa->io);
+ v4l2_ctrl_handler_log_status(&isa->hdl, isa->v4l2_dev.name);
+ return 0;
+}
+
+static int radio_isa_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type == V4L2_EVENT_CTRL)
+ return v4l2_event_subscribe(fh, sub, 0);
+ return -EINVAL;
+}
+
+static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = {
+ .s_ctrl = radio_isa_s_ctrl,
+};
+
+static const struct v4l2_file_operations radio_isa_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = {
+ .vidioc_querycap = radio_isa_querycap,
+ .vidioc_g_tuner = radio_isa_g_tuner,
+ .vidioc_s_tuner = radio_isa_s_tuner,
+ .vidioc_g_frequency = radio_isa_g_frequency,
+ .vidioc_s_frequency = radio_isa_s_frequency,
+ .vidioc_log_status = radio_isa_log_status,
+ .vidioc_subscribe_event = radio_isa_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+int radio_isa_match(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_driver *drv = pdev->platform_data;
+
+ return drv->probe || drv->io_params[dev] >= 0;
+}
+EXPORT_SYMBOL_GPL(radio_isa_match);
+
+static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io)
+{
+ int i;
+
+ for (i = 0; i < drv->num_of_io_ports; i++)
+ if (drv->io_ports[i] == io)
+ return true;
+ return false;
+}
+
+int radio_isa_probe(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_driver *drv = pdev->platform_data;
+ const struct radio_isa_ops *ops = drv->ops;
+ struct v4l2_device *v4l2_dev;
+ struct radio_isa_card *isa;
+ int res;
+
+ isa = drv->ops->alloc();
+ if (isa == NULL)
+ return -ENOMEM;
+ dev_set_drvdata(pdev, isa);
+ isa->drv = drv;
+ isa->io = drv->io_params[dev];
+ v4l2_dev = &isa->v4l2_dev;
+ strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name));
+
+ if (drv->probe && ops->probe) {
+ int i;
+
+ for (i = 0; i < drv->num_of_io_ports; ++i) {
+ int io = drv->io_ports[i];
+
+ if (request_region(io, drv->region_size, v4l2_dev->name)) {
+ bool found = ops->probe(isa, io);
+
+ release_region(io, drv->region_size);
+ if (found) {
+ isa->io = io;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!radio_isa_valid_io(drv, isa->io)) {
+ int i;
+
+ if (isa->io < 0)
+ return -ENODEV;
+ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x",
+ drv->io_ports[0]);
+ for (i = 1; i < drv->num_of_io_ports; i++)
+ printk(KERN_CONT "/0x%03x", drv->io_ports[i]);
+ printk(KERN_CONT ".\n");
+ kfree(isa);
+ return -EINVAL;
+ }
+
+ if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) {
+ v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io);
+ kfree(isa);
+ return -EBUSY;
+ }
+
+ res = v4l2_device_register(pdev, v4l2_dev);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ goto err_dev_reg;
+ }
+
+ v4l2_ctrl_handler_init(&isa->hdl, 1);
+ isa->mute = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
+ V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);
+ if (drv->max_volume)
+ isa->volume = v4l2_ctrl_new_std(&isa->hdl, &radio_isa_ctrl_ops,
+ V4L2_CID_AUDIO_VOLUME, 0, drv->max_volume, 1,
+ drv->max_volume);
+ v4l2_dev->ctrl_handler = &isa->hdl;
+ if (isa->hdl.error) {
+ res = isa->hdl.error;
+ v4l2_err(v4l2_dev, "Could not register controls\n");
+ goto err_hdl;
+ }
+ if (drv->max_volume)
+ v4l2_ctrl_cluster(2, &isa->mute);
+ v4l2_dev->ctrl_handler = &isa->hdl;
+
+ mutex_init(&isa->lock);
+ isa->vdev.lock = &isa->lock;
+ strlcpy(isa->vdev.name, v4l2_dev->name, sizeof(isa->vdev.name));
+ isa->vdev.v4l2_dev = v4l2_dev;
+ isa->vdev.fops = &radio_isa_fops;
+ isa->vdev.ioctl_ops = &radio_isa_ioctl_ops;
+ isa->vdev.release = video_device_release_empty;
+ set_bit(V4L2_FL_USE_FH_PRIO, &isa->vdev.flags);
+ video_set_drvdata(&isa->vdev, isa);
+ isa->freq = FREQ_LOW;
+ isa->stereo = drv->has_stereo;
+
+ if (ops->init)
+ res = ops->init(isa);
+ if (!res)
+ res = v4l2_ctrl_handler_setup(&isa->hdl);
+ if (!res)
+ res = ops->s_frequency(isa, isa->freq);
+ if (!res && ops->s_stereo)
+ res = ops->s_stereo(isa, isa->stereo);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not setup card\n");
+ goto err_node_reg;
+ }
+ res = video_register_device(&isa->vdev, VFL_TYPE_RADIO,
+ drv->radio_nr_params[dev]);
+ if (res < 0) {
+ v4l2_err(v4l2_dev, "Could not register device node\n");
+ goto err_node_reg;
+ }
+
+ v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n",
+ drv->card, isa->io);
+ return 0;
+
+err_node_reg:
+ v4l2_ctrl_handler_free(&isa->hdl);
+err_hdl:
+ v4l2_device_unregister(&isa->v4l2_dev);
+err_dev_reg:
+ release_region(isa->io, drv->region_size);
+ kfree(isa);
+ return res;
+}
+EXPORT_SYMBOL_GPL(radio_isa_probe);
+
+int radio_isa_remove(struct device *pdev, unsigned int dev)
+{
+ struct radio_isa_card *isa = dev_get_drvdata(pdev);
+ const struct radio_isa_ops *ops = isa->drv->ops;
+
+ ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0);
+ video_unregister_device(&isa->vdev);
+ v4l2_ctrl_handler_free(&isa->hdl);
+ v4l2_device_unregister(&isa->v4l2_dev);
+ release_region(isa->io, isa->drv->region_size);
+ v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card);
+ kfree(isa);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(radio_isa_remove);
diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h
new file mode 100644
index 000000000000..8a0ea84d86de
--- /dev/null
+++ b/drivers/media/radio/radio-isa.h
@@ -0,0 +1,105 @@
+/*
+ * Framework for ISA radio drivers.
+ * This takes care of all the V4L2 scaffolding, allowing the ISA drivers
+ * to concentrate on the actual hardware operation.
+ *
+ * Copyright (C) 2012 Hans Verkuil <hans.verkuil@cisco.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 _RADIO_ISA_H_
+#define _RADIO_ISA_H_
+
+#include <linux/isa.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+
+struct radio_isa_driver;
+struct radio_isa_ops;
+
+/* Core structure for radio ISA cards */
+struct radio_isa_card {
+ const struct radio_isa_driver *drv;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct video_device vdev;
+ struct mutex lock;
+ const struct radio_isa_ops *ops;
+ struct { /* mute/volume cluster */
+ struct v4l2_ctrl *mute;
+ struct v4l2_ctrl *volume;
+ };
+ /* I/O port */
+ int io;
+
+ /* Card is in stereo audio mode */
+ bool stereo;
+ /* Current frequency */
+ u32 freq;
+};
+
+struct radio_isa_ops {
+ /* Allocate and initialize a radio_isa_card struct */
+ struct radio_isa_card *(*alloc)(void);
+ /* Probe whether a card is present at the given port */
+ bool (*probe)(struct radio_isa_card *isa, int io);
+ /* Special card initialization can be done here, this is called after
+ * the standard controls are registered, but before they are setup,
+ * thus allowing drivers to add their own controls here. */
+ int (*init)(struct radio_isa_card *isa);
+ /* Set mute and volume. */
+ int (*s_mute_volume)(struct radio_isa_card *isa, bool mute, int volume);
+ /* Set frequency */
+ int (*s_frequency)(struct radio_isa_card *isa, u32 freq);
+ /* Set stereo/mono audio mode */
+ int (*s_stereo)(struct radio_isa_card *isa, bool stereo);
+ /* Get rxsubchans value for VIDIOC_G_TUNER */
+ u32 (*g_rxsubchans)(struct radio_isa_card *isa);
+ /* Get the signal strength for VIDIOC_G_TUNER */
+ u32 (*g_signal)(struct radio_isa_card *isa);
+};
+
+/* Top level structure needed to instantiate the cards */
+struct radio_isa_driver {
+ struct isa_driver driver;
+ const struct radio_isa_ops *ops;
+ /* The module_param_array with the specified I/O ports */
+ int *io_params;
+ /* The module_param_array with the radio_nr values */
+ int *radio_nr_params;
+ /* Whether we should probe for possible cards */
+ bool probe;
+ /* The list of possible I/O ports */
+ const int *io_ports;
+ /* The size of that list */
+ int num_of_io_ports;
+ /* The region size to request */
+ unsigned region_size;
+ /* The name of the card */
+ const char *card;
+ /* Card can capture stereo audio */
+ bool has_stereo;
+ /* The maximum volume for the volume control. If 0, then there
+ is no volume control possible. */
+ int max_volume;
+};
+
+int radio_isa_match(struct device *pdev, unsigned int dev);
+int radio_isa_probe(struct device *pdev, unsigned int dev);
+int radio_isa_remove(struct device *pdev, unsigned int dev);
+
+#endif
diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c
new file mode 100644
index 000000000000..55bd1d2937c8
--- /dev/null
+++ b/drivers/media/radio/radio-keene.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2012 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* kernel includes */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <linux/usb.h>
+#include <linux/version.h>
+#include <linux/mutex.h>
+
+/* driver and module definitions */
+MODULE_AUTHOR("Hans Verkuil <hverkuil@xs4all.nl>");
+MODULE_DESCRIPTION("Keene FM Transmitter driver");
+MODULE_LICENSE("GPL");
+
+/* Actually, it advertises itself as a Logitech */
+#define USB_KEENE_VENDOR 0x046d
+#define USB_KEENE_PRODUCT 0x0a0e
+
+/* Probably USB_TIMEOUT should be modified in module parameter */
+#define BUFFER_LENGTH 8
+#define USB_TIMEOUT 500
+
+/* Frequency limits in MHz */
+#define FREQ_MIN 76U
+#define FREQ_MAX 108U
+#define FREQ_MUL 16000U
+
+/* USB Device ID List */
+static struct usb_device_id usb_keene_device_table[] = {
+ {USB_DEVICE_AND_INTERFACE_INFO(USB_KEENE_VENDOR, USB_KEENE_PRODUCT,
+ USB_CLASS_HID, 0, 0) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, usb_keene_device_table);
+
+struct keene_device {
+ struct usb_device *usbdev;
+ struct usb_interface *intf;
+ struct video_device vdev;
+ struct v4l2_device v4l2_dev;
+ struct v4l2_ctrl_handler hdl;
+ struct mutex lock;
+
+ u8 *buffer;
+ unsigned curfreq;
+ u8 tx;
+ u8 pa;
+ bool stereo;
+ bool muted;
+ bool preemph_75_us;
+};
+
+static inline struct keene_device *to_keene_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct keene_device, v4l2_dev);
+}
+
+/* Set frequency (if non-0), PA, mute and turn on/off the FM transmitter. */
+static int keene_cmd_main(struct keene_device *radio, unsigned freq, bool play)
+{
+ unsigned short freq_send = freq ? (freq - 76 * 16000) / 800 : 0;
+ int ret;
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x50;
+ radio->buffer[2] = (freq_send >> 8) & 0xff;
+ radio->buffer[3] = freq_send & 0xff;
+ radio->buffer[4] = radio->pa;
+ /* If bit 4 is set, then tune to the frequency.
+ If bit 3 is set, then unmute; if bit 2 is set, then mute.
+ If bit 1 is set, then enter idle mode; if bit 0 is set,
+ then enter transit mode.
+ */
+ radio->buffer[5] = (radio->muted ? 4 : 8) | (play ? 1 : 2) |
+ (freq ? 0x10 : 0);
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
+
+ if (ret < 0) {
+ dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ if (freq)
+ radio->curfreq = freq;
+ return 0;
+}
+
+/* Set TX, stereo and preemphasis mode (50 us vs 75 us). */
+static int keene_cmd_set(struct keene_device *radio)
+{
+ int ret;
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x51;
+ radio->buffer[2] = radio->tx;
+ /* If bit 0 is set, then transmit mono, otherwise stereo.
+ If bit 2 is set, then enable 75 us preemphasis, otherwise
+ it is 50 us. */
+ radio->buffer[3] = (!radio->stereo) | (radio->preemph_75_us ? 4 : 0);
+ radio->buffer[4] = 0x00;
+ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ ret = usb_control_msg(radio->usbdev, usb_sndctrlpipe(radio->usbdev, 0),
+ 9, 0x21, 0x200, 2, radio->buffer, BUFFER_LENGTH, USB_TIMEOUT);
+
+ if (ret < 0) {
+ dev_warn(&radio->vdev.dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+/* Handle unplugging the device.
+ * We call video_unregister_device in any case.
+ * The last function called in this procedure is
+ * usb_keene_device_release.
+ */
+static void usb_keene_disconnect(struct usb_interface *intf)
+{
+ struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf));
+
+ v4l2_device_get(&radio->v4l2_dev);
+ mutex_lock(&radio->lock);
+ usb_set_intfdata(intf, NULL);
+ video_unregister_device(&radio->vdev);
+ v4l2_device_disconnect(&radio->v4l2_dev);
+ mutex_unlock(&radio->lock);
+ v4l2_device_put(&radio->v4l2_dev);
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ strlcpy(v->driver, "radio-keene", sizeof(v->driver));
+ strlcpy(v->card, "Keene FM Transmitter", sizeof(v->card));
+ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
+ v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_MODULATOR;
+ v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int vidioc_g_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *v)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ strlcpy(v->name, "FM", sizeof(v->name));
+ v->rangelow = FREQ_MIN * FREQ_MUL;
+ v->rangehigh = FREQ_MAX * FREQ_MUL;
+ v->txsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+ return 0;
+}
+
+static int vidioc_s_modulator(struct file *file, void *priv,
+ struct v4l2_modulator *v)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ radio->stereo = (v->txsubchans == V4L2_TUNER_SUB_STEREO);
+ return keene_cmd_set(radio);
+}
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+ f->frequency = clamp(f->frequency,
+ FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL);
+ return keene_cmd_main(radio, f->frequency, true);
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct keene_device *radio = video_drvdata(file);
+
+ if (f->tuner != 0)
+ return -EINVAL;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = radio->curfreq;
+ return 0;
+}
+
+static int keene_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ static const u8 db2tx[] = {
+ /* -15, -12, -9, -6, -3, 0 dB */
+ 0x03, 0x13, 0x02, 0x12, 0x22, 0x32,
+ /* 3, 6, 9, 12, 15, 18 dB */
+ 0x21, 0x31, 0x20, 0x30, 0x40, 0x50
+ };
+ struct keene_device *radio =
+ container_of(ctrl->handler, struct keene_device, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ radio->muted = ctrl->val;
+ return keene_cmd_main(radio, 0, true);
+
+ case V4L2_CID_TUNE_POWER_LEVEL:
+ /* To go from dBuV to the register value we apply the
+ following formula: */
+ radio->pa = (ctrl->val - 71) * 100 / 62;
+ return keene_cmd_main(radio, 0, true);
+
+ case V4L2_CID_TUNE_PREEMPHASIS:
+ radio->preemph_75_us = ctrl->val == V4L2_PREEMPHASIS_75_uS;
+ return keene_cmd_set(radio);
+
+ case V4L2_CID_AUDIO_COMPRESSION_GAIN:
+ radio->tx = db2tx[(ctrl->val - ctrl->minimum) / ctrl->step];
+ return keene_cmd_set(radio);
+ }
+ return -EINVAL;
+}
+
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ switch (sub->type) {
+ case V4L2_EVENT_CTRL:
+ return v4l2_event_subscribe(fh, sub, 0);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/* File system interface */
+static const struct v4l2_file_operations usb_keene_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = v4l2_fh_release,
+ .poll = v4l2_ctrl_poll,
+ .unlocked_ioctl = video_ioctl2,
+};
+
+static const struct v4l2_ctrl_ops keene_ctrl_ops = {
+ .s_ctrl = keene_s_ctrl,
+};
+
+static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_modulator = vidioc_g_modulator,
+ .vidioc_s_modulator = vidioc_s_modulator,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static void usb_keene_video_device_release(struct v4l2_device *v4l2_dev)
+{
+ struct keene_device *radio = to_keene_dev(v4l2_dev);
+
+ /* free rest memory */
+ v4l2_ctrl_handler_free(&radio->hdl);
+ kfree(radio->buffer);
+ kfree(radio);
+}
+
+/* check if the device is present and register with v4l and usb if it is */
+static int usb_keene_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct keene_device *radio;
+ struct v4l2_ctrl_handler *hdl;
+ int retval = 0;
+
+ /*
+ * The Keene FM transmitter USB device has the same USB ID as
+ * the Logitech AudioHub Speaker, but it should ignore the hid.
+ * Check if the name is that of the Keene device.
+ * If not, then someone connected the AudioHub and we shouldn't
+ * attempt to handle this driver.
+ * For reference: the product name of the AudioHub is
+ * "AudioHub Speaker".
+ */
+ if (dev->product && strcmp(dev->product, "B-LINK USB Audio "))
+ return -ENODEV;
+
+ radio = kzalloc(sizeof(struct keene_device), GFP_KERNEL);
+ if (radio)
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
+
+ if (!radio || !radio->buffer) {
+ dev_err(&intf->dev, "kmalloc for keene_device failed\n");
+ kfree(radio);
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ hdl = &radio->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_MUTE,
+ 0, 1, 1, 0);
+ v4l2_ctrl_new_std_menu(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_PREEMPHASIS,
+ V4L2_PREEMPHASIS_75_uS, 1, V4L2_PREEMPHASIS_50_uS);
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_TUNE_POWER_LEVEL,
+ 84, 118, 1, 118);
+ v4l2_ctrl_new_std(hdl, &keene_ctrl_ops, V4L2_CID_AUDIO_COMPRESSION_GAIN,
+ -15, 18, 3, 0);
+ radio->pa = 118;
+ radio->tx = 0x32;
+ radio->stereo = true;
+ radio->curfreq = 95.16 * FREQ_MUL;
+ if (hdl->error) {
+ retval = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ goto err_v4l2;
+ }
+ retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev);
+ if (retval < 0) {
+ dev_err(&intf->dev, "couldn't register v4l2_device\n");
+ goto err_v4l2;
+ }
+
+ mutex_init(&radio->lock);
+
+ radio->v4l2_dev.ctrl_handler = hdl;
+ radio->v4l2_dev.release = usb_keene_video_device_release;
+ strlcpy(radio->vdev.name, radio->v4l2_dev.name,
+ sizeof(radio->vdev.name));
+ radio->vdev.v4l2_dev = &radio->v4l2_dev;
+ radio->vdev.fops = &usb_keene_fops;
+ radio->vdev.ioctl_ops = &usb_keene_ioctl_ops;
+ radio->vdev.lock = &radio->lock;
+ radio->vdev.release = video_device_release_empty;
+
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->intf = intf;
+ usb_set_intfdata(intf, &radio->v4l2_dev);
+
+ video_set_drvdata(&radio->vdev, radio);
+ set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags);
+
+ retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, -1);
+ if (retval < 0) {
+ dev_err(&intf->dev, "could not register video device\n");
+ goto err_vdev;
+ }
+ v4l2_ctrl_handler_setup(hdl);
+ dev_info(&intf->dev, "V4L2 device registered as %s\n",
+ video_device_node_name(&radio->vdev));
+ return 0;
+
+err_vdev:
+ v4l2_device_unregister(&radio->v4l2_dev);
+err_v4l2:
+ kfree(radio->buffer);
+ kfree(radio);
+err:
+ return retval;
+}
+
+/* USB subsystem interface */
+static struct usb_driver usb_keene_driver = {
+ .name = "radio-keene",
+ .probe = usb_keene_probe,
+ .disconnect = usb_keene_disconnect,
+ .id_table = usb_keene_device_table,
+};
+
+static int __init keene_init(void)
+{
+ int retval = usb_register(&usb_keene_driver);
+
+ if (retval)
+ pr_err(KBUILD_MODNAME
+ ": usb_register failed. Error number %d\n", retval);
+
+ return retval;
+}
+
+static void __exit keene_exit(void)
+{
+ usb_deregister(&usb_keene_driver);
+}
+
+module_init(keene_init);
+module_exit(keene_exit);
+
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index f872a54cf3d9..740a3d5520c7 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -42,67 +42,37 @@
#include <linux/videodev2.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <sound/tea575x-tuner.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
-
-#define DRIVER_VERSION "0.7.8"
-
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
-MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
+MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000.");
MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
+MODULE_VERSION("1.0.0");
static int radio_nr = -1;
-module_param(radio_nr, int, 0);
-
-static int debug;
-
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "activates debug info");
-
-#define dprintk(dev, num, fmt, arg...) \
- v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg)
-
-#ifndef PCI_VENDOR_ID_GUILLEMOT
-#define PCI_VENDOR_ID_GUILLEMOT 0x5046
-#endif
-
-#ifndef PCI_DEVICE_ID_GUILLEMOT
-#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
-#endif
-
+module_param(radio_nr, int, 0644);
+MODULE_PARM_DESC(radio_nr, "Radio device number");
/* TEA5757 pin mappings */
static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16;
-#define FREQ_LO (87 * 16000)
-#define FREQ_HI (108 * 16000)
-
-#define FREQ_IF 171200 /* 10.7*16000 */
-#define FREQ_STEP 200 /* 12.5*16 */
-
-/* (x==fmhz*16*1000) -> bits */
-#define FREQ2BITS(x) \
- ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2)
-
-#define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+static atomic_t maxiradio_instance = ATOMIC_INIT(0);
+#define PCI_VENDOR_ID_GUILLEMOT 0x5046
+#define PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO 0x1001
struct maxiradio
{
+ struct snd_tea575x tea;
struct v4l2_device v4l2_dev;
- struct video_device vdev;
struct pci_dev *pdev;
u16 io; /* base of radio io */
- u16 muted; /* VIDEO_AUDIO_MUTE */
- u16 stereo; /* VIDEO_TUNER_STEREO_ON */
- u16 tuned; /* signal strength (0 or 0xffff) */
-
- unsigned long freq;
-
- struct mutex lock;
};
static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
@@ -110,259 +80,41 @@ static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
return container_of(v4l2_dev, struct maxiradio, v4l2_dev);
}
-static void outbit(unsigned long bit, u16 io)
-{
- int val = power | wren | (bit ? data : 0);
-
- outb(val, io);
- udelay(4);
- outb(val | clk, io);
- udelay(4);
- outb(val, io);
- udelay(4);
-}
-
-static void turn_power(struct maxiradio *dev, int p)
-{
- if (p != 0) {
- dprintk(dev, 1, "Radio powered on\n");
- outb(power, dev->io);
- } else {
- dprintk(dev, 1, "Radio powered off\n");
- outb(0, dev->io);
- }
-}
-
-static void set_freq(struct maxiradio *dev, u32 freq)
-{
- unsigned long int si;
- int bl;
- int io = dev->io;
- int val = FREQ2BITS(freq);
-
- /* TEA5757 shift register bits (see pdf) */
-
- outbit(0, io); /* 24 search */
- outbit(1, io); /* 23 search up/down */
-
- outbit(0, io); /* 22 stereo/mono */
-
- outbit(0, io); /* 21 band */
- outbit(0, io); /* 20 band (only 00=FM works I think) */
-
- outbit(0, io); /* 19 port ? */
- outbit(0, io); /* 18 port ? */
-
- outbit(0, io); /* 17 search level */
- outbit(0, io); /* 16 search level */
-
- si = 0x8000;
- for (bl = 1; bl <= 16; bl++) {
- outbit(val & si, io);
- si >>= 1;
- }
-
- dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n",
- freq / 16000,
- freq % 16000 * 100 / 16000);
-
- turn_power(dev, 1);
-}
-
-static int get_stereo(u16 io)
+static void maxiradio_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
{
- outb(power,io);
- udelay(4);
+ struct maxiradio *dev = tea->private_data;
+ u8 bits = 0;
- return !(inb(io) & mo_st);
-}
+ bits |= (pins & TEA575X_DATA) ? data : 0;
+ bits |= (pins & TEA575X_CLK) ? clk : 0;
+ bits |= (pins & TEA575X_WREN) ? wren : 0;
+ bits |= power;
-static int get_tune(u16 io)
-{
- outb(power+clk,io);
- udelay(4);
-
- return !(inb(io) & mo_st);
-}
-
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver));
- strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card));
- snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
+ outb(bits, dev->io);
}
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
+/* Note: this card cannot read out the data of the shift registers,
+ only the mono/stereo pin works. */
+static u8 maxiradio_tea575x_get_pins(struct snd_tea575x *tea)
{
- struct maxiradio *dev = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- mutex_lock(&dev->lock);
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (get_stereo(dev->io))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xffff * get_tune(dev->io);
- mutex_unlock(&dev->lock);
+ struct maxiradio *dev = tea->private_data;
+ u8 bits = inb(dev->io);
- return 0;
+ return ((bits & data) ? TEA575X_DATA : 0) |
+ ((bits & mo_st) ? TEA575X_MOST : 0);
}
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
+static void maxiradio_tea575x_set_direction(struct snd_tea575x *tea, bool output)
{
- return v->index ? -EINVAL : 0;
}
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
- dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
- f->frequency / 16000,
- f->frequency % 16000 * 100 / 16000,
- FREQ_LO / 16000, FREQ_HI / 16000);
-
- return -EINVAL;
- }
-
- mutex_lock(&dev->lock);
- dev->freq = f->frequency;
- set_freq(dev, dev->freq);
- msleep(125);
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = dev->freq;
-
- dprintk(dev, 4, "radio freq is %d.%02d MHz",
- f->frequency / 16000,
- f->frequency % 16000 * 100 / 16000);
-
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->muted;
- return 0;
- }
-
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct maxiradio *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- mutex_lock(&dev->lock);
- dev->muted = ctrl->value;
- if (dev->muted)
- turn_power(dev, 0);
- else
- set_freq(dev, dev->freq);
- mutex_unlock(&dev->lock);
- return 0;
- }
-
- return -EINVAL;
-}
-
-static const struct v4l2_file_operations maxiradio_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static struct snd_tea575x_ops maxiradio_tea_ops = {
+ .set_pins = maxiradio_tea575x_set_pins,
+ .get_pins = maxiradio_tea575x_get_pins,
+ .set_direction = maxiradio_tea575x_set_direction,
};
-static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
-};
-
-static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit maxiradio_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct maxiradio *dev;
struct v4l2_device *v4l2_dev;
@@ -375,63 +127,60 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d
}
v4l2_dev = &dev->v4l2_dev;
- mutex_init(&dev->lock);
- dev->pdev = pdev;
- dev->muted = 1;
- dev->freq = FREQ_LO;
-
- strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name));
+ v4l2_device_set_name(v4l2_dev, "maxiradio", &maxiradio_instance);
retval = v4l2_device_register(&pdev->dev, v4l2_dev);
if (retval < 0) {
v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
goto errfr;
}
+ dev->tea.private_data = dev;
+ dev->tea.ops = &maxiradio_tea_ops;
+ /* The data pin cannot be read. This may be a hardware limitation, or
+ we just don't know how to read it. */
+ dev->tea.cannot_read_data = true;
+ dev->tea.v4l2_dev = v4l2_dev;
+ dev->tea.radio_nr = radio_nr;
+ strlcpy(dev->tea.card, "Maxi Radio FM2000", sizeof(dev->tea.card));
+ snprintf(dev->tea.bus_info, sizeof(dev->tea.bus_info),
+ "PCI:%s", pci_name(pdev));
+
+ retval = -ENODEV;
if (!request_region(pci_resource_start(pdev, 0),
- pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
- v4l2_err(v4l2_dev, "can't reserve I/O ports\n");
- goto err_out;
+ pci_resource_len(pdev, 0), v4l2_dev->name)) {
+ dev_err(&pdev->dev, "can't reserve I/O ports\n");
+ goto err_hdl;
}
if (pci_enable_device(pdev))
goto err_out_free_region;
dev->io = pci_resource_start(pdev, 0);
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &maxiradio_fops;
- dev->vdev.ioctl_ops = &maxiradio_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_err(v4l2_dev, "can't register device!");
+ if (snd_tea575x_init(&dev->tea)) {
+ printk(KERN_ERR "radio-maxiradio: Unable to detect TEA575x tuner\n");
goto err_out_free_region;
}
-
- v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
-
- v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n",
- dev->io);
return 0;
err_out_free_region:
release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
-err_out:
+err_hdl:
v4l2_device_unregister(v4l2_dev);
errfr:
kfree(dev);
- return -ENODEV;
+ return retval;
}
-static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
+static void __devexit maxiradio_remove(struct pci_dev *pdev)
{
struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
struct maxiradio *dev = to_maxiradio(v4l2_dev);
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
+ snd_tea575x_exit(&dev->tea);
+ /* Turn off power */
+ outb(0, dev->io);
+ v4l2_device_unregister(v4l2_dev);
release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
}
@@ -446,19 +195,19 @@ MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
static struct pci_driver maxiradio_driver = {
.name = "radio-maxiradio",
.id_table = maxiradio_pci_tbl,
- .probe = maxiradio_init_one,
- .remove = __devexit_p(maxiradio_remove_one),
+ .probe = maxiradio_probe,
+ .remove = __devexit_p(maxiradio_remove),
};
-static int __init maxiradio_radio_init(void)
+static int __init maxiradio_init(void)
{
return pci_register_driver(&maxiradio_driver);
}
-static void __exit maxiradio_radio_exit(void)
+static void __exit maxiradio_exit(void)
{
pci_unregister_driver(&maxiradio_driver);
}
-module_init(maxiradio_radio_init);
-module_exit(maxiradio_radio_exit);
+module_init(maxiradio_init);
+module_exit(maxiradio_exit);
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 3628be617ee9..b275c5d0fe9a 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -1,11 +1,12 @@
-/* RadioTrack II driver for Linux radio support (C) 1998 Ben Pfaff
+/*
+ * RadioTrack II driver
+ * Copyright 1998 Ben Pfaff
*
* Based on RadioTrack I/RadioReveal (C) 1997 M. Kirkwood
* Converted to new API by Alan Cox <alan@lxorguk.ukuu.org.uk>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
- * TODO: Allow for more than one of these foolish entities :-)
- *
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
@@ -18,323 +19,120 @@
#include <linux/io.h> /* outb, outb_p */
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include "radio-isa.h"
MODULE_AUTHOR("Ben Pfaff");
MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_RTRACK2_PORT
#define CONFIG_RADIO_RTRACK2_PORT -1
#endif
-static int io = CONFIG_RADIO_RTRACK2_PORT;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
-module_param(radio_nr, int, 0);
-
-struct rtrack2
-{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
- unsigned long curfreq;
- int muted;
- struct mutex lock;
-};
+#define RTRACK2_MAX 2
-static struct rtrack2 rtrack2_card;
+static int io[RTRACK2_MAX] = { [0] = CONFIG_RADIO_RTRACK2_PORT,
+ [1 ... (RTRACK2_MAX - 1)] = -1 };
+static int radio_nr[RTRACK2_MAX] = { [0 ... (RTRACK2_MAX - 1)] = -1 };
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the RadioTrack card (0x20f or 0x30f)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
-/* local things */
-
-static void rt_mute(struct rtrack2 *dev)
-{
- if (dev->muted)
- return;
- mutex_lock(&dev->lock);
- outb(1, dev->io);
- mutex_unlock(&dev->lock);
- dev->muted = 1;
-}
-
-static void rt_unmute(struct rtrack2 *dev)
+static struct radio_isa_card *rtrack2_alloc(void)
{
- if(dev->muted == 0)
- return;
- mutex_lock(&dev->lock);
- outb(0, dev->io);
- mutex_unlock(&dev->lock);
- dev->muted = 0;
+ return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
}
-static void zero(struct rtrack2 *dev)
+static void zero(struct radio_isa_card *isa)
{
- outb_p(1, dev->io);
- outb_p(3, dev->io);
- outb_p(1, dev->io);
+ outb_p(1, isa->io);
+ outb_p(3, isa->io);
+ outb_p(1, isa->io);
}
-static void one(struct rtrack2 *dev)
+static void one(struct radio_isa_card *isa)
{
- outb_p(5, dev->io);
- outb_p(7, dev->io);
- outb_p(5, dev->io);
+ outb_p(5, isa->io);
+ outb_p(7, isa->io);
+ outb_p(5, isa->io);
}
-static int rt_setfreq(struct rtrack2 *dev, unsigned long freq)
+static int rtrack2_s_frequency(struct radio_isa_card *isa, u32 freq)
{
int i;
- mutex_lock(&dev->lock);
- dev->curfreq = freq;
freq = freq / 200 + 856;
- outb_p(0xc8, dev->io);
- outb_p(0xc9, dev->io);
- outb_p(0xc9, dev->io);
+ outb_p(0xc8, isa->io);
+ outb_p(0xc9, isa->io);
+ outb_p(0xc9, isa->io);
for (i = 0; i < 10; i++)
- zero(dev);
+ zero(isa);
for (i = 14; i >= 0; i--)
if (freq & (1 << i))
- one(dev);
+ one(isa);
else
- zero(dev);
-
- outb_p(0xc8, dev->io);
- if (!dev->muted)
- outb_p(0, dev->io);
-
- mutex_unlock(&dev->lock);
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
- strlcpy(v->card, "RadioTrack II", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
+ zero(isa);
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int rt_getsigstr(struct rtrack2 *dev)
-{
- int sig = 1;
-
- mutex_lock(&dev->lock);
- if (inb(dev->io) & 2) /* bit set = no signal present */
- sig = 0;
- mutex_unlock(&dev->lock);
- return sig;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 88 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * rt_getsigstr(rt);
+ outb_p(0xc8, isa->io);
+ if (!v4l2_ctrl_g_ctrl(isa->mute))
+ outb_p(0, isa->io);
return 0;
}
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
+static u32 rtrack2_g_signal(struct radio_isa_card *isa)
{
- struct rtrack2 *rt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- rt_setfreq(rt, f->frequency);
- return 0;
+ /* bit set = no signal present */
+ return (inb(isa->io) & 2) ? 0 : 0xffff;
}
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
+static int rtrack2_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- struct rtrack2 *rt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = rt->curfreq;
+ outb(mute, isa->io);
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = rt->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- if (rt->muted)
- ctrl->value = 0;
- else
- ctrl->value = 65535;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct rtrack2 *rt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- rt_mute(rt);
- else
- rt_unmute(rt);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- if (ctrl->value)
- rt_unmute(rt);
- else
- rt_mute(rt);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations rtrack2_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops rtrack2_ops = {
+ .alloc = rtrack2_alloc,
+ .s_mute_volume = rtrack2_s_mute_volume,
+ .s_frequency = rtrack2_s_frequency,
+ .g_signal = rtrack2_g_signal,
};
-static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+static const int rtrack2_ioports[] = { 0x20f, 0x30f };
+
+static struct radio_isa_driver rtrack2_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-rtrack2",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = rtrack2_ioports,
+ .num_of_io_ports = ARRAY_SIZE(rtrack2_ioports),
+ .region_size = 4,
+ .card = "AIMSlab RadioTrack II",
+ .ops = &rtrack2_ops,
+ .has_stereo = true,
};
static int __init rtrack2_init(void)
{
- struct rtrack2 *dev = &rtrack2_card;
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name));
- dev->io = io;
- if (dev->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n");
- return -EINVAL;
- }
- if (!request_region(dev->io, 4, "rtrack2")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(dev->io, 4);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &rtrack2_fops;
- dev->vdev.ioctl_ops = &rtrack2_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- /* mute card - prevents noisy bootups */
- outb(1, dev->io);
- dev->muted = 1;
-
- mutex_init(&dev->lock);
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(dev->io, 4);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n");
-
- return 0;
+ return isa_register_driver(&rtrack2_driver.driver, RTRACK2_MAX);
}
static void __exit rtrack2_exit(void)
{
- struct rtrack2 *dev = &rtrack2_card;
-
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 4);
+ isa_unregister_driver(&rtrack2_driver.driver);
}
module_init(rtrack2_init);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 7ab9afadf29b..7c69214334bf 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -9,16 +9,23 @@
#include <linux/delay.h>
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
+#include <linux/slab.h>
#include <linux/ioport.h> /* request_region */
#include <linux/io.h> /* outb, outb_p */
+#include <linux/isa.h>
#include <sound/tea575x-tuner.h>
MODULE_AUTHOR("Ondrej Zary");
MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver");
MODULE_LICENSE("GPL");
+static int radio_nr = -1;
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device number");
+
struct fmr2 {
int io;
+ struct v4l2_device v4l2_dev;
struct snd_tea575x tea;
struct v4l2_ctrl *volume;
struct v4l2_ctrl *balance;
@@ -26,7 +33,6 @@ struct fmr2 {
/* the port is hardwired so no need to support multiple cards */
#define FMR2_PORT 0x384
-static struct fmr2 fmr2_card;
/* TEA575x tuner pins */
#define STR_DATA (1 << 0)
@@ -180,26 +186,46 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea)
return 0;
}
-static int __init fmr2_init(void)
+static int __devinit fmr2_probe(struct device *pdev, unsigned int dev)
{
- struct fmr2 *fmr2 = &fmr2_card;
+ struct fmr2 *fmr2;
+ int err;
+
+ fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL);
+ if (fmr2 == NULL)
+ return -ENOMEM;
+ strlcpy(fmr2->v4l2_dev.name, dev_name(pdev),
+ sizeof(fmr2->v4l2_dev.name));
fmr2->io = FMR2_PORT;
- if (!request_region(fmr2->io, 2, "SF16-FMR2")) {
+ if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) {
printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io);
+ kfree(fmr2);
return -EBUSY;
}
+ dev_set_drvdata(pdev, fmr2);
+ err = v4l2_device_register(pdev, &fmr2->v4l2_dev);
+ if (err < 0) {
+ v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n");
+ release_region(fmr2->io, 2);
+ kfree(fmr2);
+ return err;
+ }
+ fmr2->tea.v4l2_dev = &fmr2->v4l2_dev;
fmr2->tea.private_data = fmr2;
+ fmr2->tea.radio_nr = radio_nr;
fmr2->tea.ops = &fmr2_tea_ops;
fmr2->tea.ext_init = fmr2_tea_ext_init;
strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card));
- strcpy(fmr2->tea.bus_info, "ISA");
+ snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s",
+ fmr2->v4l2_dev.name);
if (snd_tea575x_init(&fmr2->tea)) {
printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n");
release_region(fmr2->io, 2);
+ kfree(fmr2);
return -ENODEV;
}
@@ -207,12 +233,33 @@ static int __init fmr2_init(void)
return 0;
}
-static void __exit fmr2_exit(void)
+static int __exit fmr2_remove(struct device *pdev, unsigned int dev)
{
- struct fmr2 *fmr2 = &fmr2_card;
+ struct fmr2 *fmr2 = dev_get_drvdata(pdev);
snd_tea575x_exit(&fmr2->tea);
release_region(fmr2->io, 2);
+ v4l2_device_unregister(&fmr2->v4l2_dev);
+ kfree(fmr2);
+ return 0;
+}
+
+struct isa_driver fmr2_driver = {
+ .probe = fmr2_probe,
+ .remove = fmr2_remove,
+ .driver = {
+ .name = "radio-sf16fmr2",
+ },
+};
+
+static int __init fmr2_init(void)
+{
+ return isa_register_driver(&fmr2_driver, 1);
+}
+
+static void __exit fmr2_exit(void)
+{
+ isa_unregister_driver(&fmr2_driver);
}
module_init(fmr2_init);
diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
index db20904d01f0..6b1fae32b483 100644
--- a/drivers/media/radio/radio-tea5764.c
+++ b/drivers/media/radio/radio-tea5764.c
@@ -575,21 +575,7 @@ static struct i2c_driver tea5764_i2c_driver = {
.id_table = tea5764_id,
};
-/* init the driver */
-static int __init tea5764_init(void)
-{
- int ret = i2c_add_driver(&tea5764_i2c_driver);
-
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": "
- DRIVER_DESC "\n");
- return ret;
-}
-
-/* cleanup the driver */
-static void __exit tea5764_exit(void)
-{
- i2c_del_driver(&tea5764_i2c_driver);
-}
+module_i2c_driver(tea5764_i2c_driver);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -600,6 +586,3 @@ module_param(use_xtal, int, 0);
MODULE_PARM_DESC(use_xtal, "Chip have a xtal connected in board");
module_param(radio_nr, int, 0);
MODULE_PARM_DESC(radio_nr, "video4linux device number to use");
-
-module_init(tea5764_init);
-module_exit(tea5764_exit);
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index f2ed9cc3cf3b..be10a802e3a9 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -16,11 +16,7 @@
* Frequency control is done digitally -- ie out(port,encodefreq(95.8));
* Volume Control is done digitally
*
- * there is a I2C controlled RDS decoder (SAA6588) onboard, which i would like to support someday
- * (as soon i have understand how to get started :)
- * If you can help me out with that, please contact me!!
- *
- *
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
* Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
@@ -30,43 +26,24 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include "radio-isa.h"
-MODULE_AUTHOR("R.OFFERMANNS & others");
+MODULE_AUTHOR("R. Offermans & others");
MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
-
-#ifndef CONFIG_RADIO_TERRATEC_PORT
-#define CONFIG_RADIO_TERRATEC_PORT 0x590
-#endif
+MODULE_VERSION("0.1.99");
-static int io = CONFIG_RADIO_TERRATEC_PORT;
+/* Note: there seems to be only one possible port (0x590), but without
+ hardware this is hard to verify. For now, this is the only one we will
+ support. */
+static int io = 0x590;
static int radio_nr = -1;
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
-module_param(radio_nr, int, 0);
-
-static struct v4l2_queryctrl radio_qctrl[] = {
- {
- .id = V4L2_CID_AUDIO_MUTE,
- .name = "Mute",
- .minimum = 0,
- .maximum = 1,
- .default_value = 1,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- },{
- .id = V4L2_CID_AUDIO_VOLUME,
- .name = "Volume",
- .minimum = 0,
- .maximum = 0xff,
- .step = 1,
- .default_value = 0xff,
- .type = V4L2_CTRL_TYPE_INTEGER,
- }
-};
+module_param(radio_nr, int, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device number");
#define WRT_DIS 0x00
#define CLK_OFF 0x00
@@ -76,63 +53,24 @@ static struct v4l2_queryctrl radio_qctrl[] = {
#define CLK_ON 0x08
#define WRT_EN 0x10
-struct terratec
+static struct radio_isa_card *terratec_alloc(void)
{
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
- int curvol;
- unsigned long curfreq;
- int muted;
- struct mutex lock;
-};
-
-static struct terratec terratec_card;
-
-/* local things */
+ return kzalloc(sizeof(struct radio_isa_card), GFP_KERNEL);
+}
-static void tt_write_vol(struct terratec *tt, int volume)
+static int terratec_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
int i;
- volume = volume + (volume * 32); /* change both channels */
- mutex_lock(&tt->lock);
+ if (mute)
+ vol = 0;
+ vol = vol + (vol * 32); /* change both channels */
for (i = 0; i < 8; i++) {
- if (volume & (0x80 >> i))
- outb(0x80, tt->io + 1);
+ if (vol & (0x80 >> i))
+ outb(0x80, isa->io + 1);
else
- outb(0x00, tt->io + 1);
- }
- mutex_unlock(&tt->lock);
-}
-
-
-
-static void tt_mute(struct terratec *tt)
-{
- tt->muted = 1;
- tt_write_vol(tt, 0);
-}
-
-static int tt_setvol(struct terratec *tt, int vol)
-{
- if (vol == tt->curvol) { /* requested volume = current */
- if (tt->muted) { /* user is unmuting the card */
- tt->muted = 0;
- tt_write_vol(tt, vol); /* enable card */
- }
- return 0;
- }
-
- if (vol == 0) { /* volume = 0 means mute the card */
- tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */
- tt->curvol = vol; /* track the volume state! */
- return 0;
+ outb(0x00, isa->io + 1);
}
-
- tt->muted = 0;
- tt_write_vol(tt, vol);
- tt->curvol = vol;
return 0;
}
@@ -140,20 +78,15 @@ static int tt_setvol(struct terratec *tt, int vol)
/* this is the worst part in this driver */
/* many more or less strange things are going on here, but hey, it works :) */
-static int tt_setfreq(struct terratec *tt, unsigned long freq1)
+static int terratec_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- int freq;
int i;
int p;
- int temp;
+ int temp;
long rest;
unsigned char buffer[25]; /* we have to bit shift 25 registers */
- mutex_lock(&tt->lock);
-
- tt->curfreq = freq1;
-
- freq = freq1 / 160; /* convert the freq. to a nice to handle value */
+ freq = freq / 160; /* convert the freq. to a nice to handle value */
memset(buffer, 0, sizeof(buffer));
rest = freq * 10 + 10700; /* I once had understood what is going on here */
@@ -175,239 +108,61 @@ static int tt_setfreq(struct terratec *tt, unsigned long freq1)
for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */
if (buffer[i] == 1) {
- outb(WRT_EN | DATA, tt->io);
- outb(WRT_EN | DATA | CLK_ON, tt->io);
- outb(WRT_EN | DATA, tt->io);
+ outb(WRT_EN | DATA, isa->io);
+ outb(WRT_EN | DATA | CLK_ON, isa->io);
+ outb(WRT_EN | DATA, isa->io);
} else {
- outb(WRT_EN | 0x00, tt->io);
- outb(WRT_EN | 0x00 | CLK_ON, tt->io);
- }
- }
- outb(0x00, tt->io);
-
- mutex_unlock(&tt->lock);
-
- return 0;
-}
-
-static int tt_getsigstr(struct terratec *tt)
-{
- if (inb(tt->io) & 2) /* bit set = no signal present */
- return 0;
- return 1; /* signal present */
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
- strlcpy(v->card, "ActiveRadio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct terratec *tt = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * tt_getsigstr(tt);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct terratec *tt = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- tt_setfreq(tt, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct terratec *tt = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tt->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
- if (qc->id && qc->id == radio_qctrl[i].id) {
- memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
- return 0;
+ outb(WRT_EN | 0x00, isa->io);
+ outb(WRT_EN | 0x00 | CLK_ON, isa->io);
}
}
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct terratec *tt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (tt->muted)
- ctrl->value = 1;
- else
- ctrl->value = 0;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = tt->curvol * 6554;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct terratec *tt = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- tt_mute(tt);
- else
- tt_setvol(tt,tt->curvol);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- tt_setvol(tt,ctrl->value);
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
+ outb(0x00, isa->io);
return 0;
}
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+static u32 terratec_g_signal(struct radio_isa_card *isa)
{
- return a->index ? -EINVAL : 0;
+ /* bit set = no signal present */
+ return (inb(isa->io) & 2) ? 0 : 0xffff;
}
-static const struct v4l2_file_operations terratec_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops terratec_ops = {
+ .alloc = terratec_alloc,
+ .s_mute_volume = terratec_s_mute_volume,
+ .s_frequency = terratec_s_frequency,
+ .g_signal = terratec_g_signal,
};
-static const struct v4l2_ioctl_ops terratec_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+static const int terratec_ioports[] = { 0x590 };
+
+static struct radio_isa_driver terratec_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-terratec",
+ },
+ },
+ .io_params = &io,
+ .radio_nr_params = &radio_nr,
+ .io_ports = terratec_ioports,
+ .num_of_io_ports = ARRAY_SIZE(terratec_ioports),
+ .region_size = 2,
+ .card = "TerraTec ActiveRadio",
+ .ops = &terratec_ops,
+ .has_stereo = true,
+ .max_volume = 10,
};
static int __init terratec_init(void)
{
- struct terratec *tt = &terratec_card;
- struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
- tt->io = io;
- if (tt->io == -1) {
- v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
- return -EINVAL;
- }
- if (!request_region(tt->io, 2, "terratec")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(tt->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
- tt->vdev.v4l2_dev = v4l2_dev;
- tt->vdev.fops = &terratec_fops;
- tt->vdev.ioctl_ops = &terratec_ioctl_ops;
- tt->vdev.release = video_device_release_empty;
- video_set_drvdata(&tt->vdev, tt);
-
- mutex_init(&tt->lock);
-
- /* mute card - prevents noisy bootups */
- tt_write_vol(tt, 0);
-
- if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(&tt->v4l2_dev);
- release_region(tt->io, 2);
- return -EINVAL;
- }
-
- v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
- return 0;
+ return isa_register_driver(&terratec_driver.driver, 1);
}
static void __exit terratec_exit(void)
{
- struct terratec *tt = &terratec_card;
- struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
-
- video_unregister_device(&tt->vdev);
- v4l2_device_unregister(&tt->v4l2_dev);
- release_region(tt->io, 2);
- v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
+ isa_unregister_driver(&terratec_driver.driver);
}
module_init(terratec_init);
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index b3f45a019d82..26a8c6002121 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -21,13 +21,15 @@
#include <linux/ioport.h>
#include <linux/videodev2.h>
#include <linux/io.h>
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include "radio-isa.h"
MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
@@ -35,39 +37,38 @@ MODULE_VERSION("0.0.3");
#define CONFIG_RADIO_TRUST_PORT -1
#endif
-static int io = CONFIG_RADIO_TRUST_PORT;
-static int radio_nr = -1;
+#define TRUST_MAX 2
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
-module_param(radio_nr, int, 0);
+static int io[TRUST_MAX] = { [0] = CONFIG_RADIO_TRUST_PORT,
+ [1 ... (TRUST_MAX - 1)] = -1 };
+static int radio_nr[TRUST_MAX] = { [0 ... (TRUST_MAX - 1)] = -1 };
+
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Trust FM Radio card (0x350 or 0x358)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
struct trust {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
+ struct radio_isa_card isa;
int ioval;
- __u16 curvol;
- __u16 curbass;
- __u16 curtreble;
- int muted;
- unsigned long curfreq;
- int curstereo;
- int curmute;
- struct mutex lock;
};
-static struct trust trust_card;
+static struct radio_isa_card *trust_alloc(void)
+{
+ struct trust *tr = kzalloc(sizeof(*tr), GFP_KERNEL);
+
+ return tr ? &tr->isa : NULL;
+}
/* i2c addresses */
#define TDA7318_ADDR 0x88
#define TSA6060T_ADDR 0xc4
-#define TR_DELAY do { inb(tr->io); inb(tr->io); inb(tr->io); } while (0)
-#define TR_SET_SCL outb(tr->ioval |= 2, tr->io)
-#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->io)
-#define TR_SET_SDA outb(tr->ioval |= 1, tr->io)
-#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->io)
+#define TR_DELAY do { inb(tr->isa.io); inb(tr->isa.io); inb(tr->isa.io); } while (0)
+#define TR_SET_SCL outb(tr->ioval |= 2, tr->isa.io)
+#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->isa.io)
+#define TR_SET_SDA outb(tr->ioval |= 1, tr->isa.io)
+#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->isa.io)
static void write_i2c(struct trust *tr, int n, ...)
{
@@ -84,10 +85,10 @@ static void write_i2c(struct trust *tr, int n, ...)
TR_CLR_SCL;
TR_DELAY;
- for(; n; n--) {
+ for (; n; n--) {
val = va_arg(args, unsigned);
- for(mask = 0x80; mask; mask >>= 1) {
- if(val & mask)
+ for (mask = 0x80; mask; mask >>= 1) {
+ if (val & mask)
TR_SET_SDA;
else
TR_CLR_SDA;
@@ -115,317 +116,128 @@ static void write_i2c(struct trust *tr, int n, ...)
va_end(args);
}
-static void tr_setvol(struct trust *tr, __u16 vol)
+static int trust_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- mutex_lock(&tr->lock);
- tr->curvol = vol / 2048;
- write_i2c(tr, 2, TDA7318_ADDR, tr->curvol ^ 0x1f);
- mutex_unlock(&tr->lock);
-}
+ struct trust *tr = container_of(isa, struct trust, isa);
-static int basstreble2chip[15] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
-};
-
-static void tr_setbass(struct trust *tr, __u16 bass)
-{
- mutex_lock(&tr->lock);
- tr->curbass = bass / 4370;
- write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[tr->curbass]);
- mutex_unlock(&tr->lock);
-}
-
-static void tr_settreble(struct trust *tr, __u16 treble)
-{
- mutex_lock(&tr->lock);
- tr->curtreble = treble / 4370;
- write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[tr->curtreble]);
- mutex_unlock(&tr->lock);
+ tr->ioval = (tr->ioval & 0xf7) | (mute << 3);
+ outb(tr->ioval, isa->io);
+ write_i2c(tr, 2, TDA7318_ADDR, vol ^ 0x1f);
+ return 0;
}
-static void tr_setstereo(struct trust *tr, int stereo)
+static int trust_s_stereo(struct radio_isa_card *isa, bool stereo)
{
- mutex_lock(&tr->lock);
- tr->curstereo = !!stereo;
- tr->ioval = (tr->ioval & 0xfb) | (!tr->curstereo << 2);
- outb(tr->ioval, tr->io);
- mutex_unlock(&tr->lock);
-}
+ struct trust *tr = container_of(isa, struct trust, isa);
-static void tr_setmute(struct trust *tr, int mute)
-{
- mutex_lock(&tr->lock);
- tr->curmute = !!mute;
- tr->ioval = (tr->ioval & 0xf7) | (tr->curmute << 3);
- outb(tr->ioval, tr->io);
- mutex_unlock(&tr->lock);
+ tr->ioval = (tr->ioval & 0xfb) | (!stereo << 2);
+ outb(tr->ioval, isa->io);
+ return 0;
}
-static int tr_getsigstr(struct trust *tr)
+static u32 trust_g_signal(struct radio_isa_card *isa)
{
int i, v;
- mutex_lock(&tr->lock);
for (i = 0, v = 0; i < 100; i++)
- v |= inb(tr->io);
- mutex_unlock(&tr->lock);
+ v |= inb(isa->io);
return (v & 1) ? 0 : 0xffff;
}
-static int tr_getstereo(struct trust *tr)
-{
- /* don't know how to determine it, just return the setting */
- return tr->curstereo;
-}
-
-static void tr_setfreq(struct trust *tr, unsigned long f)
+static int trust_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- mutex_lock(&tr->lock);
- tr->curfreq = f;
- f /= 160; /* Convert to 10 kHz units */
- f += 1070; /* Add 10.7 MHz IF */
- write_i2c(tr, 5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
- mutex_unlock(&tr->lock);
-}
+ struct trust *tr = container_of(isa, struct trust, isa);
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-trust", sizeof(v->driver));
- strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ freq /= 160; /* Convert to 10 kHz units */
+ freq += 1070; /* Add 10.7 MHz IF */
+ write_i2c(tr, 5, TSA6060T_ADDR, (freq << 1) | 1,
+ freq >> 7, 0x60 | ((freq >> 15) & 1), 0);
return 0;
}
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct trust *tr = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87.5 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (tr_getstereo(tr))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = tr_getsigstr(tr);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct trust *tr = video_drvdata(file);
-
- if (v->index)
- return -EINVAL;
- tr_setstereo(tr, v->audmode == V4L2_TUNER_MODE_STEREO);
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct trust *tr = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- tr_setfreq(tr, f->frequency);
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct trust *tr = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = tr->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 2048, 65535);
- case V4L2_CID_AUDIO_BASS:
- case V4L2_CID_AUDIO_TREBLE:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 4370, 32768);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct trust *tr = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = tr->curmute;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = tr->curvol * 2048;
- return 0;
- case V4L2_CID_AUDIO_BASS:
- ctrl->value = tr->curbass * 4370;
- return 0;
- case V4L2_CID_AUDIO_TREBLE:
- ctrl->value = tr->curtreble * 4370;
- return 0;
- }
- return -EINVAL;
-}
+static int basstreble2chip[15] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
+};
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
+static int trust_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct trust *tr = video_drvdata(file);
+ struct radio_isa_card *isa =
+ container_of(ctrl->handler, struct radio_isa_card, hdl);
+ struct trust *tr = container_of(isa, struct trust, isa);
switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- tr_setmute(tr, ctrl->value);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- tr_setvol(tr, ctrl->value);
- return 0;
case V4L2_CID_AUDIO_BASS:
- tr_setbass(tr, ctrl->value);
+ write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[ctrl->val]);
return 0;
case V4L2_CID_AUDIO_TREBLE:
- tr_settreble(tr, ctrl->value);
+ write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[ctrl->val]);
return 0;
}
return -EINVAL;
}
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static const struct v4l2_file_operations trust_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
-};
-
-static const struct v4l2_ioctl_ops trust_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
+static const struct v4l2_ctrl_ops trust_ctrl_ops = {
+ .s_ctrl = trust_s_ctrl,
};
-static int __init trust_init(void)
+static int trust_initialize(struct radio_isa_card *isa)
{
- struct trust *tr = &trust_card;
- struct v4l2_device *v4l2_dev = &tr->v4l2_dev;
- int res;
+ struct trust *tr = container_of(isa, struct trust, isa);
- strlcpy(v4l2_dev->name, "trust", sizeof(v4l2_dev->name));
- tr->io = io;
tr->ioval = 0xf;
- mutex_init(&tr->lock);
-
- if (tr->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x0x350 or 0x358\n");
- return -EINVAL;
- }
- if (!request_region(tr->io, 2, "Trust FM Radio")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", tr->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(tr->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- strlcpy(tr->vdev.name, v4l2_dev->name, sizeof(tr->vdev.name));
- tr->vdev.v4l2_dev = v4l2_dev;
- tr->vdev.fops = &trust_fops;
- tr->vdev.ioctl_ops = &trust_ioctl_ops;
- tr->vdev.release = video_device_release_empty;
- video_set_drvdata(&tr->vdev, tr);
-
write_i2c(tr, 2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
write_i2c(tr, 2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
- tr_setvol(tr, 0xffff);
- tr_setbass(tr, 0x8000);
- tr_settreble(tr, 0x8000);
- tr_setstereo(tr, 1);
-
- /* mute card - prevents noisy bootups */
- tr_setmute(tr, 1);
+ v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops,
+ V4L2_CID_AUDIO_BASS, 0, 15, 1, 8);
+ v4l2_ctrl_new_std(&isa->hdl, &trust_ctrl_ops,
+ V4L2_CID_AUDIO_TREBLE, 0, 15, 1, 8);
+ return isa->hdl.error;
+}
- if (video_register_device(&tr->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(tr->io, 2);
- return -EINVAL;
- }
+static const struct radio_isa_ops trust_ops = {
+ .init = trust_initialize,
+ .alloc = trust_alloc,
+ .s_mute_volume = trust_s_mute_volume,
+ .s_frequency = trust_s_frequency,
+ .s_stereo = trust_s_stereo,
+ .g_signal = trust_g_signal,
+};
- v4l2_info(v4l2_dev, "Trust FM Radio card driver v1.0.\n");
+static const int trust_ioports[] = { 0x350, 0x358 };
+
+static struct radio_isa_driver trust_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-trust",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = trust_ioports,
+ .num_of_io_ports = ARRAY_SIZE(trust_ioports),
+ .region_size = 2,
+ .card = "Trust FM Radio",
+ .ops = &trust_ops,
+ .has_stereo = true,
+ .max_volume = 31,
+};
- return 0;
+static int __init trust_init(void)
+{
+ return isa_register_driver(&trust_driver.driver, TRUST_MAX);
}
-static void __exit cleanup_trust_module(void)
+static void __exit trust_exit(void)
{
- struct trust *tr = &trust_card;
-
- video_unregister_device(&tr->vdev);
- v4l2_device_unregister(&tr->v4l2_dev);
- release_region(tr->io, 2);
+ isa_unregister_driver(&trust_driver.driver);
}
module_init(trust_init);
-module_exit(cleanup_trust_module);
+module_exit(trust_exit);
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index 398726abc0c8..eb72a4d13758 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -33,63 +33,53 @@
#include <linux/ioport.h> /* request_region */
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include "radio-isa.h"
#define DRIVER_VERSION "0.1.2"
MODULE_AUTHOR("Dr. Henrik Seidel");
MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
+MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_TYPHOON_PORT
#define CONFIG_RADIO_TYPHOON_PORT -1
#endif
#ifndef CONFIG_RADIO_TYPHOON_MUTEFREQ
-#define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
+#define CONFIG_RADIO_TYPHOON_MUTEFREQ 87000
#endif
-static int io = CONFIG_RADIO_TYPHOON_PORT;
-static int radio_nr = -1;
-
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
-
-module_param(radio_nr, int, 0);
+#define TYPHOON_MAX 2
+static int io[TYPHOON_MAX] = { [0] = CONFIG_RADIO_TYPHOON_PORT,
+ [1 ... (TYPHOON_MAX - 1)] = -1 };
+static int radio_nr[TYPHOON_MAX] = { [0 ... (TYPHOON_MAX - 1)] = -1 };
static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
+
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Typhoon card (0x316 or 0x336)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
module_param(mutefreq, ulong, 0);
MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
-#define BANNER "Typhoon Radio Card driver v" DRIVER_VERSION "\n"
-
struct typhoon {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
- int curvol;
+ struct radio_isa_card isa;
int muted;
- unsigned long curfreq;
- unsigned long mutefreq;
- struct mutex lock;
};
-static struct typhoon typhoon_card;
-
-static void typhoon_setvol_generic(struct typhoon *dev, int vol)
+static struct radio_isa_card *typhoon_alloc(void)
{
- mutex_lock(&dev->lock);
- vol >>= 14; /* Map 16 bit to 2 bit */
- vol &= 3;
- outb_p(vol / 2, dev->io); /* Set the volume, high bit. */
- outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */
- mutex_unlock(&dev->lock);
+ struct typhoon *ty = kzalloc(sizeof(*ty), GFP_KERNEL);
+
+ return ty ? &ty->isa : NULL;
}
-static int typhoon_setfreq_generic(struct typhoon *dev,
- unsigned long frequency)
+static int typhoon_s_frequency(struct radio_isa_card *isa, u32 freq)
{
unsigned long outval;
unsigned long x;
@@ -105,302 +95,86 @@ static int typhoon_setfreq_generic(struct typhoon *dev,
*
*/
- mutex_lock(&dev->lock);
- x = frequency / 160;
+ x = freq / 160;
outval = (x * x + 2500) / 5000;
outval = (outval * x + 5000) / 10000;
outval -= (10 * x * x + 10433) / 20866;
outval += 4 * x - 11505;
- outb_p((outval >> 8) & 0x01, dev->io + 4);
- outb_p(outval >> 9, dev->io + 6);
- outb_p(outval & 0xff, dev->io + 8);
- mutex_unlock(&dev->lock);
-
- return 0;
-}
-
-static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
-{
- typhoon_setfreq_generic(dev, frequency);
- dev->curfreq = frequency;
- return 0;
-}
-
-static void typhoon_mute(struct typhoon *dev)
-{
- if (dev->muted == 1)
- return;
- typhoon_setvol_generic(dev, 0);
- typhoon_setfreq_generic(dev, dev->mutefreq);
- dev->muted = 1;
-}
-
-static void typhoon_unmute(struct typhoon *dev)
-{
- if (dev->muted == 0)
- return;
- typhoon_setfreq_generic(dev, dev->curfreq);
- typhoon_setvol_generic(dev, dev->curvol);
- dev->muted = 0;
-}
-
-static int typhoon_setvol(struct typhoon *dev, int vol)
-{
- if (dev->muted && vol != 0) { /* user is unmuting the card */
- dev->curvol = vol;
- typhoon_unmute(dev);
- return 0;
- }
- if (vol == dev->curvol) /* requested volume == current */
- return 0;
-
- if (vol == 0) { /* volume == 0 means mute the card */
- typhoon_mute(dev);
- dev->curvol = vol;
- return 0;
- }
- typhoon_setvol_generic(dev, vol);
- dev->curvol = vol;
- return 0;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
- strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 87.5 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO;
- v->capability = V4L2_TUNER_CAP_LOW;
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF; /* We can't get the signal strength */
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct typhoon *dev = video_drvdata(file);
-
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = dev->curfreq;
- return 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct typhoon *dev = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- dev->curfreq = f->frequency;
- typhoon_setfreq(dev, dev->curfreq);
+ outb_p((outval >> 8) & 0x01, isa->io + 4);
+ outb_p(outval >> 9, isa->io + 6);
+ outb_p(outval & 0xff, isa->io + 8);
return 0;
}
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
+static int typhoon_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535);
- }
- return -EINVAL;
-}
+ struct typhoon *ty = container_of(isa, struct typhoon, isa);
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct typhoon *dev = video_drvdata(file);
+ if (mute)
+ vol = 0;
+ vol >>= 14; /* Map 16 bit to 2 bit */
+ vol &= 3;
+ outb_p(vol / 2, isa->io); /* Set the volume, high bit. */
+ outb_p(vol % 2, isa->io + 2); /* Set the volume, low bit. */
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = dev->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = dev->curvol;
- return 0;
+ if (vol == 0 && !ty->muted) {
+ ty->muted = true;
+ return typhoon_s_frequency(isa, mutefreq << 4);
}
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl (struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct typhoon *dev = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- typhoon_mute(dev);
- else
- typhoon_unmute(dev);
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- typhoon_setvol(dev, ctrl->value);
- return 0;
+ if (vol && ty->muted) {
+ ty->muted = false;
+ return typhoon_s_frequency(isa, isa->freq);
}
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
return 0;
}
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
-}
-
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- return a->index ? -EINVAL : 0;
-}
-
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct typhoon *dev = video_drvdata(file);
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
-
- v4l2_info(v4l2_dev, BANNER);
-#ifdef MODULE
- v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n");
-#else
- v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n");
-#endif
- v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4);
- v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol);
- v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off");
- v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io);
- v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4);
- return 0;
-}
-
-static const struct v4l2_file_operations typhoon_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops typhoon_ops = {
+ .alloc = typhoon_alloc,
+ .s_mute_volume = typhoon_s_mute_volume,
+ .s_frequency = typhoon_s_frequency,
};
-static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
- .vidioc_log_status = vidioc_log_status,
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int typhoon_ioports[] = { 0x316, 0x336 };
+
+static struct radio_isa_driver typhoon_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-typhoon",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = typhoon_ioports,
+ .num_of_io_ports = ARRAY_SIZE(typhoon_ioports),
+ .region_size = 8,
+ .card = "Typhoon Radio",
+ .ops = &typhoon_ops,
+ .has_stereo = true,
+ .max_volume = 3,
};
static int __init typhoon_init(void)
{
- struct typhoon *dev = &typhoon_card;
- struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name));
- dev->io = io;
-
- if (dev->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
- return -EINVAL;
- }
-
- if (mutefreq < 87000 || mutefreq > 108500) {
- v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
- v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
- return -EINVAL;
- }
- dev->curfreq = dev->mutefreq = mutefreq << 4;
-
- mutex_init(&dev->lock);
- if (!request_region(dev->io, 8, "typhoon")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n",
- dev->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(dev->io, 8);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
+ if (mutefreq < 87000 || mutefreq > 108000) {
+ printk(KERN_ERR "%s: You must set a frequency (in kHz) used when muting the card,\n",
+ typhoon_driver.driver.driver.name);
+ printk(KERN_ERR "%s: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108000)\n",
+ typhoon_driver.driver.driver.name);
+ return -ENODEV;
}
- v4l2_info(v4l2_dev, BANNER);
-
- strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
- dev->vdev.v4l2_dev = v4l2_dev;
- dev->vdev.fops = &typhoon_fops;
- dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
- dev->vdev.release = video_device_release_empty;
- video_set_drvdata(&dev->vdev, dev);
-
- /* mute card - prevents noisy bootups */
- typhoon_mute(dev);
-
- if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 8);
- return -EINVAL;
- }
- v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
- v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", mutefreq);
-
- return 0;
+ return isa_register_driver(&typhoon_driver.driver, TYPHOON_MAX);
}
static void __exit typhoon_exit(void)
{
- struct typhoon *dev = &typhoon_card;
-
- video_unregister_device(&dev->vdev);
- v4l2_device_unregister(&dev->v4l2_dev);
- release_region(dev->io, 8);
+ isa_unregister_driver(&typhoon_driver.driver);
}
+
module_init(typhoon_init);
module_exit(typhoon_exit);
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index f5613b948203..026e88eef29c 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -1,5 +1,6 @@
-/* zoltrix radio plus driver for Linux radio support
- * (c) 1998 C. van Schaik <carl@leg.uct.ac.za>
+/*
+ * Zoltrix Radio Plus driver
+ * Copyright 1998 C. van Schaik <carl@leg.uct.ac.za>
*
* BUGS
* Due to the inconsistency in reading from the signal flags
@@ -27,6 +28,14 @@
*
* 2006-07-24 - Converted to V4L2 API
* by Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Converted to the radio-isa framework by Hans Verkuil <hans.verkuil@cisco.com>
+ *
+ * Note that this is the driver for the Zoltrix Radio Plus.
+ * This driver does not work for the Zoltrix Radio Plus 108 or the
+ * Zoltrix Radio Plus for Windows.
+ *
+ * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool.
*/
#include <linux/module.h> /* Modules */
@@ -36,82 +45,70 @@
#include <linux/videodev2.h> /* kernel radio structs */
#include <linux/mutex.h>
#include <linux/io.h> /* outb, outb_p */
+#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
+#include "radio-isa.h"
-MODULE_AUTHOR("C.van Schaik");
+MODULE_AUTHOR("C. van Schaik");
MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
MODULE_LICENSE("GPL");
-MODULE_VERSION("0.0.3");
+MODULE_VERSION("0.1.99");
#ifndef CONFIG_RADIO_ZOLTRIX_PORT
#define CONFIG_RADIO_ZOLTRIX_PORT -1
#endif
-static int io = CONFIG_RADIO_ZOLTRIX_PORT;
-static int radio_nr = -1;
+#define ZOLTRIX_MAX 2
+
+static int io[ZOLTRIX_MAX] = { [0] = CONFIG_RADIO_ZOLTRIX_PORT,
+ [1 ... (ZOLTRIX_MAX - 1)] = -1 };
+static int radio_nr[ZOLTRIX_MAX] = { [0 ... (ZOLTRIX_MAX - 1)] = -1 };
-module_param(io, int, 0);
-MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
-module_param(radio_nr, int, 0);
+module_param_array(io, int, NULL, 0444);
+MODULE_PARM_DESC(io, "I/O addresses of the Zoltrix Radio Plus card (0x20c or 0x30c)");
+module_param_array(radio_nr, int, NULL, 0444);
+MODULE_PARM_DESC(radio_nr, "Radio device numbers");
struct zoltrix {
- struct v4l2_device v4l2_dev;
- struct video_device vdev;
- int io;
+ struct radio_isa_card isa;
int curvol;
- unsigned long curfreq;
- int muted;
- unsigned int stereo;
- struct mutex lock;
+ bool muted;
};
-static struct zoltrix zoltrix_card;
+static struct radio_isa_card *zoltrix_alloc(void)
+{
+ struct zoltrix *zol = kzalloc(sizeof(*zol), GFP_KERNEL);
+
+ return zol ? &zol->isa : NULL;
+}
-static int zol_setvol(struct zoltrix *zol, int vol)
+static int zoltrix_s_mute_volume(struct radio_isa_card *isa, bool mute, int vol)
{
- zol->curvol = vol;
- if (zol->muted)
- return 0;
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
- mutex_lock(&zol->lock);
- if (vol == 0) {
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
- mutex_unlock(&zol->lock);
+ zol->curvol = vol;
+ zol->muted = mute;
+ if (mute || vol == 0) {
+ outb(0, isa->io);
+ outb(0, isa->io);
+ inb(isa->io + 3); /* Zoltrix needs to be read to confirm */
return 0;
}
- outb(zol->curvol-1, zol->io);
+ outb(vol - 1, isa->io);
msleep(10);
- inb(zol->io + 2);
- mutex_unlock(&zol->lock);
+ inb(isa->io + 2);
return 0;
}
-static void zol_mute(struct zoltrix *zol)
-{
- zol->muted = 1;
- mutex_lock(&zol->lock);
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
- mutex_unlock(&zol->lock);
-}
-
-static void zol_unmute(struct zoltrix *zol)
-{
- zol->muted = 0;
- zol_setvol(zol, zol->curvol);
-}
-
-static int zol_setfreq(struct zoltrix *zol, unsigned long freq)
+/* tunes the radio to the desired frequency */
+static int zoltrix_s_frequency(struct radio_isa_card *isa, u32 freq)
{
- /* tunes the radio to the desired frequency */
- struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
+ struct v4l2_device *v4l2_dev = &isa->v4l2_dev;
unsigned long long bitmask, f, m;
- unsigned int stereo = zol->stereo;
+ bool stereo = isa->stereo;
int i;
if (freq == 0) {
@@ -125,340 +122,125 @@ static int zol_setfreq(struct zoltrix *zol, unsigned long freq)
bitmask = 0xc480402c10080000ull;
i = 45;
- mutex_lock(&zol->lock);
-
- zol->curfreq = freq;
-
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
+ outb(0, isa->io);
+ outb(0, isa->io);
+ inb(isa->io + 3); /* Zoltrix needs to be read to confirm */
- outb(0x40, zol->io);
- outb(0xc0, zol->io);
+ outb(0x40, isa->io);
+ outb(0xc0, isa->io);
bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31));
while (i--) {
if ((bitmask & 0x8000000000000000ull) != 0) {
- outb(0x80, zol->io);
+ outb(0x80, isa->io);
udelay(50);
- outb(0x00, zol->io);
+ outb(0x00, isa->io);
udelay(50);
- outb(0x80, zol->io);
+ outb(0x80, isa->io);
udelay(50);
} else {
- outb(0xc0, zol->io);
+ outb(0xc0, isa->io);
udelay(50);
- outb(0x40, zol->io);
+ outb(0x40, isa->io);
udelay(50);
- outb(0xc0, zol->io);
+ outb(0xc0, isa->io);
udelay(50);
}
bitmask *= 2;
}
/* termination sequence */
- outb(0x80, zol->io);
- outb(0xc0, zol->io);
- outb(0x40, zol->io);
+ outb(0x80, isa->io);
+ outb(0xc0, isa->io);
+ outb(0x40, isa->io);
udelay(1000);
- inb(zol->io + 2);
-
+ inb(isa->io + 2);
udelay(1000);
- if (zol->muted) {
- outb(0, zol->io);
- outb(0, zol->io);
- inb(zol->io + 3);
- udelay(1000);
- }
-
- mutex_unlock(&zol->lock);
-
- if (!zol->muted)
- zol_setvol(zol, zol->curvol);
- return 0;
+ return zoltrix_s_mute_volume(isa, zol->muted, zol->curvol);
}
/* Get signal strength */
-static int zol_getsigstr(struct zoltrix *zol)
+static u32 zoltrix_g_rxsubchans(struct radio_isa_card *isa)
{
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
int a, b;
- mutex_lock(&zol->lock);
- outb(0x00, zol->io); /* This stuff I found to do nothing */
- outb(zol->curvol, zol->io);
+ outb(0x00, isa->io); /* This stuff I found to do nothing */
+ outb(zol->curvol, isa->io);
msleep(20);
- a = inb(zol->io);
+ a = inb(isa->io);
msleep(10);
- b = inb(zol->io);
+ b = inb(isa->io);
- mutex_unlock(&zol->lock);
-
- if (a != b)
- return 0;
-
- /* I found this out by playing with a binary scanner on the card io */
- return a == 0xcf || a == 0xdf || a == 0xef;
+ return (a == b && a == 0xcf) ?
+ V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO;
}
-static int zol_is_stereo(struct zoltrix *zol)
+static u32 zoltrix_g_signal(struct radio_isa_card *isa)
{
- int x1, x2;
-
- mutex_lock(&zol->lock);
+ struct zoltrix *zol = container_of(isa, struct zoltrix, isa);
+ int a, b;
- outb(0x00, zol->io);
- outb(zol->curvol, zol->io);
+ outb(0x00, isa->io); /* This stuff I found to do nothing */
+ outb(zol->curvol, isa->io);
msleep(20);
- x1 = inb(zol->io);
+ a = inb(isa->io);
msleep(10);
- x2 = inb(zol->io);
-
- mutex_unlock(&zol->lock);
-
- return x1 == x2 && x1 == 0xcf;
-}
-
-static int vidioc_querycap(struct file *file, void *priv,
- struct v4l2_capability *v)
-{
- strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
- strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
- strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
- v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
- return 0;
-}
-
-static int vidioc_g_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- if (v->index > 0)
- return -EINVAL;
-
- strlcpy(v->name, "FM", sizeof(v->name));
- v->type = V4L2_TUNER_RADIO;
- v->rangelow = 88 * 16000;
- v->rangehigh = 108 * 16000;
- v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
- v->capability = V4L2_TUNER_CAP_LOW;
- if (zol_is_stereo(zol))
- v->audmode = V4L2_TUNER_MODE_STEREO;
- else
- v->audmode = V4L2_TUNER_MODE_MONO;
- v->signal = 0xFFFF * zol_getsigstr(zol);
- return 0;
-}
-
-static int vidioc_s_tuner(struct file *file, void *priv,
- struct v4l2_tuner *v)
-{
- return v->index ? -EINVAL : 0;
-}
-
-static int vidioc_s_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
- return -EINVAL;
- if (zol_setfreq(zol, f->frequency) != 0)
- return -EINVAL;
- return 0;
-}
-
-static int vidioc_g_frequency(struct file *file, void *priv,
- struct v4l2_frequency *f)
-{
- struct zoltrix *zol = video_drvdata(file);
+ b = inb(isa->io);
- if (f->tuner != 0)
- return -EINVAL;
- f->type = V4L2_TUNER_RADIO;
- f->frequency = zol->curfreq;
- return 0;
-}
-
-static int vidioc_queryctrl(struct file *file, void *priv,
- struct v4l2_queryctrl *qc)
-{
- switch (qc->id) {
- case V4L2_CID_AUDIO_MUTE:
- return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
- case V4L2_CID_AUDIO_VOLUME:
- return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535);
- }
- return -EINVAL;
-}
-
-static int vidioc_g_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- ctrl->value = zol->muted;
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- ctrl->value = zol->curvol * 4096;
- return 0;
- }
- return -EINVAL;
-}
-
-static int vidioc_s_ctrl(struct file *file, void *priv,
- struct v4l2_control *ctrl)
-{
- struct zoltrix *zol = video_drvdata(file);
-
- switch (ctrl->id) {
- case V4L2_CID_AUDIO_MUTE:
- if (ctrl->value)
- zol_mute(zol);
- else {
- zol_unmute(zol);
- zol_setvol(zol, zol->curvol);
- }
- return 0;
- case V4L2_CID_AUDIO_VOLUME:
- zol_setvol(zol, ctrl->value / 4096);
+ if (a != b)
return 0;
- }
- zol->stereo = 1;
- if (zol_setfreq(zol, zol->curfreq) != 0)
- return -EINVAL;
-#if 0
-/* FIXME: Implement stereo/mono switch on V4L2 */
- if (v->mode & VIDEO_SOUND_STEREO) {
- zol->stereo = 1;
- zol_setfreq(zol, zol->curfreq);
- }
- if (v->mode & VIDEO_SOUND_MONO) {
- zol->stereo = 0;
- zol_setfreq(zol, zol->curfreq);
- }
-#endif
- return -EINVAL;
-}
-
-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
-{
- *i = 0;
- return 0;
-}
-
-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
-{
- return i ? -EINVAL : 0;
-}
-static int vidioc_g_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
-{
- a->index = 0;
- strlcpy(a->name, "Radio", sizeof(a->name));
- a->capability = V4L2_AUDCAP_STEREO;
- return 0;
+ /* I found this out by playing with a binary scanner on the card io */
+ return (a == 0xcf || a == 0xdf || a == 0xef) ? 0xffff : 0;
}
-static int vidioc_s_audio(struct file *file, void *priv,
- struct v4l2_audio *a)
+static int zoltrix_s_stereo(struct radio_isa_card *isa, bool stereo)
{
- return a->index ? -EINVAL : 0;
+ return zoltrix_s_frequency(isa, isa->freq);
}
-static const struct v4l2_file_operations zoltrix_fops =
-{
- .owner = THIS_MODULE,
- .unlocked_ioctl = video_ioctl2,
+static const struct radio_isa_ops zoltrix_ops = {
+ .alloc = zoltrix_alloc,
+ .s_mute_volume = zoltrix_s_mute_volume,
+ .s_frequency = zoltrix_s_frequency,
+ .s_stereo = zoltrix_s_stereo,
+ .g_rxsubchans = zoltrix_g_rxsubchans,
+ .g_signal = zoltrix_g_signal,
};
-static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = {
- .vidioc_querycap = vidioc_querycap,
- .vidioc_g_tuner = vidioc_g_tuner,
- .vidioc_s_tuner = vidioc_s_tuner,
- .vidioc_g_audio = vidioc_g_audio,
- .vidioc_s_audio = vidioc_s_audio,
- .vidioc_g_input = vidioc_g_input,
- .vidioc_s_input = vidioc_s_input,
- .vidioc_g_frequency = vidioc_g_frequency,
- .vidioc_s_frequency = vidioc_s_frequency,
- .vidioc_queryctrl = vidioc_queryctrl,
- .vidioc_g_ctrl = vidioc_g_ctrl,
- .vidioc_s_ctrl = vidioc_s_ctrl,
+static const int zoltrix_ioports[] = { 0x20c, 0x30c };
+
+static struct radio_isa_driver zoltrix_driver = {
+ .driver = {
+ .match = radio_isa_match,
+ .probe = radio_isa_probe,
+ .remove = radio_isa_remove,
+ .driver = {
+ .name = "radio-zoltrix",
+ },
+ },
+ .io_params = io,
+ .radio_nr_params = radio_nr,
+ .io_ports = zoltrix_ioports,
+ .num_of_io_ports = ARRAY_SIZE(zoltrix_ioports),
+ .region_size = 2,
+ .card = "Zoltrix Radio Plus",
+ .ops = &zoltrix_ops,
+ .has_stereo = true,
+ .max_volume = 15,
};
static int __init zoltrix_init(void)
{
- struct zoltrix *zol = &zoltrix_card;
- struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
- int res;
-
- strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name));
- zol->io = io;
- if (zol->io == -1) {
- v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or 0x30c\n");
- return -EINVAL;
- }
- if (zol->io != 0x20c && zol->io != 0x30c) {
- v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n");
- return -ENXIO;
- }
-
- if (!request_region(zol->io, 2, "zoltrix")) {
- v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io);
- return -EBUSY;
- }
-
- res = v4l2_device_register(NULL, v4l2_dev);
- if (res < 0) {
- release_region(zol->io, 2);
- v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
- return res;
- }
-
- mutex_init(&zol->lock);
-
- /* mute card - prevents noisy bootups */
-
- /* this ensures that the volume is all the way down */
-
- outb(0, zol->io);
- outb(0, zol->io);
- msleep(20);
- inb(zol->io + 3);
-
- zol->curvol = 0;
- zol->stereo = 1;
-
- strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name));
- zol->vdev.v4l2_dev = v4l2_dev;
- zol->vdev.fops = &zoltrix_fops;
- zol->vdev.ioctl_ops = &zoltrix_ioctl_ops;
- zol->vdev.release = video_device_release_empty;
- video_set_drvdata(&zol->vdev, zol);
-
- if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
- v4l2_device_unregister(v4l2_dev);
- release_region(zol->io, 2);
- return -EINVAL;
- }
- v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n");
-
- return 0;
+ return isa_register_driver(&zoltrix_driver.driver, ZOLTRIX_MAX);
}
static void __exit zoltrix_exit(void)
{
- struct zoltrix *zol = &zoltrix_card;
-
- video_unregister_device(&zol->vdev);
- v4l2_device_unregister(&zol->v4l2_dev);
- release_region(zol->io, 2);
+ isa_unregister_driver(&zoltrix_driver.driver);
}
module_init(zoltrix_init);
diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c
index b1193dfc5087..9474706350f8 100644
--- a/drivers/media/radio/saa7706h.c
+++ b/drivers/media/radio/saa7706h.c
@@ -434,18 +434,7 @@ static struct i2c_driver saa7706h_driver = {
.id_table = saa7706h_id,
};
-static __init int saa7706h_init(void)
-{
- return i2c_add_driver(&saa7706h_driver);
-}
-
-static __exit void saa7706h_exit(void)
-{
- i2c_del_driver(&saa7706h_driver);
-}
-
-module_init(saa7706h_init);
-module_exit(saa7706h_exit);
+module_i2c_driver(saa7706h_driver);
MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver");
MODULE_AUTHOR("Mocean Laboratories");
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index fd3541b0e91c..9b546a5523f3 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -539,33 +539,7 @@ static struct i2c_driver si470x_i2c_driver = {
.id_table = si470x_i2c_id,
};
-
-
-/**************************************************************************
- * Module Interface
- **************************************************************************/
-
-/*
- * si470x_i2c_init - module init
- */
-static int __init si470x_i2c_init(void)
-{
- printk(KERN_INFO DRIVER_DESC ", Version " DRIVER_VERSION "\n");
- return i2c_add_driver(&si470x_i2c_driver);
-}
-
-
-/*
- * si470x_i2c_exit - module exit
- */
-static void __exit si470x_i2c_exit(void)
-{
- i2c_del_driver(&si470x_i2c_driver);
-}
-
-
-module_init(si470x_i2c_init);
-module_exit(si470x_i2c_exit);
+module_i2c_driver(si470x_i2c_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
diff --git a/drivers/media/radio/si4713-i2c.c b/drivers/media/radio/si4713-i2c.c
index 27aba936fb2b..b898c8925ab7 100644
--- a/drivers/media/radio/si4713-i2c.c
+++ b/drivers/media/radio/si4713-i2c.c
@@ -2106,17 +2106,4 @@ static struct i2c_driver si4713_i2c_driver = {
.id_table = si4713_id,
};
-/* Module Interface */
-static int __init si4713_module_init(void)
-{
- return i2c_add_driver(&si4713_i2c_driver);
-}
-
-static void __exit si4713_module_exit(void)
-{
- i2c_del_driver(&si4713_i2c_driver);
-}
-
-module_init(si4713_module_init);
-module_exit(si4713_module_exit);
-
+module_i2c_driver(si4713_i2c_driver);
diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c
index 3408685b690c..6418c4c9faf1 100644
--- a/drivers/media/radio/tef6862.c
+++ b/drivers/media/radio/tef6862.c
@@ -215,20 +215,8 @@ static struct i2c_driver tef6862_driver = {
.id_table = tef6862_id,
};
-static __init int tef6862_init(void)
-{
- return i2c_add_driver(&tef6862_driver);
-}
-
-static __exit void tef6862_exit(void)
-{
- i2c_del_driver(&tef6862_driver);
-}
-
-module_init(tef6862_init);
-module_exit(tef6862_exit);
+module_i2c_driver(tef6862_driver);
MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 4df4affeea5f..a3fbb21350e9 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -266,4 +266,13 @@ config RC_LOOPBACK
To compile this driver as a module, choose M here: the module will
be called rc_loopback.
+config IR_GPIO_CIR
+ tristate "GPIO IR remote control"
+ depends on RC_CORE
+ ---help---
+ Say Y if you want to use GPIO based IR Receiver.
+
+ To compile this driver as a module, choose M here: the module will
+ be called gpio-ir-recv.
+
endif #RC_CORE
diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index fb3dee2dd845..29f364f88a94 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_IR_REDRAT3) += redrat3.o
obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
+obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c
index 7f7079b12f23..392d4be91f8f 100644
--- a/drivers/media/rc/fintek-cir.c
+++ b/drivers/media/rc/fintek-cir.c
@@ -117,7 +117,7 @@ static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
static void cir_dump_regs(struct fintek_dev *fintek)
{
fintek_config_mode_enable(fintek);
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
pr_reg("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
pr_reg(" * CR CIR BASE ADDR: 0x%x\n",
@@ -143,7 +143,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
u8 chip_major, chip_minor;
u8 vendor_major, vendor_minor;
u8 portsel, ir_class;
- u16 vendor;
+ u16 vendor, chip;
int ret = 0;
fintek_config_mode_enable(fintek);
@@ -176,6 +176,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
+ chip = chip_major << 8 | chip_minor;
vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
@@ -192,6 +193,15 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
fintek->chip_major = chip_major;
fintek->chip_minor = chip_minor;
fintek->chip_vendor = vendor;
+
+ /*
+ * Newer reviews of this chipset uses port 8 instead of 5
+ */
+ if ((chip != 0x0408) || (chip != 0x0804))
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;
+ else
+ fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;
+
spin_unlock_irqrestore(&fintek->fintek_lock, flags);
return ret;
@@ -200,7 +210,7 @@ static int fintek_hw_detect(struct fintek_dev *fintek)
static void fintek_cir_ldev_init(struct fintek_dev *fintek)
{
/* Select CIR logical device and enable */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
/* Write allocated CIR address and IRQ information to hardware */
@@ -381,7 +391,7 @@ static irqreturn_t fintek_cir_isr(int irq, void *data)
fit_dbg_verbose("%s firing", __func__);
fintek_config_mode_enable(fintek);
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_config_mode_disable(fintek);
/*
@@ -422,7 +432,7 @@ static void fintek_enable_cir(struct fintek_dev *fintek)
fintek_config_mode_enable(fintek);
/* enable the CIR logical device */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
@@ -439,7 +449,7 @@ static void fintek_disable_cir(struct fintek_dev *fintek)
fintek_config_mode_enable(fintek);
/* disable the CIR logical device */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
@@ -611,7 +621,7 @@ static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
fintek_config_mode_enable(fintek);
/* disable cir logical dev */
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
@@ -634,7 +644,7 @@ static int fintek_resume(struct pnp_dev *pdev)
/* Enable CIR logical device */
fintek_config_mode_enable(fintek);
- fintek_select_logical_dev(fintek, LOGICAL_DEV_CIR);
+ fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
fintek_config_mode_disable(fintek);
diff --git a/drivers/media/rc/fintek-cir.h b/drivers/media/rc/fintek-cir.h
index 1b10b2011f5e..82516a1d39b0 100644
--- a/drivers/media/rc/fintek-cir.h
+++ b/drivers/media/rc/fintek-cir.h
@@ -88,6 +88,7 @@ struct fintek_dev {
u8 chip_major;
u8 chip_minor;
u16 chip_vendor;
+ u8 logical_dev_cir;
/* hardware features */
bool hw_learning_capable;
@@ -172,7 +173,8 @@ struct fintek_dev {
#define LOGICAL_DEV_ENABLE 0x01
/* Logical device number of the CIR function */
-#define LOGICAL_DEV_CIR 0x05
+#define LOGICAL_DEV_CIR_REV1 0x05
+#define LOGICAL_DEV_CIR_REV2 0x08
/* CIR Logical Device (LDN 0x08) config registers */
#define CIR_CR_COMMAND_INDEX 0x04
diff --git a/drivers/media/rc/gpio-ir-recv.c b/drivers/media/rc/gpio-ir-recv.c
new file mode 100644
index 000000000000..0d875450c5ce
--- /dev/null
+++ b/drivers/media/rc/gpio-ir-recv.c
@@ -0,0 +1,205 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <media/rc-core.h>
+#include <media/gpio-ir-recv.h>
+
+#define GPIO_IR_DRIVER_NAME "gpio-rc-recv"
+#define GPIO_IR_DEVICE_NAME "gpio_ir_recv"
+
+struct gpio_rc_dev {
+ struct rc_dev *rcdev;
+ int gpio_nr;
+ bool active_low;
+};
+
+static irqreturn_t gpio_ir_recv_irq(int irq, void *dev_id)
+{
+ struct gpio_rc_dev *gpio_dev = dev_id;
+ int gval;
+ int rc = 0;
+ enum raw_event_type type = IR_SPACE;
+
+ gval = gpio_get_value_cansleep(gpio_dev->gpio_nr);
+
+ if (gval < 0)
+ goto err_get_value;
+
+ if (gpio_dev->active_low)
+ gval = !gval;
+
+ if (gval == 1)
+ type = IR_PULSE;
+
+ rc = ir_raw_event_store_edge(gpio_dev->rcdev, type);
+ if (rc < 0)
+ goto err_get_value;
+
+ ir_raw_event_handle(gpio_dev->rcdev);
+
+err_get_value:
+ return IRQ_HANDLED;
+}
+
+static int __devinit gpio_ir_recv_probe(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev;
+ struct rc_dev *rcdev;
+ const struct gpio_ir_recv_platform_data *pdata =
+ pdev->dev.platform_data;
+ int rc;
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (pdata->gpio_nr < 0)
+ return -EINVAL;
+
+ gpio_dev = kzalloc(sizeof(struct gpio_rc_dev), GFP_KERNEL);
+ if (!gpio_dev)
+ return -ENOMEM;
+
+ rcdev = rc_allocate_device();
+ if (!rcdev) {
+ rc = -ENOMEM;
+ goto err_allocate_device;
+ }
+
+ rcdev->driver_type = RC_DRIVER_IR_RAW;
+ rcdev->allowed_protos = RC_TYPE_ALL;
+ rcdev->input_name = GPIO_IR_DEVICE_NAME;
+ rcdev->input_id.bustype = BUS_HOST;
+ rcdev->driver_name = GPIO_IR_DRIVER_NAME;
+ rcdev->map_name = RC_MAP_EMPTY;
+
+ gpio_dev->rcdev = rcdev;
+ gpio_dev->gpio_nr = pdata->gpio_nr;
+ gpio_dev->active_low = pdata->active_low;
+
+ rc = gpio_request(pdata->gpio_nr, "gpio-ir-recv");
+ if (rc < 0)
+ goto err_gpio_request;
+ rc = gpio_direction_input(pdata->gpio_nr);
+ if (rc < 0)
+ goto err_gpio_direction_input;
+
+ rc = rc_register_device(rcdev);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "failed to register rc device\n");
+ goto err_register_rc_device;
+ }
+
+ platform_set_drvdata(pdev, gpio_dev);
+
+ rc = request_any_context_irq(gpio_to_irq(pdata->gpio_nr),
+ gpio_ir_recv_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "gpio-ir-recv-irq", gpio_dev);
+ if (rc < 0)
+ goto err_request_irq;
+
+ return 0;
+
+err_request_irq:
+ platform_set_drvdata(pdev, NULL);
+ rc_unregister_device(rcdev);
+err_register_rc_device:
+err_gpio_direction_input:
+ gpio_free(pdata->gpio_nr);
+err_gpio_request:
+ rc_free_device(rcdev);
+ rcdev = NULL;
+err_allocate_device:
+ kfree(gpio_dev);
+ return rc;
+}
+
+static int __devexit gpio_ir_recv_remove(struct platform_device *pdev)
+{
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ free_irq(gpio_to_irq(gpio_dev->gpio_nr), gpio_dev);
+ platform_set_drvdata(pdev, NULL);
+ rc_unregister_device(gpio_dev->rcdev);
+ gpio_free(gpio_dev->gpio_nr);
+ rc_free_device(gpio_dev->rcdev);
+ kfree(gpio_dev);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gpio_ir_recv_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+ else
+ disable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+ return 0;
+}
+
+static int gpio_ir_recv_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gpio_rc_dev *gpio_dev = platform_get_drvdata(pdev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(gpio_to_irq(gpio_dev->gpio_nr));
+ else
+ enable_irq(gpio_to_irq(gpio_dev->gpio_nr));
+
+ return 0;
+}
+
+static const struct dev_pm_ops gpio_ir_recv_pm_ops = {
+ .suspend = gpio_ir_recv_suspend,
+ .resume = gpio_ir_recv_resume,
+};
+#endif
+
+static struct platform_driver gpio_ir_recv_driver = {
+ .probe = gpio_ir_recv_probe,
+ .remove = __devexit_p(gpio_ir_recv_remove),
+ .driver = {
+ .name = GPIO_IR_DRIVER_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &gpio_ir_recv_pm_ops,
+#endif
+ },
+};
+
+static int __init gpio_ir_recv_init(void)
+{
+ return platform_driver_register(&gpio_ir_recv_driver);
+}
+module_init(gpio_ir_recv_init);
+
+static void __exit gpio_ir_recv_exit(void)
+{
+ platform_driver_unregister(&gpio_ir_recv_driver);
+}
+module_exit(gpio_ir_recv_exit);
+
+MODULE_DESCRIPTION("GPIO IR Receiver driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/rc/ir-sony-decoder.c b/drivers/media/rc/ir-sony-decoder.c
index d5e2b50aff1f..dab98b37621a 100644
--- a/drivers/media/rc/ir-sony-decoder.c
+++ b/drivers/media/rc/ir-sony-decoder.c
@@ -130,7 +130,7 @@ static int ir_sony_decode(struct rc_dev *dev, struct ir_raw_event ev)
case 15:
device = bitrev8((data->bits >> 0) & 0xFF);
subdevice = 0;
- function = bitrev8((data->bits >> 7) & 0xFD);
+ function = bitrev8((data->bits >> 7) & 0xFE);
break;
case 20:
device = bitrev8((data->bits >> 5) & 0xF8);
diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile
index 36e4d5e8dd6a..49ce2662f56b 100644
--- a/drivers/media/rc/keymaps/Makefile
+++ b/drivers/media/rc/keymaps/Makefile
@@ -41,8 +41,11 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \
rc-imon-mce.o \
rc-imon-pad.o \
rc-iodata-bctv7e.o \
+ rc-it913x-v1.o \
+ rc-it913x-v2.o \
rc-kaiomy.o \
rc-kworld-315u.o \
+ rc-kworld-pc150u.o \
rc-kworld-plus-tv-analog.o \
rc-leadtek-y04g0051.o \
rc-lirc.o \
diff --git a/drivers/media/rc/keymaps/rc-it913x-v1.c b/drivers/media/rc/keymaps/rc-it913x-v1.c
new file mode 100644
index 000000000000..0ac775fd109d
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v1.c
@@ -0,0 +1,95 @@
+/* ITE Generic remotes Version 1
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table it913x_v1_rc[] = {
+ /* Type 1 */
+ { 0x61d601, KEY_VIDEO }, /* Source */
+ { 0x61d602, KEY_3 },
+ { 0x61d603, KEY_POWER }, /* ShutDown */
+ { 0x61d604, KEY_1 },
+ { 0x61d605, KEY_5 },
+ { 0x61d606, KEY_6 },
+ { 0x61d607, KEY_CHANNELDOWN }, /* CH- */
+ { 0x61d608, KEY_2 },
+ { 0x61d609, KEY_CHANNELUP }, /* CH+ */
+ { 0x61d60a, KEY_9 },
+ { 0x61d60b, KEY_ZOOM }, /* Zoom */
+ { 0x61d60c, KEY_7 },
+ { 0x61d60d, KEY_8 },
+ { 0x61d60e, KEY_VOLUMEUP }, /* Vol+ */
+ { 0x61d60f, KEY_4 },
+ { 0x61d610, KEY_ESC }, /* [back up arrow] */
+ { 0x61d611, KEY_0 },
+ { 0x61d612, KEY_OK }, /* [enter arrow] */
+ { 0x61d613, KEY_VOLUMEDOWN }, /* Vol- */
+ { 0x61d614, KEY_RECORD }, /* Rec */
+ { 0x61d615, KEY_STOP }, /* Stop */
+ { 0x61d616, KEY_PLAY }, /* Play */
+ { 0x61d617, KEY_MUTE }, /* Mute */
+ { 0x61d618, KEY_UP },
+ { 0x61d619, KEY_DOWN },
+ { 0x61d61a, KEY_LEFT },
+ { 0x61d61b, KEY_RIGHT },
+ { 0x61d61c, KEY_RED },
+ { 0x61d61d, KEY_GREEN },
+ { 0x61d61e, KEY_YELLOW },
+ { 0x61d61f, KEY_BLUE },
+ { 0x61d643, KEY_POWER2 }, /* [red power button] */
+ /* Type 2 - 20 buttons */
+ { 0x807f0d, KEY_0 },
+ { 0x807f04, KEY_1 },
+ { 0x807f05, KEY_2 },
+ { 0x807f06, KEY_3 },
+ { 0x807f07, KEY_4 },
+ { 0x807f08, KEY_5 },
+ { 0x807f09, KEY_6 },
+ { 0x807f0a, KEY_7 },
+ { 0x807f1b, KEY_8 },
+ { 0x807f1f, KEY_9 },
+ { 0x807f12, KEY_POWER },
+ { 0x807f01, KEY_MEDIA_REPEAT}, /* Recall */
+ { 0x807f19, KEY_PAUSE }, /* Timeshift */
+ { 0x807f1e, KEY_VOLUMEUP }, /* 2 x -/+ Keys not marked */
+ { 0x807f03, KEY_VOLUMEDOWN }, /* Volume defined as right hand*/
+ { 0x807f1a, KEY_CHANNELUP },
+ { 0x807f02, KEY_CHANNELDOWN },
+ { 0x807f0c, KEY_ZOOM },
+ { 0x807f00, KEY_RECORD },
+ { 0x807f0e, KEY_STOP },
+};
+
+static struct rc_map_list it913x_v1_map = {
+ .map = {
+ .scan = it913x_v1_rc,
+ .size = ARRAY_SIZE(it913x_v1_rc),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_IT913X_V1,
+ }
+};
+
+static int __init init_rc_it913x_v1_map(void)
+{
+ return rc_map_register(&it913x_v1_map);
+}
+
+static void __exit exit_rc_it913x_v1_map(void)
+{
+ rc_map_unregister(&it913x_v1_map);
+}
+
+module_init(init_rc_it913x_v1_map)
+module_exit(exit_rc_it913x_v1_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c
new file mode 100644
index 000000000000..28e376e18b99
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-it913x-v2.c
@@ -0,0 +1,94 @@
+/* ITE Generic remotes Version 2
+ *
+ * Copyright (C) 2012 Malcolm Priestley (tvboxspy@gmail.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+
+static struct rc_map_table it913x_v2_rc[] = {
+ /* Type 1 */
+ /* 9005 remote */
+ { 0x807f12, KEY_POWER2 }, /* Power (RED POWER BUTTON)*/
+ { 0x807f1a, KEY_VIDEO }, /* Source */
+ { 0x807f1e, KEY_MUTE }, /* Mute */
+ { 0x807f01, KEY_RECORD }, /* Record */
+ { 0x807f02, KEY_CHANNELUP }, /* Channel+ */
+ { 0x807f03, KEY_TIME }, /* TimeShift */
+ { 0x807f04, KEY_VOLUMEUP }, /* Volume- */
+ { 0x807f05, KEY_SCREEN }, /* FullScreen */
+ { 0x807f06, KEY_VOLUMEDOWN }, /* Volume- */
+ { 0x807f07, KEY_0 }, /* 0 */
+ { 0x807f08, KEY_CHANNELDOWN }, /* Channel- */
+ { 0x807f09, KEY_PREVIOUS }, /* Recall */
+ { 0x807f0a, KEY_1 }, /* 1 */
+ { 0x807f1b, KEY_2 }, /* 2 */
+ { 0x807f1f, KEY_3 }, /* 3 */
+ { 0x807f0c, KEY_4 }, /* 4 */
+ { 0x807f0d, KEY_5 }, /* 5 */
+ { 0x807f0e, KEY_6 }, /* 6 */
+ { 0x807f00, KEY_7 }, /* 7 */
+ { 0x807f0f, KEY_8 }, /* 8 */
+ { 0x807f19, KEY_9 }, /* 9 */
+
+ /* Type 2 */
+ /* keys stereo, snapshot unassigned */
+ { 0x866b00, KEY_0 },
+ { 0x866b1b, KEY_1 },
+ { 0x866b02, KEY_2 },
+ { 0x866b03, KEY_3 },
+ { 0x866b04, KEY_4 },
+ { 0x866b05, KEY_5 },
+ { 0x866b06, KEY_6 },
+ { 0x866b07, KEY_7 },
+ { 0x866b08, KEY_8 },
+ { 0x866b09, KEY_9 },
+ { 0x866b12, KEY_POWER },
+ { 0x866b13, KEY_MUTE },
+ { 0x866b0a, KEY_PREVIOUS }, /* Recall */
+ { 0x866b1e, KEY_PAUSE },
+ { 0x866b0c, KEY_VOLUMEUP },
+ { 0x866b18, KEY_VOLUMEDOWN },
+ { 0x866b0b, KEY_CHANNELUP },
+ { 0x866b18, KEY_CHANNELDOWN },
+ { 0x866b10, KEY_ZOOM },
+ { 0x866b1d, KEY_RECORD },
+ { 0x866b0e, KEY_STOP },
+ { 0x866b11, KEY_EPG},
+ { 0x866b1a, KEY_FASTFORWARD },
+ { 0x866b0f, KEY_REWIND },
+ { 0x866b1c, KEY_TV },
+ { 0x866b1b, KEY_TEXT },
+
+};
+
+static struct rc_map_list it913x_v2_map = {
+ .map = {
+ .scan = it913x_v2_rc,
+ .size = ARRAY_SIZE(it913x_v2_rc),
+ .rc_type = RC_TYPE_NEC,
+ .name = RC_MAP_IT913X_V2,
+ }
+};
+
+static int __init init_rc_it913x_v2_map(void)
+{
+ return rc_map_register(&it913x_v2_map);
+}
+
+static void __exit exit_rc_it913x_v2_map(void)
+{
+ rc_map_unregister(&it913x_v2_map);
+}
+
+module_init(init_rc_it913x_v2_map)
+module_exit(exit_rc_it913x_v2_map)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Malcolm Priestley tvboxspy@gmail.com");
diff --git a/drivers/media/rc/keymaps/rc-kworld-pc150u.c b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
new file mode 100644
index 000000000000..233bb5ee087f
--- /dev/null
+++ b/drivers/media/rc/keymaps/rc-kworld-pc150u.c
@@ -0,0 +1,102 @@
+/* kworld-pc150u.c - Keytable for kworld_pc150u Remote Controller
+ *
+ * keymap imported from ir-keymaps.c
+ *
+ * Copyright (c) 2010 by Kyle Strickland
+ * (based on kworld-plus-tv-analog.c by
+ * Mauro Carvalho Chehab <mchehab@redhat.com>)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <media/rc-map.h>
+#include <linux/module.h>
+
+/* Kworld PC150-U
+ Kyle Strickland <kyle@kyle.strickland.name>
+ */
+
+static struct rc_map_table kworld_pc150u[] = {
+ { 0x0c, KEY_MEDIA }, /* Kworld key */
+ { 0x16, KEY_EJECTCLOSECD }, /* -> ) */
+ { 0x1d, KEY_POWER2 },
+
+ { 0x00, KEY_1 },
+ { 0x01, KEY_2 },
+ { 0x02, KEY_3 },
+ { 0x03, KEY_4 },
+ { 0x04, KEY_5 },
+ { 0x05, KEY_6 },
+ { 0x06, KEY_7 },
+ { 0x07, KEY_8 },
+ { 0x08, KEY_9 },
+ { 0x0a, KEY_0 },
+
+ { 0x09, KEY_AGAIN },
+ { 0x14, KEY_MUTE },
+
+ { 0x1e, KEY_LAST },
+ { 0x17, KEY_ZOOM },
+ { 0x1f, KEY_HOMEPAGE },
+ { 0x0e, KEY_ESC },
+
+ { 0x20, KEY_UP },
+ { 0x21, KEY_DOWN },
+ { 0x42, KEY_LEFT },
+ { 0x43, KEY_RIGHT },
+ { 0x0b, KEY_ENTER },
+
+ { 0x10, KEY_CHANNELUP },
+ { 0x11, KEY_CHANNELDOWN },
+
+ { 0x13, KEY_VOLUMEUP },
+ { 0x12, KEY_VOLUMEDOWN },
+
+ { 0x19, KEY_TIME}, /* Timeshift */
+ { 0x1a, KEY_STOP},
+ { 0x1b, KEY_RECORD},
+ { 0x4b, KEY_EMAIL},
+
+ { 0x40, KEY_REWIND},
+ { 0x44, KEY_PLAYPAUSE},
+ { 0x41, KEY_FORWARD},
+ { 0x22, KEY_TEXT},
+
+ { 0x15, KEY_AUDIO}, /* ((*)) */
+ { 0x0f, KEY_MODE}, /* display ratio */
+ { 0x1c, KEY_SYSRQ}, /* snapshot */
+ { 0x4a, KEY_SLEEP}, /* sleep timer */
+
+ { 0x48, KEY_SOUND}, /* switch theater mode */
+ { 0x49, KEY_BLUE}, /* A */
+ { 0x18, KEY_RED}, /* B */
+ { 0x23, KEY_GREEN}, /* C */
+};
+
+static struct rc_map_list kworld_pc150u_map = {
+ .map = {
+ .scan = kworld_pc150u,
+ .size = ARRAY_SIZE(kworld_pc150u),
+ .rc_type = RC_TYPE_UNKNOWN, /* Legacy IR type */
+ .name = RC_MAP_KWORLD_PC150U,
+ }
+};
+
+static int __init init_rc_map_kworld_pc150u(void)
+{
+ return rc_map_register(&kworld_pc150u_map);
+}
+
+static void __exit exit_rc_map_kworld_pc150u(void)
+{
+ rc_map_unregister(&kworld_pc150u_map);
+}
+
+module_init(init_rc_map_kworld_pc150u)
+module_exit(exit_rc_map_kworld_pc150u)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kyle Strickland <kyle@kyle.strickland.name>");
diff --git a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
index f3b86c8db679..8d4dae2e2ece 100644
--- a/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
+++ b/drivers/media/rc/keymaps/rc-nec-terratec-cinergy-xs.c
@@ -18,6 +18,8 @@
*/
static struct rc_map_table nec_terratec_cinergy_xs[] = {
+
+ /* Terratec Grey IR, with most keys in orange */
{ 0x1441, KEY_HOME},
{ 0x1401, KEY_POWER2},
@@ -78,6 +80,56 @@ static struct rc_map_table nec_terratec_cinergy_xs[] = {
{ 0x144e, KEY_REWIND},
{ 0x144f, KEY_FASTFORWARD},
{ 0x145c, KEY_NEXT},
+
+ /* Terratec Black IR, with most keys in black */
+ { 0x04eb01, KEY_POWER2},
+
+ { 0x04eb02, KEY_1},
+ { 0x04eb03, KEY_2},
+ { 0x04eb04, KEY_3},
+ { 0x04eb05, KEY_4},
+ { 0x04eb06, KEY_5},
+ { 0x04eb07, KEY_6},
+ { 0x04eb08, KEY_7},
+ { 0x04eb09, KEY_8},
+ { 0x04eb0a, KEY_9},
+ { 0x04eb0c, KEY_0},
+
+ { 0x04eb0b, KEY_TEXT}, /* TXT */
+ { 0x04eb0d, KEY_REFRESH}, /* Refresh */
+
+ { 0x04eb0e, KEY_HOME},
+ { 0x04eb0f, KEY_EPG},
+
+ { 0x04eb10, KEY_UP},
+ { 0x04eb11, KEY_LEFT},
+ { 0x04eb12, KEY_OK},
+ { 0x04eb13, KEY_RIGHT},
+ { 0x04eb14, KEY_DOWN},
+
+ { 0x04eb15, KEY_BACKSPACE},
+ { 0x04eb16, KEY_INFO},
+
+ { 0x04eb17, KEY_RED},
+ { 0x04eb18, KEY_GREEN},
+ { 0x04eb19, KEY_YELLOW},
+ { 0x04eb1a, KEY_BLUE},
+
+ { 0x04eb1c, KEY_VOLUMEUP},
+ { 0x04eb1e, KEY_VOLUMEDOWN},
+
+ { 0x04eb1d, KEY_MUTE},
+
+ { 0x04eb1b, KEY_CHANNELUP},
+ { 0x04eb1f, KEY_CHANNELDOWN},
+
+ { 0x04eb40, KEY_RECORD},
+ { 0x04eb4c, KEY_PLAY},
+ { 0x04eb58, KEY_PAUSE},
+
+ { 0x04eb54, KEY_REWIND},
+ { 0x04eb48, KEY_STOP},
+ { 0x04eb5c, KEY_NEXT},
};
static struct rc_map_list nec_terratec_cinergy_xs_map = {
diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c
index 21105bf9594d..e150a2e29a4b 100644
--- a/drivers/media/rc/mceusb.c
+++ b/drivers/media/rc/mceusb.c
@@ -361,6 +361,8 @@ static struct usb_device_id mceusb_dev_table[] = {
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
/* Formosa Industrial Computing */
{ USB_DEVICE(VENDOR_FORMOSA, 0xe03e) },
+ /* Formosa Industrial Computing */
+ { USB_DEVICE(VENDOR_FORMOSA, 0xe042) },
/* Fintek eHome Infrared Transceiver (HP branded) */
{ USB_DEVICE(VENDOR_FINTEK, 0x5168) },
/* Fintek eHome Infrared Transceiver */
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index b72f8580e317..96f0a8bb39ea 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -35,7 +35,7 @@ struct ir_raw_event_ctrl {
struct list_head list; /* to keep track of raw clients */
struct task_struct *thread;
spinlock_t lock;
- struct kfifo kfifo; /* fifo for the pulse/space durations */
+ struct kfifo_rec_ptr_1 kfifo; /* fifo for the pulse/space durations */
ktime_t last_event; /* when last event occurred */
enum raw_event_type last_type; /* last event type */
struct rc_dev *dev; /* pointer to the parent rc_dev */
diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index f6a930b70c69..6e16b09c24a9 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -1029,6 +1029,7 @@ EXPORT_SYMBOL_GPL(rc_free_device);
int rc_register_device(struct rc_dev *dev)
{
+ static bool raw_init = false; /* raw decoders loaded? */
static atomic_t devno = ATOMIC_INIT(0);
struct rc_map *rc_map;
const char *path;
@@ -1103,6 +1104,12 @@ int rc_register_device(struct rc_dev *dev)
kfree(path);
if (dev->driver_type == RC_DRIVER_IR_RAW) {
+ /* Load raw decoders, if they aren't already */
+ if (!raw_init) {
+ IR_dprintk(1, "Loading raw decoders\n");
+ ir_raw_init();
+ raw_init = true;
+ }
rc = ir_raw_event_register(dev);
if (rc < 0)
goto out_input;
@@ -1176,8 +1183,6 @@ static int __init rc_core_init(void)
return rc;
}
- /* Initialize/load the decoders/keymap code that will be used */
- ir_raw_init();
rc_map_register(&empty_map);
return 0;
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 9adada0d7447..f2479c5c0eb2 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -273,6 +273,16 @@ config VIDEO_ADV7180
To compile this driver as a module, choose M here: the
module will be called adv7180.
+config VIDEO_ADV7183
+ tristate "Analog Devices ADV7183 decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ V4l2 subdevice driver for the Analog Devices
+ ADV7183 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7183.
+
config VIDEO_BT819
tristate "BT819A VideoStream decoder"
depends on VIDEO_V4L2 && I2C
@@ -459,6 +469,9 @@ config VIDEO_AK881X
comment "Camera sensor devices"
+config VIDEO_APTINA_PLL
+ tristate
+
config VIDEO_OV7670
tristate "OmniVision OV7670 sensor support"
depends on I2C && VIDEO_V4L2
@@ -467,9 +480,28 @@ config VIDEO_OV7670
OV7670 VGA camera. It currently only works with the M88ALP01
controller.
+config VIDEO_VS6624
+ tristate "ST VS6624 sensor support"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ This is a Video4Linux2 sensor-level driver for the ST VS6624
+ camera.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vs6624.
+
+config VIDEO_MT9M032
+ tristate "MT9M032 camera sensor support"
+ depends on I2C && VIDEO_V4L2
+ select VIDEO_APTINA_PLL
+ ---help---
+ This driver supports MT9M032 camera sensors from Aptina, monochrome
+ models only.
+
config VIDEO_MT9P031
tristate "Aptina MT9P031 support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
+ select VIDEO_APTINA_PLL
---help---
This is a Video4Linux2 sensor-level driver for the Aptina
(Micron) mt9p031 5 Mpixel camera.
@@ -851,6 +883,8 @@ source "drivers/media/video/davinci/Kconfig"
source "drivers/media/video/omap/Kconfig"
+source "drivers/media/video/blackfin/Kconfig"
+
config VIDEO_SH_VOU
tristate "SuperH VOU video output driver"
depends on VIDEO_DEV && ARCH_SHMOBILE
@@ -1087,7 +1121,7 @@ config VIDEO_MX2_HOSTSUPPORT
config VIDEO_MX2
tristate "i.MX27/i.MX25 Camera Sensor Interface driver"
depends on VIDEO_DEV && SOC_CAMERA && (MACH_MX27 || ARCH_MX25)
- select VIDEOBUF_DMA_CONTIG
+ select VIDEOBUF2_DMA_CONTIG
select VIDEO_MX2_HOSTSUPPORT
---help---
This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor
@@ -1116,7 +1150,8 @@ config VIDEO_ATMEL_ISI
config VIDEO_S5P_MIPI_CSIS
tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver"
- depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P && VIDEO_V4L2_SUBDEV_API
+ depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P
+ depends on VIDEO_V4L2_SUBDEV_API && REGULATOR
---help---
This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver.
@@ -1176,4 +1211,14 @@ config VIDEO_SAMSUNG_S5P_MFC
help
MFC 5.1 driver for V4L2.
+config VIDEO_MX2_EMMAPRP
+ tristate "MX2 eMMa-PrP support"
+ depends on VIDEO_DEV && VIDEO_V4L2 && SOC_IMX27
+ select VIDEOBUF2_DMA_CONTIG
+ select V4L2_MEM2MEM_DEV
+ help
+ MX2X chips have a PrP that can be used to process buffers from
+ memory to memory. Operations include resizing and format
+ conversion.
+
endif # V4L_MEM2MEM_DRIVERS
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 354138804cda..a6282a3a6a82 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -12,16 +12,19 @@ omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o
videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-fh.o \
v4l2-event.o v4l2-ctrls.o v4l2-subdev.o
+ifeq ($(CONFIG_COMPAT),y)
+ videodev-objs += v4l2-compat-ioctl32.o
+endif
# V4L2 core modules
obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o
-ifeq ($(CONFIG_COMPAT),y)
- obj-$(CONFIG_VIDEO_DEV) += v4l2-compat-ioctl32.o
-endif
-
obj-$(CONFIG_VIDEO_V4L2_COMMON) += v4l2-common.o
+# Helper modules
+
+obj-$(CONFIG_VIDEO_APTINA_PLL) += aptina-pll.o
+
# All i2c modules must come first:
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
@@ -40,8 +43,10 @@ obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
obj-$(CONFIG_VIDEO_ADV7180) += adv7180.o
+obj-$(CONFIG_VIDEO_ADV7183) += adv7183.o
obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o
obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
+obj-$(CONFIG_VIDEO_VS6624) += vs6624.o
obj-$(CONFIG_VIDEO_BT819) += bt819.o
obj-$(CONFIG_VIDEO_BT856) += bt856.o
obj-$(CONFIG_VIDEO_BT866) += bt866.o
@@ -65,6 +70,7 @@ obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_OV7670) += ov7670.o
obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o
obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
+obj-$(CONFIG_VIDEO_MT9M032) += mt9m032.o
obj-$(CONFIG_VIDEO_MT9P031) += mt9p031.o
obj-$(CONFIG_VIDEO_MT9T001) += mt9t001.o
obj-$(CONFIG_VIDEO_MT9V011) += mt9v011.o
@@ -177,6 +183,8 @@ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
obj-$(CONFIG_VIDEO_OMAP1) += omap1_camera.o
obj-$(CONFIG_VIDEO_ATMEL_ISI) += atmel-isi.o
+obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
+
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_JPEG) += s5p-jpeg/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_MFC) += s5p-mfc/
@@ -184,6 +192,8 @@ obj-$(CONFIG_VIDEO_SAMSUNG_S5P_TV) += s5p-tv/
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_G2D) += s5p-g2d/
+obj-$(CONFIG_BLACKFIN) += blackfin/
+
obj-$(CONFIG_ARCH_DAVINCI) += davinci/
obj-$(CONFIG_VIDEO_SH_VOU) += sh_vou.o
@@ -199,6 +209,6 @@ obj-y += davinci/
obj-$(CONFIG_ARCH_OMAP) += omap/
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
-ccflags-y += -Idrivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c
index 12eedf4d515a..5b045b4a66fe 100644
--- a/drivers/media/video/adp1653.c
+++ b/drivers/media/video/adp1653.c
@@ -33,7 +33,6 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
-#include <linux/module.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <media/adp1653.h>
@@ -482,24 +481,7 @@ static struct i2c_driver adp1653_i2c_driver = {
.id_table = adp1653_id_table,
};
-static int __init adp1653_init(void)
-{
- int rval;
-
- rval = i2c_add_driver(&adp1653_i2c_driver);
- if (rval)
- printk(KERN_ALERT "%s: failed at i2c_add_driver\n", __func__);
-
- return rval;
-}
-
-static void __exit adp1653_exit(void)
-{
- i2c_del_driver(&adp1653_i2c_driver);
-}
-
-module_init(adp1653_init);
-module_exit(adp1653_exit);
+module_i2c_driver(adp1653_i2c_driver);
MODULE_AUTHOR("Sakari Ailus <sakari.ailus@nokia.com>");
MODULE_DESCRIPTION("Analog Devices ADP1653 LED flash driver");
diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c
index 879f1d839760..6bc01fb98ff8 100644
--- a/drivers/media/video/adv7170.c
+++ b/drivers/media/video/adv7170.c
@@ -407,15 +407,4 @@ static struct i2c_driver adv7170_driver = {
.id_table = adv7170_id,
};
-static __init int init_adv7170(void)
-{
- return i2c_add_driver(&adv7170_driver);
-}
-
-static __exit void exit_adv7170(void)
-{
- i2c_del_driver(&adv7170_driver);
-}
-
-module_init(init_adv7170);
-module_exit(exit_adv7170);
+module_i2c_driver(adv7170_driver);
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
index 206078eca853..c7640fab5730 100644
--- a/drivers/media/video/adv7175.c
+++ b/drivers/media/video/adv7175.c
@@ -457,15 +457,4 @@ static struct i2c_driver adv7175_driver = {
.id_table = adv7175_id,
};
-static __init int init_adv7175(void)
-{
- return i2c_add_driver(&adv7175_driver);
-}
-
-static __exit void exit_adv7175(void)
-{
- i2c_del_driver(&adv7175_driver);
-}
-
-module_init(init_adv7175);
-module_exit(exit_adv7175);
+module_i2c_driver(adv7175_driver);
diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c
index d2138d06bcad..b8b6c4b0cad4 100644
--- a/drivers/media/video/adv7180.c
+++ b/drivers/media/video/adv7180.c
@@ -444,20 +444,8 @@ static struct i2c_driver adv7180_driver = {
.id_table = adv7180_id,
};
-static __init int adv7180_init(void)
-{
- return i2c_add_driver(&adv7180_driver);
-}
-
-static __exit void adv7180_exit(void)
-{
- i2c_del_driver(&adv7180_driver);
-}
-
-module_init(adv7180_init);
-module_exit(adv7180_exit);
+module_i2c_driver(adv7180_driver);
MODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver");
MODULE_AUTHOR("Mocean Laboratories");
MODULE_LICENSE("GPL v2");
-
diff --git a/drivers/media/video/adv7183.c b/drivers/media/video/adv7183.c
new file mode 100644
index 000000000000..e1d4c89d7140
--- /dev/null
+++ b/drivers/media/video/adv7183.c
@@ -0,0 +1,699 @@
+/*
+ * adv7183.c Analog Devices ADV7183 video decoder driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/adv7183.h>
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+
+#include "adv7183_regs.h"
+
+struct adv7183 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+
+ v4l2_std_id std; /* Current set standard */
+ u32 input;
+ u32 output;
+ unsigned reset_pin;
+ unsigned oe_pin;
+ struct v4l2_mbus_framefmt fmt;
+};
+
+/* EXAMPLES USING 27 MHz CLOCK
+ * Mode 1 CVBS Input (Composite Video on AIN5)
+ * All standards are supported through autodetect, 8-bit, 4:2:2, ITU-R BT.656 output on P15 to P8.
+ */
+static const unsigned char adv7183_init_regs[] = {
+ ADV7183_IN_CTRL, 0x04, /* CVBS input on AIN5 */
+ ADV7183_DIGI_CLAMP_CTRL_1, 0x00, /* Slow down digital clamps */
+ ADV7183_SHAP_FILT_CTRL, 0x41, /* Set CSFM to SH1 */
+ ADV7183_ADC_CTRL, 0x16, /* Power down ADC 1 and ADC 2 */
+ ADV7183_CTI_DNR_CTRL_4, 0x04, /* Set DNR threshold to 4 for flat response */
+ /* ADI recommended programming sequence */
+ ADV7183_ADI_CTRL, 0x80,
+ ADV7183_CTI_DNR_CTRL_4, 0x20,
+ 0x52, 0x18,
+ 0x58, 0xED,
+ 0x77, 0xC5,
+ 0x7C, 0x93,
+ 0x7D, 0x00,
+ 0xD0, 0x48,
+ 0xD5, 0xA0,
+ 0xD7, 0xEA,
+ ADV7183_SD_SATURATION_CR, 0x3E,
+ ADV7183_PAL_V_END, 0x3E,
+ ADV7183_PAL_F_TOGGLE, 0x0F,
+ ADV7183_ADI_CTRL, 0x00,
+};
+
+static inline struct adv7183 *to_adv7183(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct adv7183, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct adv7183, hdl)->sd;
+}
+
+static inline int adv7183_read(struct v4l2_subdev *sd, unsigned char reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static inline int adv7183_write(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int adv7183_writeregs(struct v4l2_subdev *sd,
+ const unsigned char *regs, unsigned int num)
+{
+ unsigned char reg, data;
+ unsigned int cnt = 0;
+
+ if (num & 0x1) {
+ v4l2_err(sd, "invalid regs array\n");
+ return -1;
+ }
+
+ while (cnt < num) {
+ reg = *regs++;
+ data = *regs++;
+ cnt += 2;
+
+ adv7183_write(sd, reg, data);
+ }
+ return 0;
+}
+
+static int adv7183_log_status(struct v4l2_subdev *sd)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ v4l2_info(sd, "adv7183: Input control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_IN_CTRL));
+ v4l2_info(sd, "adv7183: Video selection = 0x%02x\n",
+ adv7183_read(sd, ADV7183_VD_SEL));
+ v4l2_info(sd, "adv7183: Output control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_OUT_CTRL));
+ v4l2_info(sd, "adv7183: Extended output control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_EXT_OUT_CTRL));
+ v4l2_info(sd, "adv7183: Autodetect enable = 0x%02x\n",
+ adv7183_read(sd, ADV7183_AUTO_DET_EN));
+ v4l2_info(sd, "adv7183: Contrast = 0x%02x\n",
+ adv7183_read(sd, ADV7183_CONTRAST));
+ v4l2_info(sd, "adv7183: Brightness = 0x%02x\n",
+ adv7183_read(sd, ADV7183_BRIGHTNESS));
+ v4l2_info(sd, "adv7183: Hue = 0x%02x\n",
+ adv7183_read(sd, ADV7183_HUE));
+ v4l2_info(sd, "adv7183: Default value Y = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DEF_Y));
+ v4l2_info(sd, "adv7183: Default value C = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DEF_C));
+ v4l2_info(sd, "adv7183: ADI control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADI_CTRL));
+ v4l2_info(sd, "adv7183: Power Management = 0x%02x\n",
+ adv7183_read(sd, ADV7183_POW_MANAGE));
+ v4l2_info(sd, "adv7183: Status 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_STATUS_1),
+ adv7183_read(sd, ADV7183_STATUS_2),
+ adv7183_read(sd, ADV7183_STATUS_3));
+ v4l2_info(sd, "adv7183: Ident = 0x%02x\n",
+ adv7183_read(sd, ADV7183_IDENT));
+ v4l2_info(sd, "adv7183: Analog clamp control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ANAL_CLAMP_CTRL));
+ v4l2_info(sd, "adv7183: Digital clamp control 1 = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DIGI_CLAMP_CTRL_1));
+ v4l2_info(sd, "adv7183: Shaping filter control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL),
+ adv7183_read(sd, ADV7183_SHAP_FILT_CTRL_2));
+ v4l2_info(sd, "adv7183: Comb filter control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_COMB_FILT_CTRL));
+ v4l2_info(sd, "adv7183: ADI control 2 = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADI_CTRL_2));
+ v4l2_info(sd, "adv7183: Pixel delay control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_PIX_DELAY_CTRL));
+ v4l2_info(sd, "adv7183: Misc gain control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_MISC_GAIN_CTRL));
+ v4l2_info(sd, "adv7183: AGC mode control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_AGC_MODE_CTRL));
+ v4l2_info(sd, "adv7183: Chroma gain control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_1),
+ adv7183_read(sd, ADV7183_CHRO_GAIN_CTRL_2));
+ v4l2_info(sd, "adv7183: Luma gain control 1 and 2 = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_1),
+ adv7183_read(sd, ADV7183_LUMA_GAIN_CTRL_2));
+ v4l2_info(sd, "adv7183: Vsync field control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_1),
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_2),
+ adv7183_read(sd, ADV7183_VS_FIELD_CTRL_3));
+ v4l2_info(sd, "adv7183: Hsync positon control 1 2 and 3 = 0x%02x 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_1),
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_2),
+ adv7183_read(sd, ADV7183_HS_POS_CTRL_3));
+ v4l2_info(sd, "adv7183: Polarity = 0x%02x\n",
+ adv7183_read(sd, ADV7183_POLARITY));
+ v4l2_info(sd, "adv7183: ADC control = 0x%02x\n",
+ adv7183_read(sd, ADV7183_ADC_CTRL));
+ v4l2_info(sd, "adv7183: SD offset Cb and Cr = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SD_OFFSET_CB),
+ adv7183_read(sd, ADV7183_SD_OFFSET_CR));
+ v4l2_info(sd, "adv7183: SD saturation Cb and Cr = 0x%02x 0x%02x\n",
+ adv7183_read(sd, ADV7183_SD_SATURATION_CB),
+ adv7183_read(sd, ADV7183_SD_SATURATION_CR));
+ v4l2_info(sd, "adv7183: Drive strength = 0x%02x\n",
+ adv7183_read(sd, ADV7183_DRIVE_STR));
+ v4l2_ctrl_handler_log_status(&decoder->hdl, sd->name);
+ return 0;
+}
+
+static int adv7183_g_std(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ *std = decoder->std;
+ return 0;
+}
+
+static int adv7183_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+ if (std == V4L2_STD_PAL_60)
+ reg |= 0x60;
+ else if (std == V4L2_STD_NTSC_443)
+ reg |= 0x70;
+ else if (std == V4L2_STD_PAL_N)
+ reg |= 0x90;
+ else if (std == V4L2_STD_PAL_M)
+ reg |= 0xA0;
+ else if (std == V4L2_STD_PAL_Nc)
+ reg |= 0xC0;
+ else if (std & V4L2_STD_PAL)
+ reg |= 0x80;
+ else if (std & V4L2_STD_NTSC)
+ reg |= 0x50;
+ else if (std & V4L2_STD_SECAM)
+ reg |= 0xE0;
+ else
+ return -EINVAL;
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+
+ decoder->std = std;
+
+ return 0;
+}
+
+static int adv7183_reset(struct v4l2_subdev *sd, u32 val)
+{
+ int reg;
+
+ reg = adv7183_read(sd, ADV7183_POW_MANAGE) | 0x80;
+ adv7183_write(sd, ADV7183_POW_MANAGE, reg);
+ /* wait 5ms before any further i2c writes are performed */
+ usleep_range(5000, 10000);
+ return 0;
+}
+
+static int adv7183_s_routing(struct v4l2_subdev *sd,
+ u32 input, u32 output, u32 config)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ if ((input > ADV7183_COMPONENT1) || (output > ADV7183_16BIT_OUT))
+ return -EINVAL;
+
+ if (input != decoder->input) {
+ decoder->input = input;
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF0;
+ switch (input) {
+ case ADV7183_COMPOSITE1:
+ reg |= 0x1;
+ break;
+ case ADV7183_COMPOSITE2:
+ reg |= 0x2;
+ break;
+ case ADV7183_COMPOSITE3:
+ reg |= 0x3;
+ break;
+ case ADV7183_COMPOSITE4:
+ reg |= 0x4;
+ break;
+ case ADV7183_COMPOSITE5:
+ reg |= 0x5;
+ break;
+ case ADV7183_COMPOSITE6:
+ reg |= 0xB;
+ break;
+ case ADV7183_COMPOSITE7:
+ reg |= 0xC;
+ break;
+ case ADV7183_COMPOSITE8:
+ reg |= 0xD;
+ break;
+ case ADV7183_COMPOSITE9:
+ reg |= 0xE;
+ break;
+ case ADV7183_COMPOSITE10:
+ reg |= 0xF;
+ break;
+ case ADV7183_SVIDEO0:
+ reg |= 0x6;
+ break;
+ case ADV7183_SVIDEO1:
+ reg |= 0x7;
+ break;
+ case ADV7183_SVIDEO2:
+ reg |= 0x8;
+ break;
+ case ADV7183_COMPONENT0:
+ reg |= 0x9;
+ break;
+ case ADV7183_COMPONENT1:
+ reg |= 0xA;
+ break;
+ default:
+ break;
+ }
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+ }
+
+ if (output != decoder->output) {
+ decoder->output = output;
+ reg = adv7183_read(sd, ADV7183_OUT_CTRL) & 0xC0;
+ switch (output) {
+ case ADV7183_16BIT_OUT:
+ reg |= 0x9;
+ break;
+ default:
+ reg |= 0xC;
+ break;
+ }
+ adv7183_write(sd, ADV7183_OUT_CTRL, reg);
+ }
+
+ return 0;
+}
+
+static int adv7183_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+ int val = ctrl->val;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (val < 0)
+ val = 127 - val;
+ adv7183_write(sd, ADV7183_BRIGHTNESS, val);
+ break;
+ case V4L2_CID_CONTRAST:
+ adv7183_write(sd, ADV7183_CONTRAST, val);
+ break;
+ case V4L2_CID_SATURATION:
+ adv7183_write(sd, ADV7183_SD_SATURATION_CB, val >> 8);
+ adv7183_write(sd, ADV7183_SD_SATURATION_CR, (val & 0xFF));
+ break;
+ case V4L2_CID_HUE:
+ adv7183_write(sd, ADV7183_SD_OFFSET_CB, val >> 8);
+ adv7183_write(sd, ADV7183_SD_OFFSET_CR, (val & 0xFF));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int adv7183_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+ int reg;
+
+ /* enable autodetection block */
+ reg = adv7183_read(sd, ADV7183_IN_CTRL) & 0xF;
+ adv7183_write(sd, ADV7183_IN_CTRL, reg);
+
+ /* wait autodetection switch */
+ mdelay(10);
+
+ /* get autodetection result */
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
+ switch ((reg >> 0x4) & 0x7) {
+ case 0:
+ *std = V4L2_STD_NTSC;
+ break;
+ case 1:
+ *std = V4L2_STD_NTSC_443;
+ break;
+ case 2:
+ *std = V4L2_STD_PAL_M;
+ break;
+ case 3:
+ *std = V4L2_STD_PAL_60;
+ break;
+ case 4:
+ *std = V4L2_STD_PAL;
+ break;
+ case 5:
+ *std = V4L2_STD_SECAM;
+ break;
+ case 6:
+ *std = V4L2_STD_PAL_Nc;
+ break;
+ case 7:
+ *std = V4L2_STD_SECAM;
+ break;
+ default:
+ *std = V4L2_STD_UNKNOWN;
+ break;
+ }
+
+ /* after std detection, write back user set std */
+ adv7183_s_std(sd, decoder->std);
+ return 0;
+}
+
+static int adv7183_g_input_status(struct v4l2_subdev *sd, u32 *status)
+{
+ int reg;
+
+ *status = V4L2_IN_ST_NO_SIGNAL;
+ reg = adv7183_read(sd, ADV7183_STATUS_1);
+ if (reg < 0)
+ return reg;
+ if (reg & 0x1)
+ *status = 0;
+ return 0;
+}
+
+static int adv7183_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index > 0)
+ return -EINVAL;
+
+ *code = V4L2_MBUS_FMT_UYVY8_2X8;
+ return 0;
+}
+
+static int adv7183_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ fmt->code = V4L2_MBUS_FMT_UYVY8_2X8;
+ fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ if (decoder->std & V4L2_STD_525_60) {
+ fmt->field = V4L2_FIELD_SEQ_TB;
+ fmt->width = 720;
+ fmt->height = 480;
+ } else {
+ fmt->field = V4L2_FIELD_SEQ_BT;
+ fmt->width = 720;
+ fmt->height = 576;
+ }
+ return 0;
+}
+
+static int adv7183_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ adv7183_try_mbus_fmt(sd, fmt);
+ decoder->fmt = *fmt;
+ return 0;
+}
+
+static int adv7183_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ *fmt = decoder->fmt;
+ return 0;
+}
+
+static int adv7183_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ if (enable)
+ gpio_direction_output(decoder->oe_pin, 0);
+ else
+ gpio_direction_output(decoder->oe_pin, 1);
+ udelay(1);
+ return 0;
+}
+
+static int adv7183_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ /* 0x11 for adv7183, 0x13 for adv7183b */
+ rev = adv7183_read(sd, ADV7183_IDENT);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7183, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int adv7183_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = adv7183_read(sd, reg->reg & 0xff);
+ reg->size = 1;
+ return 0;
+}
+
+static int adv7183_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ adv7183_write(sd, reg->reg & 0xff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops adv7183_ctrl_ops = {
+ .s_ctrl = adv7183_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops adv7183_core_ops = {
+ .log_status = adv7183_log_status,
+ .g_std = adv7183_g_std,
+ .s_std = adv7183_s_std,
+ .reset = adv7183_reset,
+ .g_chip_ident = adv7183_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = adv7183_g_register,
+ .s_register = adv7183_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops adv7183_video_ops = {
+ .s_routing = adv7183_s_routing,
+ .querystd = adv7183_querystd,
+ .g_input_status = adv7183_g_input_status,
+ .enum_mbus_fmt = adv7183_enum_mbus_fmt,
+ .try_mbus_fmt = adv7183_try_mbus_fmt,
+ .s_mbus_fmt = adv7183_s_mbus_fmt,
+ .g_mbus_fmt = adv7183_g_mbus_fmt,
+ .s_stream = adv7183_s_stream,
+};
+
+static const struct v4l2_subdev_ops adv7183_ops = {
+ .core = &adv7183_core_ops,
+ .video = &adv7183_video_ops,
+};
+
+static int adv7183_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adv7183 *decoder;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ int ret;
+ struct v4l2_mbus_framefmt fmt;
+ const unsigned *pin_array;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ pin_array = client->dev.platform_data;
+ if (pin_array == NULL)
+ return -EINVAL;
+
+ decoder = kzalloc(sizeof(struct adv7183), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+
+ decoder->reset_pin = pin_array[0];
+ decoder->oe_pin = pin_array[1];
+
+ if (gpio_request(decoder->reset_pin, "ADV7183 Reset")) {
+ v4l_err(client, "failed to request GPIO %d\n", decoder->reset_pin);
+ ret = -EBUSY;
+ goto err_free_decoder;
+ }
+
+ if (gpio_request(decoder->oe_pin, "ADV7183 Output Enable")) {
+ v4l_err(client, "failed to request GPIO %d\n", decoder->oe_pin);
+ ret = -EBUSY;
+ goto err_free_reset;
+ }
+
+ sd = &decoder->sd;
+ v4l2_i2c_subdev_init(sd, client, &adv7183_ops);
+
+ hdl = &decoder->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xFFFF, 1, 0x8080);
+ v4l2_ctrl_new_std(hdl, &adv7183_ctrl_ops,
+ V4L2_CID_HUE, 0, 0xFFFF, 1, 0x8080);
+ /* hook the control handler into the driver */
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ ret = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ goto err_free_oe;
+ }
+
+ /* v4l2 doesn't support an autodetect standard, pick PAL as default */
+ decoder->std = V4L2_STD_PAL;
+ decoder->input = ADV7183_COMPOSITE4;
+ decoder->output = ADV7183_8BIT_OUT;
+
+ gpio_direction_output(decoder->oe_pin, 1);
+ /* reset chip */
+ gpio_direction_output(decoder->reset_pin, 0);
+ /* reset pulse width at least 5ms */
+ mdelay(10);
+ gpio_direction_output(decoder->reset_pin, 1);
+ /* wait 5ms before any further i2c writes are performed */
+ mdelay(5);
+
+ adv7183_writeregs(sd, adv7183_init_regs, ARRAY_SIZE(adv7183_init_regs));
+ adv7183_s_std(sd, decoder->std);
+ fmt.width = 720;
+ fmt.height = 576;
+ adv7183_s_mbus_fmt(sd, &fmt);
+
+ /* initialize the hardware to the default control values */
+ ret = v4l2_ctrl_handler_setup(hdl);
+ if (ret) {
+ v4l2_ctrl_handler_free(hdl);
+ goto err_free_oe;
+ }
+
+ return 0;
+err_free_oe:
+ gpio_free(decoder->oe_pin);
+err_free_reset:
+ gpio_free(decoder->reset_pin);
+err_free_decoder:
+ kfree(decoder);
+ return ret;
+}
+
+static int adv7183_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct adv7183 *decoder = to_adv7183(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ gpio_free(decoder->oe_pin);
+ gpio_free(decoder->reset_pin);
+ kfree(decoder);
+ return 0;
+}
+
+static const struct i2c_device_id adv7183_id[] = {
+ {"adv7183", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, adv7183_id);
+
+static struct i2c_driver adv7183_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adv7183",
+ },
+ .probe = adv7183_probe,
+ .remove = __devexit_p(adv7183_remove),
+ .id_table = adv7183_id,
+};
+
+static __init int adv7183_init(void)
+{
+ return i2c_add_driver(&adv7183_driver);
+}
+
+static __exit void adv7183_exit(void)
+{
+ i2c_del_driver(&adv7183_driver);
+}
+
+module_init(adv7183_init);
+module_exit(adv7183_exit);
+
+MODULE_DESCRIPTION("Analog Devices ADV7183 video decoder driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/adv7183_regs.h b/drivers/media/video/adv7183_regs.h
new file mode 100644
index 000000000000..4a5b7d211d2f
--- /dev/null
+++ b/drivers/media/video/adv7183_regs.h
@@ -0,0 +1,107 @@
+/*
+ * adv7183 - Analog Devices ADV7183 video decoder registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _ADV7183_REGS_H_
+#define _ADV7183_REGS_H_
+
+#define ADV7183_IN_CTRL 0x00 /* Input control */
+#define ADV7183_VD_SEL 0x01 /* Video selection */
+#define ADV7183_OUT_CTRL 0x03 /* Output control */
+#define ADV7183_EXT_OUT_CTRL 0x04 /* Extended output control */
+#define ADV7183_AUTO_DET_EN 0x07 /* Autodetect enable */
+#define ADV7183_CONTRAST 0x08 /* Contrast */
+#define ADV7183_BRIGHTNESS 0x0A /* Brightness */
+#define ADV7183_HUE 0x0B /* Hue */
+#define ADV7183_DEF_Y 0x0C /* Default value Y */
+#define ADV7183_DEF_C 0x0D /* Default value C */
+#define ADV7183_ADI_CTRL 0x0E /* ADI control */
+#define ADV7183_POW_MANAGE 0x0F /* Power Management */
+#define ADV7183_STATUS_1 0x10 /* Status 1 */
+#define ADV7183_IDENT 0x11 /* Ident */
+#define ADV7183_STATUS_2 0x12 /* Status 2 */
+#define ADV7183_STATUS_3 0x13 /* Status 3 */
+#define ADV7183_ANAL_CLAMP_CTRL 0x14 /* Analog clamp control */
+#define ADV7183_DIGI_CLAMP_CTRL_1 0x15 /* Digital clamp control 1 */
+#define ADV7183_SHAP_FILT_CTRL 0x17 /* Shaping filter control */
+#define ADV7183_SHAP_FILT_CTRL_2 0x18 /* Shaping filter control 2 */
+#define ADV7183_COMB_FILT_CTRL 0x19 /* Comb filter control */
+#define ADV7183_ADI_CTRL_2 0x1D /* ADI control 2 */
+#define ADV7183_PIX_DELAY_CTRL 0x27 /* Pixel delay control */
+#define ADV7183_MISC_GAIN_CTRL 0x2B /* Misc gain control */
+#define ADV7183_AGC_MODE_CTRL 0x2C /* AGC mode control */
+#define ADV7183_CHRO_GAIN_CTRL_1 0x2D /* Chroma gain control 1 */
+#define ADV7183_CHRO_GAIN_CTRL_2 0x2E /* Chroma gain control 2 */
+#define ADV7183_LUMA_GAIN_CTRL_1 0x2F /* Luma gain control 1 */
+#define ADV7183_LUMA_GAIN_CTRL_2 0x30 /* Luma gain control 2 */
+#define ADV7183_VS_FIELD_CTRL_1 0x31 /* Vsync field control 1 */
+#define ADV7183_VS_FIELD_CTRL_2 0x32 /* Vsync field control 2 */
+#define ADV7183_VS_FIELD_CTRL_3 0x33 /* Vsync field control 3 */
+#define ADV7183_HS_POS_CTRL_1 0x34 /* Hsync positon control 1 */
+#define ADV7183_HS_POS_CTRL_2 0x35 /* Hsync positon control 2 */
+#define ADV7183_HS_POS_CTRL_3 0x36 /* Hsync positon control 3 */
+#define ADV7183_POLARITY 0x37 /* Polarity */
+#define ADV7183_NTSC_COMB_CTRL 0x38 /* NTSC comb control */
+#define ADV7183_PAL_COMB_CTRL 0x39 /* PAL comb control */
+#define ADV7183_ADC_CTRL 0x3A /* ADC control */
+#define ADV7183_MAN_WIN_CTRL 0x3D /* Manual window control */
+#define ADV7183_RESAMPLE_CTRL 0x41 /* Resample control */
+#define ADV7183_GEMSTAR_CTRL_1 0x48 /* Gemstar ctrl 1 */
+#define ADV7183_GEMSTAR_CTRL_2 0x49 /* Gemstar ctrl 2 */
+#define ADV7183_GEMSTAR_CTRL_3 0x4A /* Gemstar ctrl 3 */
+#define ADV7183_GEMSTAR_CTRL_4 0x4B /* Gemstar ctrl 4 */
+#define ADV7183_GEMSTAR_CTRL_5 0x4C /* Gemstar ctrl 5 */
+#define ADV7183_CTI_DNR_CTRL_1 0x4D /* CTI DNR ctrl 1 */
+#define ADV7183_CTI_DNR_CTRL_2 0x4E /* CTI DNR ctrl 2 */
+#define ADV7183_CTI_DNR_CTRL_4 0x50 /* CTI DNR ctrl 4 */
+#define ADV7183_LOCK_CNT 0x51 /* Lock count */
+#define ADV7183_FREE_LINE_LEN 0x8F /* Free-Run line length 1 */
+#define ADV7183_VBI_INFO 0x90 /* VBI info */
+#define ADV7183_WSS_1 0x91 /* WSS 1 */
+#define ADV7183_WSS_2 0x92 /* WSS 2 */
+#define ADV7183_EDTV_1 0x93 /* EDTV 1 */
+#define ADV7183_EDTV_2 0x94 /* EDTV 2 */
+#define ADV7183_EDTV_3 0x95 /* EDTV 3 */
+#define ADV7183_CGMS_1 0x96 /* CGMS 1 */
+#define ADV7183_CGMS_2 0x97 /* CGMS 2 */
+#define ADV7183_CGMS_3 0x98 /* CGMS 3 */
+#define ADV7183_CCAP_1 0x99 /* CCAP 1 */
+#define ADV7183_CCAP_2 0x9A /* CCAP 2 */
+#define ADV7183_LETTERBOX_1 0x9B /* Letterbox 1 */
+#define ADV7183_LETTERBOX_2 0x9C /* Letterbox 2 */
+#define ADV7183_LETTERBOX_3 0x9D /* Letterbox 3 */
+#define ADV7183_CRC_EN 0xB2 /* CRC enable */
+#define ADV7183_ADC_SWITCH_1 0xC3 /* ADC switch 1 */
+#define ADV7183_ADC_SWITCH_2 0xC4 /* ADC swithc 2 */
+#define ADV7183_LETTERBOX_CTRL_1 0xDC /* Letterbox control 1 */
+#define ADV7183_LETTERBOX_CTRL_2 0xDD /* Letterbox control 2 */
+#define ADV7183_SD_OFFSET_CB 0xE1 /* SD offset Cb */
+#define ADV7183_SD_OFFSET_CR 0xE2 /* SD offset Cr */
+#define ADV7183_SD_SATURATION_CB 0xE3 /* SD saturation Cb */
+#define ADV7183_SD_SATURATION_CR 0xE4 /* SD saturation Cr */
+#define ADV7183_NTSC_V_BEGIN 0xE5 /* NTSC V bit begin */
+#define ADV7183_NTSC_V_END 0xE6 /* NTSC V bit end */
+#define ADV7183_NTSC_F_TOGGLE 0xE7 /* NTSC F bit toggle */
+#define ADV7183_PAL_V_BEGIN 0xE8 /* PAL V bit begin */
+#define ADV7183_PAL_V_END 0xE9 /* PAL V bit end */
+#define ADV7183_PAL_F_TOGGLE 0xEA /* PAL F bit toggle */
+#define ADV7183_DRIVE_STR 0xF4 /* Drive strength */
+#define ADV7183_IF_COMP_CTRL 0xF8 /* IF comp control */
+#define ADV7183_VS_MODE_CTRL 0xF9 /* VS mode control */
+
+#endif
diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c
index 021fab23070d..119b60401bf3 100644
--- a/drivers/media/video/adv7343.c
+++ b/drivers/media/video/adv7343.c
@@ -475,15 +475,4 @@ static struct i2c_driver adv7343_driver = {
.id_table = adv7343_id,
};
-static __init int init_adv7343(void)
-{
- return i2c_add_driver(&adv7343_driver);
-}
-
-static __exit void exit_adv7343(void)
-{
- i2c_del_driver(&adv7343_driver);
-}
-
-module_init(init_adv7343);
-module_exit(exit_adv7343);
+module_i2c_driver(adv7343_driver);
diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c
index 53c496c00fb6..ba674656b10d 100644
--- a/drivers/media/video/ak881x.c
+++ b/drivers/media/video/ak881x.c
@@ -352,18 +352,7 @@ static struct i2c_driver ak881x_i2c_driver = {
.id_table = ak881x_id,
};
-static int __init ak881x_module_init(void)
-{
- return i2c_add_driver(&ak881x_i2c_driver);
-}
-
-static void __exit ak881x_module_exit(void)
-{
- i2c_del_driver(&ak881x_i2c_driver);
-}
-
-module_init(ak881x_module_init);
-module_exit(ak881x_module_exit);
+module_i2c_driver(ak881x_i2c_driver);
MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c
new file mode 100644
index 000000000000..0bd3813bb59d
--- /dev/null
+++ b/drivers/media/video/aptina-pll.c
@@ -0,0 +1,174 @@
+/*
+ * Aptina Sensor PLL Configuration
+ *
+ * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.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/device.h>
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/lcm.h>
+#include <linux/module.h>
+
+#include "aptina-pll.h"
+
+int aptina_pll_calculate(struct device *dev,
+ const struct aptina_pll_limits *limits,
+ struct aptina_pll *pll)
+{
+ unsigned int mf_min;
+ unsigned int mf_max;
+ unsigned int p1_min;
+ unsigned int p1_max;
+ unsigned int p1;
+ unsigned int div;
+
+ dev_dbg(dev, "PLL: ext clock %u pix clock %u\n",
+ pll->ext_clock, pll->pix_clock);
+
+ if (pll->ext_clock < limits->ext_clock_min ||
+ pll->ext_clock > limits->ext_clock_max) {
+ dev_err(dev, "pll: invalid external clock frequency.\n");
+ return -EINVAL;
+ }
+
+ if (pll->pix_clock == 0 || pll->pix_clock > limits->pix_clock_max) {
+ dev_err(dev, "pll: invalid pixel clock frequency.\n");
+ return -EINVAL;
+ }
+
+ /* Compute the multiplier M and combined N*P1 divisor. */
+ div = gcd(pll->pix_clock, pll->ext_clock);
+ pll->m = pll->pix_clock / div;
+ div = pll->ext_clock / div;
+
+ /* We now have the smallest M and N*P1 values that will result in the
+ * desired pixel clock frequency, but they might be out of the valid
+ * range. Compute the factor by which we should multiply them given the
+ * following constraints:
+ *
+ * - minimum/maximum multiplier
+ * - minimum/maximum multiplier output clock frequency assuming the
+ * minimum/maximum N value
+ * - minimum/maximum combined N*P1 divisor
+ */
+ mf_min = DIV_ROUND_UP(limits->m_min, pll->m);
+ mf_min = max(mf_min, limits->out_clock_min /
+ (pll->ext_clock / limits->n_min * pll->m));
+ mf_min = max(mf_min, limits->n_min * limits->p1_min / div);
+ mf_max = limits->m_max / pll->m;
+ mf_max = min(mf_max, limits->out_clock_max /
+ (pll->ext_clock / limits->n_max * pll->m));
+ mf_max = min(mf_max, DIV_ROUND_UP(limits->n_max * limits->p1_max, div));
+
+ dev_dbg(dev, "pll: mf min %u max %u\n", mf_min, mf_max);
+ if (mf_min > mf_max) {
+ dev_err(dev, "pll: no valid combined N*P1 divisor.\n");
+ return -EINVAL;
+ }
+
+ /*
+ * We're looking for the highest acceptable P1 value for which a
+ * multiplier factor MF exists that fulfills the following conditions:
+ *
+ * 1. p1 is in the [p1_min, p1_max] range given by the limits and is
+ * even
+ * 2. mf is in the [mf_min, mf_max] range computed above
+ * 3. div * mf is a multiple of p1, in order to compute
+ * n = div * mf / p1
+ * m = pll->m * mf
+ * 4. the internal clock frequency, given by ext_clock / n, is in the
+ * [int_clock_min, int_clock_max] range given by the limits
+ * 5. the output clock frequency, given by ext_clock / n * m, is in the
+ * [out_clock_min, out_clock_max] range given by the limits
+ *
+ * The first naive approach is to iterate over all p1 values acceptable
+ * according to (1) and all mf values acceptable according to (2), and
+ * stop at the first combination that fulfills (3), (4) and (5). This
+ * has a O(n^2) complexity.
+ *
+ * Instead of iterating over all mf values in the [mf_min, mf_max] range
+ * we can compute the mf increment between two acceptable values
+ * according to (3) with
+ *
+ * mf_inc = p1 / gcd(div, p1) (6)
+ *
+ * and round the minimum up to the nearest multiple of mf_inc. This will
+ * restrict the number of mf values to be checked.
+ *
+ * Furthermore, conditions (4) and (5) only restrict the range of
+ * acceptable p1 and mf values by modifying the minimum and maximum
+ * limits. (5) can be expressed as
+ *
+ * ext_clock / (div * mf / p1) * m * mf >= out_clock_min
+ * ext_clock / (div * mf / p1) * m * mf <= out_clock_max
+ *
+ * or
+ *
+ * p1 >= out_clock_min * div / (ext_clock * m) (7)
+ * p1 <= out_clock_max * div / (ext_clock * m)
+ *
+ * Similarly, (4) can be expressed as
+ *
+ * mf >= ext_clock * p1 / (int_clock_max * div) (8)
+ * mf <= ext_clock * p1 / (int_clock_min * div)
+ *
+ * We can thus iterate over the restricted p1 range defined by the
+ * combination of (1) and (7), and then compute the restricted mf range
+ * defined by the combination of (2), (6) and (8). If the resulting mf
+ * range is not empty, any value in the mf range is acceptable. We thus
+ * select the mf lwoer bound and the corresponding p1 value.
+ */
+ if (limits->p1_min == 0) {
+ dev_err(dev, "pll: P1 minimum value must be >0.\n");
+ return -EINVAL;
+ }
+
+ p1_min = max(limits->p1_min, DIV_ROUND_UP(limits->out_clock_min * div,
+ pll->ext_clock * pll->m));
+ p1_max = min(limits->p1_max, limits->out_clock_max * div /
+ (pll->ext_clock * pll->m));
+
+ for (p1 = p1_max & ~1; p1 >= p1_min; p1 -= 2) {
+ unsigned int mf_inc = p1 / gcd(div, p1);
+ unsigned int mf_high;
+ unsigned int mf_low;
+
+ mf_low = max(roundup(mf_min, mf_inc),
+ DIV_ROUND_UP(pll->ext_clock * p1,
+ limits->int_clock_max * div));
+ mf_high = min(mf_max, pll->ext_clock * p1 /
+ (limits->int_clock_min * div));
+
+ if (mf_low > mf_high)
+ continue;
+
+ pll->n = div * mf_low / p1;
+ pll->m *= mf_low;
+ pll->p1 = p1;
+ dev_dbg(dev, "PLL: N %u M %u P1 %u\n", pll->n, pll->m, pll->p1);
+ return 0;
+ }
+
+ dev_err(dev, "pll: no valid N and P1 divisors found.\n");
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(aptina_pll_calculate);
+
+MODULE_DESCRIPTION("Aptina PLL Helpers");
+MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/aptina-pll.h b/drivers/media/video/aptina-pll.h
new file mode 100644
index 000000000000..b370e341e75d
--- /dev/null
+++ b/drivers/media/video/aptina-pll.h
@@ -0,0 +1,56 @@
+/*
+ * Aptina Sensor PLL Configuration
+ *
+ * Copyright (C) 2012 Laurent Pinchart <laurent.pinchart@ideasonboard.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 __APTINA_PLL_H
+#define __APTINA_PLL_H
+
+struct aptina_pll {
+ unsigned int ext_clock;
+ unsigned int pix_clock;
+
+ unsigned int n;
+ unsigned int m;
+ unsigned int p1;
+};
+
+struct aptina_pll_limits {
+ unsigned int ext_clock_min;
+ unsigned int ext_clock_max;
+ unsigned int int_clock_min;
+ unsigned int int_clock_max;
+ unsigned int out_clock_min;
+ unsigned int out_clock_max;
+ unsigned int pix_clock_max;
+
+ unsigned int n_min;
+ unsigned int n_max;
+ unsigned int m_min;
+ unsigned int m_max;
+ unsigned int p1_min;
+ unsigned int p1_max;
+};
+
+struct device;
+
+int aptina_pll_calculate(struct device *dev,
+ const struct aptina_pll_limits *limits,
+ struct aptina_pll *pll);
+
+#endif /* __APTINA_PLL_H */
diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c
index f241702a0f36..7a3371f044fc 100644
--- a/drivers/media/video/as3645a.c
+++ b/drivers/media/video/as3645a.c
@@ -881,24 +881,7 @@ static struct i2c_driver as3645a_i2c_driver = {
.id_table = as3645a_id_table,
};
-static int __init as3645a_init(void)
-{
- int rval;
-
- rval = i2c_add_driver(&as3645a_i2c_driver);
- if (rval)
- pr_err("%s: Failed to register the driver\n", AS3645A_NAME);
-
- return rval;
-}
-
-static void __exit as3645a_exit(void)
-{
- i2c_del_driver(&as3645a_i2c_driver);
-}
-
-module_init(as3645a_init);
-module_exit(as3645a_exit);
+module_i2c_driver(as3645a_i2c_driver);
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
MODULE_DESCRIPTION("LED flash driver for AS3645A, LM3555 and their clones");
diff --git a/drivers/media/video/blackfin/Kconfig b/drivers/media/video/blackfin/Kconfig
new file mode 100644
index 000000000000..ecd5323768b7
--- /dev/null
+++ b/drivers/media/video/blackfin/Kconfig
@@ -0,0 +1,10 @@
+config VIDEO_BLACKFIN_CAPTURE
+ tristate "Blackfin Video Capture Driver"
+ depends on VIDEO_V4L2 && BLACKFIN && I2C
+ select VIDEOBUF2_DMA_CONTIG
+ help
+ V4L2 bridge driver for Blackfin video capture device.
+ Choose PPI or EPPI as its interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin_video_capture.
diff --git a/drivers/media/video/blackfin/Makefile b/drivers/media/video/blackfin/Makefile
new file mode 100644
index 000000000000..aa3a0a216387
--- /dev/null
+++ b/drivers/media/video/blackfin/Makefile
@@ -0,0 +1,2 @@
+bfin_video_capture-objs := bfin_capture.o ppi.o
+obj-$(CONFIG_VIDEO_BLACKFIN_CAPTURE) += bfin_video_capture.o
diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c
new file mode 100644
index 000000000000..514fcf742f5a
--- /dev/null
+++ b/drivers/media/video/blackfin/bfin_capture.c
@@ -0,0 +1,1059 @@
+/*
+ * Analog Devices video capture driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+
+#include <asm/dma.h>
+
+#include <media/blackfin/bfin_capture.h>
+#include <media/blackfin/ppi.h>
+
+#define CAPTURE_DRV_NAME "bfin_capture"
+#define BCAP_MIN_NUM_BUF 2
+
+struct bcap_format {
+ char *desc;
+ u32 pixelformat;
+ enum v4l2_mbus_pixelcode mbus_code;
+ int bpp; /* bits per pixel */
+};
+
+struct bcap_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+struct bcap_device {
+ /* capture device instance */
+ struct v4l2_device v4l2_dev;
+ /* v4l2 control handler */
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* device node data */
+ struct video_device *video_dev;
+ /* sub device instance */
+ struct v4l2_subdev *sd;
+ /* capture config */
+ struct bfin_capture_config *cfg;
+ /* ppi interface */
+ struct ppi_if *ppi;
+ /* current input */
+ unsigned int cur_input;
+ /* current selected standard */
+ v4l2_std_id std;
+ /* used to store pixel format */
+ struct v4l2_pix_format fmt;
+ /* bits per pixel*/
+ int bpp;
+ /* used to store sensor supported format */
+ struct bcap_format *sensor_formats;
+ /* number of sensor formats array */
+ int num_sensor_formats;
+ /* pointing to current video buffer */
+ struct bcap_buffer *cur_frm;
+ /* pointing to next video buffer */
+ struct bcap_buffer *next_frm;
+ /* buffer queue used in videobuf2 */
+ struct vb2_queue buffer_queue;
+ /* allocator-specific contexts for each plane */
+ struct vb2_alloc_ctx *alloc_ctx;
+ /* queue of filled frames */
+ struct list_head dma_queue;
+ /* used in videobuf2 callback */
+ spinlock_t lock;
+ /* used to access capture device */
+ struct mutex mutex;
+ /* used to wait ppi to complete one transfer */
+ struct completion comp;
+ /* prepare to stop */
+ bool stop;
+};
+
+struct bcap_fh {
+ struct v4l2_fh fh;
+ /* indicates whether this file handle is doing IO */
+ bool io_allowed;
+};
+
+static const struct bcap_format bcap_formats[] = {
+ {
+ .desc = "YCbCr 4:2:2 Interleaved UYVY",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .bpp = 16,
+ },
+ {
+ .desc = "YCbCr 4:2:2 Interleaved YUYV",
+ .pixelformat = V4L2_PIX_FMT_YUYV,
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .bpp = 16,
+ },
+ {
+ .desc = "RGB 565",
+ .pixelformat = V4L2_PIX_FMT_RGB565,
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .bpp = 16,
+ },
+ {
+ .desc = "RGB 444",
+ .pixelformat = V4L2_PIX_FMT_RGB444,
+ .mbus_code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_LE,
+ .bpp = 16,
+ },
+
+};
+#define BCAP_MAX_FMTS ARRAY_SIZE(bcap_formats)
+
+static irqreturn_t bcap_isr(int irq, void *dev_id);
+
+static struct bcap_buffer *to_bcap_vb(struct vb2_buffer *vb)
+{
+ return container_of(vb, struct bcap_buffer, vb);
+}
+
+static int bcap_init_sensor_formats(struct bcap_device *bcap_dev)
+{
+ enum v4l2_mbus_pixelcode code;
+ struct bcap_format *sf;
+ unsigned int num_formats = 0;
+ int i, j;
+
+ while (!v4l2_subdev_call(bcap_dev->sd, video,
+ enum_mbus_fmt, num_formats, &code))
+ num_formats++;
+ if (!num_formats)
+ return -ENXIO;
+
+ sf = kzalloc(num_formats * sizeof(*sf), GFP_KERNEL);
+ if (!sf)
+ return -ENOMEM;
+
+ for (i = 0; i < num_formats; i++) {
+ v4l2_subdev_call(bcap_dev->sd, video,
+ enum_mbus_fmt, i, &code);
+ for (j = 0; j < BCAP_MAX_FMTS; j++)
+ if (code == bcap_formats[j].mbus_code)
+ break;
+ if (j == BCAP_MAX_FMTS) {
+ /* we don't allow this sensor working with our bridge */
+ kfree(sf);
+ return -EINVAL;
+ }
+ sf[i] = bcap_formats[j];
+ }
+ bcap_dev->sensor_formats = sf;
+ bcap_dev->num_sensor_formats = num_formats;
+ return 0;
+}
+
+static void bcap_free_sensor_formats(struct bcap_device *bcap_dev)
+{
+ bcap_dev->num_sensor_formats = 0;
+ kfree(bcap_dev->sensor_formats);
+ bcap_dev->sensor_formats = NULL;
+}
+
+static int bcap_open(struct file *file)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct video_device *vfd = bcap_dev->video_dev;
+ struct bcap_fh *bcap_fh;
+
+ if (!bcap_dev->sd) {
+ v4l2_err(&bcap_dev->v4l2_dev, "No sub device registered\n");
+ return -ENODEV;
+ }
+
+ bcap_fh = kzalloc(sizeof(*bcap_fh), GFP_KERNEL);
+ if (!bcap_fh) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "unable to allocate memory for file handle object\n");
+ return -ENOMEM;
+ }
+
+ v4l2_fh_init(&bcap_fh->fh, vfd);
+
+ /* store pointer to v4l2_fh in private_data member of file */
+ file->private_data = &bcap_fh->fh;
+ v4l2_fh_add(&bcap_fh->fh);
+ bcap_fh->io_allowed = false;
+ return 0;
+}
+
+static int bcap_release(struct file *file)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ /* if this instance is doing IO */
+ if (bcap_fh->io_allowed)
+ vb2_queue_release(&bcap_dev->buffer_queue);
+
+ file->private_data = NULL;
+ v4l2_fh_del(&bcap_fh->fh);
+ v4l2_fh_exit(&bcap_fh->fh);
+ kfree(bcap_fh);
+ return 0;
+}
+
+static int bcap_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_mmap(&bcap_dev->buffer_queue, vma);
+}
+
+#ifndef CONFIG_MMU
+static unsigned long bcap_get_unmapped_area(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_get_unmapped_area(&bcap_dev->buffer_queue,
+ addr,
+ len,
+ pgoff,
+ flags);
+}
+#endif
+
+static unsigned int bcap_poll(struct file *file, poll_table *wait)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_poll(&bcap_dev->buffer_queue, file, wait);
+}
+
+static int bcap_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+
+ if (*nbuffers < BCAP_MIN_NUM_BUF)
+ *nbuffers = BCAP_MIN_NUM_BUF;
+
+ *nplanes = 1;
+ sizes[0] = bcap_dev->fmt.sizeimage;
+ alloc_ctxs[0] = bcap_dev->alloc_ctx;
+
+ return 0;
+}
+
+static int bcap_buffer_init(struct vb2_buffer *vb)
+{
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+
+ INIT_LIST_HEAD(&buf->list);
+ return 0;
+}
+
+static int bcap_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long size;
+
+ size = bcap_dev->fmt.sizeimage;
+ if (vb2_plane_size(vb, 0) < size) {
+ v4l2_err(&bcap_dev->v4l2_dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+ vb2_set_plane_payload(&buf->vb, 0, size);
+
+ return 0;
+}
+
+static void bcap_buffer_queue(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcap_dev->lock, flags);
+ list_add_tail(&buf->list, &bcap_dev->dma_queue);
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_buffer_cleanup(struct vb2_buffer *vb)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vb->vb2_queue);
+ struct bcap_buffer *buf = to_bcap_vb(vb);
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcap_dev->lock, flags);
+ list_del_init(&buf->list);
+ spin_unlock_irqrestore(&bcap_dev->lock, flags);
+}
+
+static void bcap_lock(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ mutex_lock(&bcap_dev->mutex);
+}
+
+static void bcap_unlock(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ mutex_unlock(&bcap_dev->mutex);
+}
+
+static int bcap_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ struct ppi_if *ppi = bcap_dev->ppi;
+ struct ppi_params params;
+ int ret;
+
+ /* enable streamon on the sub device */
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 1);
+ if (ret && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "stream on failed in subdev\n");
+ return ret;
+ }
+
+ /* set ppi params */
+ params.width = bcap_dev->fmt.width;
+ params.height = bcap_dev->fmt.height;
+ params.bpp = bcap_dev->bpp;
+ params.ppi_control = bcap_dev->cfg->ppi_control;
+ params.int_mask = bcap_dev->cfg->int_mask;
+ params.blank_clocks = bcap_dev->cfg->blank_clocks;
+ ret = ppi->ops->set_params(ppi, &params);
+ if (ret < 0) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Error in setting ppi params\n");
+ return ret;
+ }
+
+ /* attach ppi DMA irq handler */
+ ret = ppi->ops->attach_irq(ppi, bcap_isr);
+ if (ret < 0) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Error in attaching interrupt handler\n");
+ return ret;
+ }
+
+ INIT_COMPLETION(bcap_dev->comp);
+ bcap_dev->stop = false;
+ return 0;
+}
+
+static int bcap_stop_streaming(struct vb2_queue *vq)
+{
+ struct bcap_device *bcap_dev = vb2_get_drv_priv(vq);
+ struct ppi_if *ppi = bcap_dev->ppi;
+ int ret;
+
+ if (!vb2_is_streaming(vq))
+ return 0;
+
+ bcap_dev->stop = true;
+ wait_for_completion(&bcap_dev->comp);
+ ppi->ops->stop(ppi);
+ ppi->ops->detach_irq(ppi);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_stream, 0);
+ if (ret && (ret != -ENOIOCTLCMD))
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "stream off failed in subdev\n");
+
+ /* release all active buffers */
+ while (!list_empty(&bcap_dev->dma_queue)) {
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->next_frm->list);
+ vb2_buffer_done(&bcap_dev->next_frm->vb, VB2_BUF_STATE_ERROR);
+ }
+ return 0;
+}
+
+static struct vb2_ops bcap_video_qops = {
+ .queue_setup = bcap_queue_setup,
+ .buf_init = bcap_buffer_init,
+ .buf_prepare = bcap_buffer_prepare,
+ .buf_cleanup = bcap_buffer_cleanup,
+ .buf_queue = bcap_buffer_queue,
+ .wait_prepare = bcap_unlock,
+ .wait_finish = bcap_lock,
+ .start_streaming = bcap_start_streaming,
+ .stop_streaming = bcap_stop_streaming,
+};
+
+static int bcap_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *req_buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct vb2_queue *vq = &bcap_dev->buffer_queue;
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (vb2_is_busy(vq))
+ return -EBUSY;
+
+ bcap_fh->io_allowed = true;
+
+ return vb2_reqbufs(vq, req_buf);
+}
+
+static int bcap_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return vb2_querybuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (!bcap_fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_qbuf(&bcap_dev->buffer_queue, buf);
+}
+
+static int bcap_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_fh *fh = file->private_data;
+ struct bcap_fh *bcap_fh = container_of(fh, struct bcap_fh, fh);
+
+ if (!bcap_fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_dqbuf(&bcap_dev->buffer_queue,
+ buf, file->f_flags & O_NONBLOCK);
+}
+
+static irqreturn_t bcap_isr(int irq, void *dev_id)
+{
+ struct ppi_if *ppi = dev_id;
+ struct bcap_device *bcap_dev = ppi->priv;
+ struct timeval timevalue;
+ struct vb2_buffer *vb = &bcap_dev->cur_frm->vb;
+ dma_addr_t addr;
+
+ spin_lock(&bcap_dev->lock);
+
+ if (bcap_dev->cur_frm != bcap_dev->next_frm) {
+ do_gettimeofday(&timevalue);
+ vb->v4l2_buf.timestamp = timevalue;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+ bcap_dev->cur_frm = bcap_dev->next_frm;
+ }
+
+ ppi->ops->stop(ppi);
+
+ if (bcap_dev->stop) {
+ complete(&bcap_dev->comp);
+ } else {
+ if (!list_empty(&bcap_dev->dma_queue)) {
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ list_del(&bcap_dev->next_frm->list);
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->next_frm->vb, 0);
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
+ }
+ ppi->ops->start(ppi);
+ }
+
+ spin_unlock(&bcap_dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int bcap_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_fh *fh = file->private_data;
+ struct ppi_if *ppi = bcap_dev->ppi;
+ dma_addr_t addr;
+ int ret;
+
+ if (!fh->io_allowed)
+ return -EBUSY;
+
+ /* call streamon to start streaming in videobuf */
+ ret = vb2_streamon(&bcap_dev->buffer_queue, buf_type);
+ if (ret)
+ return ret;
+
+ /* if dma queue is empty, return error */
+ if (list_empty(&bcap_dev->dma_queue)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "dma queue is empty\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* get the next frame from the dma queue */
+ bcap_dev->next_frm = list_entry(bcap_dev->dma_queue.next,
+ struct bcap_buffer, list);
+ bcap_dev->cur_frm = bcap_dev->next_frm;
+ /* remove buffer from the dma queue */
+ list_del(&bcap_dev->cur_frm->list);
+ addr = vb2_dma_contig_plane_dma_addr(&bcap_dev->cur_frm->vb, 0);
+ /* update DMA address */
+ ppi->ops->update_addr(ppi, (unsigned long)addr);
+ /* enable ppi */
+ ppi->ops->start(ppi);
+
+ return 0;
+err:
+ vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+ return ret;
+}
+
+static int bcap_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type buf_type)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_fh *fh = file->private_data;
+
+ if (!fh->io_allowed)
+ return -EBUSY;
+
+ return vb2_streamoff(&bcap_dev->buffer_queue, buf_type);
+}
+
+static int bcap_querystd(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, video, querystd, std);
+}
+
+static int bcap_g_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *std = bcap_dev->std;
+ return 0;
+}
+
+static int bcap_s_std(struct file *file, void *priv, v4l2_std_id *std)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ int ret;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ ret = v4l2_subdev_call(bcap_dev->sd, core, s_std, *std);
+ if (ret < 0)
+ return ret;
+
+ bcap_dev->std = *std;
+ return 0;
+}
+
+static int bcap_enum_input(struct file *file, void *priv,
+ struct v4l2_input *input)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bfin_capture_config *config = bcap_dev->cfg;
+ int ret;
+ u32 status;
+
+ if (input->index >= config->num_inputs)
+ return -EINVAL;
+
+ *input = config->inputs[input->index];
+ /* get input status */
+ ret = v4l2_subdev_call(bcap_dev->sd, video, g_input_status, &status);
+ if (!ret)
+ input->status = status;
+ return 0;
+}
+
+static int bcap_g_input(struct file *file, void *priv, unsigned int *index)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ *index = bcap_dev->cur_input;
+ return 0;
+}
+
+static int bcap_s_input(struct file *file, void *priv, unsigned int index)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bfin_capture_config *config = bcap_dev->cfg;
+ struct bcap_route *route;
+ int ret;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ if (index >= config->num_inputs)
+ return -EINVAL;
+
+ route = &config->routes[index];
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_routing,
+ route->input, route->output, 0);
+ if ((ret < 0) && (ret != -ENOIOCTLCMD)) {
+ v4l2_err(&bcap_dev->v4l2_dev, "Failed to set input\n");
+ return ret;
+ }
+ bcap_dev->cur_input = index;
+ return 0;
+}
+
+static int bcap_try_format(struct bcap_device *bcap,
+ struct v4l2_pix_format *pixfmt,
+ enum v4l2_mbus_pixelcode *mbus_code,
+ int *bpp)
+{
+ struct bcap_format *sf = bcap->sensor_formats;
+ struct bcap_format *fmt = NULL;
+ struct v4l2_mbus_framefmt mbus_fmt;
+ int ret, i;
+
+ for (i = 0; i < bcap->num_sensor_formats; i++) {
+ fmt = &sf[i];
+ if (pixfmt->pixelformat == fmt->pixelformat)
+ break;
+ }
+ if (i == bcap->num_sensor_formats)
+ fmt = &sf[0];
+
+ if (mbus_code)
+ *mbus_code = fmt->mbus_code;
+ if (bpp)
+ *bpp = fmt->bpp;
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, fmt->mbus_code);
+ ret = v4l2_subdev_call(bcap->sd, video,
+ try_mbus_fmt, &mbus_fmt);
+ if (ret < 0)
+ return ret;
+ v4l2_fill_pix_format(pixfmt, &mbus_fmt);
+ pixfmt->bytesperline = pixfmt->width * fmt->bpp / 8;
+ pixfmt->sizeimage = pixfmt->bytesperline * pixfmt->height;
+ return 0;
+}
+
+static int bcap_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct bcap_format *sf = bcap_dev->sensor_formats;
+
+ if (fmt->index >= bcap_dev->num_sensor_formats)
+ return -EINVAL;
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strlcpy(fmt->description,
+ sf[fmt->index].desc,
+ sizeof(fmt->description));
+ fmt->pixelformat = sf[fmt->index].pixelformat;
+ return 0;
+}
+
+static int bcap_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+
+ return bcap_try_format(bcap_dev, pixfmt, NULL, NULL);
+}
+
+static int bcap_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ fmt->fmt.pix = bcap_dev->fmt;
+ return 0;
+}
+
+static int bcap_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *fmt)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ struct v4l2_mbus_framefmt mbus_fmt;
+ enum v4l2_mbus_pixelcode mbus_code;
+ struct v4l2_pix_format *pixfmt = &fmt->fmt.pix;
+ int ret, bpp;
+
+ if (vb2_is_busy(&bcap_dev->buffer_queue))
+ return -EBUSY;
+
+ /* see if format works */
+ ret = bcap_try_format(bcap_dev, pixfmt, &mbus_code, &bpp);
+ if (ret < 0)
+ return ret;
+
+ v4l2_fill_mbus_format(&mbus_fmt, pixfmt, mbus_code);
+ ret = v4l2_subdev_call(bcap_dev->sd, video, s_mbus_fmt, &mbus_fmt);
+ if (ret < 0)
+ return ret;
+ bcap_dev->fmt = *pixfmt;
+ bcap_dev->bpp = bpp;
+ return 0;
+}
+
+static int bcap_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ strlcpy(cap->driver, CAPTURE_DRV_NAME, sizeof(cap->driver));
+ strlcpy(cap->bus_info, "Blackfin Platform", sizeof(cap->bus_info));
+ strlcpy(cap->card, bcap_dev->cfg->card_name, sizeof(cap->card));
+ return 0;
+}
+
+static int bcap_g_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return v4l2_subdev_call(bcap_dev->sd, video, g_parm, a);
+}
+
+static int bcap_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm *a)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ return v4l2_subdev_call(bcap_dev->sd, video, s_parm, a);
+}
+
+static int bcap_g_chip_ident(struct file *file, void *priv,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+ if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER &&
+ chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
+ return -EINVAL;
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ g_chip_ident, chip);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int bcap_dbg_g_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ g_register, reg);
+}
+
+static int bcap_dbg_s_register(struct file *file, void *priv,
+ struct v4l2_dbg_register *reg)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+
+ return v4l2_subdev_call(bcap_dev->sd, core,
+ s_register, reg);
+}
+#endif
+
+static int bcap_log_status(struct file *file, void *priv)
+{
+ struct bcap_device *bcap_dev = video_drvdata(file);
+ /* status for sub devices */
+ v4l2_device_call_all(&bcap_dev->v4l2_dev, 0, core, log_status);
+ return 0;
+}
+
+static const struct v4l2_ioctl_ops bcap_ioctl_ops = {
+ .vidioc_querycap = bcap_querycap,
+ .vidioc_g_fmt_vid_cap = bcap_g_fmt_vid_cap,
+ .vidioc_enum_fmt_vid_cap = bcap_enum_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = bcap_s_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = bcap_try_fmt_vid_cap,
+ .vidioc_enum_input = bcap_enum_input,
+ .vidioc_g_input = bcap_g_input,
+ .vidioc_s_input = bcap_s_input,
+ .vidioc_querystd = bcap_querystd,
+ .vidioc_s_std = bcap_s_std,
+ .vidioc_g_std = bcap_g_std,
+ .vidioc_reqbufs = bcap_reqbufs,
+ .vidioc_querybuf = bcap_querybuf,
+ .vidioc_qbuf = bcap_qbuf,
+ .vidioc_dqbuf = bcap_dqbuf,
+ .vidioc_streamon = bcap_streamon,
+ .vidioc_streamoff = bcap_streamoff,
+ .vidioc_g_parm = bcap_g_parm,
+ .vidioc_s_parm = bcap_s_parm,
+ .vidioc_g_chip_ident = bcap_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = bcap_dbg_g_register,
+ .vidioc_s_register = bcap_dbg_s_register,
+#endif
+ .vidioc_log_status = bcap_log_status,
+};
+
+static struct v4l2_file_operations bcap_fops = {
+ .owner = THIS_MODULE,
+ .open = bcap_open,
+ .release = bcap_release,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = bcap_mmap,
+#ifndef CONFIG_MMU
+ .get_unmapped_area = bcap_get_unmapped_area,
+#endif
+ .poll = bcap_poll
+};
+
+static int __devinit bcap_probe(struct platform_device *pdev)
+{
+ struct bcap_device *bcap_dev;
+ struct video_device *vfd;
+ struct i2c_adapter *i2c_adap;
+ struct bfin_capture_config *config;
+ struct vb2_queue *q;
+ int ret;
+
+ config = pdev->dev.platform_data;
+ if (!config) {
+ v4l2_err(pdev->dev.driver, "Unable to get board config\n");
+ return -ENODEV;
+ }
+
+ bcap_dev = kzalloc(sizeof(*bcap_dev), GFP_KERNEL);
+ if (!bcap_dev) {
+ v4l2_err(pdev->dev.driver, "Unable to alloc bcap_dev\n");
+ return -ENOMEM;
+ }
+
+ bcap_dev->cfg = config;
+
+ bcap_dev->ppi = ppi_create_instance(config->ppi_info);
+ if (!bcap_dev->ppi) {
+ v4l2_err(pdev->dev.driver, "Unable to create ppi\n");
+ ret = -ENODEV;
+ goto err_free_dev;
+ }
+ bcap_dev->ppi->priv = bcap_dev;
+
+ bcap_dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(bcap_dev->alloc_ctx)) {
+ ret = PTR_ERR(bcap_dev->alloc_ctx);
+ goto err_free_ppi;
+ }
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ ret = -ENOMEM;
+ v4l2_err(pdev->dev.driver, "Unable to alloc video device\n");
+ goto err_cleanup_ctx;
+ }
+
+ /* initialize field of video device */
+ vfd->release = video_device_release;
+ vfd->fops = &bcap_fops;
+ vfd->ioctl_ops = &bcap_ioctl_ops;
+ vfd->tvnorms = 0;
+ vfd->v4l2_dev = &bcap_dev->v4l2_dev;
+ set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
+ strncpy(vfd->name, CAPTURE_DRV_NAME, sizeof(vfd->name));
+ bcap_dev->video_dev = vfd;
+
+ ret = v4l2_device_register(&pdev->dev, &bcap_dev->v4l2_dev);
+ if (ret) {
+ v4l2_err(pdev->dev.driver,
+ "Unable to register v4l2 device\n");
+ goto err_release_vdev;
+ }
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 device registered\n");
+
+ bcap_dev->v4l2_dev.ctrl_handler = &bcap_dev->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(&bcap_dev->ctrl_handler, 0);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to init control handler\n");
+ goto err_unreg_v4l2;
+ }
+
+ spin_lock_init(&bcap_dev->lock);
+ /* initialize queue */
+ q = &bcap_dev->buffer_queue;
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP;
+ q->drv_priv = bcap_dev;
+ q->buf_struct_size = sizeof(struct bcap_buffer);
+ q->ops = &bcap_video_qops;
+ q->mem_ops = &vb2_dma_contig_memops;
+
+ vb2_queue_init(q);
+
+ mutex_init(&bcap_dev->mutex);
+ init_completion(&bcap_dev->comp);
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&bcap_dev->dma_queue);
+
+ vfd->lock = &bcap_dev->mutex;
+
+ /* register video device */
+ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to register video device\n");
+ goto err_free_handler;
+ }
+ video_set_drvdata(bcap_dev->video_dev, bcap_dev);
+ v4l2_info(&bcap_dev->v4l2_dev, "video device registered as: %s\n",
+ video_device_node_name(vfd));
+
+ /* load up the subdevice */
+ i2c_adap = i2c_get_adapter(config->i2c_adapter_id);
+ if (!i2c_adap) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to find i2c adapter\n");
+ goto err_unreg_vdev;
+
+ }
+ bcap_dev->sd = v4l2_i2c_new_subdev_board(&bcap_dev->v4l2_dev,
+ i2c_adap,
+ &config->board_info,
+ NULL);
+ if (bcap_dev->sd) {
+ int i;
+ /* update tvnorms from the sub devices */
+ for (i = 0; i < config->num_inputs; i++)
+ vfd->tvnorms |= config->inputs[i].std;
+ } else {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to register sub device\n");
+ goto err_unreg_vdev;
+ }
+
+ v4l2_info(&bcap_dev->v4l2_dev, "v4l2 sub device registered\n");
+
+ /* now we can probe the default state */
+ if (vfd->tvnorms) {
+ v4l2_std_id std;
+ ret = v4l2_subdev_call(bcap_dev->sd, core, g_std, &std);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to get std\n");
+ goto err_unreg_vdev;
+ }
+ bcap_dev->std = std;
+ }
+ ret = bcap_init_sensor_formats(bcap_dev);
+ if (ret) {
+ v4l2_err(&bcap_dev->v4l2_dev,
+ "Unable to create sensor formats table\n");
+ goto err_unreg_vdev;
+ }
+ return 0;
+err_unreg_vdev:
+ video_unregister_device(bcap_dev->video_dev);
+ bcap_dev->video_dev = NULL;
+err_free_handler:
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
+err_unreg_v4l2:
+ v4l2_device_unregister(&bcap_dev->v4l2_dev);
+err_release_vdev:
+ if (bcap_dev->video_dev)
+ video_device_release(bcap_dev->video_dev);
+err_cleanup_ctx:
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+err_free_ppi:
+ ppi_delete_instance(bcap_dev->ppi);
+err_free_dev:
+ kfree(bcap_dev);
+ return ret;
+}
+
+static int __devexit bcap_remove(struct platform_device *pdev)
+{
+ struct v4l2_device *v4l2_dev = platform_get_drvdata(pdev);
+ struct bcap_device *bcap_dev = container_of(v4l2_dev,
+ struct bcap_device, v4l2_dev);
+
+ bcap_free_sensor_formats(bcap_dev);
+ video_unregister_device(bcap_dev->video_dev);
+ v4l2_ctrl_handler_free(&bcap_dev->ctrl_handler);
+ v4l2_device_unregister(v4l2_dev);
+ vb2_dma_contig_cleanup_ctx(bcap_dev->alloc_ctx);
+ ppi_delete_instance(bcap_dev->ppi);
+ kfree(bcap_dev);
+ return 0;
+}
+
+static struct platform_driver bcap_driver = {
+ .driver = {
+ .name = CAPTURE_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = bcap_probe,
+ .remove = __devexit_p(bcap_remove),
+};
+
+static __init int bcap_init(void)
+{
+ return platform_driver_register(&bcap_driver);
+}
+
+static __exit void bcap_exit(void)
+{
+ platform_driver_unregister(&bcap_driver);
+}
+
+module_init(bcap_init);
+module_exit(bcap_exit);
+
+MODULE_DESCRIPTION("Analog Devices blackfin video capture driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/blackfin/ppi.c b/drivers/media/video/blackfin/ppi.c
new file mode 100644
index 000000000000..d29592186b02
--- /dev/null
+++ b/drivers/media/video/blackfin/ppi.c
@@ -0,0 +1,271 @@
+/*
+ * ppi.c Analog Devices Parallel Peripheral Interface driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/slab.h>
+
+#include <asm/bfin_ppi.h>
+#include <asm/blackfin.h>
+#include <asm/cacheflush.h>
+#include <asm/dma.h>
+#include <asm/portmux.h>
+
+#include <media/blackfin/ppi.h>
+
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler);
+static void ppi_detach_irq(struct ppi_if *ppi);
+static int ppi_start(struct ppi_if *ppi);
+static int ppi_stop(struct ppi_if *ppi);
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params);
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr);
+
+static const struct ppi_ops ppi_ops = {
+ .attach_irq = ppi_attach_irq,
+ .detach_irq = ppi_detach_irq,
+ .start = ppi_start,
+ .stop = ppi_stop,
+ .set_params = ppi_set_params,
+ .update_addr = ppi_update_addr,
+};
+
+static irqreturn_t ppi_irq_err(int irq, void *dev_id)
+{
+ struct ppi_if *ppi = dev_id;
+ const struct ppi_info *info = ppi->info;
+
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+ unsigned short status;
+
+ /* register on bf561 is cleared when read
+ * others are W1C
+ */
+ status = bfin_read16(&reg->status);
+ bfin_write16(&reg->status, 0xff00);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+ bfin_write16(&reg->status, 0xffff);
+ break;
+ }
+ default:
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int ppi_attach_irq(struct ppi_if *ppi, irq_handler_t handler)
+{
+ const struct ppi_info *info = ppi->info;
+ int ret;
+
+ ret = request_dma(info->dma_ch, "PPI_DMA");
+
+ if (ret) {
+ pr_err("Unable to allocate DMA channel for PPI\n");
+ return ret;
+ }
+ set_dma_callback(info->dma_ch, handler, ppi);
+
+ if (ppi->err_int) {
+ ret = request_irq(info->irq_err, ppi_irq_err, 0, "PPI ERROR", ppi);
+ if (ret) {
+ pr_err("Unable to allocate IRQ for PPI\n");
+ free_dma(info->dma_ch);
+ }
+ }
+ return ret;
+}
+
+static void ppi_detach_irq(struct ppi_if *ppi)
+{
+ const struct ppi_info *info = ppi->info;
+
+ if (ppi->err_int)
+ free_irq(info->irq_err, ppi);
+ free_dma(info->dma_ch);
+}
+
+static int ppi_start(struct ppi_if *ppi)
+{
+ const struct ppi_info *info = ppi->info;
+
+ /* enable DMA */
+ enable_dma(info->dma_ch);
+
+ /* enable PPI */
+ ppi->ppi_control |= PORT_EN;
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+ bfin_write16(&reg->control, ppi->ppi_control);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+ bfin_write32(&reg->control, ppi->ppi_control);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ SSYNC();
+ return 0;
+}
+
+static int ppi_stop(struct ppi_if *ppi)
+{
+ const struct ppi_info *info = ppi->info;
+
+ /* disable PPI */
+ ppi->ppi_control &= ~PORT_EN;
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+ bfin_write16(&reg->control, ppi->ppi_control);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+ bfin_write32(&reg->control, ppi->ppi_control);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ /* disable DMA */
+ clear_dma_irqstat(info->dma_ch);
+ disable_dma(info->dma_ch);
+
+ SSYNC();
+ return 0;
+}
+
+static int ppi_set_params(struct ppi_if *ppi, struct ppi_params *params)
+{
+ const struct ppi_info *info = ppi->info;
+ int dma32 = 0;
+ int dma_config, bytes_per_line, lines_per_frame;
+
+ bytes_per_line = params->width * params->bpp / 8;
+ lines_per_frame = params->height;
+ if (params->int_mask == 0xFFFFFFFF)
+ ppi->err_int = false;
+ else
+ ppi->err_int = true;
+
+ dma_config = (DMA_FLOW_STOP | WNR | RESTART | DMA2D | DI_EN);
+ ppi->ppi_control = params->ppi_control & ~PORT_EN;
+ switch (info->type) {
+ case PPI_TYPE_PPI:
+ {
+ struct bfin_ppi_regs *reg = info->base;
+
+ if (params->ppi_control & DMA32)
+ dma32 = 1;
+
+ bfin_write16(&reg->control, ppi->ppi_control);
+ bfin_write16(&reg->count, bytes_per_line - 1);
+ bfin_write16(&reg->frame, lines_per_frame);
+ break;
+ }
+ case PPI_TYPE_EPPI:
+ {
+ struct bfin_eppi_regs *reg = info->base;
+
+ if ((params->ppi_control & PACK_EN)
+ || (params->ppi_control & 0x38000) > DLEN_16)
+ dma32 = 1;
+
+ bfin_write32(&reg->control, ppi->ppi_control);
+ bfin_write16(&reg->line, bytes_per_line + params->blank_clocks);
+ bfin_write16(&reg->frame, lines_per_frame);
+ bfin_write16(&reg->hdelay, 0);
+ bfin_write16(&reg->vdelay, 0);
+ bfin_write16(&reg->hcount, bytes_per_line);
+ bfin_write16(&reg->vcount, lines_per_frame);
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ if (dma32) {
+ dma_config |= WDSIZE_32;
+ set_dma_x_count(info->dma_ch, bytes_per_line >> 2);
+ set_dma_x_modify(info->dma_ch, 4);
+ set_dma_y_modify(info->dma_ch, 4);
+ } else {
+ dma_config |= WDSIZE_16;
+ set_dma_x_count(info->dma_ch, bytes_per_line >> 1);
+ set_dma_x_modify(info->dma_ch, 2);
+ set_dma_y_modify(info->dma_ch, 2);
+ }
+ set_dma_y_count(info->dma_ch, lines_per_frame);
+ set_dma_config(info->dma_ch, dma_config);
+
+ SSYNC();
+ return 0;
+}
+
+static void ppi_update_addr(struct ppi_if *ppi, unsigned long addr)
+{
+ set_dma_start_addr(ppi->info->dma_ch, addr);
+}
+
+struct ppi_if *ppi_create_instance(const struct ppi_info *info)
+{
+ struct ppi_if *ppi;
+
+ if (!info || !info->pin_req)
+ return NULL;
+
+ if (peripheral_request_list(info->pin_req, KBUILD_MODNAME)) {
+ pr_err("request peripheral failed\n");
+ return NULL;
+ }
+
+ ppi = kzalloc(sizeof(*ppi), GFP_KERNEL);
+ if (!ppi) {
+ peripheral_free_list(info->pin_req);
+ pr_err("unable to allocate memory for ppi handle\n");
+ return NULL;
+ }
+ ppi->ops = &ppi_ops;
+ ppi->info = info;
+
+ pr_info("ppi probe success\n");
+ return ppi;
+}
+
+void ppi_delete_instance(struct ppi_if *ppi)
+{
+ peripheral_free_list(ppi->info->pin_req);
+ kfree(ppi);
+}
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
index 859eabf57978..377bf05b1efd 100644
--- a/drivers/media/video/bt819.c
+++ b/drivers/media/video/bt819.c
@@ -514,15 +514,4 @@ static struct i2c_driver bt819_driver = {
.id_table = bt819_id,
};
-static __init int init_bt819(void)
-{
- return i2c_add_driver(&bt819_driver);
-}
-
-static __exit void exit_bt819(void)
-{
- i2c_del_driver(&bt819_driver);
-}
-
-module_init(init_bt819);
-module_exit(exit_bt819);
+module_i2c_driver(bt819_driver);
diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c
index a43059d4c799..7e5bd365c239 100644
--- a/drivers/media/video/bt856.c
+++ b/drivers/media/video/bt856.c
@@ -270,15 +270,4 @@ static struct i2c_driver bt856_driver = {
.id_table = bt856_id,
};
-static __init int init_bt856(void)
-{
- return i2c_add_driver(&bt856_driver);
-}
-
-static __exit void exit_bt856(void)
-{
- i2c_del_driver(&bt856_driver);
-}
-
-module_init(init_bt856);
-module_exit(exit_bt856);
+module_i2c_driver(bt856_driver);
diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c
index 4e5dcea0501d..905320b67a1c 100644
--- a/drivers/media/video/bt866.c
+++ b/drivers/media/video/bt866.c
@@ -240,15 +240,4 @@ static struct i2c_driver bt866_driver = {
.id_table = bt866_id,
};
-static __init int init_bt866(void)
-{
- return i2c_add_driver(&bt866_driver);
-}
-
-static __exit void exit_bt866(void)
-{
- i2c_del_driver(&bt866_driver);
-}
-
-module_init(init_bt866);
-module_exit(exit_bt866);
+module_i2c_driver(bt866_driver);
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 76c301f05095..e581b37be789 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -2035,11 +2035,7 @@ static int bttv_log_status(struct file *file, void *f)
struct bttv_fh *fh = f;
struct bttv *btv = fh->btv;
- pr_info("%d: ======== START STATUS CARD #%d ========\n",
- btv->c.nr, btv->c.nr);
bttv_call_all(btv, core, log_status);
- pr_info("%d: ======== END STATUS CARD #%d ========\n",
- btv->c.nr, btv->c.nr);
return 0;
}
diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
index 1d64af9adf71..c8581e26fa9c 100644
--- a/drivers/media/video/cs5345.c
+++ b/drivers/media/video/cs5345.c
@@ -249,15 +249,4 @@ static struct i2c_driver cs5345_driver = {
.id_table = cs5345_id,
};
-static __init int init_cs5345(void)
-{
- return i2c_add_driver(&cs5345_driver);
-}
-
-static __exit void exit_cs5345(void)
-{
- i2c_del_driver(&cs5345_driver);
-}
-
-module_init(init_cs5345);
-module_exit(exit_cs5345);
+module_i2c_driver(cs5345_driver);
diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
index 51c5b9ad67d8..b293912206eb 100644
--- a/drivers/media/video/cs53l32a.c
+++ b/drivers/media/video/cs53l32a.c
@@ -248,15 +248,4 @@ static struct i2c_driver cs53l32a_driver = {
.id_table = cs53l32a_id,
};
-static __init int init_cs53l32a(void)
-{
- return i2c_add_driver(&cs53l32a_driver);
-}
-
-static __exit void exit_cs53l32a(void)
-{
- i2c_del_driver(&cs53l32a_driver);
-}
-
-module_init(init_cs53l32a);
-module_exit(exit_cs53l32a);
+module_i2c_driver(cs53l32a_driver);
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index 349bd9c2aff5..b55d57cc1a1c 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -38,7 +38,7 @@
#include "cx18-ioctl.h"
#include "cx18-controls.h"
#include "tuner-xc2028.h"
-
+#include <linux/dma-mapping.h>
#include <media/tveeprom.h>
/* If you have already X v4l cards, then set this to X. This way
@@ -75,7 +75,7 @@ static int radio[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1 };
static unsigned cardtype_c = 1;
static unsigned tuner_c = 1;
-static bool radio_c = 1;
+static unsigned radio_c = 1;
static char pal[] = "--";
static char secam[] = "--";
static char ntsc[] = "-";
@@ -110,7 +110,7 @@ static int retry_mmio = 1;
int cx18_debug;
module_param_array(tuner, int, &tuner_c, 0644);
-module_param_array(radio, bool, &radio_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
module_param_array(cardtype, int, &cardtype_c, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
@@ -812,7 +812,7 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
CX18_ERR("Can't enable device %d!\n", cx->instance);
return -EIO;
}
- if (pci_set_dma_mask(pci_dev, 0xffffffff)) {
+ if (pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32))) {
CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
return -EIO;
}
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index b9a94fc5146d..7a37e0ee136f 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -44,8 +44,6 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
-#include <linux/dvb/video.h>
-#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-device.h>
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 66b1c15c3541..be49f68ddf37 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -1085,8 +1085,6 @@ static int cx18_log_status(struct file *file, void *fh)
struct v4l2_audio audin;
int i;
- CX18_INFO("================= START STATUS CARD #%d "
- "=================\n", cx->instance);
CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
if (cx->hw_flags & CX18_HW_TVEEPROM) {
struct tveeprom tv;
@@ -1120,8 +1118,6 @@ static int cx18_log_status(struct file *file, void *fh)
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
(long long)cx->mpg_data_received,
(long long)cx->vbi_data_inserted);
- CX18_INFO("================== END STATUS CARD #%d "
- "==================\n", cx->instance);
return 0;
}
diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c
index f8f0e59cd583..d4327dab5a36 100644
--- a/drivers/media/video/cx231xx/cx231xx-417.c
+++ b/drivers/media/video/cx231xx/cx231xx-417.c
@@ -1686,7 +1686,6 @@ static struct v4l2_capability pvr_capability = {
.capabilities = (V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
V4L2_CAP_STREAMING | V4L2_CAP_READWRITE),
- .reserved = {0, 0, 0, 0}
};
static int vidioc_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
index 875a7ce94736..8ed460d692e0 100644
--- a/drivers/media/video/cx231xx/cx231xx-cards.c
+++ b/drivers/media/video/cx231xx/cx231xx-cards.c
@@ -861,7 +861,6 @@ void cx231xx_release_resources(struct cx231xx *dev)
kfree(dev->sliced_cc_mode.alt_max_pkt_size);
kfree(dev->ts1_mode.alt_max_pkt_size);
kfree(dev);
- dev = NULL;
}
/*
diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
index 829a41b0c9ef..7f916f0685e9 100644
--- a/drivers/media/video/cx231xx/cx231xx-video.c
+++ b/drivers/media/video/cx231xx/cx231xx-video.c
@@ -2319,8 +2319,7 @@ static int cx231xx_v4l2_close(struct file *filp)
if (dev->state & DEV_DISCONNECTED) {
if (atomic_read(&dev->devlist_count) > 0) {
cx231xx_release_resources(dev);
- kfree(dev);
- dev = NULL;
+ fh->dev = NULL;
return 0;
}
return 0;
@@ -2350,8 +2349,7 @@ static int cx231xx_v4l2_close(struct file *filp)
free the remaining resources */
if (dev->state & DEV_DISCONNECTED) {
cx231xx_release_resources(dev);
- kfree(dev);
- dev = NULL;
+ fh->dev = NULL;
return 0;
}
diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c
index f617474f9073..7930ca58349f 100644
--- a/drivers/media/video/cx25821/cx25821-core.c
+++ b/drivers/media/video/cx25821/cx25821-core.c
@@ -1474,8 +1474,13 @@ static DEFINE_PCI_DEVICE_TABLE(cx25821_pci_tbl) = {
.device = 0x8210,
.subvendor = 0x14f1,
.subdevice = 0x0920,
- },
- {
+ }, {
+ /* CX25821 No Brand */
+ .vendor = 0x14f1,
+ .device = 0x8210,
+ .subvendor = 0x0000,
+ .subdevice = 0x0000,
+ }, {
/* --- end of list --- */
}
};
diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
index 05247d4c340a..fc1ff69cffd0 100644
--- a/drivers/media/video/cx25840/cx25840-core.c
+++ b/drivers/media/video/cx25840/cx25840-core.c
@@ -5301,15 +5301,4 @@ static struct i2c_driver cx25840_driver = {
.id_table = cx25840_id,
};
-static __init int init_cx25840(void)
-{
- return i2c_add_driver(&cx25840_driver);
-}
-
-static __exit void exit_cx25840(void)
-{
- i2c_del_driver(&cx25840_driver);
-}
-
-module_init(init_cx25840);
-module_exit(exit_cx25840);
+module_i2c_driver(cx25840_driver);
diff --git a/drivers/media/video/davinci/vpif.h b/drivers/media/video/davinci/vpif.h
index 25036cb11bea..8bcac65f9294 100644
--- a/drivers/media/video/davinci/vpif.h
+++ b/drivers/media/video/davinci/vpif.h
@@ -18,8 +18,6 @@
#include <linux/io.h>
#include <linux/videodev2.h>
-#include <mach/hardware.h>
-#include <mach/dm646x.h>
#include <media/davinci/vpif_types.h>
/* Maximum channel allowed */
diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c
index 286f02910044..7fa34b4fae26 100644
--- a/drivers/media/video/davinci/vpif_display.c
+++ b/drivers/media/video/davinci/vpif_display.c
@@ -39,8 +39,6 @@
#include <media/v4l2-ioctl.h>
#include <media/v4l2-chip-ident.h>
-#include <mach/dm646x.h>
-
#include "vpif_display.h"
#include "vpif.h"
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 4561cd89938d..9fd8cc7dbb23 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -353,6 +353,44 @@ static struct em28xx_reg_seq hauppauge_930c_digital[] = {
};
#endif
+/* 1b80:e425 MaxMedia UB425-TC
+ * GPIO_6 - demod reset, 0=active
+ * GPIO_7 - LED, 0=active
+ */
+static struct em28xx_reg_seq maxmedia_ub425_tc[] = {
+ {EM2874_R80_GPIO, 0x83, 0xff, 100},
+ {EM2874_R80_GPIO, 0xc3, 0xff, 100}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0x43, 0xff, 000}, /* GPIO_7 = 0 */
+ {-1, -1, -1, -1},
+};
+
+/* 2304:0242 PCTV QuatroStick (510e)
+ * GPIO_2: decoder reset, 0=active
+ * GPIO_4: decoder suspend, 0=active
+ * GPIO_6: demod reset, 0=active
+ * GPIO_7: LED, 1=active
+ */
+static struct em28xx_reg_seq pctv_510e[] = {
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ { -1, -1, -1, -1},
+};
+
+/* 2013:0251 PCTV QuatroStick nano (520e)
+ * GPIO_2: decoder reset, 0=active
+ * GPIO_4: decoder suspend, 0=active
+ * GPIO_6: demod reset, 0=active
+ * GPIO_7: LED, 1=active
+ */
+static struct em28xx_reg_seq pctv_520e[] = {
+ {EM2874_R80_GPIO, 0x10, 0xff, 100},
+ {EM2874_R80_GPIO, 0x14, 0xff, 100}, /* GPIO_2 = 1 */
+ {EM2874_R80_GPIO, 0x54, 0xff, 050}, /* GPIO_6 = 1 */
+ {EM2874_R80_GPIO, 0xd4, 0xff, 000}, /* GPIO_7 = 1 */
+ { -1, -1, -1, -1},
+};
+
/*
* Board definitions
*/
@@ -1908,6 +1946,41 @@ struct em28xx_board em28xx_boards[] = {
.amux = EM28XX_AMUX_LINE_IN,
} },
},
+ /* 1b80:e425 MaxMedia UB425-TC
+ * Empia EM2874B + Micronas DRX 3913KA2 + NXP TDA18271HDC2 */
+ [EM2874_BOARD_MAXMEDIA_UB425_TC] = {
+ .name = "MaxMedia UB425-TC",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = maxmedia_ub425_tc,
+ .has_dvb = 1,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ /* 2304:0242 PCTV QuatroStick (510e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ [EM2884_BOARD_PCTV_510E] = {
+ .name = "PCTV QuatroStick (510e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_510e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
+ /* 2013:0251 PCTV QuatroStick nano (520e)
+ * Empia EM2884 + Micronas DRX 3926K + NXP TDA18271HDC2 */
+ [EM2884_BOARD_PCTV_520E] = {
+ .name = "PCTV QuatroStick nano (520e)",
+ .tuner_type = TUNER_ABSENT,
+ .tuner_gpio = pctv_520e,
+ .has_dvb = 1,
+ .ir_codes = RC_MAP_PINNACLE_PCTV_HD,
+ .i2c_speed = EM2874_I2C_SECONDARY_BUS_SELECT |
+ EM28XX_I2C_CLK_WAIT_ENABLE |
+ EM28XX_I2C_FREQ_400_KHZ,
+ },
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -2059,6 +2132,12 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2860_BOARD_HT_VIDBOX_NW03 },
{ USB_DEVICE(0x1b80, 0xe309), /* Sveon STV40 */
.driver_info = EM2860_BOARD_EASYCAP },
+ { USB_DEVICE(0x1b80, 0xe425),
+ .driver_info = EM2874_BOARD_MAXMEDIA_UB425_TC },
+ { USB_DEVICE(0x2304, 0x0242),
+ .driver_info = EM2884_BOARD_PCTV_510E },
+ { USB_DEVICE(0x2013, 0x0251),
+ .driver_info = EM2884_BOARD_PCTV_520E },
{ },
};
MODULE_DEVICE_TABLE(usb, em28xx_id_table);
@@ -3122,7 +3201,6 @@ static int em28xx_usb_probe(struct usb_interface *interface,
int i, nr;
const int ifnum = interface->altsetting[0].desc.bInterfaceNumber;
char *speed;
- char descr[255] = "";
udev = usb_get_dev(interface_to_usbdev(interface));
@@ -3227,21 +3305,11 @@ static int em28xx_usb_probe(struct usb_interface *interface,
speed = "unknown";
}
- if (udev->manufacturer)
- strlcpy(descr, udev->manufacturer, sizeof(descr));
-
- if (udev->product) {
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
- strlcat(descr, udev->product, sizeof(descr));
- }
-
- if (*descr)
- strlcat(descr, " ", sizeof(descr));
-
printk(KERN_INFO DRIVER_NAME
- ": New device %s@ %s Mbps (%04x:%04x, interface %d, class %d)\n",
- descr,
+ ": New device %s %s @ %s Mbps "
+ "(%04x:%04x, interface %d, class %d)\n",
+ udev->manufacturer ? udev->manufacturer : "",
+ udev->product ? udev->product : "",
speed,
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct),
@@ -3307,6 +3375,17 @@ static int em28xx_usb_probe(struct usb_interface *interface,
goto unlock_and_free;
}
+ if (has_dvb) {
+ /* pre-allocate DVB isoc transfer buffers */
+ retval = em28xx_alloc_isoc(dev, EM28XX_DIGITAL_MODE,
+ EM28XX_DVB_MAX_PACKETS,
+ EM28XX_DVB_NUM_BUFS,
+ dev->dvb_max_pkt_size);
+ if (retval) {
+ goto unlock_and_free;
+ }
+ }
+
request_modules(dev);
/* Should be the last thing to do, to avoid newer udev's to
@@ -3379,7 +3458,7 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
video_device_node_name(dev->vdev));
dev->state |= DEV_MISCONFIGURED;
- em28xx_uninit_isoc(dev);
+ em28xx_uninit_isoc(dev, dev->mode);
dev->state |= DEV_DISCONNECTED;
wake_up_interruptible(&dev->wait_frame);
wake_up_interruptible(&dev->wait_stream);
@@ -3388,6 +3467,9 @@ static void em28xx_usb_disconnect(struct usb_interface *interface)
em28xx_release_resources(dev);
}
+ /* free DVB isoc buffers */
+ em28xx_uninit_isoc(dev, EM28XX_DIGITAL_MODE);
+
mutex_unlock(&dev->lock);
em28xx_close_extension(dev);
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 0aacc96f9a23..53a9fb91e97e 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -666,6 +666,7 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
+EXPORT_SYMBOL_GPL(em28xx_capture_start);
int em28xx_vbi_supported(struct em28xx *dev)
{
@@ -961,146 +962,192 @@ static void em28xx_irq_callback(struct urb *urb)
/*
* Stop and Deallocate URBs
*/
-void em28xx_uninit_isoc(struct em28xx *dev)
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode)
{
struct urb *urb;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
int i;
- em28xx_isocdbg("em28xx: called em28xx_uninit_isoc\n");
+ em28xx_isocdbg("em28xx: called em28xx_uninit_isoc in mode %d\n", mode);
+
+ if (mode == EM28XX_DIGITAL_MODE)
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ else
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
dev->isoc_ctl.nfields = -1;
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- urb = dev->isoc_ctl.urb[i];
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = isoc_bufs->urb[i];
if (urb) {
if (!irqs_disabled())
usb_kill_urb(urb);
else
usb_unlink_urb(urb);
- if (dev->isoc_ctl.transfer_buffer[i]) {
+ if (isoc_bufs->transfer_buffer[i]) {
usb_free_coherent(dev->udev,
urb->transfer_buffer_length,
- dev->isoc_ctl.transfer_buffer[i],
+ isoc_bufs->transfer_buffer[i],
urb->transfer_dma);
}
usb_free_urb(urb);
- dev->isoc_ctl.urb[i] = NULL;
+ isoc_bufs->urb[i] = NULL;
}
- dev->isoc_ctl.transfer_buffer[i] = NULL;
+ isoc_bufs->transfer_buffer[i] = NULL;
}
- kfree(dev->isoc_ctl.urb);
- kfree(dev->isoc_ctl.transfer_buffer);
+ kfree(isoc_bufs->urb);
+ kfree(isoc_bufs->transfer_buffer);
- dev->isoc_ctl.urb = NULL;
- dev->isoc_ctl.transfer_buffer = NULL;
- dev->isoc_ctl.num_bufs = 0;
+ isoc_bufs->urb = NULL;
+ isoc_bufs->transfer_buffer = NULL;
+ isoc_bufs->num_bufs = 0;
em28xx_capture_start(dev, 0);
}
EXPORT_SYMBOL_GPL(em28xx_uninit_isoc);
/*
- * Allocate URBs and start IRQ
+ * Allocate URBs
*/
-int em28xx_init_isoc(struct em28xx *dev, int max_packets,
- int num_bufs, int max_pkt_size,
- int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size)
{
- struct em28xx_dmaqueue *dma_q = &dev->vidq;
- struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
int i;
int sb_size, pipe;
struct urb *urb;
int j, k;
- int rc;
- em28xx_isocdbg("em28xx: called em28xx_prepare_isoc\n");
+ em28xx_isocdbg("em28xx: called em28xx_alloc_isoc in mode %d\n", mode);
+
+ if (mode == EM28XX_DIGITAL_MODE)
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ else
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
/* De-allocates all pending stuff */
- em28xx_uninit_isoc(dev);
+ em28xx_uninit_isoc(dev, mode);
- dev->isoc_ctl.isoc_copy = isoc_copy;
- dev->isoc_ctl.num_bufs = num_bufs;
+ isoc_bufs->num_bufs = num_bufs;
- dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
- if (!dev->isoc_ctl.urb) {
+ isoc_bufs->urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
+ if (!isoc_bufs->urb) {
em28xx_errdev("cannot alloc memory for usb buffers\n");
return -ENOMEM;
}
- dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
- GFP_KERNEL);
- if (!dev->isoc_ctl.transfer_buffer) {
+ isoc_bufs->transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
+ GFP_KERNEL);
+ if (!isoc_bufs->transfer_buffer) {
em28xx_errdev("cannot allocate memory for usb transfer\n");
- kfree(dev->isoc_ctl.urb);
+ kfree(isoc_bufs->urb);
return -ENOMEM;
}
- dev->isoc_ctl.max_pkt_size = max_pkt_size;
+ isoc_bufs->max_pkt_size = max_pkt_size;
+ isoc_bufs->num_packets = max_packets;
dev->isoc_ctl.vid_buf = NULL;
dev->isoc_ctl.vbi_buf = NULL;
- sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
+ sb_size = isoc_bufs->num_packets * isoc_bufs->max_pkt_size;
/* allocate urbs and transfer buffers */
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- urb = usb_alloc_urb(max_packets, GFP_KERNEL);
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ urb = usb_alloc_urb(isoc_bufs->num_packets, GFP_KERNEL);
if (!urb) {
em28xx_err("cannot alloc isoc_ctl.urb %i\n", i);
- em28xx_uninit_isoc(dev);
+ em28xx_uninit_isoc(dev, mode);
return -ENOMEM;
}
- dev->isoc_ctl.urb[i] = urb;
+ isoc_bufs->urb[i] = urb;
- dev->isoc_ctl.transfer_buffer[i] = usb_alloc_coherent(dev->udev,
+ isoc_bufs->transfer_buffer[i] = usb_alloc_coherent(dev->udev,
sb_size, GFP_KERNEL, &urb->transfer_dma);
- if (!dev->isoc_ctl.transfer_buffer[i]) {
+ if (!isoc_bufs->transfer_buffer[i]) {
em28xx_err("unable to allocate %i bytes for transfer"
" buffer %i%s\n",
sb_size, i,
in_interrupt() ? " while in int" : "");
- em28xx_uninit_isoc(dev);
+ em28xx_uninit_isoc(dev, mode);
return -ENOMEM;
}
- memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
+ memset(isoc_bufs->transfer_buffer[i], 0, sb_size);
/* FIXME: this is a hack - should be
'desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK'
should also be using 'desc.bInterval'
*/
pipe = usb_rcvisocpipe(dev->udev,
- dev->mode == EM28XX_ANALOG_MODE ?
+ mode == EM28XX_ANALOG_MODE ?
EM28XX_EP_ANALOG : EM28XX_EP_DIGITAL);
usb_fill_int_urb(urb, dev->udev, pipe,
- dev->isoc_ctl.transfer_buffer[i], sb_size,
+ isoc_bufs->transfer_buffer[i], sb_size,
em28xx_irq_callback, dev, 1);
- urb->number_of_packets = max_packets;
+ urb->number_of_packets = isoc_bufs->num_packets;
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
k = 0;
- for (j = 0; j < max_packets; j++) {
+ for (j = 0; j < isoc_bufs->num_packets; j++) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length =
- dev->isoc_ctl.max_pkt_size;
- k += dev->isoc_ctl.max_pkt_size;
+ isoc_bufs->max_pkt_size;
+ k += isoc_bufs->max_pkt_size;
}
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(em28xx_alloc_isoc);
+
+/*
+ * Allocate URBs and start IRQ
+ */
+int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size,
+ int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
+{
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ struct em28xx_usb_isoc_bufs *isoc_bufs;
+ int i;
+ int rc;
+ int alloc;
+
+ em28xx_isocdbg("em28xx: called em28xx_init_isoc in mode %d\n", mode);
+
+ dev->isoc_ctl.isoc_copy = isoc_copy;
+
+ if (mode == EM28XX_DIGITAL_MODE) {
+ isoc_bufs = &dev->isoc_ctl.digital_bufs;
+ /* no need to free/alloc isoc buffers in digital mode */
+ alloc = 0;
+ } else {
+ isoc_bufs = &dev->isoc_ctl.analog_bufs;
+ alloc = 1;
+ }
+
+ if (alloc) {
+ rc = em28xx_alloc_isoc(dev, mode, max_packets,
+ num_bufs, max_pkt_size);
+ if (rc)
+ return rc;
+ }
+
init_waitqueue_head(&dma_q->wq);
init_waitqueue_head(&vbi_dma_q->wq);
em28xx_capture_start(dev, 1);
/* submit urbs and enables IRQ */
- for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
- rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
+ for (i = 0; i < isoc_bufs->num_bufs; i++) {
+ rc = usb_submit_urb(isoc_bufs->urb[i], GFP_ATOMIC);
if (rc) {
em28xx_err("submit of urb %i failed (error=%i)\n", i,
rc);
- em28xx_uninit_isoc(dev);
+ em28xx_uninit_isoc(dev, mode);
return rc;
}
}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index aabbf4854f66..503a8d5b5382 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -61,9 +61,6 @@ if (debug >= level) \
printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
} while (0)
-#define EM28XX_DVB_NUM_BUFS 5
-#define EM28XX_DVB_MAX_PACKETS 64
-
struct em28xx_dvb {
struct dvb_frontend *fe[2];
@@ -172,20 +169,21 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb)
max_dvb_packet_size = dev->dvb_max_pkt_size;
if (max_dvb_packet_size < 0)
return max_dvb_packet_size;
- dprintk(1, "Using %d buffers each with %d bytes\n",
+ dprintk(1, "Using %d buffers each with %d x %d bytes\n",
EM28XX_DVB_NUM_BUFS,
+ EM28XX_DVB_MAX_PACKETS,
max_dvb_packet_size);
- return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS,
- EM28XX_DVB_NUM_BUFS, max_dvb_packet_size,
- em28xx_dvb_isoc_copy);
+ return em28xx_init_isoc(dev, EM28XX_DIGITAL_MODE,
+ EM28XX_DVB_MAX_PACKETS, EM28XX_DVB_NUM_BUFS,
+ max_dvb_packet_size, em28xx_dvb_isoc_copy);
}
static int em28xx_stop_streaming(struct em28xx_dvb *dvb)
{
struct em28xx *dev = dvb->adapter.priv;
- em28xx_uninit_isoc(dev);
+ em28xx_capture_start(dev, 0);
em28xx_set_mode(dev, EM28XX_SUSPEND);
@@ -327,6 +325,19 @@ struct drxk_config hauppauge_930c_drxk = {
.chunk_size = 56,
};
+struct drxk_config maxmedia_ub425_tc_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .no_i2c_bridge = 1,
+};
+
+struct drxk_config pctv_520e_drxk = {
+ .adr = 0x29,
+ .single_master = 1,
+ .microcode_name = "dvb-demod-drxk-pctv.fw",
+ .chunk_size = 58,
+};
+
static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable)
{
struct em28xx_dvb *dvb = fe->sec_priv;
@@ -460,6 +471,33 @@ static void terratec_h5_init(struct em28xx *dev)
em28xx_gpio_set(dev, terratec_h5_end);
};
+static void pctv_520e_init(struct em28xx *dev)
+{
+ /*
+ * Init TDA8295(?) analog demodulator. Looks like I2C traffic to
+ * digital demodulator and tuner are routed via TDA8295.
+ */
+ int i;
+ struct {
+ unsigned char r[4];
+ int len;
+ } regs[] = {
+ {{ 0x06, 0x02, 0x00, 0x31 }, 4},
+ {{ 0x01, 0x02 }, 2},
+ {{ 0x01, 0x02, 0x00, 0xc6 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0xff, 0xaf }, 4},
+ {{ 0x01, 0x00, 0x03, 0xa0 }, 4},
+ {{ 0x01, 0x00 }, 2},
+ {{ 0x01, 0x00, 0x73, 0xaf }, 4},
+ };
+
+ dev->i2c_client.addr = 0x82 >> 1; /* 0x41 */
+
+ for (i = 0; i < ARRAY_SIZE(regs); i++)
+ i2c_master_send(&dev->i2c_client, regs[i].r, regs[i].len);
+};
+
static int em28xx_mt352_terratec_xs_init(struct dvb_frontend *fe)
{
/* Values extracted from a USB trace of the Terratec Windows driver */
@@ -938,6 +976,48 @@ static int em28xx_dvb_init(struct em28xx *dev)
dvb_attach(a8293_attach, dvb->fe[0], &dev->i2c_adap,
&em28xx_a8293_config);
break;
+ case EM2874_BOARD_MAXMEDIA_UB425_TC:
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &maxmedia_ub425_tc_drxk,
+ &dev->i2c_adap);
+
+ if (dvb->fe[0]) {
+ /* disable I2C-gate */
+ dvb->fe[0]->ops.i2c_gate_ctrl = NULL;
+
+ /* attach tuner */
+ if (!dvb_attach(tda18271c2dd_attach, dvb->fe[0],
+ &dev->i2c_adap, 0x60)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+
+ /* TODO: we need drx-3913k firmware in order to support DVB-T */
+ em28xx_info("MaxMedia UB425-TC: only DVB-C supported by that " \
+ "driver version\n");
+
+ break;
+ case EM2884_BOARD_PCTV_510E:
+ case EM2884_BOARD_PCTV_520E:
+ pctv_520e_init(dev);
+
+ /* attach demodulator */
+ dvb->fe[0] = dvb_attach(drxk_attach, &pctv_520e_drxk,
+ &dev->i2c_adap);
+
+ if (dvb->fe[0]) {
+ /* attach tuner */
+ if (!dvb_attach(tda18271_attach, dvb->fe[0], 0x60,
+ &dev->i2c_adap,
+ &em28xx_cxd2820r_tda18271_config)) {
+ dvb_frontend_detach(dvb->fe[0]);
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
default:
em28xx_errdev("/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n");
diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
index 36f5a9bc8b76..a88e169dba23 100644
--- a/drivers/media/video/em28xx/em28xx-i2c.c
+++ b/drivers/media/video/em28xx/em28xx-i2c.c
@@ -41,14 +41,6 @@ static unsigned int i2c_debug;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
-
-#define dprintk1(lvl, fmt, args...) \
-do { \
- if (i2c_debug >= lvl) { \
- printk(fmt, ##args); \
- } \
-} while (0)
-
#define dprintk2(lvl, fmt, args...) \
do { \
if (i2c_debug >= lvl) { \
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 613300b51a9e..324b695c0724 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -760,17 +760,19 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
goto fail;
}
- if (!dev->isoc_ctl.num_bufs)
+ if (!dev->isoc_ctl.analog_bufs.num_bufs)
urb_init = 1;
if (urb_init) {
if (em28xx_vbi_supported(dev) == 1)
- rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
+ EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS,
dev->max_pkt_size,
em28xx_isoc_copy_vbi);
else
- rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+ rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE,
+ EM28XX_NUM_PACKETS,
EM28XX_NUM_BUFS,
dev->max_pkt_size,
em28xx_isoc_copy);
@@ -2267,7 +2269,7 @@ static int em28xx_v4l2_close(struct file *filp)
v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0);
/* do this before setting alternate! */
- em28xx_uninit_isoc(dev);
+ em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE);
em28xx_set_mode(dev, EM28XX_SUSPEND);
/* set alternate 0 */
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 22e252bcc41e..2868b19f8b54 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -125,6 +125,9 @@
#define EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C 81
#define EM2884_BOARD_CINERGY_HTC_STICK 82
#define EM2860_BOARD_HT_VIDBOX_NW03 83
+#define EM2874_BOARD_MAXMEDIA_UB425_TC 84
+#define EM2884_BOARD_PCTV_510E 85
+#define EM2884_BOARD_PCTV_520E 86
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -151,12 +154,14 @@
/* number of buffers for isoc transfers */
#define EM28XX_NUM_BUFS 5
+#define EM28XX_DVB_NUM_BUFS 5
/* number of packets for each buffer
windows requests only 64 packets .. so we better do the same
this is what I found out for all alternate numbers there!
*/
#define EM28XX_NUM_PACKETS 64
+#define EM28XX_DVB_MAX_PACKETS 64
#define EM28XX_INTERLACED_DEFAULT 1
@@ -197,10 +202,13 @@ enum em28xx_mode {
struct em28xx;
-struct em28xx_usb_isoc_ctl {
+struct em28xx_usb_isoc_bufs {
/* max packet size of isoc transaction */
int max_pkt_size;
+ /* number of packets in each buffer */
+ int num_packets;
+
/* number of allocated urbs */
int num_bufs;
@@ -209,6 +217,14 @@ struct em28xx_usb_isoc_ctl {
/* transfer buffers for isoc transfer */
char **transfer_buffer;
+};
+
+struct em28xx_usb_isoc_ctl {
+ /* isoc transfer buffers for analog mode */
+ struct em28xx_usb_isoc_bufs analog_bufs;
+
+ /* isoc transfer buffers for digital mode */
+ struct em28xx_usb_isoc_bufs digital_bufs;
/* Last buffer command and region */
u8 cmd;
@@ -600,9 +616,6 @@ struct em28xx {
unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
int dvb_alt; /* alternate for DVB */
unsigned int dvb_max_pkt_size; /* wMaxPacketSize for DVB */
- struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */
- char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc
- transfer */
char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
/* helper funcs that call usb_control_msg */
@@ -676,10 +689,12 @@ int em28xx_vbi_supported(struct em28xx *dev);
int em28xx_set_outfmt(struct em28xx *dev);
int em28xx_resolution_set(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
-int em28xx_init_isoc(struct em28xx *dev, int max_packets,
- int num_bufs, int max_pkt_size,
+int em28xx_alloc_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size);
+int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode,
+ int max_packets, int num_bufs, int max_pkt_size,
int (*isoc_copy) (struct em28xx *dev, struct urb *urb));
-void em28xx_uninit_isoc(struct em28xx *dev);
+void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode);
int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev);
int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode);
int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio);
diff --git a/drivers/media/video/gspca/gl860/Makefile b/drivers/media/video/gspca/gl860/Makefile
index f511eccdfd9c..773ea3426561 100644
--- a/drivers/media/video/gspca/gl860/Makefile
+++ b/drivers/media/video/gspca/gl860/Makefile
@@ -6,5 +6,5 @@ gspca_gl860-objs := gl860.o \
gl860-ov9655.o \
gl860-mi2020.o
-ccflags-y += -Idrivers/media/video/gspca
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile
index 7f52961f439c..575b75bacb62 100644
--- a/drivers/media/video/gspca/m5602/Makefile
+++ b/drivers/media/video/gspca/m5602/Makefile
@@ -8,4 +8,4 @@ gspca_m5602-objs := m5602_core.o \
m5602_s5k83a.o \
m5602_s5k4aa.o
-ccflags-y += -Idrivers/media/video/gspca
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
index fbfa02affa13..e6601b886032 100644
--- a/drivers/media/video/gspca/ov534_9.c
+++ b/drivers/media/video/gspca/ov534_9.c
@@ -1107,16 +1107,34 @@ static void setbrightness(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 val;
+ s8 sval;
if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS))
return;
- val = sd->ctrls[BRIGHTNESS].val;
- if (val < 8)
- val = 15 - val; /* f .. 8 */
- else
- val = val - 8; /* 0 .. 7 */
- sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
- 0x0f | (val << 4));
+ if (sd->sensor == SENSOR_OV562x) {
+ sval = sd->ctrls[BRIGHTNESS].val;
+ val = 0x76;
+ val += sval;
+ sccb_write(gspca_dev, 0x24, val);
+ val = 0x6a;
+ val += sval;
+ sccb_write(gspca_dev, 0x25, val);
+ if (sval < -40)
+ val = 0x71;
+ else if (sval < 20)
+ val = 0x94;
+ else
+ val = 0xe6;
+ sccb_write(gspca_dev, 0x26, val);
+ } else {
+ val = sd->ctrls[BRIGHTNESS].val;
+ if (val < 8)
+ val = 15 - val; /* f .. 8 */
+ else
+ val = val - 8; /* 0 .. 7 */
+ sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
+ 0x0f | (val << 4));
+ }
}
static void setcontrast(struct gspca_dev *gspca_dev)
@@ -1339,7 +1357,16 @@ static int sd_init(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x56, 0x17);
} else if ((sensor_id & 0xfff0) == 0x5620) {
sd->sensor = SENSOR_OV562x;
-
+ gspca_dev->ctrl_dis = (1 << CONTRAST) |
+ (1 << AUTOGAIN) |
+ (1 << EXPOSURE) |
+ (1 << SHARPNESS) |
+ (1 << SATUR) |
+ (1 << LIGHTFREQ);
+
+ sd->ctrls[BRIGHTNESS].min = -90;
+ sd->ctrls[BRIGHTNESS].max = 90;
+ sd->ctrls[BRIGHTNESS].def = 0;
gspca_dev->cam.cam_mode = ov562x_mode;
gspca_dev->cam.nmodes = ARRAY_SIZE(ov562x_mode);
@@ -1360,8 +1387,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (sd->sensor == SENSOR_OV971x || sd->sensor == SENSOR_OV562x)
+ if (sd->sensor == SENSOR_OV971x)
return gspca_dev->usb_err;
+ else if (sd->sensor == SENSOR_OV562x) {
+ setbrightness(gspca_dev);
+ return gspca_dev->usb_err;
+ }
switch (gspca_dev->curr_mode) {
case QVGA_MODE: /* 320x240 */
sccb_w_array(gspca_dev, ov965x_start_1_vga,
diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
index 9db2b34d172c..30662fccb0cf 100644
--- a/drivers/media/video/gspca/pac7302.c
+++ b/drivers/media/video/gspca/pac7302.c
@@ -1,8 +1,8 @@
/*
- * Pixart PAC7302 library
- * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
+ * Pixart PAC7302 driver
*
- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2008-2012 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
*
* Separated from Pixart PAC7311 library by Márton Németh
* Camera button input handling by Márton Németh <nm127@freemail.hu>
@@ -63,67 +63,61 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define MODULE_NAME "pac7302"
-
#include <linux/input.h>
#include <media/v4l2-chip-ident.h>
#include "gspca.h"
+/* Include pac common sof detection functions */
+#include "pac_common.h"
-MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Thomas Kaiser thomas@kaiser-linux.li");
MODULE_DESCRIPTION("Pixart PAC7302");
MODULE_LICENSE("GPL");
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ COLORS,
+ WHITE_BALANCE,
+ RED_BALANCE,
+ BLUE_BALANCE,
+ GAIN,
+ AUTOGAIN,
+ EXPOSURE,
+ VFLIP,
+ HFLIP,
+ NCTRLS /* number of controls */
+};
+
/* specific webcam descriptor for pac7302 */
struct sd {
struct gspca_dev gspca_dev; /* !! must be the first item */
- unsigned char brightness;
- unsigned char contrast;
- unsigned char colors;
- unsigned char white_balance;
- unsigned char red_balance;
- unsigned char blue_balance;
- unsigned char gain;
- unsigned char autogain;
- unsigned short exposure;
- __u8 hflip;
- __u8 vflip;
+ struct gspca_ctrl ctrls[NCTRLS];
+
u8 flags;
#define FL_HFLIP 0x01 /* mirrored by default */
#define FL_VFLIP 0x02 /* vertical flipped by default */
u8 sof_read;
- u8 autogain_ignore_frames;
+ s8 autogain_ignore_frames;
atomic_t avg_lum;
};
/* V4L2 controls supported by the driver */
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+static void setbrightcont(struct gspca_dev *gspca_dev);
+static void setcolors(struct gspca_dev *gspca_dev);
+static void setwhitebalance(struct gspca_dev *gspca_dev);
+static void setredbalance(struct gspca_dev *gspca_dev);
+static void setbluebalance(struct gspca_dev *gspca_dev);
+static void setgain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static void setautogain(struct gspca_dev *gspca_dev);
+static void sethvflip(struct gspca_dev *gspca_dev);
static const struct ctrl sd_ctrls[] = {
- {
+[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -132,13 +126,11 @@ static const struct ctrl sd_ctrls[] = {
#define BRIGHTNESS_MAX 0x20
.maximum = BRIGHTNESS_MAX,
.step = 1,
-#define BRIGHTNESS_DEF 0x10
- .default_value = BRIGHTNESS_DEF,
+ .default_value = 0x10,
},
- .set = sd_setbrightness,
- .get = sd_getbrightness,
+ .set_control = setbrightcont
},
- {
+[CONTRAST] = {
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -147,13 +139,11 @@ static const struct ctrl sd_ctrls[] = {
#define CONTRAST_MAX 255
.maximum = CONTRAST_MAX,
.step = 1,
-#define CONTRAST_DEF 127
- .default_value = CONTRAST_DEF,
+ .default_value = 127,
},
- .set = sd_setcontrast,
- .get = sd_getcontrast,
+ .set_control = setbrightcont
},
- {
+[COLORS] = {
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -162,13 +152,11 @@ static const struct ctrl sd_ctrls[] = {
#define COLOR_MAX 255
.maximum = COLOR_MAX,
.step = 1,
-#define COLOR_DEF 127
- .default_value = COLOR_DEF,
+ .default_value = 127
},
- .set = sd_setcolors,
- .get = sd_getcolors,
+ .set_control = setcolors
},
- {
+[WHITE_BALANCE] = {
{
.id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -176,13 +164,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 255,
.step = 1,
-#define WHITEBALANCE_DEF 4
- .default_value = WHITEBALANCE_DEF,
+ .default_value = 4,
},
- .set = sd_setwhitebalance,
- .get = sd_getwhitebalance,
+ .set_control = setwhitebalance
},
- {
+[RED_BALANCE] = {
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -190,13 +176,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 3,
.step = 1,
-#define REDBALANCE_DEF 1
- .default_value = REDBALANCE_DEF,
+ .default_value = 1,
},
- .set = sd_setredbalance,
- .get = sd_getredbalance,
+ .set_control = setredbalance
},
- {
+[BLUE_BALANCE] = {
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -204,29 +188,25 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 3,
.step = 1,
-#define BLUEBALANCE_DEF 1
- .default_value = BLUEBALANCE_DEF,
+ .default_value = 1,
},
- .set = sd_setbluebalance,
- .get = sd_getbluebalance,
+ .set_control = setbluebalance
},
- {
+[GAIN] = {
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Gain",
.minimum = 0,
-#define GAIN_MAX 255
- .maximum = GAIN_MAX,
+ .maximum = 255,
.step = 1,
#define GAIN_DEF 127
#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
.default_value = GAIN_DEF,
},
- .set = sd_setgain,
- .get = sd_getgain,
+ .set_control = setgain
},
- {
+[EXPOSURE] = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -238,10 +218,9 @@ static const struct ctrl sd_ctrls[] = {
#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */
.default_value = EXPOSURE_DEF,
},
- .set = sd_setexposure,
- .get = sd_getexposure,
+ .set_control = setexposure
},
- {
+[AUTOGAIN] = {
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -252,10 +231,9 @@ static const struct ctrl sd_ctrls[] = {
#define AUTOGAIN_DEF 1
.default_value = AUTOGAIN_DEF,
},
- .set = sd_setautogain,
- .get = sd_getautogain,
+ .set_control = setautogain,
},
- {
+[HFLIP] = {
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -263,13 +241,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define HFLIP_DEF 0
- .default_value = HFLIP_DEF,
+ .default_value = 0,
},
- .set = sd_sethflip,
- .get = sd_gethflip,
+ .set_control = sethvflip,
},
- {
+[VFLIP] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -277,11 +253,9 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define VFLIP_DEF 0
- .default_value = VFLIP_DEF,
+ .default_value = 0,
},
- .set = sd_setvflip,
- .get = sd_getvflip,
+ .set_control = sethvflip
},
};
@@ -290,21 +264,21 @@ static const struct v4l2_pix_format vga_mode[] = {
.bytesperline = 640,
.sizeimage = 640 * 480 * 3 / 8 + 590,
.colorspace = V4L2_COLORSPACE_JPEG,
- .priv = 0},
+ },
};
#define LOAD_PAGE3 255
#define END_OF_SEQUENCE 0
/* pac 7302 */
-static const __u8 init_7302[] = {
+static const u8 init_7302[] = {
/* index,value */
0xff, 0x01, /* page 1 */
0x78, 0x00, /* deactivate */
0xff, 0x01,
0x78, 0x40, /* led off */
};
-static const __u8 start_7302[] = {
+static const u8 start_7302[] = {
/* index, len, [value]* */
0xff, 1, 0x00, /* page 0 */
0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
@@ -319,7 +293,7 @@ static const __u8 start_7302[] = {
0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
0x00, 0x54, 0x11,
0x55, 1, 0x00,
- 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
+ 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
0x6b, 1, 0x00,
0x6e, 3, 0x08, 0x06, 0x00,
0x72, 3, 0x00, 0xff, 0x00,
@@ -370,7 +344,7 @@ static const __u8 start_7302[] = {
#define SKIP 0xaa
/* page 3 - the value SKIP says skip the index - see reg_w_page() */
-static const __u8 page3_7302[] = {
+static const u8 page3_7302[] = {
0x90, 0x40, 0x03, 0x00, 0xc0, 0x01, 0x14, 0x16,
0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -394,7 +368,7 @@ static const __u8 page3_7302[] = {
};
static void reg_w_buf(struct gspca_dev *gspca_dev,
- __u8 index,
+ u8 index,
const u8 *buffer, int len)
{
int ret;
@@ -410,7 +384,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, len,
500);
if (ret < 0) {
- pr_err("reg_w_buf failed index 0x%02x, error %d\n",
+ pr_err("reg_w_buf failed i: %02x error %d\n",
index, ret);
gspca_dev->usb_err = ret;
}
@@ -418,8 +392,8 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
static void reg_w(struct gspca_dev *gspca_dev,
- __u8 index,
- __u8 value)
+ u8 index,
+ u8 value)
{
int ret;
@@ -433,14 +407,14 @@ static void reg_w(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
+ pr_err("reg_w() failed i: %02x v: %02x error %d\n",
index, value, ret);
gspca_dev->usb_err = ret;
}
}
static void reg_w_seq(struct gspca_dev *gspca_dev,
- const __u8 *seq, int len)
+ const u8 *seq, int len)
{
while (--len >= 0) {
reg_w(gspca_dev, seq[0], seq[1]);
@@ -450,7 +424,7 @@ static void reg_w_seq(struct gspca_dev *gspca_dev,
/* load the beginning of a page */
static void reg_w_page(struct gspca_dev *gspca_dev,
- const __u8 *page, int len)
+ const u8 *page, int len)
{
int index;
int ret = 0;
@@ -468,7 +442,7 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
0, index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
+ pr_err("reg_w_page() failed i: %02x v: %02x error %d\n",
index, page[index], ret);
gspca_dev->usb_err = ret;
break;
@@ -478,8 +452,8 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
/* output a variable sequence */
static void reg_w_var(struct gspca_dev *gspca_dev,
- const __u8 *seq,
- const __u8 *page3, unsigned int page3_len)
+ const u8 *seq,
+ const u8 *page3, unsigned int page3_len)
{
int index, len;
@@ -493,11 +467,13 @@ static void reg_w_var(struct gspca_dev *gspca_dev,
reg_w_page(gspca_dev, page3, page3_len);
break;
default:
+#ifdef GSPCA_DEBUG
if (len > USB_BUF_SZ) {
PDEBUG(D_ERR|D_STREAM,
"Incorrect variable sequence");
return;
}
+#endif
while (len > 0) {
if (len < 8) {
reg_w_buf(gspca_dev,
@@ -524,21 +500,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
- PDEBUG(D_CONF, "Find Sensor PAC7302");
cam->cam_mode = vga_mode; /* only 640x480 */
cam->nmodes = ARRAY_SIZE(vga_mode);
- sd->brightness = BRIGHTNESS_DEF;
- sd->contrast = CONTRAST_DEF;
- sd->colors = COLOR_DEF;
- sd->white_balance = WHITEBALANCE_DEF;
- sd->red_balance = REDBALANCE_DEF;
- sd->blue_balance = BLUEBALANCE_DEF;
- sd->gain = GAIN_DEF;
- sd->exposure = EXPOSURE_DEF;
- sd->autogain = AUTOGAIN_DEF;
- sd->hflip = HFLIP_DEF;
- sd->vflip = VFLIP_DEF;
+ gspca_dev->cam.ctrls = sd->ctrls;
+
sd->flags = id->driver_info;
return 0;
}
@@ -548,19 +514,19 @@ static void setbrightcont(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
int i, v;
- static const __u8 max[10] =
+ static const u8 max[10] =
{0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
0xd4, 0xec};
- static const __u8 delta[10] =
+ static const u8 delta[10] =
{0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
0x11, 0x0b};
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
for (i = 0; i < 10; i++) {
v = max[i];
- v += (sd->brightness - BRIGHTNESS_MAX)
+ v += (sd->ctrls[BRIGHTNESS].val - BRIGHTNESS_MAX)
* 150 / BRIGHTNESS_MAX; /* 200 ? */
- v -= delta[i] * sd->contrast / CONTRAST_MAX;
+ v -= delta[i] * sd->ctrls[CONTRAST].val / CONTRAST_MAX;
if (v < 0)
v = 0;
else if (v > 0xff)
@@ -584,12 +550,11 @@ static void setcolors(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x11, 0x01);
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
for (i = 0; i < 9; i++) {
- v = a[i] * sd->colors / COLOR_MAX + b[i];
+ v = a[i] * sd->ctrls[COLORS].val / COLOR_MAX + b[i];
reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
}
reg_w(gspca_dev, 0xdc, 0x01);
- PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
}
static void setwhitebalance(struct gspca_dev *gspca_dev)
@@ -597,10 +562,9 @@ static void setwhitebalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc6, sd->white_balance);
+ reg_w(gspca_dev, 0xc6, sd->ctrls[WHITE_BALANCE].val);
reg_w(gspca_dev, 0xdc, 0x01);
- PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance);
}
static void setredbalance(struct gspca_dev *gspca_dev)
@@ -608,10 +572,9 @@ static void setredbalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc5, sd->red_balance);
+ reg_w(gspca_dev, 0xc5, sd->ctrls[RED_BALANCE].val);
reg_w(gspca_dev, 0xdc, 0x01);
- PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance);
}
static void setbluebalance(struct gspca_dev *gspca_dev)
@@ -619,10 +582,9 @@ static void setbluebalance(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
- reg_w(gspca_dev, 0xc7, sd->blue_balance);
+ reg_w(gspca_dev, 0xc7, sd->ctrls[BLUE_BALANCE].val);
reg_w(gspca_dev, 0xdc, 0x01);
- PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance);
}
static void setgain(struct gspca_dev *gspca_dev)
@@ -630,7 +592,7 @@ static void setgain(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
- reg_w(gspca_dev, 0x10, sd->gain >> 3);
+ reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3);
/* load registers to sensor (Bit 0, auto clear) */
reg_w(gspca_dev, 0x11, 0x01);
@@ -639,13 +601,13 @@ static void setgain(struct gspca_dev *gspca_dev)
static void setexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- __u8 clockdiv;
- __u16 exposure;
+ u8 clockdiv;
+ u16 exposure;
/* register 2 of frame 3 contains the clock divider configuring the
no fps according to the formula: 90 / reg. sd->exposure is the
desired exposure time in 0.5 ms. */
- clockdiv = (90 * sd->exposure + 1999) / 2000;
+ clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000;
/* Note clockdiv = 3 also works, but when running at 30 fps, depending
on the scene being recorded, the camera switches to another
@@ -664,7 +626,7 @@ static void setexposure(struct gspca_dev *gspca_dev)
/* frame exposure time in ms = 1000 * clockdiv / 90 ->
exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */
- exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv);
+ exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv);
/* 0 = use full frametime, 448 = no exposure, reverse it */
exposure = 448 - exposure;
@@ -677,15 +639,35 @@ static void setexposure(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x11, 0x01);
}
+static void setautogain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ /* when switching to autogain set defaults to make sure
+ we are on a valid point of the autogain gain /
+ exposure knee graph, and give this change time to
+ take effect before doing autogain. */
+ if (sd->ctrls[AUTOGAIN].val) {
+ sd->ctrls[EXPOSURE].val = EXPOSURE_DEF;
+ sd->ctrls[GAIN].val = GAIN_DEF;
+ sd->autogain_ignore_frames =
+ PAC_AUTOGAIN_IGNORE_FRAMES;
+ } else {
+ sd->autogain_ignore_frames = -1;
+ }
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+}
+
static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 data, hflip, vflip;
- hflip = sd->hflip;
+ hflip = sd->ctrls[HFLIP].val;
if (sd->flags & FL_HFLIP)
hflip = !hflip;
- vflip = sd->vflip;
+ vflip = sd->ctrls[VFLIP].val;
if (sd->flags & FL_VFLIP)
vflip = !vflip;
@@ -708,8 +690,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- sd->sof_read = 0;
-
reg_w_var(gspca_dev, start_7302,
page3_7302, sizeof(page3_7302));
setbrightcont(gspca_dev);
@@ -717,15 +697,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
setwhitebalance(gspca_dev);
setredbalance(gspca_dev);
setbluebalance(gspca_dev);
- setgain(gspca_dev);
- setexposure(gspca_dev);
+ setautogain(gspca_dev);
sethvflip(gspca_dev);
/* only resolution 640x480 is supported for pac7302 */
sd->sof_read = 0;
- sd->autogain_ignore_frames = 0;
- atomic_set(&sd->avg_lum, -1);
+ atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val);
/* start stream */
reg_w(gspca_dev, 0xff, 0x01);
@@ -751,8 +729,10 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x78, 0x40);
}
-/* Include pac common sof detection functions */
-#include "pac_common.h"
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt flags
+#define exp_too_high_cnt sof_read
+#include "autogain_functions.h"
static void do_autogain(struct gspca_dev *gspca_dev)
{
@@ -761,65 +741,44 @@ static void do_autogain(struct gspca_dev *gspca_dev)
int desired_lum;
const int deadzone = 30;
- if (avg_lum == -1)
+ if (sd->autogain_ignore_frames < 0)
return;
- desired_lum = 270 + sd->brightness;
-
- if (sd->autogain_ignore_frames > 0)
+ if (sd->autogain_ignore_frames > 0) {
sd->autogain_ignore_frames--;
- else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
- deadzone, GAIN_KNEE, EXPOSURE_KNEE))
+ } else {
+ desired_lum = 270 + sd->ctrls[BRIGHTNESS].val;
+
+ auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
+ deadzone, GAIN_KNEE, EXPOSURE_KNEE);
sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
}
-/* JPEG header, part 1 */
-static const unsigned char pac_jpeg_header1[] = {
- 0xff, 0xd8, /* SOI: Start of Image */
-
- 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
- 0x00, 0x11, /* length = 17 bytes (including this length field) */
- 0x08 /* Precision: 8 */
- /* 2 bytes is placed here: number of image lines */
- /* 2 bytes is placed here: samples per line */
-};
-
-/* JPEG header, continued */
-static const unsigned char pac_jpeg_header2[] = {
- 0x03, /* Number of image components: 3 */
- 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
- 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
- 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
-
- 0xff, 0xda, /* SOS: Start Of Scan */
- 0x00, 0x0c, /* length = 12 bytes (including this length field) */
- 0x03, /* number of components: 3 */
- 0x01, 0x00, /* selector 1, table 0x00 */
- 0x02, 0x11, /* selector 2, table 0x11 */
- 0x03, 0x11, /* selector 3, table 0x11 */
- 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
- 0x00 /* Successive approximation: 0 */
+/* JPEG header */
+static const u8 jpeg_header[] = {
+ 0xff, 0xd8, /* SOI: Start of Image */
+
+ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
+ 0x00, 0x11, /* length = 17 bytes (including this length field) */
+ 0x08, /* Precision: 8 */
+ 0x02, 0x80, /* height = 640 (image rotated) */
+ 0x01, 0xe0, /* width = 480 */
+ 0x03, /* Number of image components: 3 */
+ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
+ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
+ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
+
+ 0xff, 0xda, /* SOS: Start Of Scan */
+ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
+ 0x03, /* number of components: 3 */
+ 0x01, 0x00, /* selector 1, table 0x00 */
+ 0x02, 0x11, /* selector 2, table 0x11 */
+ 0x03, 0x11, /* selector 3, table 0x11 */
+ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
+ 0x00 /* Successive approximation: 0 */
};
-static void pac_start_frame(struct gspca_dev *gspca_dev,
- __u16 lines, __u16 samples_per_line)
-{
- unsigned char tmpbuf[4];
-
- gspca_frame_add(gspca_dev, FIRST_PACKET,
- pac_jpeg_header1, sizeof(pac_jpeg_header1));
-
- tmpbuf[0] = lines >> 8;
- tmpbuf[1] = lines & 0xff;
- tmpbuf[2] = samples_per_line >> 8;
- tmpbuf[3] = samples_per_line & 0xff;
-
- gspca_frame_add(gspca_dev, INTER_PACKET,
- tmpbuf, sizeof(tmpbuf));
- gspca_frame_add(gspca_dev, INTER_PACKET,
- pac_jpeg_header2, sizeof(pac_jpeg_header2));
-}
-
/* this function is run at interrupt level */
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
@@ -827,7 +786,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
{
struct sd *sd = (struct sd *) gspca_dev;
u8 *image;
- unsigned char *sof;
+ u8 *sof;
sof = pac_find_sof(&sd->sof_read, data, len);
if (sof) {
@@ -864,234 +823,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
n >= lum_offset)
atomic_set(&sd->avg_lum, data[-lum_offset] +
data[-lum_offset + 1]);
- else
- atomic_set(&sd->avg_lum, -1);
/* Start the new frame with the jpeg header */
/* The PAC7302 has the image rotated 90 degrees */
- pac_start_frame(gspca_dev,
- gspca_dev->width, gspca_dev->height);
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ jpeg_header, sizeof jpeg_header);
}
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- setbrightcont(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->brightness;
- return 0;
-}
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- setbrightcont(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->colors = val;
- if (gspca_dev->streaming)
- setcolors(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->colors;
- return 0;
-}
-
-static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->white_balance = val;
- if (gspca_dev->streaming)
- setwhitebalance(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->white_balance;
- return 0;
-}
-
-static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->red_balance = val;
- if (gspca_dev->streaming)
- setredbalance(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->red_balance;
- return 0;
-}
-
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->blue_balance = val;
- if (gspca_dev->streaming)
- setbluebalance(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->blue_balance;
- return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- setgain(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->gain;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- setexposure(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->autogain = val;
- /* when switching to autogain set defaults to make sure
- we are on a valid point of the autogain gain /
- exposure knee graph, and give this change time to
- take effect before doing autogain. */
- if (sd->autogain) {
- sd->exposure = EXPOSURE_DEF;
- sd->gain = GAIN_DEF;
- if (gspca_dev->streaming) {
- sd->autogain_ignore_frames =
- PAC_AUTOGAIN_IGNORE_FRAMES;
- setexposure(gspca_dev);
- setgain(gspca_dev);
- }
- }
-
- return gspca_dev->usb_err;
-}
-
-static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->autogain;
- return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hflip = val;
- if (gspca_dev->streaming)
- sethvflip(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->hflip;
- return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->vflip = val;
- if (gspca_dev->streaming)
- sethvflip(gspca_dev);
- return gspca_dev->usb_err;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- *val = sd->vflip;
- return 0;
-}
-
#ifdef CONFIG_VIDEO_ADV_DEBUG
static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
struct v4l2_dbg_register *reg)
{
- __u8 index;
- __u8 value;
+ u8 index;
+ u8 value;
/* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
long on the USB bus)
@@ -1103,8 +849,8 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
) {
/* Currently writing to page 0 is only supported. */
/* reg_w() only supports 8bit index */
- index = reg->reg & 0x000000ff;
- value = reg->val & 0x000000ff;
+ index = reg->reg;
+ value = reg->val;
/* Note that there shall be no access to other page
by any other function between the page swith and
@@ -1165,7 +911,7 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
/* sub-driver description for pac7302 */
static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
@@ -1187,6 +933,7 @@ static const struct sd_desc sd_desc = {
/* -- module initialisation -- */
static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x06f8, 0x3009)},
+ {USB_DEVICE(0x06f8, 0x301b)},
{USB_DEVICE(0x093a, 0x2620)},
{USB_DEVICE(0x093a, 0x2621)},
{USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
@@ -1211,7 +958,7 @@ static int sd_probe(struct usb_interface *intf,
}
static struct usb_driver sd_driver = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.id_table = device_table,
.probe = sd_probe,
.disconnect = gspca_disconnect,
diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
index 9e198b45c3c8..7e71aa2d2522 100644
--- a/drivers/media/video/gspca/sn9c20x.c
+++ b/drivers/media/video/gspca/sn9c20x.c
@@ -1,5 +1,7 @@
/*
* Sonix sn9c201 sn9c202 library
+ *
+ * Copyright (C) 2012 Jean-Francois Moine <http://moinejf.free.fr>
* Copyright (C) 2008-2009 microdia project <microdia@googlegroups.com>
* Copyright (C) 2009 Brian Johnson <brijohn@gmail.com>
*
@@ -33,8 +35,6 @@ MODULE_AUTHOR("Brian Johnson <brijohn@gmail.com>, "
MODULE_DESCRIPTION("GSPCA/SN9C20X USB Camera Driver");
MODULE_LICENSE("GPL");
-#define MODULE_NAME "sn9c20x"
-
/*
* Pixel format private data
*/
@@ -66,10 +66,37 @@ MODULE_LICENSE("GPL");
#define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */
#define FLIP_DETECT 0x4
+enum e_ctrl {
+ BRIGHTNESS,
+ CONTRAST,
+ SATURATION,
+ HUE,
+ GAMMA,
+ BLUE,
+ RED,
+ VFLIP,
+ HFLIP,
+ EXPOSURE,
+ GAIN,
+ AUTOGAIN,
+ QUALITY,
+ NCTRLS /* number of controls */
+};
+
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev;
+ struct gspca_ctrl ctrls[NCTRLS];
+
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u32 pktsz; /* (used by pkt_scan) */
+ u16 npkt;
+ s8 nchg;
+ u8 fmt; /* (used for JPEG QTAB update */
+
#define MIN_AVG_LUM 80
#define MAX_AVG_LUM 130
atomic_t avg_lum;
@@ -77,31 +104,18 @@ struct sd {
u8 older_step;
u8 exposure_step;
- u8 brightness;
- u8 contrast;
- u8 saturation;
- s16 hue;
- u8 gamma;
- u8 red;
- u8 blue;
-
- u8 hflip;
- u8 vflip;
- u8 gain;
- u16 exposure;
- u8 auto_exposure;
-
u8 i2c_addr;
u8 sensor;
u8 hstart;
u8 vstart;
u8 jpeg_hdr[JPEG_HDR_SZ];
- u8 quality;
u8 flags;
};
+static void qual_upd(struct work_struct *work);
+
struct i2c_reg_u8 {
u8 reg;
u8 val;
@@ -112,31 +126,6 @@ struct i2c_reg_u16 {
u16 val;
};
-static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_sethue(struct gspca_dev *gspca_dev, s32 val);
-static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val);
-static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setgain(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val);
-static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val);
-static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val);
-
static const struct dmi_system_id flip_dmi_table[] = {
{
.ident = "MSI MS-1034",
@@ -177,9 +166,16 @@ static const struct dmi_system_id flip_dmi_table[] = {
{}
};
-static const struct ctrl sd_ctrls[] = {
- {
-#define BRIGHTNESS_IDX 0
+static void set_cmatrix(struct gspca_dev *gspca_dev);
+static void set_gamma(struct gspca_dev *gspca_dev);
+static void set_redblue(struct gspca_dev *gspca_dev);
+static void set_hvflip(struct gspca_dev *gspca_dev);
+static void set_exposure(struct gspca_dev *gspca_dev);
+static void set_gain(struct gspca_dev *gspca_dev);
+static void set_quality(struct gspca_dev *gspca_dev);
+
+static const struct ctrl sd_ctrls[NCTRLS] = {
+[BRIGHTNESS] = {
{
.id = V4L2_CID_BRIGHTNESS,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -187,14 +183,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0xff,
.step = 1,
-#define BRIGHTNESS_DEFAULT 0x7f
- .default_value = BRIGHTNESS_DEFAULT,
+ .default_value = 0x7f
},
- .set = sd_setbrightness,
- .get = sd_getbrightness,
+ .set_control = set_cmatrix
},
- {
-#define CONTRAST_IDX 1
+[CONTRAST] = {
{
.id = V4L2_CID_CONTRAST,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -202,14 +195,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0xff,
.step = 1,
-#define CONTRAST_DEFAULT 0x7f
- .default_value = CONTRAST_DEFAULT,
+ .default_value = 0x7f
},
- .set = sd_setcontrast,
- .get = sd_getcontrast,
+ .set_control = set_cmatrix
},
- {
-#define SATURATION_IDX 2
+[SATURATION] = {
{
.id = V4L2_CID_SATURATION,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -217,14 +207,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0xff,
.step = 1,
-#define SATURATION_DEFAULT 0x7f
- .default_value = SATURATION_DEFAULT,
+ .default_value = 0x7f
},
- .set = sd_setsaturation,
- .get = sd_getsaturation,
+ .set_control = set_cmatrix
},
- {
-#define HUE_IDX 3
+[HUE] = {
{
.id = V4L2_CID_HUE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -232,14 +219,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = -180,
.maximum = 180,
.step = 1,
-#define HUE_DEFAULT 0
- .default_value = HUE_DEFAULT,
+ .default_value = 0
},
- .set = sd_sethue,
- .get = sd_gethue,
+ .set_control = set_cmatrix
},
- {
-#define GAMMA_IDX 4
+[GAMMA] = {
{
.id = V4L2_CID_GAMMA,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -247,14 +231,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0xff,
.step = 1,
-#define GAMMA_DEFAULT 0x10
- .default_value = GAMMA_DEFAULT,
+ .default_value = 0x10
},
- .set = sd_setgamma,
- .get = sd_getgamma,
+ .set_control = set_gamma
},
- {
-#define BLUE_IDX 5
+[BLUE] = {
{
.id = V4L2_CID_BLUE_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -262,14 +243,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0x7f,
.step = 1,
-#define BLUE_DEFAULT 0x28
- .default_value = BLUE_DEFAULT,
+ .default_value = 0x28
},
- .set = sd_setbluebalance,
- .get = sd_getbluebalance,
+ .set_control = set_redblue
},
- {
-#define RED_IDX 6
+[RED] = {
{
.id = V4L2_CID_RED_BALANCE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -277,14 +255,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0x7f,
.step = 1,
-#define RED_DEFAULT 0x28
- .default_value = RED_DEFAULT,
+ .default_value = 0x28
},
- .set = sd_setredbalance,
- .get = sd_getredbalance,
+ .set_control = set_redblue
},
- {
-#define HFLIP_IDX 7
+[HFLIP] = {
{
.id = V4L2_CID_HFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -292,14 +267,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define HFLIP_DEFAULT 0
- .default_value = HFLIP_DEFAULT,
+ .default_value = 0,
},
- .set = sd_sethflip,
- .get = sd_gethflip,
+ .set_control = set_hvflip
},
- {
-#define VFLIP_IDX 8
+[VFLIP] = {
{
.id = V4L2_CID_VFLIP,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -307,14 +279,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define VFLIP_DEFAULT 0
- .default_value = VFLIP_DEFAULT,
+ .default_value = 0,
},
- .set = sd_setvflip,
- .get = sd_getvflip,
+ .set_control = set_hvflip
},
- {
-#define EXPOSURE_IDX 9
+[EXPOSURE] = {
{
.id = V4L2_CID_EXPOSURE,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -322,14 +291,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 0x1780,
.step = 1,
-#define EXPOSURE_DEFAULT 0x33
- .default_value = EXPOSURE_DEFAULT,
+ .default_value = 0x33,
},
- .set = sd_setexposure,
- .get = sd_getexposure,
+ .set_control = set_exposure
},
- {
-#define GAIN_IDX 10
+[GAIN] = {
{
.id = V4L2_CID_GAIN,
.type = V4L2_CTRL_TYPE_INTEGER,
@@ -337,14 +303,11 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 28,
.step = 1,
-#define GAIN_DEFAULT 0x00
- .default_value = GAIN_DEFAULT,
+ .default_value = 0,
},
- .set = sd_setgain,
- .get = sd_getgain,
+ .set_control = set_gain
},
- {
-#define AUTOGAIN_IDX 11
+[AUTOGAIN] = {
{
.id = V4L2_CID_AUTOGAIN,
.type = V4L2_CTRL_TYPE_BOOLEAN,
@@ -352,11 +315,23 @@ static const struct ctrl sd_ctrls[] = {
.minimum = 0,
.maximum = 1,
.step = 1,
-#define AUTO_EXPOSURE_DEFAULT 1
- .default_value = AUTO_EXPOSURE_DEFAULT,
+ .default_value = 1,
+ },
+ },
+[QUALITY] = {
+ {
+ .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Compression Quality",
+#define QUALITY_MIN 50
+#define QUALITY_MAX 90
+#define QUALITY_DEF 80
+ .minimum = QUALITY_MIN,
+ .maximum = QUALITY_MAX,
+ .step = 1,
+ .default_value = QUALITY_DEF,
},
- .set = sd_setautoexposure,
- .get = sd_getautoexposure,
+ .set_control = set_quality
},
};
@@ -876,7 +851,7 @@ static u8 hv7131r_gain[] = {
};
static struct i2c_reg_u8 soi968_init[] = {
- {0x12, 0x80}, {0x0c, 0x00}, {0x0f, 0x1f},
+ {0x0c, 0x00}, {0x0f, 0x1f},
{0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00},
{0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c},
{0x37, 0x04}, {0x45, 0x04}, {0x47, 0xff},
@@ -902,7 +877,7 @@ static struct i2c_reg_u8 ov7660_init[] = {
};
static struct i2c_reg_u8 ov7670_init[] = {
- {0x12, 0x80}, {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
+ {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01},
{0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00},
{0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0},
{0xa2, 0x02}, {0x13, 0xe0}, {0x00, 0x00}, {0x10, 0x00},
@@ -959,7 +934,7 @@ static struct i2c_reg_u8 ov7670_init[] = {
};
static struct i2c_reg_u8 ov9650_init[] = {
- {0x12, 0x80}, {0x00, 0x00}, {0x01, 0x78},
+ {0x00, 0x00}, {0x01, 0x78},
{0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03},
{0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00},
{0x09, 0x01}, {0x0c, 0x00}, {0x0d, 0x00},
@@ -989,7 +964,7 @@ static struct i2c_reg_u8 ov9650_init[] = {
};
static struct i2c_reg_u8 ov9655_init[] = {
- {0x12, 0x80}, {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
+ {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba},
{0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08},
{0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d},
{0x35, 0x00}, {0x38, 0x12}, {0x0f, 0x42}, {0x39, 0x57},
@@ -1112,10 +1087,13 @@ static struct i2c_reg_u8 hv7131r_init[] = {
{0x23, 0x09}, {0x01, 0x08},
};
-static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
+static void reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
{
struct usb_device *dev = gspca_dev->dev;
int result;
+
+ if (gspca_dev->usb_err < 0)
+ return;
result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
0x00,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
@@ -1125,17 +1103,19 @@ static int reg_r(struct gspca_dev *gspca_dev, u16 reg, u16 length)
length,
500);
if (unlikely(result < 0 || result != length)) {
- pr_err("Read register failed 0x%02X\n", reg);
- return -EIO;
+ pr_err("Read register %02x failed %d\n", reg, result);
+ gspca_dev->usb_err = result;
}
- return 0;
}
-static int reg_w(struct gspca_dev *gspca_dev, u16 reg,
+static void reg_w(struct gspca_dev *gspca_dev, u16 reg,
const u8 *buffer, int length)
{
struct usb_device *dev = gspca_dev->dev;
int result;
+
+ if (gspca_dev->usb_err < 0)
+ return;
memcpy(gspca_dev->usb_buf, buffer, length);
result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
0x08,
@@ -1146,38 +1126,41 @@ static int reg_w(struct gspca_dev *gspca_dev, u16 reg,
length,
500);
if (unlikely(result < 0 || result != length)) {
- pr_err("Write register failed index 0x%02X\n", reg);
- return -EIO;
+ pr_err("Write register %02x failed %d\n", reg, result);
+ gspca_dev->usb_err = result;
}
- return 0;
}
-static int reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
+static void reg_w1(struct gspca_dev *gspca_dev, u16 reg, const u8 value)
{
- u8 data[1] = {value};
- return reg_w(gspca_dev, reg, data, 1);
+ reg_w(gspca_dev, reg, &value, 1);
}
-static int i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
+static void i2c_w(struct gspca_dev *gspca_dev, const u8 *buffer)
{
int i;
+
reg_w(gspca_dev, 0x10c0, buffer, 8);
for (i = 0; i < 5; i++) {
reg_r(gspca_dev, 0x10c0, 1);
+ if (gspca_dev->usb_err < 0)
+ return;
if (gspca_dev->usb_buf[0] & 0x04) {
- if (gspca_dev->usb_buf[0] & 0x08)
- return -EIO;
- return 0;
+ if (gspca_dev->usb_buf[0] & 0x08) {
+ pr_err("i2c_w error\n");
+ gspca_dev->usb_err = -EIO;
+ }
+ return;
}
- msleep(1);
+ msleep(10);
}
- return -EIO;
+ pr_err("i2c_w reg %02x no response\n", buffer[2]);
+/* gspca_dev->usb_err = -EIO; fixme: may occur */
}
-static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
{
struct sd *sd = (struct sd *) gspca_dev;
-
u8 row[8];
/*
@@ -1193,10 +1176,19 @@ static int i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
row[6] = 0x00;
row[7] = 0x10;
- return i2c_w(gspca_dev, row);
+ i2c_w(gspca_dev, row);
+}
+
+static void i2c_w1_buf(struct gspca_dev *gspca_dev,
+ struct i2c_reg_u8 *buf, int sz)
+{
+ while (--sz >= 0) {
+ i2c_w1(gspca_dev, buf->reg, buf->val);
+ buf++;
+ }
}
-static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
+static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
@@ -1208,16 +1200,25 @@ static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
row[0] = 0x81 | (3 << 4);
row[1] = sd->i2c_addr;
row[2] = reg;
- row[3] = (val >> 8) & 0xff;
- row[4] = val & 0xff;
+ row[3] = val >> 8;
+ row[4] = val;
row[5] = 0x00;
row[6] = 0x00;
row[7] = 0x10;
- return i2c_w(gspca_dev, row);
+ i2c_w(gspca_dev, row);
+}
+
+static void i2c_w2_buf(struct gspca_dev *gspca_dev,
+ struct i2c_reg_u16 *buf, int sz)
+{
+ while (--sz >= 0) {
+ i2c_w2(gspca_dev, buf->reg, buf->val);
+ buf++;
+ }
}
-static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
@@ -1230,19 +1231,15 @@ static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
row[5] = 0;
row[6] = 0;
row[7] = 0x10;
- if (i2c_w(gspca_dev, row) < 0)
- return -EIO;
+ i2c_w(gspca_dev, row);
row[0] = 0x81 | (1 << 4) | 0x02;
row[2] = 0;
- if (i2c_w(gspca_dev, row) < 0)
- return -EIO;
- if (reg_r(gspca_dev, 0x10c2, 5) < 0)
- return -EIO;
+ i2c_w(gspca_dev, row);
+ reg_r(gspca_dev, 0x10c2, 5);
*val = gspca_dev->usb_buf[4];
- return 0;
}
-static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 row[8];
@@ -1255,233 +1252,204 @@ static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
row[5] = 0;
row[6] = 0;
row[7] = 0x10;
- if (i2c_w(gspca_dev, row) < 0)
- return -EIO;
+ i2c_w(gspca_dev, row);
row[0] = 0x81 | (2 << 4) | 0x02;
row[2] = 0;
- if (i2c_w(gspca_dev, row) < 0)
- return -EIO;
- if (reg_r(gspca_dev, 0x10c2, 5) < 0)
- return -EIO;
+ i2c_w(gspca_dev, row);
+ reg_r(gspca_dev, 0x10c2, 5);
*val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
- return 0;
}
-static int ov9650_init_sensor(struct gspca_dev *gspca_dev)
+static void ov9650_init_sensor(struct gspca_dev *gspca_dev)
{
- int i;
u16 id;
struct sd *sd = (struct sd *) gspca_dev;
- if (i2c_r2(gspca_dev, 0x1c, &id) < 0)
- return -EINVAL;
+ i2c_r2(gspca_dev, 0x1c, &id);
+ if (gspca_dev->usb_err < 0)
+ return;
if (id != 0x7fa2) {
pr_err("sensor id for ov9650 doesn't match (0x%04x)\n", id);
- return -ENODEV;
+ gspca_dev->usb_err = -ENODEV;
+ return;
}
- for (i = 0; i < ARRAY_SIZE(ov9650_init); i++) {
- if (i2c_w1(gspca_dev, ov9650_init[i].reg,
- ov9650_init[i].val) < 0) {
- pr_err("OV9650 sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov9650_init, ARRAY_SIZE(ov9650_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV9650 sensor initialization failed\n");
sd->hstart = 1;
sd->vstart = 7;
- return 0;
}
-static int ov9655_init_sensor(struct gspca_dev *gspca_dev)
+static void ov9655_init_sensor(struct gspca_dev *gspca_dev)
{
- int i;
struct sd *sd = (struct sd *) gspca_dev;
- for (i = 0; i < ARRAY_SIZE(ov9655_init); i++) {
- if (i2c_w1(gspca_dev, ov9655_init[i].reg,
- ov9655_init[i].val) < 0) {
- pr_err("OV9655 sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov9655_init, ARRAY_SIZE(ov9655_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV9655 sensor initialization failed\n");
+
/* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
sd->hstart = 1;
sd->vstart = 2;
- return 0;
}
-static int soi968_init_sensor(struct gspca_dev *gspca_dev)
+static void soi968_init_sensor(struct gspca_dev *gspca_dev)
{
- int i;
struct sd *sd = (struct sd *) gspca_dev;
- for (i = 0; i < ARRAY_SIZE(soi968_init); i++) {
- if (i2c_w1(gspca_dev, soi968_init[i].reg,
- soi968_init[i].val) < 0) {
- pr_err("SOI968 sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, soi968_init, ARRAY_SIZE(soi968_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("SOI968 sensor initialization failed\n");
+
/* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX)
- | (1 << EXPOSURE_IDX);
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP)
+ | (1 << EXPOSURE);
sd->hstart = 60;
sd->vstart = 11;
- return 0;
}
-static int ov7660_init_sensor(struct gspca_dev *gspca_dev)
+static void ov7660_init_sensor(struct gspca_dev *gspca_dev)
{
- int i;
struct sd *sd = (struct sd *) gspca_dev;
- for (i = 0; i < ARRAY_SIZE(ov7660_init); i++) {
- if (i2c_w1(gspca_dev, ov7660_init[i].reg,
- ov7660_init[i].val) < 0) {
- pr_err("OV7660 sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov7660_init, ARRAY_SIZE(ov7660_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV7660 sensor initialization failed\n");
sd->hstart = 3;
sd->vstart = 3;
- return 0;
}
-static int ov7670_init_sensor(struct gspca_dev *gspca_dev)
+static void ov7670_init_sensor(struct gspca_dev *gspca_dev)
{
- int i;
struct sd *sd = (struct sd *) gspca_dev;
- for (i = 0; i < ARRAY_SIZE(ov7670_init); i++) {
- if (i2c_w1(gspca_dev, ov7670_init[i].reg,
- ov7670_init[i].val) < 0) {
- pr_err("OV7670 sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w1(gspca_dev, 0x12, 0x80); /* sensor reset */
+ msleep(200);
+ i2c_w1_buf(gspca_dev, ov7670_init, ARRAY_SIZE(ov7670_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("OV7670 sensor initialization failed\n");
+
/* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
sd->hstart = 0;
sd->vstart = 1;
- return 0;
}
-static int mt9v_init_sensor(struct gspca_dev *gspca_dev)
+static void mt9v_init_sensor(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int i;
u16 value;
- int ret;
sd->i2c_addr = 0x5d;
- ret = i2c_r2(gspca_dev, 0xff, &value);
- if ((ret == 0) && (value == 0x8243)) {
- for (i = 0; i < ARRAY_SIZE(mt9v011_init); i++) {
- if (i2c_w2(gspca_dev, mt9v011_init[i].reg,
- mt9v011_init[i].val) < 0) {
- pr_err("MT9V011 sensor initialization failed\n");
- return -ENODEV;
- }
+ i2c_r2(gspca_dev, 0xff, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x8243) {
+ i2c_w2_buf(gspca_dev, mt9v011_init, ARRAY_SIZE(mt9v011_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V011 sensor initialization failed\n");
+ return;
}
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V011;
pr_info("MT9V011 sensor detected\n");
- return 0;
+ return;
}
+ gspca_dev->usb_err = 0;
sd->i2c_addr = 0x5c;
i2c_w2(gspca_dev, 0x01, 0x0004);
- ret = i2c_r2(gspca_dev, 0xff, &value);
- if ((ret == 0) && (value == 0x823a)) {
- for (i = 0; i < ARRAY_SIZE(mt9v111_init); i++) {
- if (i2c_w2(gspca_dev, mt9v111_init[i].reg,
- mt9v111_init[i].val) < 0) {
- pr_err("MT9V111 sensor initialization failed\n");
- return -ENODEV;
- }
+ i2c_r2(gspca_dev, 0xff, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x823a) {
+ i2c_w2_buf(gspca_dev, mt9v111_init, ARRAY_SIZE(mt9v111_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V111 sensor initialization failed\n");
+ return;
}
- gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX)
- | (1 << AUTOGAIN_IDX)
- | (1 << GAIN_IDX);
+ gspca_dev->ctrl_dis = (1 << EXPOSURE)
+ | (1 << AUTOGAIN)
+ | (1 << GAIN);
sd->hstart = 2;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V111;
pr_info("MT9V111 sensor detected\n");
- return 0;
+ return;
}
+ gspca_dev->usb_err = 0;
sd->i2c_addr = 0x5d;
- ret = i2c_w2(gspca_dev, 0xf0, 0x0000);
- if (ret < 0) {
+ i2c_w2(gspca_dev, 0xf0, 0x0000);
+ if (gspca_dev->usb_err < 0) {
+ gspca_dev->usb_err = 0;
sd->i2c_addr = 0x48;
i2c_w2(gspca_dev, 0xf0, 0x0000);
}
- ret = i2c_r2(gspca_dev, 0x00, &value);
- if ((ret == 0) && (value == 0x1229)) {
- for (i = 0; i < ARRAY_SIZE(mt9v112_init); i++) {
- if (i2c_w2(gspca_dev, mt9v112_init[i].reg,
- mt9v112_init[i].val) < 0) {
- pr_err("MT9V112 sensor initialization failed\n");
- return -ENODEV;
- }
+ i2c_r2(gspca_dev, 0x00, &value);
+ if (gspca_dev->usb_err >= 0
+ && value == 0x1229) {
+ i2c_w2_buf(gspca_dev, mt9v112_init, ARRAY_SIZE(mt9v112_init));
+ if (gspca_dev->usb_err < 0) {
+ pr_err("MT9V112 sensor initialization failed\n");
+ return;
}
sd->hstart = 6;
sd->vstart = 2;
sd->sensor = SENSOR_MT9V112;
pr_info("MT9V112 sensor detected\n");
- return 0;
+ return;
}
- return -ENODEV;
+ gspca_dev->usb_err = -ENODEV;
}
-static int mt9m112_init_sensor(struct gspca_dev *gspca_dev)
+static void mt9m112_init_sensor(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int i;
- for (i = 0; i < ARRAY_SIZE(mt9m112_init); i++) {
- if (i2c_w2(gspca_dev, mt9m112_init[i].reg,
- mt9m112_init[i].val) < 0) {
- pr_err("MT9M112 sensor initialization failed\n");
- return -ENODEV;
- }
- }
- gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)
- | (1 << GAIN_IDX);
+
+ i2c_w2_buf(gspca_dev, mt9m112_init, ARRAY_SIZE(mt9m112_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M112 sensor initialization failed\n");
+
+ gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
+ | (1 << GAIN);
sd->hstart = 0;
sd->vstart = 2;
- return 0;
}
-static int mt9m111_init_sensor(struct gspca_dev *gspca_dev)
+static void mt9m111_init_sensor(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int i;
- for (i = 0; i < ARRAY_SIZE(mt9m111_init); i++) {
- if (i2c_w2(gspca_dev, mt9m111_init[i].reg,
- mt9m111_init[i].val) < 0) {
- pr_err("MT9M111 sensor initialization failed\n");
- return -ENODEV;
- }
- }
- gspca_dev->ctrl_dis = (1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)
- | (1 << GAIN_IDX);
+
+ i2c_w2_buf(gspca_dev, mt9m111_init, ARRAY_SIZE(mt9m111_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M111 sensor initialization failed\n");
+
+ gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN)
+ | (1 << GAIN);
sd->hstart = 0;
sd->vstart = 2;
- return 0;
}
-static int mt9m001_init_sensor(struct gspca_dev *gspca_dev)
+static void mt9m001_init_sensor(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- int i;
u16 id;
- if (i2c_r2(gspca_dev, 0x00, &id) < 0)
- return -EINVAL;
+ i2c_r2(gspca_dev, 0x00, &id);
+ if (gspca_dev->usb_err < 0)
+ return;
/* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */
switch (id) {
@@ -1494,85 +1462,78 @@ static int mt9m001_init_sensor(struct gspca_dev *gspca_dev)
break;
default:
pr_err("No MT9M001 chip detected, ID = %x\n\n", id);
- return -ENODEV;
+ gspca_dev->usb_err = -ENODEV;
+ return;
}
- for (i = 0; i < ARRAY_SIZE(mt9m001_init); i++) {
- if (i2c_w2(gspca_dev, mt9m001_init[i].reg,
- mt9m001_init[i].val) < 0) {
- pr_err("MT9M001 sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w2_buf(gspca_dev, mt9m001_init, ARRAY_SIZE(mt9m001_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("MT9M001 sensor initialization failed\n");
+
/* disable hflip and vflip */
- gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX);
+ gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP);
sd->hstart = 1;
sd->vstart = 1;
- return 0;
}
-static int hv7131r_init_sensor(struct gspca_dev *gspca_dev)
+static void hv7131r_init_sensor(struct gspca_dev *gspca_dev)
{
- int i;
struct sd *sd = (struct sd *) gspca_dev;
- for (i = 0; i < ARRAY_SIZE(hv7131r_init); i++) {
- if (i2c_w1(gspca_dev, hv7131r_init[i].reg,
- hv7131r_init[i].val) < 0) {
- pr_err("HV7131R Sensor initialization failed\n");
- return -ENODEV;
- }
- }
+ i2c_w1_buf(gspca_dev, hv7131r_init, ARRAY_SIZE(hv7131r_init));
+ if (gspca_dev->usb_err < 0)
+ pr_err("HV7131R Sensor initialization failed\n");
+
sd->hstart = 0;
sd->vstart = 1;
- return 0;
}
-static int set_cmatrix(struct gspca_dev *gspca_dev)
+static void set_cmatrix(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- s32 hue_coord, hue_index = 180 + sd->hue;
+ int satur;
+ s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val;
u8 cmatrix[21];
memset(cmatrix, 0, sizeof cmatrix);
- cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26;
+ cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26;
cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
- cmatrix[18] = sd->brightness - 0x80;
+ cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80;
- hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8;
+ satur = sd->ctrls[SATURATION].val;
+ hue_coord = (hsv_red_x[hue_index] * satur) >> 8;
cmatrix[6] = hue_coord;
cmatrix[7] = (hue_coord >> 8) & 0x0f;
- hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8;
+ hue_coord = (hsv_red_y[hue_index] * satur) >> 8;
cmatrix[8] = hue_coord;
cmatrix[9] = (hue_coord >> 8) & 0x0f;
- hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8;
+ hue_coord = (hsv_green_x[hue_index] * satur) >> 8;
cmatrix[10] = hue_coord;
cmatrix[11] = (hue_coord >> 8) & 0x0f;
- hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8;
+ hue_coord = (hsv_green_y[hue_index] * satur) >> 8;
cmatrix[12] = hue_coord;
cmatrix[13] = (hue_coord >> 8) & 0x0f;
- hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8;
+ hue_coord = (hsv_blue_x[hue_index] * satur) >> 8;
cmatrix[14] = hue_coord;
cmatrix[15] = (hue_coord >> 8) & 0x0f;
- hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8;
+ hue_coord = (hsv_blue_y[hue_index] * satur) >> 8;
cmatrix[16] = hue_coord;
cmatrix[17] = (hue_coord >> 8) & 0x0f;
- return reg_w(gspca_dev, 0x10e1, cmatrix, 21);
+ reg_w(gspca_dev, 0x10e1, cmatrix, 21);
}
-static int set_gamma(struct gspca_dev *gspca_dev)
+static void set_gamma(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 gamma[17];
- u8 gval = sd->gamma * 0xb8 / 0x100;
-
+ u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100;
gamma[0] = 0x0a;
gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8);
@@ -1592,29 +1553,29 @@ static int set_gamma(struct gspca_dev *gspca_dev)
gamma[15] = 0xea + (gval * (0xf9 - 0xea) / 0xb8);
gamma[16] = 0xf5;
- return reg_w(gspca_dev, 0x1190, gamma, 17);
+ reg_w(gspca_dev, 0x1190, gamma, 17);
}
-static int set_redblue(struct gspca_dev *gspca_dev)
+static void set_redblue(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- reg_w1(gspca_dev, 0x118c, sd->red);
- reg_w1(gspca_dev, 0x118f, sd->blue);
- return 0;
+
+ reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val);
+ reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val);
}
-static int set_hvflip(struct gspca_dev *gspca_dev)
+static void set_hvflip(struct gspca_dev *gspca_dev)
{
u8 value, tslb, hflip, vflip;
u16 value2;
struct sd *sd = (struct sd *) gspca_dev;
if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) {
- hflip = !sd->hflip;
- vflip = !sd->vflip;
+ hflip = !sd->ctrls[HFLIP].val;
+ vflip = !sd->ctrls[VFLIP].val;
} else {
- hflip = sd->hflip;
- vflip = sd->vflip;
+ hflip = sd->ctrls[HFLIP].val;
+ vflip = sd->ctrls[VFLIP].val;
}
switch (sd->sensor) {
@@ -1625,8 +1586,9 @@ static int set_hvflip(struct gspca_dev *gspca_dev)
if (vflip) {
value |= 0x10;
sd->vstart = 2;
- } else
+ } else {
sd->vstart = 3;
+ }
reg_w1(gspca_dev, 0x1182, sd->vstart);
i2c_w1(gspca_dev, 0x1e, value);
break;
@@ -1674,13 +1636,15 @@ static int set_hvflip(struct gspca_dev *gspca_dev)
i2c_w1(gspca_dev, 0x01, value);
break;
}
- return 0;
}
-static int set_exposure(struct gspca_dev *gspca_dev)
+static void set_exposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e};
+ int expo;
+
+ expo = sd->ctrls[EXPOSURE].val;
switch (sd->sensor) {
case SENSOR_OV7660:
case SENSOR_OV7670:
@@ -1688,35 +1652,37 @@ static int set_exposure(struct gspca_dev *gspca_dev)
case SENSOR_OV9650:
exp[0] |= (3 << 4);
exp[2] = 0x2d;
- exp[3] = sd->exposure & 0xff;
- exp[4] = sd->exposure >> 8;
+ exp[3] = expo;
+ exp[4] = expo >> 8;
break;
case SENSOR_MT9M001:
case SENSOR_MT9V112:
case SENSOR_MT9V011:
exp[0] |= (3 << 4);
exp[2] = 0x09;
- exp[3] = sd->exposure >> 8;
- exp[4] = sd->exposure & 0xff;
+ exp[3] = expo >> 8;
+ exp[4] = expo;
break;
case SENSOR_HV7131R:
exp[0] |= (4 << 4);
exp[2] = 0x25;
- exp[3] = (sd->exposure >> 5) & 0xff;
- exp[4] = (sd->exposure << 3) & 0xff;
+ exp[3] = expo >> 5;
+ exp[4] = expo << 3;
exp[5] = 0;
break;
default:
- return 0;
+ return;
}
i2c_w(gspca_dev, exp);
- return 0;
}
-static int set_gain(struct gspca_dev *gspca_dev)
+static void set_gain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d};
+ int g;
+
+ g = sd->ctrls[GAIN].val;
switch (sd->sensor) {
case SENSOR_OV7660:
case SENSOR_OV7670:
@@ -1724,238 +1690,50 @@ static int set_gain(struct gspca_dev *gspca_dev)
case SENSOR_OV9655:
case SENSOR_OV9650:
gain[0] |= (2 << 4);
- gain[3] = ov_gain[sd->gain];
+ gain[3] = ov_gain[g];
break;
case SENSOR_MT9V011:
gain[0] |= (3 << 4);
gain[2] = 0x35;
- gain[3] = micron1_gain[sd->gain] >> 8;
- gain[4] = micron1_gain[sd->gain] & 0xff;
+ gain[3] = micron1_gain[g] >> 8;
+ gain[4] = micron1_gain[g];
break;
case SENSOR_MT9V112:
gain[0] |= (3 << 4);
gain[2] = 0x2f;
- gain[3] = micron1_gain[sd->gain] >> 8;
- gain[4] = micron1_gain[sd->gain] & 0xff;
+ gain[3] = micron1_gain[g] >> 8;
+ gain[4] = micron1_gain[g];
break;
case SENSOR_MT9M001:
gain[0] |= (3 << 4);
gain[2] = 0x2f;
- gain[3] = micron2_gain[sd->gain] >> 8;
- gain[4] = micron2_gain[sd->gain] & 0xff;
+ gain[3] = micron2_gain[g] >> 8;
+ gain[4] = micron2_gain[g];
break;
case SENSOR_HV7131R:
gain[0] |= (2 << 4);
gain[2] = 0x30;
- gain[3] = hv7131r_gain[sd->gain];
+ gain[3] = hv7131r_gain[g];
break;
default:
- return 0;
+ return;
}
i2c_w(gspca_dev, gain);
- return 0;
-}
-
-static int sd_setbrightness(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->brightness = val;
- if (gspca_dev->streaming)
- return set_cmatrix(gspca_dev);
- return 0;
-}
-
-static int sd_getbrightness(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->brightness;
- return 0;
-}
-
-
-static int sd_setcontrast(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->contrast = val;
- if (gspca_dev->streaming)
- return set_cmatrix(gspca_dev);
- return 0;
-}
-
-static int sd_getcontrast(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->contrast;
- return 0;
-}
-
-static int sd_setsaturation(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->saturation = val;
- if (gspca_dev->streaming)
- return set_cmatrix(gspca_dev);
- return 0;
}
-static int sd_getsaturation(struct gspca_dev *gspca_dev, s32 *val)
+static void set_quality(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->saturation;
- return 0;
-}
-
-static int sd_sethue(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hue = val;
- if (gspca_dev->streaming)
- return set_cmatrix(gspca_dev);
- return 0;
-}
-
-static int sd_gethue(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->hue;
- return 0;
-}
-
-static int sd_setgamma(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gamma = val;
- if (gspca_dev->streaming)
- return set_gamma(gspca_dev);
- return 0;
-}
-
-static int sd_getgamma(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->gamma;
- return 0;
-}
-
-static int sd_setredbalance(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->red = val;
- if (gspca_dev->streaming)
- return set_redblue(gspca_dev);
- return 0;
-}
-
-static int sd_getredbalance(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->red;
- return 0;
-}
-
-static int sd_setbluebalance(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->blue = val;
- if (gspca_dev->streaming)
- return set_redblue(gspca_dev);
- return 0;
-}
-
-static int sd_getbluebalance(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->blue;
- return 0;
-}
-
-static int sd_sethflip(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->hflip = val;
- if (gspca_dev->streaming)
- return set_hvflip(gspca_dev);
- return 0;
-}
-
-static int sd_gethflip(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->hflip;
- return 0;
-}
-
-static int sd_setvflip(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->vflip = val;
- if (gspca_dev->streaming)
- return set_hvflip(gspca_dev);
- return 0;
-}
-
-static int sd_getvflip(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->vflip;
- return 0;
-}
-
-static int sd_setexposure(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->exposure = val;
- if (gspca_dev->streaming)
- return set_exposure(gspca_dev);
- return 0;
-}
-
-static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->exposure;
- return 0;
-}
-
-static int sd_setgain(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
-
- sd->gain = val;
- if (gspca_dev->streaming)
- return set_gain(gspca_dev);
- return 0;
-}
-
-static int sd_getgain(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->gain;
- return 0;
-}
-
-static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- sd->auto_exposure = val;
- return 0;
-}
-static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val)
-{
- struct sd *sd = (struct sd *) gspca_dev;
- *val = sd->auto_exposure;
- return 0;
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+ reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */
+ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64);
+ reg_w(gspca_dev, 0x1140, &sd->jpeg_hdr[JPEG_QT1_OFFSET], 64);
+ reg_w1(gspca_dev, 0x1061, 0x03); /* restart transfer */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
+ sd->fmt ^= 0x0c; /* invert QTAB use + write */
+ reg_w1(gspca_dev, 0x10e0, sd->fmt);
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -1963,28 +1741,26 @@ static int sd_dbg_g_register(struct gspca_dev *gspca_dev,
struct v4l2_dbg_register *reg)
{
struct sd *sd = (struct sd *) gspca_dev;
+
switch (reg->match.type) {
case V4L2_CHIP_MATCH_HOST:
if (reg->match.addr != 0)
return -EINVAL;
if (reg->reg < 0x1000 || reg->reg > 0x11ff)
return -EINVAL;
- if (reg_r(gspca_dev, reg->reg, 1) < 0)
- return -EINVAL;
+ reg_r(gspca_dev, reg->reg, 1);
reg->val = gspca_dev->usb_buf[0];
- return 0;
+ return gspca_dev->usb_err;
case V4L2_CHIP_MATCH_I2C_ADDR:
if (reg->match.addr != sd->i2c_addr)
return -EINVAL;
if (sd->sensor >= SENSOR_MT9V011 &&
sd->sensor <= SENSOR_MT9M112) {
- if (i2c_r2(gspca_dev, reg->reg, (u16 *)&reg->val) < 0)
- return -EINVAL;
+ i2c_r2(gspca_dev, reg->reg, (u16 *) &reg->val);
} else {
- if (i2c_r1(gspca_dev, reg->reg, (u8 *)&reg->val) < 0)
- return -EINVAL;
+ i2c_r1(gspca_dev, reg->reg, (u8 *) &reg->val);
}
- return 0;
+ return gspca_dev->usb_err;
}
return -EINVAL;
}
@@ -1993,27 +1769,25 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
struct v4l2_dbg_register *reg)
{
struct sd *sd = (struct sd *) gspca_dev;
+
switch (reg->match.type) {
case V4L2_CHIP_MATCH_HOST:
if (reg->match.addr != 0)
return -EINVAL;
if (reg->reg < 0x1000 || reg->reg > 0x11ff)
return -EINVAL;
- if (reg_w1(gspca_dev, reg->reg, reg->val) < 0)
- return -EINVAL;
- return 0;
+ reg_w1(gspca_dev, reg->reg, reg->val);
+ return gspca_dev->usb_err;
case V4L2_CHIP_MATCH_I2C_ADDR:
if (reg->match.addr != sd->i2c_addr)
return -EINVAL;
if (sd->sensor >= SENSOR_MT9V011 &&
sd->sensor <= SENSOR_MT9M112) {
- if (i2c_w2(gspca_dev, reg->reg, reg->val) < 0)
- return -EINVAL;
+ i2c_w2(gspca_dev, reg->reg, reg->val);
} else {
- if (i2c_w1(gspca_dev, reg->reg, reg->val) < 0)
- return -EINVAL;
+ i2c_w1(gspca_dev, reg->reg, reg->val);
}
- return 0;
+ return gspca_dev->usb_err;
}
return -EINVAL;
}
@@ -2050,9 +1824,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
cam = &gspca_dev->cam;
cam->needs_full_bandwidth = 1;
- sd->sensor = (id->driver_info >> 8) & 0xff;
- sd->i2c_addr = id->driver_info & 0xff;
- sd->flags = (id->driver_info >> 16) & 0xff;
+ sd->sensor = id->driver_info >> 8;
+ sd->i2c_addr = id->driver_info;
+ sd->flags = id->driver_info >> 16;
switch (sd->sensor) {
case SENSOR_MT9M112:
@@ -2076,21 +1850,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->older_step = 0;
sd->exposure_step = 16;
- sd->brightness = BRIGHTNESS_DEFAULT;
- sd->contrast = CONTRAST_DEFAULT;
- sd->saturation = SATURATION_DEFAULT;
- sd->hue = HUE_DEFAULT;
- sd->gamma = GAMMA_DEFAULT;
- sd->red = RED_DEFAULT;
- sd->blue = BLUE_DEFAULT;
+ gspca_dev->cam.ctrls = sd->ctrls;
- sd->hflip = HFLIP_DEFAULT;
- sd->vflip = VFLIP_DEFAULT;
- sd->exposure = EXPOSURE_DEFAULT;
- sd->gain = GAIN_DEFAULT;
- sd->auto_exposure = AUTO_EXPOSURE_DEFAULT;
-
- sd->quality = 95;
+ INIT_WORK(&sd->work, qual_upd);
return 0;
}
@@ -2105,9 +1867,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
for (i = 0; i < ARRAY_SIZE(bridge_init); i++) {
value = bridge_init[i][1];
- if (reg_w(gspca_dev, bridge_init[i][0], &value, 1) < 0) {
+ reg_w(gspca_dev, bridge_init[i][0], &value, 1);
+ if (gspca_dev->usb_err < 0) {
pr_err("Device initialization failed\n");
- return -ENODEV;
+ return gspca_dev->usb_err;
}
}
@@ -2116,72 +1879,85 @@ static int sd_init(struct gspca_dev *gspca_dev)
else
reg_w1(gspca_dev, 0x1006, 0x20);
- if (reg_w(gspca_dev, 0x10c0, i2c_init, 9) < 0) {
+ reg_w(gspca_dev, 0x10c0, i2c_init, 9);
+ if (gspca_dev->usb_err < 0) {
pr_err("Device initialization failed\n");
- return -ENODEV;
+ return gspca_dev->usb_err;
}
switch (sd->sensor) {
case SENSOR_OV9650:
- if (ov9650_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ ov9650_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("OV9650 sensor detected\n");
break;
case SENSOR_OV9655:
- if (ov9655_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ ov9655_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("OV9655 sensor detected\n");
break;
case SENSOR_SOI968:
- if (soi968_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ soi968_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("SOI968 sensor detected\n");
break;
case SENSOR_OV7660:
- if (ov7660_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ ov7660_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("OV7660 sensor detected\n");
break;
case SENSOR_OV7670:
- if (ov7670_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ ov7670_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("OV7670 sensor detected\n");
break;
case SENSOR_MT9VPRB:
- if (mt9v_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ mt9v_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
+ pr_info("MT9VPRB sensor detected\n");
break;
case SENSOR_MT9M111:
- if (mt9m111_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ mt9m111_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("MT9M111 sensor detected\n");
break;
case SENSOR_MT9M112:
- if (mt9m112_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ mt9m112_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("MT9M112 sensor detected\n");
break;
case SENSOR_MT9M001:
- if (mt9m001_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ mt9m001_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
break;
case SENSOR_HV7131R:
- if (hv7131r_init_sensor(gspca_dev) < 0)
- return -ENODEV;
+ hv7131r_init_sensor(gspca_dev);
+ if (gspca_dev->usb_err < 0)
+ break;
pr_info("HV7131R sensor detected\n");
break;
default:
- pr_info("Unsupported Sensor\n");
- return -ENODEV;
+ pr_err("Unsupported sensor\n");
+ gspca_dev->usb_err = -ENODEV;
}
- return 0;
+ return gspca_dev->usb_err;
}
static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
{
struct sd *sd = (struct sd *) gspca_dev;
u8 value;
+
switch (sd->sensor) {
case SENSOR_SOI968:
if (mode & MODE_SXGA) {
@@ -2264,6 +2040,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev)
break;
default: /* >= 640x480 */
gspca_dev->alt = 9;
+ break;
}
}
@@ -2290,14 +2067,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
jpeg_define(sd->jpeg_hdr, height, width,
0x21);
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
if (mode & MODE_RAW)
fmt = 0x2d;
else if (mode & MODE_JPEG)
- fmt = 0x2c;
+ fmt = 0x24;
else
fmt = 0x2f; /* YUV 420 */
+ sd->fmt = fmt;
switch (mode & SCALE_MASK) {
case SCALE_1280x1024:
@@ -2334,18 +2112,37 @@ static int sd_start(struct gspca_dev *gspca_dev)
set_hvflip(gspca_dev);
reg_w1(gspca_dev, 0x1007, 0x20);
+ reg_w1(gspca_dev, 0x1061, 0x03);
+
+ /* if JPEG, prepare the compression quality update */
+ if (mode & MODE_JPEG) {
+ sd->pktsz = sd->npkt = 0;
+ sd->nchg = 0;
+ sd->work_thread =
+ create_singlethread_workqueue(KBUILD_MODNAME);
+ }
- reg_r(gspca_dev, 0x1061, 1);
- reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] | 0x02);
- return 0;
+ return gspca_dev->usb_err;
}
static void sd_stopN(struct gspca_dev *gspca_dev)
{
reg_w1(gspca_dev, 0x1007, 0x00);
+ reg_w1(gspca_dev, 0x1061, 0x01);
+}
+
+/* called on streamoff with alt==0 and on disconnect */
+/* the usb_lock is held at entry - restore on exit */
+static void sd_stop0(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
- reg_r(gspca_dev, 0x1061, 1);
- reg_w1(gspca_dev, 0x1061, gspca_dev->usb_buf[0] & ~0x02);
+ if (sd->work_thread != NULL) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(sd->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ sd->work_thread = NULL;
+ }
}
static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
@@ -2359,15 +2156,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
* and exposure steps
*/
if (avg_lum < MIN_AVG_LUM) {
- if (sd->exposure > 0x1770)
+ if (sd->ctrls[EXPOSURE].val > 0x1770)
return;
- new_exp = sd->exposure + sd->exposure_step;
+ new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step;
if (new_exp > 0x1770)
new_exp = 0x1770;
if (new_exp < 0x10)
new_exp = 0x10;
- sd->exposure = new_exp;
+ sd->ctrls[EXPOSURE].val = new_exp;
set_exposure(gspca_dev);
sd->older_step = sd->old_step;
@@ -2379,14 +2176,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum)
sd->exposure_step += 2;
}
if (avg_lum > MAX_AVG_LUM) {
- if (sd->exposure < 0x10)
+ if (sd->ctrls[EXPOSURE].val < 0x10)
return;
- new_exp = sd->exposure - sd->exposure_step;
+ new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step;
if (new_exp > 0x1700)
new_exp = 0x1770;
if (new_exp < 0x10)
new_exp = 0x10;
- sd->exposure = new_exp;
+ sd->ctrls[EXPOSURE].val = new_exp;
set_exposure(gspca_dev);
sd->older_step = sd->old_step;
sd->old_step = 0;
@@ -2403,14 +2200,14 @@ static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
struct sd *sd = (struct sd *) gspca_dev;
if (avg_lum < MIN_AVG_LUM) {
- if (sd->gain + 1 <= 28) {
- sd->gain++;
+ if (sd->ctrls[GAIN].val + 1 <= 28) {
+ sd->ctrls[GAIN].val++;
set_gain(gspca_dev);
}
}
if (avg_lum > MAX_AVG_LUM) {
- if (sd->gain > 0) {
- sd->gain--;
+ if (sd->ctrls[GAIN].val > 0) {
+ sd->ctrls[GAIN].val--;
set_gain(gspca_dev);
}
}
@@ -2421,7 +2218,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int avg_lum;
- if (!sd->auto_exposure)
+ if (!sd->ctrls[AUTOGAIN].val)
return;
avg_lum = atomic_read(&sd->avg_lum);
@@ -2431,33 +2228,92 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
do_autoexposure(gspca_dev, avg_lum);
}
+/* JPEG quality update */
+/* This function is executed from a work queue. */
+static void qual_upd(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+
+ mutex_lock(&gspca_dev->usb_lock);
+ PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val);
+ set_quality(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* interrupt packet */
int len) /* interrupt packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
- int ret = -EINVAL;
+
if (!(sd->flags & HAS_NO_BUTTON) && len == 1) {
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
- input_sync(gspca_dev->input_dev);
- input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
- input_sync(gspca_dev->input_dev);
- ret = 0;
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
+ input_sync(gspca_dev->input_dev);
+ input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
+ input_sync(gspca_dev->input_dev);
+ return 0;
}
- return ret;
+ return -EINVAL;
}
#endif
+/* check the JPEG compression */
+static void transfer_check(struct gspca_dev *gspca_dev,
+ u8 *data)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int new_qual, r;
+
+ new_qual = 0;
+
+ /* if USB error, discard the frame and decrease the quality */
+ if (data[6] & 0x08) { /* USB FIFO full */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ new_qual = -5;
+ } else {
+
+ /* else, compute the filling rate and a new JPEG quality */
+ r = (sd->pktsz * 100) /
+ (sd->npkt *
+ gspca_dev->urb[0]->iso_frame_desc[0].length);
+ if (r >= 85)
+ new_qual = -3;
+ else if (r < 75)
+ new_qual = 2;
+ }
+ if (new_qual != 0) {
+ sd->nchg += new_qual;
+ if (sd->nchg < -6 || sd->nchg >= 12) {
+ sd->nchg = 0;
+ new_qual += sd->ctrls[QUALITY].val;
+ if (new_qual < QUALITY_MIN)
+ new_qual = QUALITY_MIN;
+ else if (new_qual > QUALITY_MAX)
+ new_qual = QUALITY_MAX;
+ if (new_qual != sd->ctrls[QUALITY].val) {
+ sd->ctrls[QUALITY].val = new_qual;
+ queue_work(sd->work_thread, &sd->work);
+ }
+ }
+ } else {
+ sd->nchg = 0;
+ }
+ sd->pktsz = sd->npkt = 0;
+}
+
static void sd_pkt_scan(struct gspca_dev *gspca_dev,
u8 *data, /* isoc packet */
int len) /* iso packet length */
{
struct sd *sd = (struct sd *) gspca_dev;
- int avg_lum;
+ int avg_lum, is_jpeg;
static u8 frame_header[] =
{0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
- if (len == 64 && memcmp(data, frame_header, 6) == 0) {
+
+ is_jpeg = (sd->fmt & 0x03) == 0;
+ if (len >= 64 && memcmp(data, frame_header, 6) == 0) {
avg_lum = ((data[35] >> 2) & 3) |
(data[20] << 2) |
(data[19] << 10);
@@ -2484,12 +2340,18 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
(data[33] << 10);
avg_lum >>= 9;
atomic_set(&sd->avg_lum, avg_lum);
+
+ if (is_jpeg)
+ transfer_check(gspca_dev, data);
+
gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
- return;
+ len -= 64;
+ if (len == 0)
+ return;
+ data += 64;
}
if (gspca_dev->last_packet_type == LAST_PACKET) {
- if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
- & MODE_JPEG) {
+ if (is_jpeg) {
gspca_frame_add(gspca_dev, FIRST_PACKET,
sd->jpeg_hdr, JPEG_HDR_SZ);
gspca_frame_add(gspca_dev, INTER_PACKET,
@@ -2499,13 +2361,18 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
data, len);
}
} else {
+ /* if JPEG, count the packets and their size */
+ if (is_jpeg) {
+ sd->npkt++;
+ sd->pktsz += len;
+ }
gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
}
}
/* sub-driver description */
static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
@@ -2513,6 +2380,7 @@ static const struct sd_desc sd_desc = {
.isoc_init = sd_isoc_init,
.start = sd_start,
.stopN = sd_stopN,
+ .stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
.int_pkt_scan = sd_int_pkt_scan,
@@ -2581,7 +2449,7 @@ static int sd_probe(struct usb_interface *intf,
}
static struct usb_driver sd_driver = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.id_table = device_table,
.probe = sd_probe,
.disconnect = gspca_disconnect,
diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
index 0c9e6ddabd2c..db8e5084df06 100644
--- a/drivers/media/video/gspca/sonixj.c
+++ b/drivers/media/video/gspca/sonixj.c
@@ -39,7 +39,9 @@ enum e_ctrl {
BLUE,
RED,
GAMMA,
+ EXPOSURE,
AUTOGAIN,
+ GAIN,
HFLIP,
VFLIP,
SHARPNESS,
@@ -131,7 +133,9 @@ static void setcontrast(struct gspca_dev *gspca_dev);
static void setcolors(struct gspca_dev *gspca_dev);
static void setredblue(struct gspca_dev *gspca_dev);
static void setgamma(struct gspca_dev *gspca_dev);
-static void setautogain(struct gspca_dev *gspca_dev);
+static void setexposure(struct gspca_dev *gspca_dev);
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+static void setgain(struct gspca_dev *gspca_dev);
static void sethvflip(struct gspca_dev *gspca_dev);
static void setsharpness(struct gspca_dev *gspca_dev);
static void setillum(struct gspca_dev *gspca_dev);
@@ -213,6 +217,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
},
.set_control = setgamma
},
+[EXPOSURE] = {
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Exposure",
+ .minimum = 500,
+ .maximum = 1500,
+ .step = 1,
+ .default_value = 1024
+ },
+ .set_control = setexposure
+ },
[AUTOGAIN] = {
{
.id = V4L2_CID_AUTOGAIN,
@@ -223,7 +239,19 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
.step = 1,
.default_value = 1
},
- .set_control = setautogain
+ .set = sd_setautogain,
+ },
+[GAIN] = {
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+ .minimum = 4,
+ .maximum = 49,
+ .step = 1,
+ .default_value = 15
+ },
+ .set_control = setgain
},
[HFLIP] = {
{
@@ -290,60 +318,87 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
/* table of the disabled controls */
static const __u32 ctrl_dis[] = {
-[SENSOR_ADCM1700] = (1 << AUTOGAIN) |
+[SENSOR_ADCM1700] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
(1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_GC0307] = (1 << HFLIP) |
+[SENSOR_GC0307] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_HV7131R] = (1 << HFLIP) |
+[SENSOR_HV7131R] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << FREQ),
-[SENSOR_MI0360] = (1 << HFLIP) |
+[SENSOR_MI0360] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_MI0360B] = (1 << HFLIP) |
+[SENSOR_MI0360B] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_MO4000] = (1 << HFLIP) |
+[SENSOR_MO4000] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_MT9V111] = (1 << HFLIP) |
+[SENSOR_MT9V111] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_OM6802] = (1 << HFLIP) |
+[SENSOR_OM6802] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_OV7630] = (1 << HFLIP),
+[SENSOR_OV7630] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP),
-[SENSOR_OV7648] = (1 << HFLIP),
+[SENSOR_OV7648] = (1 << EXPOSURE) |
+ (1 << GAIN) |
+ (1 << HFLIP),
-[SENSOR_OV7660] = (1 << AUTOGAIN) |
+[SENSOR_OV7660] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
(1 << HFLIP) |
(1 << VFLIP),
-[SENSOR_PO1030] = (1 << AUTOGAIN) |
+[SENSOR_PO1030] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
(1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_PO2030N] = (1 << AUTOGAIN) |
- (1 << FREQ),
+[SENSOR_PO2030N] = (1 << FREQ),
-[SENSOR_SOI768] = (1 << AUTOGAIN) |
+[SENSOR_SOI768] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
(1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
-[SENSOR_SP80708] = (1 << AUTOGAIN) |
+[SENSOR_SP80708] = (1 << EXPOSURE) |
+ (1 << AUTOGAIN) |
+ (1 << GAIN) |
(1 << HFLIP) |
(1 << VFLIP) |
(1 << FREQ),
@@ -1242,14 +1297,6 @@ static const u8 po2030n_sensor_param1[][8] = {
{0xa1, 0x6e, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
{0xa1, 0x6e, 0x07, 0x25, 0x00, 0x00, 0x00, 0x10},
- {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10},
- {0xc1, 0x6e, 0x16, 0x52, 0x40, 0x48, 0x00, 0x10},
-/*after start*/
- {0xa1, 0x6e, 0x15, 0x0f, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
- {0xa1, 0x6e, 0x1a, 0x05, 0x00, 0x00, 0x00, 0x10},
- {DELAY, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 5ms */
- {0xa1, 0x6e, 0x1b, 0x53, 0x00, 0x00, 0x00, 0x10},
{}
};
@@ -1858,7 +1905,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
return gspca_dev->usb_err;
}
-static u32 setexposure(struct gspca_dev *gspca_dev,
+static u32 expo_adjust(struct gspca_dev *gspca_dev,
u32 expo)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -1982,28 +2029,28 @@ static void setbrightness(struct gspca_dev *gspca_dev)
expo = 0x002dc6c0;
else if (expo < 0x02a0)
expo = 0x02a0;
- sd->exposure = setexposure(gspca_dev, expo);
+ sd->exposure = expo_adjust(gspca_dev, expo);
break;
case SENSOR_MI0360:
case SENSOR_MO4000:
expo = brightness << 4;
- sd->exposure = setexposure(gspca_dev, expo);
+ sd->exposure = expo_adjust(gspca_dev, expo);
break;
case SENSOR_MI0360B:
expo = brightness << 2;
- sd->exposure = setexposure(gspca_dev, expo);
+ sd->exposure = expo_adjust(gspca_dev, expo);
break;
case SENSOR_GC0307:
expo = brightness;
- sd->exposure = setexposure(gspca_dev, expo);
+ sd->exposure = expo_adjust(gspca_dev, expo);
return; /* don't set the Y offset */
case SENSOR_MT9V111:
expo = brightness << 2;
- sd->exposure = setexposure(gspca_dev, expo);
+ sd->exposure = expo_adjust(gspca_dev, expo);
return; /* don't set the Y offset */
case SENSOR_OM6802:
expo = brightness << 2;
- sd->exposure = setexposure(gspca_dev, expo);
+ sd->exposure = expo_adjust(gspca_dev, expo);
return; /* Y offset already set */
}
@@ -2112,6 +2159,23 @@ static void setgamma(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
}
+static void setexposure(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rexpo[] = /* 1a: expo H, 1b: expo M */
+ {0xa1, 0x6e, 0x1a, 0x00, 0x40, 0x00, 0x00, 0x10};
+
+ rexpo[3] = sd->ctrls[EXPOSURE].val >> 8;
+ i2c_w8(gspca_dev, rexpo);
+ msleep(6);
+ rexpo[2] = 0x1b;
+ rexpo[3] = sd->ctrls[EXPOSURE].val;
+ i2c_w8(gspca_dev, rexpo);
+ }
+}
+
static void setautogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2139,6 +2203,19 @@ static void setautogain(struct gspca_dev *gspca_dev)
sd->ag_cnt = -1;
}
+static void setgain(struct gspca_dev *gspca_dev)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ u8 rgain[] = /* 15: gain */
+ {0xa1, 0x6e, 0x15, 0x00, 0x40, 0x00, 0x00, 0x15};
+
+ rgain[3] = sd->ctrls[GAIN].val;
+ i2c_w8(gspca_dev, rgain);
+ }
+}
+
static void sethvflip(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2623,6 +2700,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
setcontrast(gspca_dev);
setcolors(gspca_dev);
setautogain(gspca_dev);
+ if (!(gspca_dev->ctrl_inac & ((1 << EXPOSURE) | (1 << GAIN)))) {
+ setexposure(gspca_dev);
+ setgain(gspca_dev);
+ }
setfreq(gspca_dev);
sd->pktsz = sd->npkt = 0;
@@ -2719,6 +2800,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
}
}
+/* !! coarse_grained_expo_autogain is not used !! */
+#define exp_too_low_cnt bridge
+#define exp_too_high_cnt sensor
+
+#include "autogain_functions.h"
+
static void do_autogain(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
@@ -2736,6 +2823,13 @@ static void do_autogain(struct gspca_dev *gspca_dev)
delta = atomic_read(&sd->avg_lum);
PDEBUG(D_FRAM, "mean lum %d", delta);
+
+ if (sd->sensor == SENSOR_PO2030N) {
+ auto_gain_n_exposure(gspca_dev, delta, luma_mean, luma_delta,
+ 15, 1024);
+ return;
+ }
+
if (delta < luma_mean - luma_delta ||
delta > luma_mean + luma_delta) {
switch (sd->sensor) {
@@ -2744,7 +2838,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes += (luma_mean - delta) >> 6;
if (expotimes < 0)
expotimes = 0;
- sd->exposure = setexposure(gspca_dev,
+ sd->exposure = expo_adjust(gspca_dev,
(unsigned int) expotimes);
break;
case SENSOR_HV7131R:
@@ -2752,7 +2846,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes += (luma_mean - delta) >> 4;
if (expotimes < 0)
expotimes = 0;
- sd->exposure = setexposure(gspca_dev,
+ sd->exposure = expo_adjust(gspca_dev,
(unsigned int) (expotimes << 8));
break;
case SENSOR_OM6802:
@@ -2761,7 +2855,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes += (luma_mean - delta) >> 2;
if (expotimes < 0)
expotimes = 0;
- sd->exposure = setexposure(gspca_dev,
+ sd->exposure = expo_adjust(gspca_dev,
(unsigned int) expotimes);
setredblue(gspca_dev);
break;
@@ -2773,7 +2867,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
expotimes += (luma_mean - delta) >> 6;
if (expotimes < 0)
expotimes = 0;
- sd->exposure = setexposure(gspca_dev,
+ sd->exposure = expo_adjust(gspca_dev,
(unsigned int) expotimes);
setredblue(gspca_dev);
break;
@@ -2948,16 +3042,18 @@ marker_found:
}
}
-static int sd_get_jcomp(struct gspca_dev *gspca_dev,
- struct v4l2_jpegcompression *jcomp)
+static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
{
struct sd *sd = (struct sd *) gspca_dev;
- memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
- jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
- | V4L2_JPEG_MARKER_DQT;
- return 0;
+ sd->ctrls[AUTOGAIN].val = val;
+ if (val)
+ gspca_dev->ctrl_inac |= (1 << EXPOSURE) | (1 << GAIN);
+ else
+ gspca_dev->ctrl_inac &= ~(1 << EXPOSURE) & ~(1 << GAIN);
+ if (gspca_dev->streaming)
+ setautogain(gspca_dev);
+ return gspca_dev->usb_err;
}
static int sd_querymenu(struct gspca_dev *gspca_dev,
@@ -3012,7 +3108,6 @@ static const struct sd_desc sd_desc = {
.stop0 = sd_stop0,
.pkt_scan = sd_pkt_scan,
.dq_callback = do_autogain,
- .get_jcomp = sd_get_jcomp,
.querymenu = sd_querymenu,
#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
.int_pkt_scan = sd_int_pkt_scan,
diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile
index 5b318faf9aa8..38bc41061d83 100644
--- a/drivers/media/video/gspca/stv06xx/Makefile
+++ b/drivers/media/video/gspca/stv06xx/Makefile
@@ -6,5 +6,5 @@ gspca_stv06xx-objs := stv06xx.o \
stv06xx_pb0100.o \
stv06xx_st6422.o
-ccflags-y += -Idrivers/media/video/gspca
+ccflags-y += -I$(srctree)/drivers/media/video/gspca
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
index b9e15bb0328b..7d9a4f1be9dc 100644
--- a/drivers/media/video/gspca/zc3xx.c
+++ b/drivers/media/video/gspca/zc3xx.c
@@ -1,7 +1,7 @@
/*
- * Z-Star/Vimicro zc301/zc302p/vc30x library
+ * Z-Star/Vimicro zc301/zc302p/vc30x driver
*
- * Copyright (C) 2009-2011 Jean-Francois Moine <http://moinejf.free.fr>
+ * Copyright (C) 2009-2012 Jean-Francois Moine <http://moinejf.free.fr>
* Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr
*
* This program is free software; you can redistribute it and/or modify
@@ -21,8 +21,6 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#define MODULE_NAME "zc3xx"
-
#include <linux/input.h>
#include "gspca.h"
#include "jpeg.h"
@@ -34,7 +32,7 @@ MODULE_LICENSE("GPL");
static int force_sensor = -1;
-#define QUANT_VAL 1 /* quantization table */
+#define REG08_DEF 3 /* default JPEG compression (70%) */
#include "zc3xx-reg.h"
/* controls */
@@ -46,6 +44,7 @@ enum e_ctrl {
AUTOGAIN,
LIGHTFREQ,
SHARPNESS,
+ QUALITY,
NCTRLS /* number of controls */
};
@@ -57,10 +56,10 @@ struct sd {
struct gspca_ctrl ctrls[NCTRLS];
- u8 quality; /* image quality */
-#define QUALITY_MIN 50
-#define QUALITY_MAX 80
-#define QUALITY_DEF 70
+ struct work_struct work;
+ struct workqueue_struct *work_thread;
+
+ u8 reg08; /* webcam compression quality */
u8 bridge;
u8 sensor; /* Type of image sensor chip */
@@ -101,6 +100,7 @@ static void setexposure(struct gspca_dev *gspca_dev);
static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
static void setlightfreq(struct gspca_dev *gspca_dev);
static void setsharpness(struct gspca_dev *gspca_dev);
+static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val);
static const struct ctrl sd_ctrls[NCTRLS] = {
[BRIGHTNESS] = {
@@ -188,6 +188,18 @@ static const struct ctrl sd_ctrls[NCTRLS] = {
},
.set_control = setsharpness
},
+[QUALITY] = {
+ {
+ .id = V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Compression Quality",
+ .minimum = 40,
+ .maximum = 70,
+ .step = 1,
+ .default_value = 70 /* updated in sd_init() */
+ },
+ .set = sd_setquality
+ },
};
static const struct v4l2_pix_format vga_mode[] = {
@@ -229,6 +241,9 @@ static const struct v4l2_pix_format sif_mode[] = {
.priv = 0},
};
+/* bridge reg08 -> JPEG quality conversion table */
+static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/};
+
/* usb exchanges */
struct usb_action {
u8 req;
@@ -3894,7 +3909,6 @@ static const struct usb_action pas106b_Initial[] = { /* 352x288 */
/* Gains */
{0xa0, 0x20, ZC3XX_R1A9_DIGITALLIMITDIFF},
{0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP},
- {0xa0, 0xa0, ZC3XX_R11D_GLOBALGAIN},
{0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
/* Auto correction */
{0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
@@ -5640,7 +5654,7 @@ static const struct usb_action gc0303_NoFlikerScale[] = {
{}
};
-static u8 reg_r_i(struct gspca_dev *gspca_dev,
+static u8 reg_r(struct gspca_dev *gspca_dev,
u16 index)
{
int ret;
@@ -5655,24 +5669,14 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev,
index, gspca_dev->usb_buf, 1,
500);
if (ret < 0) {
- pr_err("reg_r_i err %d\n", ret);
+ pr_err("reg_r err %d\n", ret);
gspca_dev->usb_err = ret;
return 0;
}
return gspca_dev->usb_buf[0];
}
-static u8 reg_r(struct gspca_dev *gspca_dev,
- u16 index)
-{
- u8 ret;
-
- ret = reg_r_i(gspca_dev, index);
- PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret);
- return ret;
-}
-
-static void reg_w_i(struct gspca_dev *gspca_dev,
+static void reg_w(struct gspca_dev *gspca_dev,
u8 value,
u16 index)
{
@@ -5692,14 +5696,6 @@ static void reg_w_i(struct gspca_dev *gspca_dev,
}
}
-static void reg_w(struct gspca_dev *gspca_dev,
- u8 value,
- u16 index)
-{
- PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
- reg_w_i(gspca_dev, value, index);
-}
-
static u16 i2c_read(struct gspca_dev *gspca_dev,
u8 reg)
{
@@ -5708,16 +5704,14 @@ static u16 i2c_read(struct gspca_dev *gspca_dev,
if (gspca_dev->usb_err < 0)
return 0;
- reg_w_i(gspca_dev, reg, 0x0092);
- reg_w_i(gspca_dev, 0x02, 0x0090); /* <- read command */
+ reg_w(gspca_dev, reg, 0x0092);
+ reg_w(gspca_dev, 0x02, 0x0090); /* <- read command */
msleep(20);
- retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
pr_err("i2c_r status error %02x\n", retbyte);
- retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
- retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */
- PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
- reg, retval, retbyte);
+ retval = reg_r(gspca_dev, 0x0095); /* read Lowbyte */
+ retval |= reg_r(gspca_dev, 0x0096) << 8; /* read Hightbyte */
return retval;
}
@@ -5730,16 +5724,14 @@ static u8 i2c_write(struct gspca_dev *gspca_dev,
if (gspca_dev->usb_err < 0)
return 0;
- reg_w_i(gspca_dev, reg, 0x92);
- reg_w_i(gspca_dev, valL, 0x93);
- reg_w_i(gspca_dev, valH, 0x94);
- reg_w_i(gspca_dev, 0x01, 0x90); /* <- write command */
+ reg_w(gspca_dev, reg, 0x92);
+ reg_w(gspca_dev, valL, 0x93);
+ reg_w(gspca_dev, valH, 0x94);
+ reg_w(gspca_dev, 0x01, 0x90); /* <- write command */
msleep(1);
- retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
+ retbyte = reg_r(gspca_dev, 0x0091); /* read status */
if (retbyte != 0x00)
pr_err("i2c_w status error %02x\n", retbyte);
- PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
- reg, valH, valL, retbyte);
return retbyte;
}
@@ -5906,6 +5898,8 @@ static void getexposure(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ if (sd->sensor != SENSOR_HV7131R)
+ return;
sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9)
| (i2c_read(gspca_dev, 0x26) << 1)
| (i2c_read(gspca_dev, 0x27) >> 7);
@@ -5916,6 +5910,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
struct sd *sd = (struct sd *) gspca_dev;
int val;
+ if (sd->sensor != SENSOR_HV7131R)
+ return;
val = sd->ctrls[EXPOSURE].val;
i2c_write(gspca_dev, 0x25, val >> 9, 0x00);
i2c_write(gspca_dev, 0x26, val >> 1, 0x00);
@@ -5925,32 +5921,20 @@ static void setexposure(struct gspca_dev *gspca_dev)
static void setquality(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
- u8 frxt;
+ s8 reg07;
+ reg07 = 0;
switch (sd->sensor) {
- case SENSOR_ADCM2700:
- case SENSOR_GC0305:
- case SENSOR_HV7131B:
- case SENSOR_HV7131R:
case SENSOR_OV7620:
+ reg07 = 0x30;
+ break;
+ case SENSOR_HV7131R:
case SENSOR_PAS202B:
- case SENSOR_PO2030:
- return;
+ return; /* done by work queue */
}
-/*fixme: is it really 0008 0007 0018 for all other sensors? */
- reg_w(gspca_dev, QUANT_VAL, 0x0008);
- frxt = 0x30;
- reg_w(gspca_dev, frxt, 0x0007);
-#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2
- frxt = 0xff;
-#elif QUANT_VAL == 3
- frxt = 0xf0;
-#elif QUANT_VAL == 4
- frxt = 0xe0;
-#else
- frxt = 0x20;
-#endif
- reg_w(gspca_dev, frxt, 0x0018);
+ reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
+ if (reg07 != 0)
+ reg_w(gspca_dev, reg07, 0x0007);
}
/* Matches the sensor's internal frame rate to the lighting frequency.
@@ -6084,6 +6068,115 @@ static void setautogain(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, autoval, 0x0180);
}
+/* update the transfer parameters */
+/* This function is executed from a work queue. */
+/* The exact use of the bridge registers 07 and 08 is not known.
+ * The following algorithm has been adapted from ms-win traces */
+static void transfer_update(struct work_struct *work)
+{
+ struct sd *sd = container_of(work, struct sd, work);
+ struct gspca_dev *gspca_dev = &sd->gspca_dev;
+ int change, good;
+ u8 reg07, reg11;
+
+ /* synchronize with the main driver and initialize the registers */
+ mutex_lock(&gspca_dev->usb_lock);
+ reg07 = 0; /* max */
+ reg_w(gspca_dev, reg07, 0x0007);
+ reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+ good = 0;
+ for (;;) {
+ msleep(100);
+
+ /* get the transfer status */
+ /* the bit 0 of the bridge register 11 indicates overflow */
+ mutex_lock(&gspca_dev->usb_lock);
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ goto err;
+ reg11 = reg_r(gspca_dev, 0x0011);
+ if (gspca_dev->usb_err < 0
+ || !gspca_dev->present || !gspca_dev->streaming)
+ goto err;
+
+ change = reg11 & 0x01;
+ if (change) { /* overflow */
+ switch (reg07) {
+ case 0: /* max */
+ reg07 = sd->sensor == SENSOR_HV7131R
+ ? 0x30 : 0x32;
+ if (sd->reg08 != 0) {
+ change = 3;
+ sd->reg08--;
+ }
+ break;
+ case 0x32:
+ reg07 -= 4;
+ break;
+ default:
+ reg07 -= 2;
+ break;
+ case 2:
+ change = 0; /* already min */
+ break;
+ }
+ good = 0;
+ } else { /* no overflow */
+ if (reg07 != 0) { /* if not max */
+ good++;
+ if (good >= 10) {
+ good = 0;
+ change = 1;
+ reg07 += 2;
+ switch (reg07) {
+ case 0x30:
+ if (sd->sensor == SENSOR_PAS202B)
+ reg07 += 2;
+ break;
+ case 0x32:
+ case 0x34:
+ reg07 = 0;
+ break;
+ }
+ }
+ } else { /* reg07 max */
+ if (sd->reg08 < sizeof jpeg_qual - 1) {
+ good++;
+ if (good > 10) {
+ sd->reg08++;
+ change = 2;
+ }
+ }
+ }
+ }
+ if (change) {
+ if (change & 1) {
+ reg_w(gspca_dev, reg07, 0x0007);
+ if (gspca_dev->usb_err < 0
+ || !gspca_dev->present
+ || !gspca_dev->streaming)
+ goto err;
+ }
+ if (change & 2) {
+ reg_w(gspca_dev, sd->reg08,
+ ZC3XX_R008_CLOCKSETTING);
+ if (gspca_dev->usb_err < 0
+ || !gspca_dev->present
+ || !gspca_dev->streaming)
+ goto err;
+ sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08];
+ jpeg_set_qual(sd->jpeg_hdr,
+ jpeg_qual[sd->reg08]);
+ }
+ }
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ return;
+err:
+ mutex_unlock(&gspca_dev->usb_lock);
+}
+
static void send_unknown(struct gspca_dev *gspca_dev, int sensor)
{
reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */
@@ -6411,7 +6504,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
sd->sensor = id->driver_info;
gspca_dev->cam.ctrls = sd->ctrls;
- sd->quality = QUALITY_DEF;
+ sd->reg08 = REG08_DEF;
+
+ INIT_WORK(&sd->work, transfer_update);
return 0;
}
@@ -6464,6 +6559,27 @@ static int sd_init(struct gspca_dev *gspca_dev)
[SENSOR_PO2030] = 1,
[SENSOR_TAS5130C] = 1,
};
+ static const u8 reg08_tb[SENSOR_MAX] = {
+ [SENSOR_ADCM2700] = 1,
+ [SENSOR_CS2102] = 3,
+ [SENSOR_CS2102K] = 3,
+ [SENSOR_GC0303] = 2,
+ [SENSOR_GC0305] = 3,
+ [SENSOR_HDCS2020] = 1,
+ [SENSOR_HV7131B] = 3,
+ [SENSOR_HV7131R] = 3,
+ [SENSOR_ICM105A] = 3,
+ [SENSOR_MC501CB] = 3,
+ [SENSOR_MT9V111_1] = 3,
+ [SENSOR_MT9V111_3] = 3,
+ [SENSOR_OV7620] = 1,
+ [SENSOR_OV7630C] = 3,
+ [SENSOR_PAS106] = 3,
+ [SENSOR_PAS202B] = 3,
+ [SENSOR_PB0330] = 3,
+ [SENSOR_PO2030] = 2,
+ [SENSOR_TAS5130C] = 3,
+ };
sensor = zcxx_probeSensor(gspca_dev);
if (sensor >= 0)
@@ -6528,7 +6644,6 @@ static int sd_init(struct gspca_dev *gspca_dev)
case 0x0e:
PDEBUG(D_PROBE, "Find Sensor PAS202B");
sd->sensor = SENSOR_PAS202B;
-/* sd->sharpness = 1; */
break;
case 0x0f:
PDEBUG(D_PROBE, "Find Sensor PAS106");
@@ -6616,13 +6731,21 @@ static int sd_init(struct gspca_dev *gspca_dev)
}
sd->ctrls[GAMMA].def = gamma[sd->sensor];
+ sd->reg08 = reg08_tb[sd->sensor];
+ sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08];
+ sd->ctrls[QUALITY].min = jpeg_qual[0];
+ sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1];
switch (sd->sensor) {
case SENSOR_HV7131R:
+ gspca_dev->ctrl_dis = (1 << QUALITY);
break;
case SENSOR_OV7630C:
gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE);
break;
+ case SENSOR_PAS202B:
+ gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE);
+ break;
default:
gspca_dev->ctrl_dis = (1 << EXPOSURE);
break;
@@ -6685,7 +6808,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
/* create the JPEG header */
jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
0x21); /* JPEG 422 */
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
switch (sd->sensor) {
@@ -6761,10 +6883,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_r(gspca_dev, 0x0180); /* from win */
reg_w(gspca_dev, 0x00, 0x0180);
break;
- default:
- setquality(gspca_dev);
- break;
}
+ setquality(gspca_dev);
+ jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]);
setlightfreq(gspca_dev);
switch (sd->sensor) {
@@ -6776,8 +6897,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
reg_w(gspca_dev, 0x40, 0x0117);
break;
case SENSOR_HV7131R:
- if (!sd->ctrls[AUTOGAIN].val)
- setexposure(gspca_dev);
+ setexposure(gspca_dev);
reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN);
break;
case SENSOR_GC0305:
@@ -6802,13 +6922,19 @@ static int sd_start(struct gspca_dev *gspca_dev)
}
setautogain(gspca_dev);
- switch (sd->sensor) {
- case SENSOR_PO2030:
- msleep(50);
- reg_w(gspca_dev, 0x00, 0x0007); /* (from win traces) */
- reg_w(gspca_dev, 0x02, ZC3XX_R008_CLOCKSETTING);
- break;
+
+ /* start the transfer update thread if needed */
+ if (gspca_dev->usb_err >= 0) {
+ switch (sd->sensor) {
+ case SENSOR_HV7131R:
+ case SENSOR_PAS202B:
+ sd->work_thread =
+ create_singlethread_workqueue(KBUILD_MODNAME);
+ queue_work(sd->work_thread, &sd->work);
+ break;
+ }
}
+
return gspca_dev->usb_err;
}
@@ -6817,6 +6943,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
{
struct sd *sd = (struct sd *) gspca_dev;
+ if (sd->work_thread != NULL) {
+ mutex_unlock(&gspca_dev->usb_lock);
+ destroy_workqueue(sd->work_thread);
+ mutex_lock(&gspca_dev->usb_lock);
+ sd->work_thread = NULL;
+ }
if (!gspca_dev->present)
return;
send_unknown(gspca_dev, sd->sensor);
@@ -6893,19 +7025,33 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
return -EINVAL;
}
+static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val)
+{
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) {
+ if (val <= jpeg_qual[i])
+ break;
+ }
+ if (i > 0
+ && i == sd->reg08
+ && val < jpeg_qual[sd->reg08])
+ i--;
+ sd->reg08 = i;
+ sd->ctrls[QUALITY].val = jpeg_qual[i];
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val);
+ return gspca_dev->usb_err;
+}
+
static int sd_set_jcomp(struct gspca_dev *gspca_dev,
struct v4l2_jpegcompression *jcomp)
{
struct sd *sd = (struct sd *) gspca_dev;
- if (jcomp->quality < QUALITY_MIN)
- sd->quality = QUALITY_MIN;
- else if (jcomp->quality > QUALITY_MAX)
- sd->quality = QUALITY_MAX;
- else
- sd->quality = jcomp->quality;
- if (gspca_dev->streaming)
- jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+ sd_setquality(gspca_dev, jcomp->quality);
+ jcomp->quality = sd->ctrls[QUALITY].val;
return gspca_dev->usb_err;
}
@@ -6915,7 +7061,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
struct sd *sd = (struct sd *) gspca_dev;
memset(jcomp, 0, sizeof *jcomp);
- jcomp->quality = sd->quality;
+ jcomp->quality = sd->ctrls[QUALITY].val;
jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
| V4L2_JPEG_MARKER_DQT;
return 0;
@@ -6938,7 +7084,7 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
#endif
static const struct sd_desc sd_desc = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.ctrls = sd_ctrls,
.nctrls = ARRAY_SIZE(sd_ctrls),
.config = sd_config,
@@ -7023,7 +7169,7 @@ static int sd_probe(struct usb_interface *intf,
/* USB driver */
static struct usb_driver sd_driver = {
- .name = MODULE_NAME,
+ .name = KBUILD_MODNAME,
.id_table = device_table,
.probe = sd_probe,
.disconnect = gspca_disconnect,
diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c
index eec75bb57203..351e9bafe8fe 100644
--- a/drivers/media/video/imx074.c
+++ b/drivers/media/video/imx074.c
@@ -468,18 +468,7 @@ static struct i2c_driver imx074_i2c_driver = {
.id_table = imx074_id,
};
-static int __init imx074_mod_init(void)
-{
- return i2c_add_driver(&imx074_i2c_driver);
-}
-
-static void __exit imx074_mod_exit(void)
-{
- i2c_del_driver(&imx074_i2c_driver);
-}
-
-module_init(imx074_mod_init);
-module_exit(imx074_mod_exit);
+module_i2c_driver(imx074_i2c_driver);
MODULE_DESCRIPTION("Sony IMX074 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c
index e5ed4db32e7b..548236333cce 100644
--- a/drivers/media/video/indycam.c
+++ b/drivers/media/video/indycam.c
@@ -387,15 +387,4 @@ static struct i2c_driver indycam_driver = {
.id_table = indycam_id,
};
-static __init int init_indycam(void)
-{
- return i2c_add_driver(&indycam_driver);
-}
-
-static __exit void exit_indycam(void)
-{
- i2c_del_driver(&indycam_driver);
-}
-
-module_init(init_indycam);
-module_exit(exit_indycam);
+module_i2c_driver(indycam_driver);
diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
index a7c41d32f414..04f192a0398a 100644
--- a/drivers/media/video/ir-kbd-i2c.c
+++ b/drivers/media/video/ir-kbd-i2c.c
@@ -471,7 +471,7 @@ static const struct i2c_device_id ir_kbd_id[] = {
{ }
};
-static struct i2c_driver driver = {
+static struct i2c_driver ir_kbd_driver = {
.driver = {
.name = "ir-kbd-i2c",
},
@@ -480,21 +480,10 @@ static struct i2c_driver driver = {
.id_table = ir_kbd_id,
};
+module_i2c_driver(ir_kbd_driver);
+
/* ----------------------------------------------------------------------- */
MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, Ulrich Mueller");
MODULE_DESCRIPTION("input driver for i2c IR remote controls");
MODULE_LICENSE("GPL");
-
-static int __init ir_init(void)
-{
- return i2c_add_driver(&driver);
-}
-
-static void __exit ir_fini(void)
-{
- i2c_del_driver(&driver);
-}
-
-module_init(ir_init);
-module_exit(ir_fini);
diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile
index 71ab76a5ab26..77de8a45b46f 100644
--- a/drivers/media/video/ivtv/Makefile
+++ b/drivers/media/video/ivtv/Makefile
@@ -7,8 +7,8 @@ ivtv-objs := ivtv-routing.o ivtv-cards.o ivtv-controls.o \
obj-$(CONFIG_VIDEO_IVTV) += ivtv.o
obj-$(CONFIG_VIDEO_FB_IVTV) += ivtvfb.o
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/video
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
index b31ee1bceef8..c60424601cb9 100644
--- a/drivers/media/video/ivtv/ivtv-controls.c
+++ b/drivers/media/video/ivtv/ivtv-controls.c
@@ -21,6 +21,7 @@
#include "ivtv-driver.h"
#include "ivtv-ioctl.h"
#include "ivtv-controls.h"
+#include "ivtv-mailbox.h"
static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
{
@@ -99,3 +100,64 @@ struct cx2341x_handler_ops ivtv_cxhdl_ops = {
.s_video_encoding = ivtv_s_video_encoding,
.s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
};
+
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame)
+{
+ u32 data[CX2341X_MBOX_MAX_DATA];
+
+ if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
+ *pts = (s64)((u64)itv->last_dec_timing[2] << 32) |
+ (u64)itv->last_dec_timing[1];
+ *frame = itv->last_dec_timing[0];
+ return 0;
+ }
+ *pts = 0;
+ *frame = 0;
+ if (atomic_read(&itv->decoding)) {
+ if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
+ IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
+ return -EIO;
+ }
+ memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
+ set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
+ *pts = (s64)((u64) data[2] << 32) | (u64) data[1];
+ *frame = data[0];
+ /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
+ }
+ return 0;
+}
+
+static int ivtv_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
+
+ switch (ctrl->id) {
+ /* V4L2_CID_MPEG_VIDEO_DEC_PTS and V4L2_CID_MPEG_VIDEO_DEC_FRAME
+ control cluster */
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ return ivtv_g_pts_frame(itv, &itv->ctrl_pts->val64,
+ &itv->ctrl_frame->val64);
+ }
+ return 0;
+}
+
+static int ivtv_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct ivtv *itv = container_of(ctrl->handler, struct ivtv, cxhdl.hdl);
+
+ switch (ctrl->id) {
+ /* V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK and MULTILINGUAL_PLAYBACK
+ control cluster */
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ itv->audio_stereo_mode = itv->ctrl_audio_playback->val - 1;
+ itv->audio_bilingual_mode = itv->ctrl_audio_multilingual_playback->val - 1;
+ ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
+ break;
+ }
+ return 0;
+}
+
+const struct v4l2_ctrl_ops ivtv_hdl_out_ops = {
+ .s_ctrl = ivtv_s_ctrl,
+ .g_volatile_ctrl = ivtv_g_volatile_ctrl,
+};
diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h
index d12893dd0183..3999e6358312 100644
--- a/drivers/media/video/ivtv/ivtv-controls.h
+++ b/drivers/media/video/ivtv/ivtv-controls.h
@@ -22,5 +22,7 @@
#define IVTV_CONTROLS_H
extern struct cx2341x_handler_ops ivtv_cxhdl_ops;
+extern const struct v4l2_ctrl_ops ivtv_hdl_out_ops;
+int ivtv_g_pts_frame(struct ivtv *itv, s64 *pts, s64 *frame);
#endif
diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
index 3949b7dc2368..679262ed13bc 100644
--- a/drivers/media/video/ivtv/ivtv-driver.c
+++ b/drivers/media/video/ivtv/ivtv-driver.c
@@ -55,7 +55,7 @@
#include "ivtv-routing.h"
#include "ivtv-controls.h"
#include "ivtv-gpio.h"
-
+#include <linux/dma-mapping.h>
#include <media/tveeprom.h>
#include <media/saa7115.h>
#include <media/v4l2-chip-ident.h>
@@ -99,7 +99,7 @@ static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
static unsigned int cardtype_c = 1;
static unsigned int tuner_c = 1;
-static bool radio_c = 1;
+static int radio_c = 1;
static unsigned int i2c_clock_period_c = 1;
static char pal[] = "---";
static char secam[] = "--";
@@ -139,7 +139,7 @@ static int tunertype = -1;
static int newi2c = -1;
module_param_array(tuner, int, &tuner_c, 0644);
-module_param_array(radio, bool, &radio_c, 0644);
+module_param_array(radio, int, &radio_c, 0644);
module_param_array(cardtype, int, &cardtype_c, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
@@ -744,8 +744,6 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
itv->cur_dma_stream = -1;
itv->cur_pio_stream = -1;
- itv->audio_stereo_mode = AUDIO_STEREO;
- itv->audio_bilingual_mode = AUDIO_MONO_LEFT;
/* Ctrls */
itv->speed = 1000;
@@ -815,7 +813,7 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev,
IVTV_ERR("Can't enable device!\n");
return -EIO;
}
- if (pci_set_dma_mask(pdev, 0xffffffff)) {
+ if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
IVTV_ERR("No suitable DMA available.\n");
return -EIO;
}
@@ -1200,6 +1198,32 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
itv->tuner_std = itv->std;
if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) {
+ struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler;
+
+ itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0);
+ itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0);
+ /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported,
+ mask that menu item. */
+ itv->ctrl_audio_playback =
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_STEREO);
+ itv->ctrl_audio_multilingual_playback =
+ v4l2_ctrl_new_std_menu(hdl, &ivtv_hdl_out_ops,
+ V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_SWAPPED_STEREO,
+ 1 << V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO,
+ V4L2_MPEG_AUDIO_DEC_PLAYBACK_LEFT);
+ if (hdl->error) {
+ retval = hdl->error;
+ goto free_i2c;
+ }
+ v4l2_ctrl_cluster(2, &itv->ctrl_pts);
+ v4l2_ctrl_cluster(2, &itv->ctrl_audio_playback);
ivtv_call_all(itv, video, s_std_output, itv->std);
/* Turn off the output signal. The mpeg decoder is not yet
active so without this you would get a green image until the
@@ -1236,6 +1260,7 @@ free_streams:
free_irq:
free_irq(itv->pdev->irq, (void *)itv);
free_i2c:
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
exit_ivtv_i2c(itv);
free_io:
ivtv_iounmap(itv);
@@ -1375,7 +1400,7 @@ static void ivtv_remove(struct pci_dev *pdev)
else
type = IVTV_DEC_STREAM_TYPE_MPG;
ivtv_stop_v4l2_decode_stream(&itv->streams[type],
- VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+ V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
}
ivtv_halt_firmware(itv);
}
@@ -1391,6 +1416,8 @@ static void ivtv_remove(struct pci_dev *pdev)
ivtv_streams_cleanup(itv, 1);
ivtv_udma_free(itv);
+ v4l2_ctrl_handler_free(&itv->cxhdl.hdl);
+
exit_ivtv_i2c(itv);
free_irq(itv->pdev->irq, (void *)itv);
diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
index 06f3d78389bf..2e220028aad2 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>
@@ -331,6 +330,7 @@ struct ivtv_stream {
struct ivtv *itv; /* for ease of use */
const char *name; /* name of the stream */
int type; /* stream type */
+ u32 caps; /* V4L2 capabilities */
struct v4l2_fh *fh; /* pointer to the streaming filehandle */
spinlock_t qlock; /* locks access to the queues */
@@ -630,6 +630,16 @@ struct ivtv {
struct v4l2_device v4l2_dev;
struct cx2341x_handler cxhdl;
+ struct {
+ /* PTS/Frame count control cluster */
+ struct v4l2_ctrl *ctrl_pts;
+ struct v4l2_ctrl *ctrl_frame;
+ };
+ struct {
+ /* Audio Playback control cluster */
+ struct v4l2_ctrl *ctrl_audio_playback;
+ struct v4l2_ctrl *ctrl_audio_multilingual_playback;
+ };
struct v4l2_ctrl_handler hdl_gpio;
struct v4l2_subdev sd_gpio; /* GPIO sub-device */
u16 instance;
@@ -649,7 +659,6 @@ struct ivtv {
u8 audio_stereo_mode; /* decoder setting how to handle stereo MPEG audio */
u8 audio_bilingual_mode; /* decoder setting how to handle bilingual MPEG audio */
-
/* Locking */
spinlock_t lock; /* lock access to this struct */
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
index 2cd6c89b7d91..c9663e885b9f 100644
--- a/drivers/media/video/ivtv/ivtv-fileops.c
+++ b/drivers/media/video/ivtv/ivtv-fileops.c
@@ -900,7 +900,7 @@ int ivtv_v4l2_close(struct file *filp)
if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) {
struct ivtv_stream *s_vout = &itv->streams[IVTV_DEC_STREAM_TYPE_VOUT];
- ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0);
+ ivtv_stop_decoding(id, V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY, 0);
/* If all output streams are closed, and if the user doesn't have
IVTV_DEC_STREAM_TYPE_VOUT open, then disable CC on TV-out. */
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
index c4bc48143098..5452beef8e11 100644
--- a/drivers/media/video/ivtv/ivtv-ioctl.c
+++ b/drivers/media/video/ivtv/ivtv-ioctl.c
@@ -246,34 +246,40 @@ static int ivtv_validate_speed(int cur_speed, int new_speed)
}
static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
- struct video_command *vc, int try)
+ struct v4l2_decoder_cmd *dc, int try)
{
struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG];
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return -EINVAL;
- switch (vc->cmd) {
- case VIDEO_CMD_PLAY: {
- vc->flags = 0;
- vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed);
- if (vc->play.speed < 0)
- vc->play.format = VIDEO_PLAY_FMT_GOP;
+ switch (dc->cmd) {
+ case V4L2_DEC_CMD_START: {
+ dc->flags &= V4L2_DEC_CMD_START_MUTE_AUDIO;
+ dc->start.speed = ivtv_validate_speed(itv->speed, dc->start.speed);
+ if (dc->start.speed < 0)
+ dc->start.format = V4L2_DEC_START_FMT_GOP;
+ else
+ dc->start.format = V4L2_DEC_START_FMT_NONE;
+ if (dc->start.speed != 500 && dc->start.speed != 1500)
+ dc->flags = dc->start.speed == 1000 ? 0 :
+ V4L2_DEC_CMD_START_MUTE_AUDIO;
if (try) break;
+ itv->speed_mute_audio = dc->flags & V4L2_DEC_CMD_START_MUTE_AUDIO;
if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG)
return -EBUSY;
if (test_and_clear_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags)) {
/* forces ivtv_set_speed to be called */
itv->speed = 0;
}
- return ivtv_start_decoding(id, vc->play.speed);
+ return ivtv_start_decoding(id, dc->start.speed);
}
- case VIDEO_CMD_STOP:
- vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK;
- if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY)
- vc->stop.pts = 0;
+ case V4L2_DEC_CMD_STOP:
+ dc->flags &= V4L2_DEC_CMD_STOP_IMMEDIATELY | V4L2_DEC_CMD_STOP_TO_BLACK;
+ if (dc->flags & V4L2_DEC_CMD_STOP_IMMEDIATELY)
+ dc->stop.pts = 0;
if (try) break;
if (atomic_read(&itv->decoding) == 0)
return 0;
@@ -281,22 +287,22 @@ static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id,
return -EBUSY;
itv->output_mode = OUT_NONE;
- return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts);
+ return ivtv_stop_v4l2_decode_stream(s, dc->flags, dc->stop.pts);
- case VIDEO_CMD_FREEZE:
- vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK;
+ case V4L2_DEC_CMD_PAUSE:
+ dc->flags &= V4L2_DEC_CMD_PAUSE_TO_BLACK;
if (try) break;
if (itv->output_mode != OUT_MPG)
return -EBUSY;
if (atomic_read(&itv->decoding) > 0) {
ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1,
- (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0);
+ (dc->flags & V4L2_DEC_CMD_PAUSE_TO_BLACK) ? 1 : 0);
set_bit(IVTV_F_I_DEC_PAUSED, &itv->i_flags);
}
break;
- case VIDEO_CMD_CONTINUE:
- vc->flags = 0;
+ case V4L2_DEC_CMD_RESUME:
+ dc->flags = 0;
if (try) break;
if (itv->output_mode != OUT_MPG)
return -EBUSY;
@@ -754,12 +760,15 @@ static int ivtv_s_register(struct file *file, void *fh, struct v4l2_dbg_register
static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vcap)
{
- struct ivtv *itv = fh2id(fh)->itv;
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+ struct ivtv_stream *s = &itv->streams[id->type];
strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
- vcap->capabilities = itv->v4l2_cap; /* capabilities */
+ vcap->capabilities = itv->v4l2_cap | V4L2_CAP_DEVICE_CAPS;
+ vcap->device_caps = s->caps;
return 0;
}
@@ -1476,8 +1485,6 @@ static int ivtv_log_status(struct file *file, void *fh)
struct v4l2_audio audin;
int i;
- IVTV_INFO("================= START STATUS CARD #%d =================\n",
- itv->instance);
IVTV_INFO("Version: %s Card: %s\n", IVTV_VERSION, itv->card_name);
if (itv->hw_flags & IVTV_HW_TVEEPROM) {
struct tveeprom tv;
@@ -1501,13 +1508,6 @@ static int ivtv_log_status(struct file *file, void *fh)
"YUV Frames",
"Passthrough",
};
- static const char * const audio_modes[5] = {
- "Stereo",
- "Left",
- "Right",
- "Mono",
- "Swapped"
- };
static const char * const alpha_mode[4] = {
"None",
"Global",
@@ -1536,9 +1536,6 @@ static int ivtv_log_status(struct file *file, void *fh)
ivtv_get_output(itv, itv->active_output, &vidout);
ivtv_get_audio_output(itv, 0, &audout);
IVTV_INFO("Video Output: %s\n", vidout.name);
- IVTV_INFO("Audio Output: %s (Stereo/Bilingual: %s/%s)\n", audout.name,
- audio_modes[itv->audio_stereo_mode],
- audio_modes[itv->audio_bilingual_mode]);
if (mode < 0 || mode > OUT_PASSTHROUGH)
mode = OUT_NONE;
IVTV_INFO("Output Mode: %s\n", output_modes[mode]);
@@ -1566,12 +1563,27 @@ static int ivtv_log_status(struct file *file, void *fh)
IVTV_INFO("Read MPG/VBI: %lld/%lld bytes\n",
(long long)itv->mpg_data_received,
(long long)itv->vbi_data_inserted);
- IVTV_INFO("================== END STATUS CARD #%d ==================\n",
- itv->instance);
-
return 0;
}
+static int ivtv_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_DECODER_CMD %d\n", dec->cmd);
+ return ivtv_video_command(itv, id, dec, false);
+}
+
+static int ivtv_try_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *dec)
+{
+ struct ivtv_open_id *id = fh2id(file->private_data);
+ struct ivtv *itv = id->itv;
+
+ IVTV_DEBUG_IOCTL("VIDIOC_TRY_DECODER_CMD %d\n", dec->cmd);
+ return ivtv_video_command(itv, id, dec, true);
+}
+
static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
{
struct ivtv_open_id *id = fh2id(filp->private_data);
@@ -1605,9 +1617,15 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
return ivtv_yuv_prep_frame(itv, args);
}
+ case IVTV_IOC_PASSTHROUGH_MODE:
+ IVTV_DEBUG_IOCTL("IVTV_IOC_PASSTHROUGH_MODE\n");
+ if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
+ return -EINVAL;
+ return ivtv_passthrough_mode(itv, *(int *)arg != 0);
+
case VIDEO_GET_PTS: {
- u32 data[CX2341X_MBOX_MAX_DATA];
- u64 *pts = arg;
+ s64 *pts = arg;
+ s64 frame;
IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n");
if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
@@ -1616,29 +1634,12 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
}
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return -EINVAL;
-
- if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
- *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) |
- (u64)itv->last_dec_timing[1];
- break;
- }
- *pts = 0;
- if (atomic_read(&itv->decoding)) {
- if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
- IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
- return -EIO;
- }
- memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
- set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
- *pts = (u64) ((u64) data[2] << 32) | (u64) data[1];
- /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/
- }
- break;
+ return ivtv_g_pts_frame(itv, pts, &frame);
}
case VIDEO_GET_FRAME_COUNT: {
- u32 data[CX2341X_MBOX_MAX_DATA];
- u64 *frame = arg;
+ s64 *frame = arg;
+ s64 pts;
IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n");
if (s->type < IVTV_DEC_STREAM_TYPE_MPG) {
@@ -1647,71 +1648,58 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
}
if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT))
return -EINVAL;
-
- if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) {
- *frame = itv->last_dec_timing[0];
- break;
- }
- *frame = 0;
- if (atomic_read(&itv->decoding)) {
- if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) {
- IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n");
- return -EIO;
- }
- memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing));
- set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags);
- *frame = data[0];
- }
- break;
+ return ivtv_g_pts_frame(itv, &pts, frame);
}
case VIDEO_PLAY: {
- struct video_command vc;
+ struct v4l2_decoder_cmd dc;
IVTV_DEBUG_IOCTL("VIDEO_PLAY\n");
- memset(&vc, 0, sizeof(vc));
- vc.cmd = VIDEO_CMD_PLAY;
- return ivtv_video_command(itv, id, &vc, 0);
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_START;
+ return ivtv_video_command(itv, id, &dc, 0);
}
case VIDEO_STOP: {
- struct video_command vc;
+ struct v4l2_decoder_cmd dc;
IVTV_DEBUG_IOCTL("VIDEO_STOP\n");
- memset(&vc, 0, sizeof(vc));
- vc.cmd = VIDEO_CMD_STOP;
- vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY;
- return ivtv_video_command(itv, id, &vc, 0);
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_STOP;
+ dc.flags = V4L2_DEC_CMD_STOP_TO_BLACK | V4L2_DEC_CMD_STOP_IMMEDIATELY;
+ return ivtv_video_command(itv, id, &dc, 0);
}
case VIDEO_FREEZE: {
- struct video_command vc;
+ struct v4l2_decoder_cmd dc;
IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n");
- memset(&vc, 0, sizeof(vc));
- vc.cmd = VIDEO_CMD_FREEZE;
- return ivtv_video_command(itv, id, &vc, 0);
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_PAUSE;
+ return ivtv_video_command(itv, id, &dc, 0);
}
case VIDEO_CONTINUE: {
- struct video_command vc;
+ struct v4l2_decoder_cmd dc;
IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n");
- memset(&vc, 0, sizeof(vc));
- vc.cmd = VIDEO_CMD_CONTINUE;
- return ivtv_video_command(itv, id, &vc, 0);
+ memset(&dc, 0, sizeof(dc));
+ dc.cmd = V4L2_DEC_CMD_RESUME;
+ return ivtv_video_command(itv, id, &dc, 0);
}
case VIDEO_COMMAND:
case VIDEO_TRY_COMMAND: {
- struct video_command *vc = arg;
+ /* Note: struct v4l2_decoder_cmd has the same layout as
+ struct video_command */
+ struct v4l2_decoder_cmd *dc = arg;
int try = (cmd == VIDEO_TRY_COMMAND);
if (try)
- IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", vc->cmd);
+ IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND %d\n", dc->cmd);
else
- IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", vc->cmd);
- return ivtv_video_command(itv, id, vc, try);
+ IVTV_DEBUG_IOCTL("VIDEO_COMMAND %d\n", dc->cmd);
+ return ivtv_video_command(itv, id, dc, try);
}
case VIDEO_GET_EVENT: {
@@ -1775,17 +1763,13 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg)
IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n");
if (iarg > AUDIO_STEREO_SWAPPED)
return -EINVAL;
- itv->audio_stereo_mode = iarg;
- ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
- return 0;
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_playback, iarg);
case AUDIO_BILINGUAL_CHANNEL_SELECT:
IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n");
if (iarg > AUDIO_STEREO_SWAPPED)
return -EINVAL;
- itv->audio_bilingual_mode = iarg;
- ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode);
- return 0;
+ return v4l2_ctrl_s_ctrl(itv->ctrl_audio_multilingual_playback, iarg);
default:
return -EINVAL;
@@ -1800,6 +1784,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio,
if (!valid_prio) {
switch (cmd) {
+ case IVTV_IOC_PASSTHROUGH_MODE:
case VIDEO_PLAY:
case VIDEO_STOP:
case VIDEO_FREEZE:
@@ -1825,6 +1810,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio,
}
case IVTV_IOC_DMA_FRAME:
+ case IVTV_IOC_PASSTHROUGH_MODE:
case VIDEO_GET_PTS:
case VIDEO_GET_FRAME_COUNT:
case VIDEO_GET_EVENT:
@@ -1889,6 +1875,8 @@ static const struct v4l2_ioctl_ops ivtv_ioctl_ops = {
.vidioc_enum_fmt_vid_cap = ivtv_enum_fmt_vid_cap,
.vidioc_encoder_cmd = ivtv_encoder_cmd,
.vidioc_try_encoder_cmd = ivtv_try_encoder_cmd,
+ .vidioc_decoder_cmd = ivtv_decoder_cmd,
+ .vidioc_try_decoder_cmd = ivtv_try_decoder_cmd,
.vidioc_enum_fmt_vid_out = ivtv_enum_fmt_vid_out,
.vidioc_g_fmt_vid_cap = ivtv_g_fmt_vid_cap,
.vidioc_g_fmt_vbi_cap = ivtv_g_fmt_vbi_cap,
diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
index c6e28b4ebbed..7ea5ca7f012b 100644
--- a/drivers/media/video/ivtv/ivtv-streams.c
+++ b/drivers/media/video/ivtv/ivtv-streams.c
@@ -78,60 +78,73 @@ static struct {
int num_offset;
int dma, pio;
enum v4l2_buf_type buf_type;
+ u32 v4l2_caps;
const struct v4l2_file_operations *fops;
} ivtv_stream_info[] = {
{ /* IVTV_ENC_STREAM_TYPE_MPG */
"encoder MPG",
VFL_TYPE_GRABBER, 0,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_YUV */
"encoder YUV",
VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_VBI */
"encoder VBI",
VFL_TYPE_VBI, 0,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_TUNER |
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_PCM */
"encoder PCM",
VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET,
PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE,
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_ENC_STREAM_TYPE_RAD */
"encoder radio",
VFL_TYPE_RADIO, 0,
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE,
+ V4L2_CAP_RADIO | V4L2_CAP_TUNER,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_DEC_STREAM_TYPE_MPG */
"decoder MPG",
VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET,
PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_dec_fops
},
{ /* IVTV_DEC_STREAM_TYPE_VBI */
"decoder VBI",
VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET,
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_READWRITE,
&ivtv_v4l2_enc_fops
},
{ /* IVTV_DEC_STREAM_TYPE_VOUT */
"decoder VOUT",
VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET,
PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT,
+ V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_dec_fops
},
{ /* IVTV_DEC_STREAM_TYPE_YUV */
"decoder YUV",
VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET,
PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT,
+ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_AUDIO | V4L2_CAP_READWRITE,
&ivtv_v4l2_dec_fops
}
};
@@ -149,6 +162,7 @@ static void ivtv_stream_init(struct ivtv *itv, int type)
s->itv = itv;
s->type = type;
s->name = ivtv_stream_info[type].name;
+ s->caps = ivtv_stream_info[type].v4l2_caps;
if (ivtv_stream_info[type].pio)
s->dma = PCI_DMA_NONE;
@@ -209,8 +223,8 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
s->vdev->num = num;
s->vdev->v4l2_dev = &itv->v4l2_dev;
- s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->fops = ivtv_stream_info[type].fops;
+ s->vdev->ctrl_handler = itv->v4l2_dev.ctrl_handler;
s->vdev->release = video_device_release;
s->vdev->tvnorms = V4L2_STD_ALL;
s->vdev->lock = &itv->serialize_lock;
@@ -891,7 +905,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", (unsigned long long)pts, flags);
/* Stop Decoder */
- if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) {
+ if (!(flags & V4L2_DEC_CMD_STOP_IMMEDIATELY) || pts) {
u32 tmp = 0;
/* Wait until the decoder is no longer running */
@@ -911,7 +925,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
break;
}
}
- ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0);
+ ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & V4L2_DEC_CMD_STOP_TO_BLACK, 0, 0);
/* turn off notification of dual/stereo mode change */
ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1);
diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
index afa91182b448..ee7ca2dcca2f 100644
--- a/drivers/media/video/ks0127.c
+++ b/drivers/media/video/ks0127.c
@@ -721,15 +721,4 @@ static struct i2c_driver ks0127_driver = {
.id_table = ks0127_id,
};
-static __init int init_ks0127(void)
-{
- return i2c_add_driver(&ks0127_driver);
-}
-
-static __exit void exit_ks0127(void)
-{
- i2c_del_driver(&ks0127_driver);
-}
-
-module_init(init_ks0127);
-module_exit(exit_ks0127);
+module_i2c_driver(ks0127_driver);
diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
index 303ffa7df4ac..0991576f4c82 100644
--- a/drivers/media/video/m52790.c
+++ b/drivers/media/video/m52790.c
@@ -213,15 +213,4 @@ static struct i2c_driver m52790_driver = {
.id_table = m52790_id,
};
-static __init int init_m52790(void)
-{
- return i2c_add_driver(&m52790_driver);
-}
-
-static __exit void exit_m52790(void)
-{
- i2c_del_driver(&m52790_driver);
-}
-
-module_init(init_m52790);
-module_exit(exit_m52790);
+module_i2c_driver(m52790_driver);
diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c
index 93d768db9f33..d718aee01c77 100644
--- a/drivers/media/video/m5mols/m5mols_core.c
+++ b/drivers/media/video/m5mols/m5mols_core.c
@@ -982,8 +982,8 @@ static int __devinit m5mols_probe(struct i2c_client *client,
}
sd = &info->sd;
- strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
v4l2_i2c_subdev_init(sd, client, &m5mols_ops);
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
sd->internal_ops = &m5mols_subdev_internal_ops;
@@ -1057,18 +1057,7 @@ static struct i2c_driver m5mols_i2c_driver = {
.id_table = m5mols_id,
};
-static int __init m5mols_mod_init(void)
-{
- return i2c_add_driver(&m5mols_i2c_driver);
-}
-
-static void __exit m5mols_mod_exit(void)
-{
- i2c_del_driver(&m5mols_i2c_driver);
-}
-
-module_init(m5mols_mod_init);
-module_exit(m5mols_mod_exit);
+module_i2c_driver(m5mols_i2c_driver);
MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
MODULE_AUTHOR("Dongsoo Kim <dongsoo45.kim@samsung.com>");
diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c
index 37d20e73908a..996ac34d9a89 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.c
+++ b/drivers/media/video/marvell-ccic/mcam-core.c
@@ -509,11 +509,17 @@ static void mcam_sg_next_buffer(struct mcam_camera *cam)
buf = list_first_entry(&cam->buffers, struct mcam_vb_buffer, queue);
list_del_init(&buf->queue);
+ /*
+ * Very Bad Not Good Things happen if you don't clear
+ * C1_DESC_ENA before making any descriptor changes.
+ */
+ mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
mcam_reg_write(cam, REG_DMA_DESC_Y, buf->dma_desc_pa);
mcam_reg_write(cam, REG_DESC_LEN_Y,
buf->dma_desc_nent*sizeof(struct mcam_dma_desc));
mcam_reg_write(cam, REG_DESC_LEN_U, 0);
mcam_reg_write(cam, REG_DESC_LEN_V, 0);
+ mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
cam->vb_bufs[0] = buf;
}
@@ -533,7 +539,6 @@ static void mcam_ctlr_dma_sg(struct mcam_camera *cam)
mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_3WORD);
mcam_sg_next_buffer(cam);
- mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
cam->nbufs = 3;
}
@@ -556,17 +561,16 @@ static void mcam_dma_sg_done(struct mcam_camera *cam, int frame)
struct mcam_vb_buffer *buf = cam->vb_bufs[0];
/*
- * Very Bad Not Good Things happen if you don't clear
- * C1_DESC_ENA before making any descriptor changes.
+ * If we're no longer supposed to be streaming, don't do anything.
*/
- mcam_reg_clear_bit(cam, REG_CTRL1, C1_DESC_ENA);
+ if (cam->state != S_STREAMING)
+ return;
/*
* If we have another buffer available, put it in and
* restart the engine.
*/
if (!list_empty(&cam->buffers)) {
mcam_sg_next_buffer(cam);
- mcam_reg_set_bit(cam, REG_CTRL1, C1_DESC_ENA);
mcam_ctlr_start(cam);
/*
* Otherwise set CF_SG_RESTART and the controller will
@@ -737,7 +741,14 @@ static void mcam_ctlr_stop_dma(struct mcam_camera *cam)
mcam_ctlr_stop(cam);
cam->state = S_IDLE;
spin_unlock_irqrestore(&cam->dev_lock, flags);
- msleep(40);
+ /*
+ * This is a brutally long sleep, but experience shows that
+ * it can take the controller a while to get the message that
+ * it needs to stop grabbing frames. In particular, we can
+ * sometimes (on mmp) get a frame at the end WITHOUT the
+ * start-of-frame indication.
+ */
+ msleep(150);
if (test_bit(CF_DMA_ACTIVE, &cam->flags))
cam_err(cam, "Timeout waiting for DMA to end\n");
/* This would be bad news - what now? */
@@ -880,6 +891,7 @@ static int mcam_read_setup(struct mcam_camera *cam)
* Turn it loose.
*/
spin_lock_irqsave(&cam->dev_lock, flags);
+ clear_bit(CF_DMA_ACTIVE, &cam->flags);
mcam_reset_buffers(cam);
mcam_ctlr_irq_enable(cam);
cam->state = S_STREAMING;
@@ -922,7 +934,7 @@ static void mcam_vb_buf_queue(struct vb2_buffer *vb)
spin_lock_irqsave(&cam->dev_lock, flags);
start = (cam->state == S_BUFWAIT) && !list_empty(&cam->buffers);
list_add(&mvb->queue, &cam->buffers);
- if (test_bit(CF_SG_RESTART, &cam->flags))
+ if (cam->state == S_STREAMING && test_bit(CF_SG_RESTART, &cam->flags))
mcam_sg_restart(cam);
spin_unlock_irqrestore(&cam->dev_lock, flags);
if (start)
@@ -1555,15 +1567,12 @@ static int mcam_v4l_release(struct file *filp)
{
struct mcam_camera *cam = filp->private_data;
- cam_err(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
+ cam_dbg(cam, "Release, %d frames, %d singles, %d delivered\n", frames,
singles, delivered);
mutex_lock(&cam->s_mutex);
(cam->users)--;
- if (filp == cam->owner) {
- mcam_ctlr_stop_dma(cam);
- cam->owner = NULL;
- }
if (cam->users == 0) {
+ mcam_ctlr_stop_dma(cam);
mcam_cleanup_vb2(cam);
mcam_ctlr_power_down(cam);
if (cam->buffer_mode == B_vmalloc && alloc_bufs_at_read)
@@ -1688,6 +1697,8 @@ int mccic_irq(struct mcam_camera *cam, unsigned int irqs)
if (irqs & (IRQ_EOF0 << frame)) {
mcam_frame_complete(cam, frame);
handled = 1;
+ if (cam->buffer_mode == B_DMA_sg)
+ break;
}
/*
* If a frame starts, note that we have DMA active. This
diff --git a/drivers/media/video/marvell-ccic/mcam-core.h b/drivers/media/video/marvell-ccic/mcam-core.h
index 917200e63255..bd6acba9fb37 100644
--- a/drivers/media/video/marvell-ccic/mcam-core.h
+++ b/drivers/media/video/marvell-ccic/mcam-core.h
@@ -107,7 +107,6 @@ struct mcam_camera {
enum mcam_state state;
unsigned long flags; /* Buffer status, mainly (dev_lock) */
int users; /* How many open FDs */
- struct file *owner; /* Who has data access (v4l2) */
/*
* Subsystem structures.
diff --git a/drivers/media/video/marvell-ccic/mmp-driver.c b/drivers/media/video/marvell-ccic/mmp-driver.c
index 0d64e2d7474a..d23552323f45 100644
--- a/drivers/media/video/marvell-ccic/mmp-driver.c
+++ b/drivers/media/video/marvell-ccic/mmp-driver.c
@@ -106,6 +106,13 @@ static struct mmp_camera *mmpcam_find_device(struct platform_device *pdev)
/*
* Power control.
*/
+static void mmpcam_power_up_ctlr(struct mmp_camera *cam)
+{
+ iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
+ iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
+ mdelay(1);
+}
+
static void mmpcam_power_up(struct mcam_camera *mcam)
{
struct mmp_camera *cam = mcam_to_cam(mcam);
@@ -113,9 +120,7 @@ static void mmpcam_power_up(struct mcam_camera *mcam)
/*
* Turn on power and clocks to the controller.
*/
- iowrite32(0x3f, cam->power_regs + REG_CCIC_DCGCR);
- iowrite32(0x3805b, cam->power_regs + REG_CCIC_CRCR);
- mdelay(1);
+ mmpcam_power_up_ctlr(cam);
/*
* Provide power to the sensor.
*/
@@ -335,7 +340,7 @@ static int mmpcam_resume(struct platform_device *pdev)
* touch a register even if nothing was active before; trust
* me, it's better this way.
*/
- mmpcam_power_up(&cam->mcam);
+ mmpcam_power_up_ctlr(cam);
return mccic_resume(&cam->mcam);
}
diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
index d7cd0f633f63..82ce50721de3 100644
--- a/drivers/media/video/msp3400-driver.c
+++ b/drivers/media/video/msp3400-driver.c
@@ -881,18 +881,7 @@ static struct i2c_driver msp_driver = {
.id_table = msp_id,
};
-static __init int init_msp(void)
-{
- return i2c_add_driver(&msp_driver);
-}
-
-static __exit void exit_msp(void)
-{
- i2c_del_driver(&msp_driver);
-}
-
-module_init(init_msp);
-module_exit(exit_msp);
+module_i2c_driver(msp_driver);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
index 097c9d3d04a8..7e648183f157 100644
--- a/drivers/media/video/mt9m001.c
+++ b/drivers/media/video/mt9m001.c
@@ -730,18 +730,7 @@ static struct i2c_driver mt9m001_i2c_driver = {
.id_table = mt9m001_id,
};
-static int __init mt9m001_mod_init(void)
-{
- return i2c_add_driver(&mt9m001_i2c_driver);
-}
-
-static void __exit mt9m001_mod_exit(void)
-{
- i2c_del_driver(&mt9m001_i2c_driver);
-}
-
-module_init(mt9m001_mod_init);
-module_exit(mt9m001_mod_exit);
+module_i2c_driver(mt9m001_i2c_driver);
MODULE_DESCRIPTION("Micron MT9M001 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c
new file mode 100644
index 000000000000..7636672c3548
--- /dev/null
+++ b/drivers/media/video/mt9m032.c
@@ -0,0 +1,868 @@
+/*
+ * Driver for MT9M032 CMOS Image Sensor from Micron
+ *
+ * Copyright (C) 2010-2011 Lund Engineering
+ * Contact: Gil Lund <gwlund@lundeng.com>
+ * Author: Martin Hostettler <martin@neutronstar.dyndns.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.
+ *
+ * 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/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/v4l2-mediabus.h>
+
+#include <media/media-entity.h>
+#include <media/mt9m032.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "aptina-pll.h"
+
+/*
+ * width and height include active boundary and black parts
+ *
+ * column 0- 15 active boundary
+ * column 16-1455 image
+ * column 1456-1471 active boundary
+ * column 1472-1599 black
+ *
+ * row 0- 51 black
+ * row 53- 59 active boundary
+ * row 60-1139 image
+ * row 1140-1147 active boundary
+ * row 1148-1151 black
+ */
+
+#define MT9M032_PIXEL_ARRAY_WIDTH 1600
+#define MT9M032_PIXEL_ARRAY_HEIGHT 1152
+
+#define MT9M032_CHIP_VERSION 0x00
+#define MT9M032_CHIP_VERSION_VALUE 0x1402
+#define MT9M032_ROW_START 0x01
+#define MT9M032_ROW_START_MIN 0
+#define MT9M032_ROW_START_MAX 1152
+#define MT9M032_ROW_START_DEF 60
+#define MT9M032_COLUMN_START 0x02
+#define MT9M032_COLUMN_START_MIN 0
+#define MT9M032_COLUMN_START_MAX 1600
+#define MT9M032_COLUMN_START_DEF 16
+#define MT9M032_ROW_SIZE 0x03
+#define MT9M032_ROW_SIZE_MIN 32
+#define MT9M032_ROW_SIZE_MAX 1152
+#define MT9M032_ROW_SIZE_DEF 1080
+#define MT9M032_COLUMN_SIZE 0x04
+#define MT9M032_COLUMN_SIZE_MIN 32
+#define MT9M032_COLUMN_SIZE_MAX 1600
+#define MT9M032_COLUMN_SIZE_DEF 1440
+#define MT9M032_HBLANK 0x05
+#define MT9M032_VBLANK 0x06
+#define MT9M032_VBLANK_MAX 0x7ff
+#define MT9M032_SHUTTER_WIDTH_HIGH 0x08
+#define MT9M032_SHUTTER_WIDTH_LOW 0x09
+#define MT9M032_SHUTTER_WIDTH_MIN 1
+#define MT9M032_SHUTTER_WIDTH_MAX 1048575
+#define MT9M032_SHUTTER_WIDTH_DEF 1943
+#define MT9M032_PIX_CLK_CTRL 0x0a
+#define MT9M032_PIX_CLK_CTRL_INV_PIXCLK 0x8000
+#define MT9M032_RESTART 0x0b
+#define MT9M032_RESET 0x0d
+#define MT9M032_PLL_CONFIG1 0x11
+#define MT9M032_PLL_CONFIG1_OUTDIV_MASK 0x3f
+#define MT9M032_PLL_CONFIG1_MUL_SHIFT 8
+#define MT9M032_READ_MODE1 0x1e
+#define MT9M032_READ_MODE2 0x20
+#define MT9M032_READ_MODE2_VFLIP_SHIFT 15
+#define MT9M032_READ_MODE2_HFLIP_SHIFT 14
+#define MT9M032_READ_MODE2_ROW_BLC 0x40
+#define MT9M032_GAIN_GREEN1 0x2b
+#define MT9M032_GAIN_BLUE 0x2c
+#define MT9M032_GAIN_RED 0x2d
+#define MT9M032_GAIN_GREEN2 0x2e
+
+/* write only */
+#define MT9M032_GAIN_ALL 0x35
+#define MT9M032_GAIN_DIGITAL_MASK 0x7f
+#define MT9M032_GAIN_DIGITAL_SHIFT 8
+#define MT9M032_GAIN_AMUL_SHIFT 6
+#define MT9M032_GAIN_ANALOG_MASK 0x3f
+#define MT9M032_FORMATTER1 0x9e
+#define MT9M032_FORMATTER2 0x9f
+#define MT9M032_FORMATTER2_DOUT_EN 0x1000
+#define MT9M032_FORMATTER2_PIXCLK_EN 0x2000
+
+/*
+ * The available MT9M032 datasheet is missing documentation for register 0x10
+ * MT9P031 seems to be close enough, so use constants from that datasheet for
+ * now.
+ * But keep the name MT9P031 to remind us, that this isn't really confirmed
+ * for this sensor.
+ */
+#define MT9P031_PLL_CONTROL 0x10
+#define MT9P031_PLL_CONTROL_PWROFF 0x0050
+#define MT9P031_PLL_CONTROL_PWRON 0x0051
+#define MT9P031_PLL_CONTROL_USEPLL 0x0052
+#define MT9P031_PLL_CONFIG2 0x11
+#define MT9P031_PLL_CONFIG2_P1_DIV_MASK 0x1f
+
+struct mt9m032 {
+ struct v4l2_subdev subdev;
+ struct media_pad pad;
+ struct mt9m032_platform_data *pdata;
+
+ unsigned int pix_clock;
+
+ struct v4l2_ctrl_handler ctrls;
+ struct {
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
+ };
+
+ struct mutex lock; /* Protects streaming, format, interval and crop */
+
+ bool streaming;
+
+ struct v4l2_mbus_framefmt format;
+ struct v4l2_rect crop;
+ struct v4l2_fract frame_interval;
+};
+
+#define to_mt9m032(sd) container_of(sd, struct mt9m032, subdev)
+#define to_dev(sensor) \
+ (&((struct i2c_client *)v4l2_get_subdevdata(&(sensor)->subdev))->dev)
+
+static int mt9m032_read(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_word_swapped(client, reg);
+}
+
+static int mt9m032_write(struct i2c_client *client, u8 reg, const u16 data)
+{
+ return i2c_smbus_write_word_swapped(client, reg, data);
+}
+
+static u32 mt9m032_row_time(struct mt9m032 *sensor, unsigned int width)
+{
+ unsigned int effective_width;
+ u32 ns;
+
+ effective_width = width + 716; /* empirical value */
+ ns = div_u64(1000000000ULL * effective_width, sensor->pix_clock);
+ dev_dbg(to_dev(sensor), "MT9M032 line time: %u ns\n", ns);
+ return ns;
+}
+
+static int mt9m032_update_timing(struct mt9m032 *sensor,
+ struct v4l2_fract *interval)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ struct v4l2_rect *crop = &sensor->crop;
+ unsigned int min_vblank;
+ unsigned int vblank;
+ u32 row_time;
+
+ if (!interval)
+ interval = &sensor->frame_interval;
+
+ row_time = mt9m032_row_time(sensor, crop->width);
+
+ vblank = div_u64(1000000000ULL * interval->numerator,
+ (u64)row_time * interval->denominator)
+ - crop->height;
+
+ if (vblank > MT9M032_VBLANK_MAX) {
+ /* hardware limits to 11 bit values */
+ interval->denominator = 1000;
+ interval->numerator =
+ div_u64((crop->height + MT9M032_VBLANK_MAX) *
+ (u64)row_time * interval->denominator,
+ 1000000000ULL);
+ vblank = div_u64(1000000000ULL * interval->numerator,
+ (u64)row_time * interval->denominator)
+ - crop->height;
+ }
+ /* enforce minimal 1.6ms blanking time. */
+ min_vblank = 1600000 / row_time;
+ vblank = clamp_t(unsigned int, vblank, min_vblank, MT9M032_VBLANK_MAX);
+
+ return mt9m032_write(client, MT9M032_VBLANK, vblank);
+}
+
+static int mt9m032_update_geom_timing(struct mt9m032 *sensor)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int ret;
+
+ ret = mt9m032_write(client, MT9M032_COLUMN_SIZE,
+ sensor->crop.width - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_ROW_SIZE,
+ sensor->crop.height - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_COLUMN_START,
+ sensor->crop.left);
+ if (!ret)
+ ret = mt9m032_write(client, MT9M032_ROW_START,
+ sensor->crop.top);
+ if (!ret)
+ ret = mt9m032_update_timing(sensor, NULL);
+ return ret;
+}
+
+static int update_formatter2(struct mt9m032 *sensor, bool streaming)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ u16 reg_val = MT9M032_FORMATTER2_DOUT_EN
+ | 0x0070; /* parts reserved! */
+ /* possibly for changing to 14-bit mode */
+
+ if (streaming)
+ reg_val |= MT9M032_FORMATTER2_PIXCLK_EN; /* pixclock enable */
+
+ return mt9m032_write(client, MT9M032_FORMATTER2, reg_val);
+}
+
+static int mt9m032_setup_pll(struct mt9m032 *sensor)
+{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 8000000,
+ .ext_clock_max = 16500000,
+ .int_clock_min = 2000000,
+ .int_clock_max = 24000000,
+ .out_clock_min = 322000000,
+ .out_clock_max = 693000000,
+ .pix_clock_max = 99000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 255,
+ .p1_min = 1,
+ .p1_max = 128,
+ };
+
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ struct mt9m032_platform_data *pdata = sensor->pdata;
+ struct aptina_pll pll;
+ int ret;
+
+ pll.ext_clock = pdata->ext_clock;
+ pll.pix_clock = pdata->pix_clock;
+
+ ret = aptina_pll_calculate(&client->dev, &limits, &pll);
+ if (ret < 0)
+ return ret;
+
+ sensor->pix_clock = pdata->pix_clock;
+
+ ret = mt9m032_write(client, MT9M032_PLL_CONFIG1,
+ (pll.m << MT9M032_PLL_CONFIG1_MUL_SHIFT)
+ | (pll.p1 - 1));
+ if (!ret)
+ ret = mt9m032_write(client, MT9P031_PLL_CONFIG2, pll.n - 1);
+ if (!ret)
+ ret = mt9m032_write(client, MT9P031_PLL_CONTROL,
+ MT9P031_PLL_CONTROL_PWRON |
+ MT9P031_PLL_CONTROL_USEPLL);
+ if (!ret) /* more reserved, Continuous, Master Mode */
+ ret = mt9m032_write(client, MT9M032_READ_MODE1, 0x8006);
+ if (!ret) /* Set 14-bit mode, select 7 divider */
+ ret = mt9m032_write(client, MT9M032_FORMATTER1, 0x111e);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Subdev pad operations
+ */
+
+static int mt9m032_enum_mbus_code(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->index != 0)
+ return -EINVAL;
+
+ code->code = V4L2_MBUS_FMT_Y8_1X8;
+ return 0;
+}
+
+static int mt9m032_enum_frame_size(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_frame_size_enum *fse)
+{
+ if (fse->index != 0 || fse->code != V4L2_MBUS_FMT_Y8_1X8)
+ return -EINVAL;
+
+ fse->min_width = MT9M032_COLUMN_SIZE_DEF;
+ fse->max_width = MT9M032_COLUMN_SIZE_DEF;
+ fse->min_height = MT9M032_ROW_SIZE_DEF;
+ fse->max_height = MT9M032_ROW_SIZE_DEF;
+
+ return 0;
+}
+
+/**
+ * __mt9m032_get_pad_crop() - get crop rect
+ * @sensor: pointer to the sensor struct
+ * @fh: file handle for getting the try crop rect from
+ * @which: select try or active crop rect
+ *
+ * Returns a pointer the current active or fh relative try crop rect
+ */
+static struct v4l2_rect *
+__mt9m032_get_pad_crop(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_crop(fh, 0);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &sensor->crop;
+ default:
+ return NULL;
+ }
+}
+
+/**
+ * __mt9m032_get_pad_format() - get format
+ * @sensor: pointer to the sensor struct
+ * @fh: file handle for getting the try format from
+ * @which: select try or active format
+ *
+ * Returns a pointer the current active or fh relative try format
+ */
+static struct v4l2_mbus_framefmt *
+__mt9m032_get_pad_format(struct mt9m032 *sensor, struct v4l2_subdev_fh *fh,
+ enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_get_try_format(fh, 0);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &sensor->format;
+ default:
+ return NULL;
+ }
+}
+
+static int mt9m032_get_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ fmt->format = *__mt9m032_get_pad_format(sensor, fh, fmt->which);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_pad_format(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_format *fmt)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming && fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Scaling is not supported, the format is thus fixed. */
+ ret = mt9m032_get_pad_format(subdev, fh, fmt);
+
+done:
+ mutex_lock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_get_pad_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ crop->rect = *__mt9m032_get_pad_crop(sensor, fh, crop->which);
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_pad_crop(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_fh *fh,
+ struct v4l2_subdev_crop *crop)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ struct v4l2_mbus_framefmt *format;
+ struct v4l2_rect *__crop;
+ struct v4l2_rect rect;
+ int ret = 0;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming && crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Clamp the crop rectangle boundaries and align them to a multiple of 2
+ * pixels to ensure a GRBG Bayer pattern.
+ */
+ rect.left = clamp(ALIGN(crop->rect.left, 2), MT9M032_COLUMN_START_MIN,
+ MT9M032_COLUMN_START_MAX);
+ rect.top = clamp(ALIGN(crop->rect.top, 2), MT9M032_ROW_START_MIN,
+ MT9M032_ROW_START_MAX);
+ rect.width = clamp(ALIGN(crop->rect.width, 2), MT9M032_COLUMN_SIZE_MIN,
+ MT9M032_COLUMN_SIZE_MAX);
+ rect.height = clamp(ALIGN(crop->rect.height, 2), MT9M032_ROW_SIZE_MIN,
+ MT9M032_ROW_SIZE_MAX);
+
+ rect.width = min(rect.width, MT9M032_PIXEL_ARRAY_WIDTH - rect.left);
+ rect.height = min(rect.height, MT9M032_PIXEL_ARRAY_HEIGHT - rect.top);
+
+ __crop = __mt9m032_get_pad_crop(sensor, fh, crop->which);
+
+ if (rect.width != __crop->width || rect.height != __crop->height) {
+ /* Reset the output image size if the crop rectangle size has
+ * been modified.
+ */
+ format = __mt9m032_get_pad_format(sensor, fh, crop->which);
+ format->width = rect.width;
+ format->height = rect.height;
+ }
+
+ *__crop = rect;
+ crop->rect = rect;
+
+ if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE)
+ ret = mt9m032_update_geom_timing(sensor);
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_get_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ mutex_lock(&sensor->lock);
+ memset(fi, 0, sizeof(*fi));
+ fi->interval = sensor->frame_interval;
+ mutex_unlock(&sensor->lock);
+
+ return 0;
+}
+
+static int mt9m032_set_frame_interval(struct v4l2_subdev *subdev,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+
+ if (sensor->streaming) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ /* Avoid divisions by 0. */
+ if (fi->interval.denominator == 0)
+ fi->interval.denominator = 1;
+
+ ret = mt9m032_update_timing(sensor, &fi->interval);
+ if (!ret)
+ sensor->frame_interval = fi->interval;
+
+done:
+ mutex_unlock(&sensor->lock);
+ return ret;
+}
+
+static int mt9m032_s_stream(struct v4l2_subdev *subdev, int streaming)
+{
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+ int ret;
+
+ mutex_lock(&sensor->lock);
+ ret = update_formatter2(sensor, streaming);
+ if (!ret)
+ sensor->streaming = streaming;
+ mutex_unlock(&sensor->lock);
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev core operations
+ */
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int mt9m032_g_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct mt9m032 *sensor = to_mt9m032(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int val;
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ val = mt9m032_read(client, reg->reg);
+ if (val < 0)
+ return -EIO;
+
+ reg->size = 2;
+ reg->val = val;
+
+ return 0;
+}
+
+static int mt9m032_s_register(struct v4l2_subdev *sd,
+ struct v4l2_dbg_register *reg)
+{
+ struct mt9m032 *sensor = to_mt9m032(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+
+ if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff)
+ return -EINVAL;
+
+ if (reg->match.addr != client->addr)
+ return -ENODEV;
+
+ return mt9m032_write(client, reg->reg, reg->val);
+}
+#endif
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev control operations
+ */
+
+static int update_read_mode2(struct mt9m032 *sensor, bool vflip, bool hflip)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int reg_val = (vflip << MT9M032_READ_MODE2_VFLIP_SHIFT)
+ | (hflip << MT9M032_READ_MODE2_HFLIP_SHIFT)
+ | MT9M032_READ_MODE2_ROW_BLC
+ | 0x0007;
+
+ return mt9m032_write(client, MT9M032_READ_MODE2, reg_val);
+}
+
+static int mt9m032_set_gain(struct mt9m032 *sensor, s32 val)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int digital_gain_val; /* in 1/8th (0..127) */
+ int analog_mul; /* 0 or 1 */
+ int analog_gain_val; /* in 1/16th. (0..63) */
+ u16 reg_val;
+
+ digital_gain_val = 51; /* from setup example */
+
+ if (val < 63) {
+ analog_mul = 0;
+ analog_gain_val = val;
+ } else {
+ analog_mul = 1;
+ analog_gain_val = val / 2;
+ }
+
+ /* a_gain = (1 + analog_mul) + (analog_gain_val + 1) / 16 */
+ /* overall_gain = a_gain * (1 + digital_gain_val / 8) */
+
+ reg_val = ((digital_gain_val & MT9M032_GAIN_DIGITAL_MASK)
+ << MT9M032_GAIN_DIGITAL_SHIFT)
+ | ((analog_mul & 1) << MT9M032_GAIN_AMUL_SHIFT)
+ | (analog_gain_val & MT9M032_GAIN_ANALOG_MASK);
+
+ return mt9m032_write(client, MT9M032_GAIN_ALL, reg_val);
+}
+
+static int mt9m032_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ if (ctrl->id == V4L2_CID_GAIN && ctrl->val >= 63) {
+ /* round because of multiplier used for values >= 63 */
+ ctrl->val &= ~1;
+ }
+
+ return 0;
+}
+
+static int mt9m032_set_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct mt9m032 *sensor =
+ container_of(ctrl->handler, struct mt9m032, ctrls);
+ struct i2c_client *client = v4l2_get_subdevdata(&sensor->subdev);
+ int ret;
+
+ switch (ctrl->id) {
+ case V4L2_CID_GAIN:
+ return mt9m032_set_gain(sensor, ctrl->val);
+
+ case V4L2_CID_HFLIP:
+ /* case V4L2_CID_VFLIP: -- In the same cluster */
+ return update_read_mode2(sensor, sensor->vflip->val,
+ sensor->hflip->val);
+
+ case V4L2_CID_EXPOSURE:
+ ret = mt9m032_write(client, MT9M032_SHUTTER_WIDTH_HIGH,
+ (ctrl->val >> 16) & 0xffff);
+ if (ret < 0)
+ return ret;
+
+ return mt9m032_write(client, MT9M032_SHUTTER_WIDTH_LOW,
+ ctrl->val & 0xffff);
+ }
+
+ return 0;
+}
+
+static struct v4l2_ctrl_ops mt9m032_ctrl_ops = {
+ .s_ctrl = mt9m032_set_ctrl,
+ .try_ctrl = mt9m032_try_ctrl,
+};
+
+/* -------------------------------------------------------------------------- */
+
+static const struct v4l2_subdev_core_ops mt9m032_core_ops = {
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = mt9m032_g_register,
+ .s_register = mt9m032_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops mt9m032_video_ops = {
+ .s_stream = mt9m032_s_stream,
+ .g_frame_interval = mt9m032_get_frame_interval,
+ .s_frame_interval = mt9m032_set_frame_interval,
+};
+
+static const struct v4l2_subdev_pad_ops mt9m032_pad_ops = {
+ .enum_mbus_code = mt9m032_enum_mbus_code,
+ .enum_frame_size = mt9m032_enum_frame_size,
+ .get_fmt = mt9m032_get_pad_format,
+ .set_fmt = mt9m032_set_pad_format,
+ .set_crop = mt9m032_set_pad_crop,
+ .get_crop = mt9m032_get_pad_crop,
+};
+
+static const struct v4l2_subdev_ops mt9m032_ops = {
+ .core = &mt9m032_core_ops,
+ .video = &mt9m032_video_ops,
+ .pad = &mt9m032_pad_ops,
+};
+
+/* -----------------------------------------------------------------------------
+ * Driver initialization and probing
+ */
+
+static int mt9m032_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ struct mt9m032 *sensor;
+ int chip_version;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_warn(&client->dev,
+ "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
+ return -EIO;
+ }
+
+ if (!client->dev.platform_data)
+ return -ENODEV;
+
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL)
+ return -ENOMEM;
+
+ mutex_init(&sensor->lock);
+
+ sensor->pdata = client->dev.platform_data;
+
+ v4l2_i2c_subdev_init(&sensor->subdev, client, &mt9m032_ops);
+ sensor->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+
+ chip_version = mt9m032_read(client, MT9M032_CHIP_VERSION);
+ if (chip_version != MT9M032_CHIP_VERSION_VALUE) {
+ dev_err(&client->dev, "MT9M032 not detected, wrong version "
+ "0x%04x\n", chip_version);
+ ret = -ENODEV;
+ goto error_sensor;
+ }
+
+ dev_info(&client->dev, "MT9M032 detected at address 0x%02x\n",
+ client->addr);
+
+ sensor->frame_interval.numerator = 1;
+ sensor->frame_interval.denominator = 30;
+
+ sensor->crop.left = MT9M032_COLUMN_START_DEF;
+ sensor->crop.top = MT9M032_ROW_START_DEF;
+ sensor->crop.width = MT9M032_COLUMN_SIZE_DEF;
+ sensor->crop.height = MT9M032_ROW_SIZE_DEF;
+
+ sensor->format.width = sensor->crop.width;
+ sensor->format.height = sensor->crop.height;
+ sensor->format.code = V4L2_MBUS_FMT_Y8_1X8;
+ sensor->format.field = V4L2_FIELD_NONE;
+ sensor->format.colorspace = V4L2_COLORSPACE_SRGB;
+
+ v4l2_ctrl_handler_init(&sensor->ctrls, 4);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_GAIN, 0, 127, 1, 64);
+
+ sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls,
+ &mt9m032_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls,
+ &mt9m032_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+
+ v4l2_ctrl_new_std(&sensor->ctrls, &mt9m032_ctrl_ops,
+ V4L2_CID_EXPOSURE, MT9M032_SHUTTER_WIDTH_MIN,
+ MT9M032_SHUTTER_WIDTH_MAX, 1,
+ MT9M032_SHUTTER_WIDTH_DEF);
+
+ if (sensor->ctrls.error) {
+ ret = sensor->ctrls.error;
+ dev_err(&client->dev, "control initialization error %d\n", ret);
+ goto error_ctrl;
+ }
+
+ v4l2_ctrl_cluster(2, &sensor->hflip);
+
+ sensor->subdev.ctrl_handler = &sensor->ctrls;
+ sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ ret = media_entity_init(&sensor->subdev.entity, 1, &sensor->pad, 0);
+ if (ret < 0)
+ goto error_ctrl;
+
+ ret = mt9m032_write(client, MT9M032_RESET, 1); /* reset on */
+ if (ret < 0)
+ goto error_entity;
+ mt9m032_write(client, MT9M032_RESET, 0); /* reset off */
+ if (ret < 0)
+ goto error_entity;
+
+ ret = mt9m032_setup_pll(sensor);
+ if (ret < 0)
+ goto error_entity;
+ usleep_range(10000, 11000);
+
+ ret = v4l2_ctrl_handler_setup(&sensor->ctrls);
+ if (ret < 0)
+ goto error_entity;
+
+ /* SIZE */
+ ret = mt9m032_update_geom_timing(sensor);
+ if (ret < 0)
+ goto error_entity;
+
+ ret = mt9m032_write(client, 0x41, 0x0000); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x42, 0x0003); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x43, 0x0003); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ ret = mt9m032_write(client, 0x7f, 0x0000); /* reserved !!! */
+ if (ret < 0)
+ goto error_entity;
+ if (sensor->pdata->invert_pixclock) {
+ ret = mt9m032_write(client, MT9M032_PIX_CLK_CTRL,
+ MT9M032_PIX_CLK_CTRL_INV_PIXCLK);
+ if (ret < 0)
+ goto error_entity;
+ }
+
+ ret = mt9m032_write(client, MT9M032_RESTART, 1); /* Restart on */
+ if (ret < 0)
+ goto error_entity;
+ msleep(100);
+ ret = mt9m032_write(client, MT9M032_RESTART, 0); /* Restart off */
+ if (ret < 0)
+ goto error_entity;
+ msleep(100);
+ ret = update_formatter2(sensor, false);
+ if (ret < 0)
+ goto error_entity;
+
+ return ret;
+
+error_entity:
+ media_entity_cleanup(&sensor->subdev.entity);
+error_ctrl:
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+error_sensor:
+ mutex_destroy(&sensor->lock);
+ kfree(sensor);
+ return ret;
+}
+
+static int mt9m032_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *subdev = i2c_get_clientdata(client);
+ struct mt9m032 *sensor = to_mt9m032(subdev);
+
+ v4l2_device_unregister_subdev(&sensor->subdev);
+ v4l2_ctrl_handler_free(&sensor->ctrls);
+ media_entity_cleanup(&sensor->subdev.entity);
+ mutex_destroy(&sensor->lock);
+ kfree(sensor);
+ return 0;
+}
+
+static const struct i2c_device_id mt9m032_id_table[] = {
+ { MT9M032_NAME, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, mt9m032_id_table);
+
+static struct i2c_driver mt9m032_i2c_driver = {
+ .driver = {
+ .name = MT9M032_NAME,
+ },
+ .probe = mt9m032_probe,
+ .remove = mt9m032_remove,
+ .id_table = mt9m032_id_table,
+};
+
+module_i2c_driver(mt9m032_i2c_driver);
+
+MODULE_AUTHOR("Martin Hostettler <martin@neutronstar.dyndns.org>");
+MODULE_DESCRIPTION("MT9M032 camera sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
index bee65bff46e8..b0c529964329 100644
--- a/drivers/media/video/mt9m111.c
+++ b/drivers/media/video/mt9m111.c
@@ -1008,18 +1008,7 @@ static struct i2c_driver mt9m111_i2c_driver = {
.id_table = mt9m111_id,
};
-static int __init mt9m111_mod_init(void)
-{
- return i2c_add_driver(&mt9m111_i2c_driver);
-}
-
-static void __exit mt9m111_mod_exit(void)
-{
- i2c_del_driver(&mt9m111_i2c_driver);
-}
-
-module_init(mt9m111_mod_init);
-module_exit(mt9m111_mod_exit);
+module_i2c_driver(mt9m111_i2c_driver);
MODULE_DESCRIPTION("Micron/Aptina MT9M111/MT9M112/MT9M131 Camera driver");
MODULE_AUTHOR("Robert Jarzmik");
diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c
index 93c3ec7426e8..c81eaf4fbe01 100644
--- a/drivers/media/video/mt9p031.c
+++ b/drivers/media/video/mt9p031.c
@@ -19,7 +19,6 @@
#include <linux/log2.h>
#include <linux/pm.h>
#include <linux/slab.h>
-#include <media/v4l2-subdev.h>
#include <linux/videodev2.h>
#include <media/mt9p031.h>
@@ -28,6 +27,8 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
+#include "aptina-pll.h"
+
#define MT9P031_PIXEL_ARRAY_WIDTH 2752
#define MT9P031_PIXEL_ARRAY_HEIGHT 2004
@@ -98,14 +99,6 @@
#define MT9P031_TEST_PATTERN_RED 0xa2
#define MT9P031_TEST_PATTERN_BLUE 0xa3
-struct mt9p031_pll_divs {
- u32 ext_freq;
- u32 target_freq;
- u8 m;
- u8 n;
- u8 p1;
-};
-
struct mt9p031 {
struct v4l2_subdev subdev;
struct media_pad pad;
@@ -115,10 +108,8 @@ struct mt9p031 {
struct mt9p031_platform_data *pdata;
struct mutex power_lock; /* lock to protect power_count */
int power_count;
- u16 xskip;
- u16 yskip;
- const struct mt9p031_pll_divs *pll;
+ struct aptina_pll pll;
/* Registers cache */
u16 output_control;
@@ -186,33 +177,31 @@ static int mt9p031_reset(struct mt9p031 *mt9p031)
0);
}
-/*
- * This static table uses ext_freq and vdd_io values to select suitable
- * PLL dividers m, n and p1 which have been calculated as specifiec in p36
- * of Aptina's mt9p031 datasheet. New values should be added here.
- */
-static const struct mt9p031_pll_divs mt9p031_divs[] = {
- /* ext_freq target_freq m n p1 */
- {21000000, 48000000, 26, 2, 6}
-};
-
-static int mt9p031_pll_get_divs(struct mt9p031 *mt9p031)
+static int mt9p031_pll_setup(struct mt9p031 *mt9p031)
{
+ static const struct aptina_pll_limits limits = {
+ .ext_clock_min = 6000000,
+ .ext_clock_max = 27000000,
+ .int_clock_min = 2000000,
+ .int_clock_max = 13500000,
+ .out_clock_min = 180000000,
+ .out_clock_max = 360000000,
+ .pix_clock_max = 96000000,
+ .n_min = 1,
+ .n_max = 64,
+ .m_min = 16,
+ .m_max = 255,
+ .p1_min = 1,
+ .p1_max = 128,
+ };
+
struct i2c_client *client = v4l2_get_subdevdata(&mt9p031->subdev);
- int i;
+ struct mt9p031_platform_data *pdata = mt9p031->pdata;
- for (i = 0; i < ARRAY_SIZE(mt9p031_divs); i++) {
- if (mt9p031_divs[i].ext_freq == mt9p031->pdata->ext_freq &&
- mt9p031_divs[i].target_freq == mt9p031->pdata->target_freq) {
- mt9p031->pll = &mt9p031_divs[i];
- return 0;
- }
- }
+ mt9p031->pll.ext_clock = pdata->ext_freq;
+ mt9p031->pll.pix_clock = pdata->target_freq;
- dev_err(&client->dev, "Couldn't find PLL dividers for ext_freq = %d, "
- "target_freq = %d\n", mt9p031->pdata->ext_freq,
- mt9p031->pdata->target_freq);
- return -EINVAL;
+ return aptina_pll_calculate(&client->dev, &limits, &mt9p031->pll);
}
static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
@@ -226,11 +215,11 @@ static int mt9p031_pll_enable(struct mt9p031 *mt9p031)
return ret;
ret = mt9p031_write(client, MT9P031_PLL_CONFIG_1,
- (mt9p031->pll->m << 8) | (mt9p031->pll->n - 1));
+ (mt9p031->pll.m << 8) | (mt9p031->pll.n - 1));
if (ret < 0)
return ret;
- ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll->p1 - 1);
+ ret = mt9p031_write(client, MT9P031_PLL_CONFIG_2, mt9p031->pll.p1 - 1);
if (ret < 0)
return ret;
@@ -785,8 +774,6 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh)
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
- mt9p031->xskip = 1;
- mt9p031->yskip = 1;
return mt9p031_set_power(subdev, 1);
}
@@ -905,7 +892,7 @@ static int mt9p031_probe(struct i2c_client *client,
mt9p031->format.field = V4L2_FIELD_NONE;
mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB;
- ret = mt9p031_pll_get_divs(mt9p031);
+ ret = mt9p031_pll_setup(mt9p031);
done:
if (ret < 0) {
@@ -945,18 +932,7 @@ static struct i2c_driver mt9p031_i2c_driver = {
.id_table = mt9p031_id,
};
-static int __init mt9p031_mod_init(void)
-{
- return i2c_add_driver(&mt9p031_i2c_driver);
-}
-
-static void __exit mt9p031_mod_exit(void)
-{
- i2c_del_driver(&mt9p031_i2c_driver);
-}
-
-module_init(mt9p031_mod_init);
-module_exit(mt9p031_mod_exit);
+module_i2c_driver(mt9p031_i2c_driver);
MODULE_DESCRIPTION("Aptina MT9P031 Camera driver");
MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
diff --git a/drivers/media/video/mt9t001.c b/drivers/media/video/mt9t001.c
index cd81d04a529e..49ca3cbfc6f1 100644
--- a/drivers/media/video/mt9t001.c
+++ b/drivers/media/video/mt9t001.c
@@ -817,18 +817,7 @@ static struct i2c_driver mt9t001_driver = {
.id_table = mt9t001_id,
};
-static int __init mt9t001_init(void)
-{
- return i2c_add_driver(&mt9t001_driver);
-}
-
-static void __exit mt9t001_exit(void)
-{
- i2c_del_driver(&mt9t001_driver);
-}
-
-module_init(mt9t001_init);
-module_exit(mt9t001_exit);
+module_i2c_driver(mt9t001_driver);
MODULE_DESCRIPTION("Aptina (Micron) MT9T001 Camera driver");
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
index 84add1aef139..1415074138a5 100644
--- a/drivers/media/video/mt9t031.c
+++ b/drivers/media/video/mt9t031.c
@@ -850,18 +850,7 @@ static struct i2c_driver mt9t031_i2c_driver = {
.id_table = mt9t031_id,
};
-static int __init mt9t031_mod_init(void)
-{
- return i2c_add_driver(&mt9t031_i2c_driver);
-}
-
-static void __exit mt9t031_mod_exit(void)
-{
- i2c_del_driver(&mt9t031_i2c_driver);
-}
-
-module_init(mt9t031_mod_init);
-module_exit(mt9t031_mod_exit);
+module_i2c_driver(mt9t031_i2c_driver);
MODULE_DESCRIPTION("Micron MT9T031 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c
index 7b34b11daf24..8d1445f12708 100644
--- a/drivers/media/video/mt9t112.c
+++ b/drivers/media/video/mt9t112.c
@@ -1117,21 +1117,7 @@ static struct i2c_driver mt9t112_i2c_driver = {
.id_table = mt9t112_id,
};
-/************************************************************************
- module function
-************************************************************************/
-static int __init mt9t112_module_init(void)
-{
- return i2c_add_driver(&mt9t112_i2c_driver);
-}
-
-static void __exit mt9t112_module_exit(void)
-{
- i2c_del_driver(&mt9t112_i2c_driver);
-}
-
-module_init(mt9t112_module_init);
-module_exit(mt9t112_module_exit);
+module_i2c_driver(mt9t112_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for mt9t112");
MODULE_AUTHOR("Kuninori Morimoto");
diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c
index db74dd27c722..6bf01ad62765 100644
--- a/drivers/media/video/mt9v011.c
+++ b/drivers/media/video/mt9v011.c
@@ -709,15 +709,4 @@ static struct i2c_driver mt9v011_driver = {
.id_table = mt9v011_id,
};
-static __init int init_mt9v011(void)
-{
- return i2c_add_driver(&mt9v011_driver);
-}
-
-static __exit void exit_mt9v011(void)
-{
- i2c_del_driver(&mt9v011_driver);
-}
-
-module_init(init_mt9v011);
-module_exit(exit_mt9v011);
+module_i2c_driver(mt9v011_driver);
diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
index 944940758fa3..bf63417adb8f 100644
--- a/drivers/media/video/mt9v022.c
+++ b/drivers/media/video/mt9v022.c
@@ -872,18 +872,7 @@ static struct i2c_driver mt9v022_i2c_driver = {
.id_table = mt9v022_id,
};
-static int __init mt9v022_mod_init(void)
-{
- return i2c_add_driver(&mt9v022_i2c_driver);
-}
-
-static void __exit mt9v022_mod_exit(void)
-{
- i2c_del_driver(&mt9v022_i2c_driver);
-}
-
-module_init(mt9v022_mod_init);
-module_exit(mt9v022_mod_exit);
+module_i2c_driver(mt9v022_i2c_driver);
MODULE_DESCRIPTION("Micron MT9V022 Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>");
diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c
index d90b982cc218..75e253a343c5 100644
--- a/drivers/media/video/mt9v032.c
+++ b/drivers/media/video/mt9v032.c
@@ -756,18 +756,7 @@ static struct i2c_driver mt9v032_driver = {
.id_table = mt9v032_id,
};
-static int __init mt9v032_init(void)
-{
- return i2c_add_driver(&mt9v032_driver);
-}
-
-static void __exit mt9v032_exit(void)
-{
- i2c_del_driver(&mt9v032_driver);
-}
-
-module_init(mt9v032_init);
-module_exit(mt9v032_exit);
+module_i2c_driver(mt9v032_driver);
MODULE_DESCRIPTION("Aptina MT9V032 Camera driver");
MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>");
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 04aab0c538aa..18afaeeadb7b 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2008, Sascha Hauer, Pengutronix
* Copyright (C) 2010, Baruch Siach, Orex Computed Radiography
+ * Copyright (C) 2012, Javier Martin, Vista Silicon S.L.
*
* 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
@@ -18,6 +19,7 @@
#include <linux/dma-mapping.h>
#include <linux/errno.h>
#include <linux/fs.h>
+#include <linux/gcd.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mm.h>
@@ -30,17 +32,14 @@
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
-#include <media/videobuf-core.h>
-#include <media/videobuf-dma-contig.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-dma-contig.h>
#include <media/soc_camera.h>
#include <media/soc_mediabus.h>
#include <linux/videodev2.h>
#include <mach/mx2_cam.h>
-#ifdef CONFIG_MACH_MX27
-#include <mach/dma-mx1-mx2.h>
-#endif
#include <mach/hardware.h>
#include <asm/dma.h>
@@ -206,10 +205,23 @@
#define PRP_INTR_LBOVF (1 << 7)
#define PRP_INTR_CH2OVF (1 << 8)
-#define mx27_camera_emma(pcdev) (cpu_is_mx27() && pcdev->use_emma)
+/* Resizing registers */
+#define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24)
+#define PRP_RZ_VALID_BILINEAR (1 << 31)
#define MAX_VIDEO_MEM 16
+#define RESIZE_NUM_MIN 1
+#define RESIZE_NUM_MAX 20
+#define BC_COEF 3
+#define SZ_COEF (1 << BC_COEF)
+
+#define RESIZE_DIR_H 0
+#define RESIZE_DIR_V 1
+
+#define RESIZE_ALGO_BILINEAR 0
+#define RESIZE_ALGO_AVERAGING 1
+
struct mx2_prp_cfg {
int channel;
u32 in_fmt;
@@ -219,6 +231,13 @@ struct mx2_prp_cfg {
u32 irq_flags;
};
+/* prp resizing parameters */
+struct emma_prp_resize {
+ int algo; /* type of algorithm used */
+ int len; /* number of coefficients */
+ unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */
+};
+
/* prp configuration for a client-host fmt pair */
struct mx2_fmt_cfg {
enum v4l2_mbus_pixelcode in_fmt;
@@ -226,6 +245,26 @@ struct mx2_fmt_cfg {
struct mx2_prp_cfg cfg;
};
+enum mx2_buffer_state {
+ MX2_STATE_QUEUED,
+ MX2_STATE_ACTIVE,
+ MX2_STATE_DONE,
+};
+
+struct mx2_buf_internal {
+ struct list_head queue;
+ int bufnum;
+ bool discard;
+};
+
+/* buffer for one video frame */
+struct mx2_buffer {
+ /* common v4l buffer stuff -- must be first */
+ struct vb2_buffer vb;
+ enum mx2_buffer_state state;
+ struct mx2_buf_internal internal;
+};
+
struct mx2_camera_dev {
struct device *dev;
struct soc_camera_host soc_host;
@@ -242,6 +281,7 @@ struct mx2_camera_dev {
struct list_head capture;
struct list_head active_bufs;
+ struct list_head discard;
spinlock_t lock;
@@ -250,26 +290,23 @@ struct mx2_camera_dev {
struct mx2_buffer *fb1_active;
struct mx2_buffer *fb2_active;
- int use_emma;
-
u32 csicr1;
+ struct mx2_buf_internal buf_discard[2];
void *discard_buffer;
dma_addr_t discard_buffer_dma;
size_t discard_size;
struct mx2_fmt_cfg *emma_prp;
+ struct emma_prp_resize resizing[2];
+ unsigned int s_width, s_height;
u32 frame_count;
+ struct vb2_alloc_ctx *alloc_ctx;
};
-/* buffer for one video frame */
-struct mx2_buffer {
- /* common v4l buffer stuff -- must be first */
- struct videobuf_buffer vb;
-
- enum v4l2_mbus_pixelcode code;
-
- int bufnum;
-};
+static struct mx2_buffer *mx2_ibuf_to_buf(struct mx2_buf_internal *int_buf)
+{
+ return container_of(int_buf, struct mx2_buffer, internal);
+}
static struct mx2_fmt_cfg mx27_emma_prp_table[] = {
/*
@@ -324,13 +361,36 @@ static struct mx2_fmt_cfg *mx27_emma_prp_get_format(
return &mx27_emma_prp_table[0];
};
+static void mx27_update_emma_buf(struct mx2_camera_dev *pcdev,
+ unsigned long phys, int bufnum)
+{
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+
+ if (prp->cfg.channel == 1) {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum);
+ } else {
+ writel(phys, pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum);
+ if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
+ u32 imgsize = pcdev->icd->user_height *
+ pcdev->icd->user_width;
+
+ writel(phys + imgsize, pcdev->base_emma +
+ PRP_DEST_CB_PTR - 0x14 * bufnum);
+ writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
+ PRP_DEST_CR_PTR - 0x14 * bufnum);
+ }
+ }
+}
+
static void mx2_camera_deactivate(struct mx2_camera_dev *pcdev)
{
unsigned long flags;
clk_disable(pcdev->clk_csi);
writel(0, pcdev->base_csi + CSICR1);
- if (mx27_camera_emma(pcdev)) {
+ if (cpu_is_mx27()) {
writel(0, pcdev->base_emma + PRP_CNTL);
} else if (cpu_is_mx25()) {
spin_lock_irqsave(&pcdev->lock, flags);
@@ -362,7 +422,7 @@ static int mx2_camera_add_device(struct soc_camera_device *icd)
csicr1 = CSICR1_MCLKEN;
- if (mx27_camera_emma(pcdev)) {
+ if (cpu_is_mx27()) {
csicr1 |= CSICR1_PRP_IF_EN | CSICR1_FCC |
CSICR1_RXFF_LEVEL(0);
} else if (cpu_is_mx27())
@@ -392,56 +452,13 @@ static void mx2_camera_remove_device(struct soc_camera_device *icd)
mx2_camera_deactivate(pcdev);
- if (pcdev->discard_buffer) {
- dma_free_coherent(ici->v4l2_dev.dev, pcdev->discard_size,
- pcdev->discard_buffer,
- pcdev->discard_buffer_dma);
- pcdev->discard_buffer = NULL;
- }
-
pcdev->icd = NULL;
}
-#ifdef CONFIG_MACH_MX27
-static void mx27_camera_dma_enable(struct mx2_camera_dev *pcdev)
-{
- u32 tmp;
-
- imx_dma_enable(pcdev->dma);
-
- tmp = readl(pcdev->base_csi + CSICR1);
- tmp |= CSICR1_RF_OR_INTEN;
- writel(tmp, pcdev->base_csi + CSICR1);
-}
-
-static irqreturn_t mx27_camera_irq(int irq_csi, void *data)
-{
- struct mx2_camera_dev *pcdev = data;
- u32 status = readl(pcdev->base_csi + CSISR);
-
- if (status & CSISR_SOF_INT && pcdev->active) {
- u32 tmp;
-
- tmp = readl(pcdev->base_csi + CSICR1);
- writel(tmp | CSICR1_CLR_RXFIFO, pcdev->base_csi + CSICR1);
- mx27_camera_dma_enable(pcdev);
- }
-
- writel(CSISR_SOF_INT | CSISR_RFF_OR_INT, pcdev->base_csi + CSISR);
-
- return IRQ_HANDLED;
-}
-#else
-static irqreturn_t mx27_camera_irq(int irq_csi, void *data)
-{
- return IRQ_NONE;
-}
-#endif /* CONFIG_MACH_MX27 */
-
static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
int state)
{
- struct videobuf_buffer *vb;
+ struct vb2_buffer *vb;
struct mx2_buffer *buf;
struct mx2_buffer **fb_active = fb == 1 ? &pcdev->fb1_active :
&pcdev->fb2_active;
@@ -454,25 +471,24 @@ static void mx25_camera_frame_done(struct mx2_camera_dev *pcdev, int fb,
goto out;
vb = &(*fb_active)->vb;
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
- vb->state = state;
- do_gettimeofday(&vb->ts);
- vb->field_count++;
-
- wake_up(&vb->done);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence++;
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
if (list_empty(&pcdev->capture)) {
buf = NULL;
writel(0, pcdev->base_csi + fb_reg);
} else {
- buf = list_entry(pcdev->capture.next, struct mx2_buffer,
- vb.queue);
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
vb = &buf->vb;
- list_del(&vb->queue);
- vb->state = VIDEOBUF_ACTIVE;
- writel(videobuf_to_dma_contig(vb), pcdev->base_csi + fb_reg);
+ list_del(&buf->internal.queue);
+ buf->state = MX2_STATE_ACTIVE;
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
+ pcdev->base_csi + fb_reg);
}
*fb_active = buf;
@@ -487,9 +503,9 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
u32 status = readl(pcdev->base_csi + CSISR);
if (status & CSISR_DMA_TSF_FB1_INT)
- mx25_camera_frame_done(pcdev, 1, VIDEOBUF_DONE);
+ mx25_camera_frame_done(pcdev, 1, MX2_STATE_DONE);
else if (status & CSISR_DMA_TSF_FB2_INT)
- mx25_camera_frame_done(pcdev, 2, VIDEOBUF_DONE);
+ mx25_camera_frame_done(pcdev, 2, MX2_STATE_DONE);
/* FIXME: handle CSISR_RFF_OR_INT */
@@ -501,59 +517,50 @@ static irqreturn_t mx25_camera_irq(int irq_csi, void *data)
/*
* Videobuf operations
*/
-static int mx2_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
- unsigned int *size)
+static int mx2_videobuf_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *count, unsigned int *num_planes,
+ unsigned int sizes[], void *alloc_ctxs[])
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vq);
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
- dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size);
+ dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]);
+
+ /* TODO: support for VIDIOC_CREATE_BUFS not ready */
+ if (fmt != NULL)
+ return -ENOTTY;
if (bytes_per_line < 0)
return bytes_per_line;
- *size = bytes_per_line * icd->user_height;
+ alloc_ctxs[0] = pcdev->alloc_ctx;
+
+ sizes[0] = bytes_per_line * icd->user_height;
if (0 == *count)
*count = 32;
- if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
- *count = (MAX_VIDEO_MEM * 1024 * 1024) / *size;
+ if (!*num_planes &&
+ sizes[0] * *count > MAX_VIDEO_MEM * 1024 * 1024)
+ *count = (MAX_VIDEO_MEM * 1024 * 1024) / sizes[0];
- return 0;
-}
+ *num_planes = 1;
-static void free_buffer(struct videobuf_queue *vq, struct mx2_buffer *buf)
-{
- struct soc_camera_device *icd = vq->priv_data;
- struct videobuf_buffer *vb = &buf->vb;
-
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
-
- /*
- * This waits until this buffer is out of danger, i.e., until it is no
- * longer in state VIDEOBUF_QUEUED or VIDEOBUF_ACTIVE
- */
- videobuf_waiton(vq, vb, 0, 0);
-
- videobuf_dma_contig_free(vq, vb);
- dev_dbg(icd->parent, "%s freed\n", __func__);
-
- vb->state = VIDEOBUF_NEEDS_INIT;
+ return 0;
}
-static int mx2_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb, enum v4l2_field field)
+static int mx2_videobuf_prepare(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
- struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width,
icd->current_fmt->host_fmt);
int ret = 0;
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
if (bytes_per_line < 0)
return bytes_per_line;
@@ -563,99 +570,58 @@ static int mx2_videobuf_prepare(struct videobuf_queue *vq,
* This can be useful if you want to see if we actually fill
* the buffer with something
*/
- memset((void *)vb->baddr, 0xaa, vb->bsize);
+ memset((void *)vb2_plane_vaddr(vb, 0),
+ 0xaa, vb2_get_plane_payload(vb, 0));
#endif
- if (buf->code != icd->current_fmt->code ||
- vb->width != icd->user_width ||
- vb->height != icd->user_height ||
- vb->field != field) {
- buf->code = icd->current_fmt->code;
- vb->width = icd->user_width;
- vb->height = icd->user_height;
- vb->field = field;
- vb->state = VIDEOBUF_NEEDS_INIT;
- }
-
- vb->size = bytes_per_line * vb->height;
- if (vb->baddr && vb->bsize < vb->size) {
+ vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height);
+ if (vb2_plane_vaddr(vb, 0) &&
+ vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
ret = -EINVAL;
goto out;
}
- if (vb->state == VIDEOBUF_NEEDS_INIT) {
- ret = videobuf_iolock(vq, vb, NULL);
- if (ret)
- goto fail;
-
- vb->state = VIDEOBUF_PREPARED;
- }
-
return 0;
-fail:
- free_buffer(vq, buf);
out:
return ret;
}
-static void mx2_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void mx2_videobuf_queue(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici =
to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
unsigned long flags;
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
spin_lock_irqsave(&pcdev->lock, flags);
- vb->state = VIDEOBUF_QUEUED;
- list_add_tail(&vb->queue, &pcdev->capture);
+ buf->state = MX2_STATE_QUEUED;
+ list_add_tail(&buf->internal.queue, &pcdev->capture);
- if (mx27_camera_emma(pcdev)) {
- goto out;
-#ifdef CONFIG_MACH_MX27
- } else if (cpu_is_mx27()) {
- int ret;
-
- if (pcdev->active == NULL) {
- ret = imx_dma_setup_single(pcdev->dma,
- videobuf_to_dma_contig(vb), vb->size,
- (u32)pcdev->base_dma + 0x10,
- DMA_MODE_READ);
- if (ret) {
- vb->state = VIDEOBUF_ERROR;
- wake_up(&vb->done);
- goto out;
- }
-
- vb->state = VIDEOBUF_ACTIVE;
- pcdev->active = buf;
- }
-#endif
- } else { /* cpu_is_mx25() */
+ if (cpu_is_mx25()) {
u32 csicr3, dma_inten = 0;
if (pcdev->fb1_active == NULL) {
- writel(videobuf_to_dma_contig(vb),
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
pcdev->base_csi + CSIDMASA_FB1);
pcdev->fb1_active = buf;
dma_inten = CSICR1_FB1_DMA_INTEN;
} else if (pcdev->fb2_active == NULL) {
- writel(videobuf_to_dma_contig(vb),
+ writel(vb2_dma_contig_plane_dma_addr(vb, 0),
pcdev->base_csi + CSIDMASA_FB2);
pcdev->fb2_active = buf;
dma_inten = CSICR1_FB2_DMA_INTEN;
}
if (dma_inten) {
- list_del(&vb->queue);
- vb->state = VIDEOBUF_ACTIVE;
+ list_del(&buf->internal.queue);
+ buf->state = MX2_STATE_ACTIVE;
csicr3 = readl(pcdev->base_csi + CSICR3);
@@ -674,36 +640,31 @@ static void mx2_videobuf_queue(struct videobuf_queue *vq,
}
}
-out:
spin_unlock_irqrestore(&pcdev->lock, flags);
}
-static void mx2_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
+static void mx2_videobuf_release(struct vb2_buffer *vb)
{
- struct soc_camera_device *icd = vq->priv_data;
+ struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue);
struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct mx2_buffer *buf = container_of(vb, struct mx2_buffer, vb);
unsigned long flags;
#ifdef DEBUG
- dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
+ dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__,
+ vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0));
- switch (vb->state) {
- case VIDEOBUF_ACTIVE:
+ switch (buf->state) {
+ case MX2_STATE_ACTIVE:
dev_info(icd->parent, "%s (active)\n", __func__);
break;
- case VIDEOBUF_QUEUED:
+ case MX2_STATE_QUEUED:
dev_info(icd->parent, "%s (queued)\n", __func__);
break;
- case VIDEOBUF_PREPARED:
- dev_info(icd->parent, "%s (prepared)\n", __func__);
- break;
default:
dev_info(icd->parent, "%s (unknown) %d\n", __func__,
- vb->state);
+ buf->state);
break;
}
#endif
@@ -717,11 +678,9 @@ static void mx2_videobuf_release(struct videobuf_queue *vq,
* state. This requires a specific handling for each of the these DMA
* types.
*/
+
spin_lock_irqsave(&pcdev->lock, flags);
- if (vb->state == VIDEOBUF_QUEUED) {
- list_del(&vb->queue);
- vb->state = VIDEOBUF_ERROR;
- } else if (cpu_is_mx25() && vb->state == VIDEOBUF_ACTIVE) {
+ if (cpu_is_mx25() && buf->state == MX2_STATE_ACTIVE) {
if (pcdev->fb1_active == buf) {
pcdev->csicr1 &= ~CSICR1_FB1_DMA_INTEN;
writel(0, pcdev->base_csi + CSIDMASA_FB1);
@@ -732,75 +691,178 @@ static void mx2_videobuf_release(struct videobuf_queue *vq,
pcdev->fb2_active = NULL;
}
writel(pcdev->csicr1, pcdev->base_csi + CSICR1);
- vb->state = VIDEOBUF_ERROR;
}
spin_unlock_irqrestore(&pcdev->lock, flags);
-
- free_buffer(vq, buf);
}
-static struct videobuf_queue_ops mx2_videobuf_ops = {
- .buf_setup = mx2_videobuf_setup,
- .buf_prepare = mx2_videobuf_prepare,
- .buf_queue = mx2_videobuf_queue,
- .buf_release = mx2_videobuf_release,
-};
-
-static void mx2_camera_init_videobuf(struct videobuf_queue *q,
- struct soc_camera_device *icd)
+static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
+ int bytesperline)
{
- struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
- videobuf_queue_dma_contig_init(q, &mx2_videobuf_ops, pcdev->dev,
- &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_NONE, sizeof(struct mx2_buffer),
- icd, &icd->video_lock);
-}
+ writel((pcdev->s_width << 16) | pcdev->s_height,
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+ writel(prp->cfg.src_pixel,
+ pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
+ writel(bytesperline,
+ pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
+ writel(prp->cfg.ch1_pixel,
+ pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
+ } else { /* channel 2 */
+ writel((icd->user_width << 16) | icd->user_height,
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+ }
-#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
- V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_VSYNC_ACTIVE_LOW | \
- V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
- V4L2_MBUS_HSYNC_ACTIVE_LOW | \
- V4L2_MBUS_PCLK_SAMPLE_RISING | \
- V4L2_MBUS_PCLK_SAMPLE_FALLING | \
- V4L2_MBUS_DATA_ACTIVE_HIGH | \
- V4L2_MBUS_DATA_ACTIVE_LOW)
+ /* Enable interrupts */
+ writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+}
-static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev)
{
- u32 cntl;
- int count = 0;
+ int dir;
- cntl = readl(pcdev->base_emma + PRP_CNTL);
- writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
- while (count++ < 100) {
- if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
- return 0;
- barrier();
- udelay(1);
- }
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ unsigned char *s = pcdev->resizing[dir].s;
+ int len = pcdev->resizing[dir].len;
+ unsigned int coeff[2] = {0, 0};
+ unsigned int valid = 0;
+ int i;
- return -ETIMEDOUT;
+ if (len == 0)
+ continue;
+
+ for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) {
+ int j;
+
+ j = i > 9 ? 1 : 0;
+ coeff[j] = (coeff[j] << BC_COEF) |
+ (s[i] & (SZ_COEF - 1));
+
+ if (i == 5 || i == 15)
+ coeff[j] <<= 1;
+
+ valid = (valid << 1) | (s[i] >> BC_COEF);
+ }
+
+ valid |= PRP_RZ_VALID_TBL_LEN(len);
+
+ if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR)
+ valid |= PRP_RZ_VALID_BILINEAR;
+
+ if (pcdev->emma_prp->cfg.channel == 1) {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH1_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH1_RZ_VERT_VALID);
+ }
+ } else {
+ if (dir == RESIZE_DIR_H) {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_HORI_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_HORI_VALID);
+ } else {
+ writel(coeff[0], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF1);
+ writel(coeff[1], pcdev->base_emma +
+ PRP_CH2_RZ_VERT_COEF2);
+ writel(valid, pcdev->base_emma +
+ PRP_CH2_RZ_VERT_VALID);
+ }
+ }
+ }
}
-static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
- int bytesperline)
+static int mx2_start_streaming(struct vb2_queue *q, unsigned int count)
{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
struct soc_camera_host *ici =
to_soc_camera_host(icd->parent);
struct mx2_camera_dev *pcdev = ici->priv;
struct mx2_fmt_cfg *prp = pcdev->emma_prp;
- u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;
+ struct vb2_buffer *vb;
+ struct mx2_buffer *buf;
+ unsigned long phys;
+ int bytesperline;
- if (prp->cfg.channel == 1) {
- writel(pcdev->discard_buffer_dma,
- pcdev->base_emma + PRP_DEST_RGB1_PTR);
- writel(pcdev->discard_buffer_dma,
- pcdev->base_emma + PRP_DEST_RGB2_PTR);
+ if (cpu_is_mx27()) {
+ unsigned long flags;
+ if (count < 2)
+ return -EINVAL;
+
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 0;
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
+
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
+ buf->internal.bufnum = 1;
+ vb = &buf->vb;
+ buf->state = MX2_STATE_ACTIVE;
- writel(PRP_CNTL_CH1EN |
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, buf->internal.bufnum);
+ list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
+
+ bytesperline = soc_mbus_bytes_per_line(icd->user_width,
+ icd->current_fmt->host_fmt);
+ if (bytesperline < 0)
+ return bytesperline;
+
+ /*
+ * I didn't manage to properly enable/disable the prp
+ * on a per frame basis during running transfers,
+ * thus we allocate a buffer here and use it to
+ * discard frames when no buffer is available.
+ * Feel free to work on this ;)
+ */
+ pcdev->discard_size = icd->user_height * bytesperline;
+ pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, &pcdev->discard_buffer_dma,
+ GFP_KERNEL);
+ if (!pcdev->discard_buffer)
+ return -ENOMEM;
+
+ pcdev->buf_discard[0].discard = true;
+ list_add_tail(&pcdev->buf_discard[0].queue,
+ &pcdev->discard);
+
+ pcdev->buf_discard[1].discard = true;
+ list_add_tail(&pcdev->buf_discard[1].queue,
+ &pcdev->discard);
+
+ mx2_prp_resize_commit(pcdev);
+
+ mx27_camera_emma_buf_init(icd, bytesperline);
+
+ if (prp->cfg.channel == 1) {
+ writel(PRP_CNTL_CH1EN |
PRP_CNTL_CSIEN |
prp->cfg.in_fmt |
prp->cfg.out_fmt |
@@ -809,56 +871,107 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd,
PRP_CNTL_CH1_TSKIP(0) |
PRP_CNTL_IN_TSKIP(0),
pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(PRP_CNTL_CH2EN |
+ PRP_CNTL_CSIEN |
+ prp->cfg.in_fmt |
+ prp->cfg.out_fmt |
+ PRP_CNTL_CH2_LEN |
+ PRP_CNTL_CH2_TSKIP(0) |
+ PRP_CNTL_IN_TSKIP(0),
+ pcdev->base_emma + PRP_CNTL);
+ }
+ spin_unlock_irqrestore(&pcdev->lock, flags);
+ }
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_CH1_OUT_IMAGE_SIZE);
- writel(bytesperline,
- pcdev->base_emma + PRP_DEST_CH1_LINE_STRIDE);
- writel(prp->cfg.src_pixel,
- pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
- writel(prp->cfg.ch1_pixel,
- pcdev->base_emma + PRP_CH1_PIXEL_FORMAT_CNTL);
- } else { /* channel 2 */
- writel(pcdev->discard_buffer_dma,
- pcdev->base_emma + PRP_DEST_Y_PTR);
- writel(pcdev->discard_buffer_dma,
- pcdev->base_emma + PRP_SOURCE_Y_PTR);
-
- if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) {
- writel(pcdev->discard_buffer_dma + imgsize,
- pcdev->base_emma + PRP_DEST_CB_PTR);
- writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4),
- pcdev->base_emma + PRP_DEST_CR_PTR);
- writel(pcdev->discard_buffer_dma + imgsize,
- pcdev->base_emma + PRP_SOURCE_CB_PTR);
- writel(pcdev->discard_buffer_dma + ((5 * imgsize) / 4),
- pcdev->base_emma + PRP_SOURCE_CR_PTR);
+ return 0;
+}
+
+static int mx2_stop_streaming(struct vb2_queue *q)
+{
+ struct soc_camera_device *icd = soc_camera_from_vb2q(q);
+ struct soc_camera_host *ici =
+ to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
+ struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+ unsigned long flags;
+ void *b;
+ u32 cntl;
+
+ if (cpu_is_mx27()) {
+ spin_lock_irqsave(&pcdev->lock, flags);
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ if (prp->cfg.channel == 1) {
+ writel(cntl & ~PRP_CNTL_CH1EN,
+ pcdev->base_emma + PRP_CNTL);
+ } else {
+ writel(cntl & ~PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
}
+ INIT_LIST_HEAD(&pcdev->capture);
+ INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
- writel(PRP_CNTL_CH2EN |
- PRP_CNTL_CSIEN |
- prp->cfg.in_fmt |
- prp->cfg.out_fmt |
- PRP_CNTL_CH2_LEN |
- PRP_CNTL_CH2_TSKIP(0) |
- PRP_CNTL_IN_TSKIP(0),
- pcdev->base_emma + PRP_CNTL);
+ b = pcdev->discard_buffer;
+ pcdev->discard_buffer = NULL;
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+ spin_unlock_irqrestore(&pcdev->lock, flags);
- writel((icd->user_width << 16) | icd->user_height,
- pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+ dma_free_coherent(ici->v4l2_dev.dev,
+ pcdev->discard_size, b, pcdev->discard_buffer_dma);
+ }
- writel(prp->cfg.src_pixel,
- pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL);
+ return 0;
+}
+
+static struct vb2_ops mx2_videobuf_ops = {
+ .queue_setup = mx2_videobuf_setup,
+ .buf_prepare = mx2_videobuf_prepare,
+ .buf_queue = mx2_videobuf_queue,
+ .buf_cleanup = mx2_videobuf_release,
+ .start_streaming = mx2_start_streaming,
+ .stop_streaming = mx2_stop_streaming,
+};
+
+static int mx2_camera_init_videobuf(struct vb2_queue *q,
+ struct soc_camera_device *icd)
+{
+ q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ q->io_modes = VB2_MMAP | VB2_USERPTR;
+ q->drv_priv = icd;
+ q->ops = &mx2_videobuf_ops;
+ q->mem_ops = &vb2_dma_contig_memops;
+ q->buf_struct_size = sizeof(struct mx2_buffer);
+
+ return vb2_queue_init(q);
+}
+#define MX2_BUS_FLAGS (V4L2_MBUS_MASTER | \
+ V4L2_MBUS_VSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_VSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_HSYNC_ACTIVE_HIGH | \
+ V4L2_MBUS_HSYNC_ACTIVE_LOW | \
+ V4L2_MBUS_PCLK_SAMPLE_RISING | \
+ V4L2_MBUS_PCLK_SAMPLE_FALLING | \
+ V4L2_MBUS_DATA_ACTIVE_HIGH | \
+ V4L2_MBUS_DATA_ACTIVE_LOW)
+
+static int mx27_camera_emma_prp_reset(struct mx2_camera_dev *pcdev)
+{
+ u32 cntl;
+ int count = 0;
+
+ cntl = readl(pcdev->base_emma + PRP_CNTL);
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ while (count++ < 100) {
+ if (!(readl(pcdev->base_emma + PRP_CNTL) & PRP_CNTL_SWRST))
+ return 0;
+ barrier();
+ udelay(1);
}
- /* Enable interrupts */
- writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL);
+ return -ETIMEDOUT;
}
static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
@@ -939,31 +1052,10 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd)
if (bytesperline < 0)
return bytesperline;
- if (mx27_camera_emma(pcdev)) {
+ if (cpu_is_mx27()) {
ret = mx27_camera_emma_prp_reset(pcdev);
if (ret)
return ret;
-
- if (pcdev->discard_buffer)
- dma_free_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, pcdev->discard_buffer,
- pcdev->discard_buffer_dma);
-
- /*
- * I didn't manage to properly enable/disable the prp
- * on a per frame basis during running transfers,
- * thus we allocate a buffer here and use it to
- * discard frames when no buffer is available.
- * Feel free to work on this ;)
- */
- pcdev->discard_size = icd->user_height * bytesperline;
- pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev,
- pcdev->discard_size, &pcdev->discard_buffer_dma,
- GFP_KERNEL);
- if (!pcdev->discard_buffer)
- return -ENOMEM;
-
- mx27_camera_emma_buf_init(icd, bytesperline);
} else if (cpu_is_mx25()) {
writel((bytesperline * icd->user_height) >> 2,
pcdev->base_csi + CSIRXCNT);
@@ -1052,6 +1144,123 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd,
return formats;
}
+static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev,
+ struct v4l2_mbus_framefmt *mf_in,
+ struct v4l2_pix_format *pix_out, bool apply)
+{
+ int num, den;
+ unsigned long m;
+ int i, dir;
+
+ for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) {
+ struct emma_prp_resize tmprsz;
+ unsigned char *s = tmprsz.s;
+ int len = 0;
+ int in, out;
+
+ if (dir == RESIZE_DIR_H) {
+ in = mf_in->width;
+ out = pix_out->width;
+ } else {
+ in = mf_in->height;
+ out = pix_out->height;
+ }
+
+ if (in < out)
+ return -EINVAL;
+ else if (in == out)
+ continue;
+
+ /* Calculate ratio */
+ m = gcd(in, out);
+ num = in / m;
+ den = out / m;
+ if (num > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ if ((num >= 2 * den) && (den == 1) &&
+ (num < 9) && (!(num & 0x01))) {
+ int sum = 0;
+ int j;
+
+ /* Average scaling for >= 2:1 ratios */
+ /* Support can be added for num >=9 and odd values */
+
+ tmprsz.algo = RESIZE_ALGO_AVERAGING;
+ len = num;
+
+ for (i = 0; i < (len / 2); i++)
+ s[i] = 8;
+
+ do {
+ for (i = 0; i < (len / 2); i++) {
+ s[i] = s[i] >> 1;
+ sum = 0;
+ for (j = 0; j < (len / 2); j++)
+ sum += s[j];
+ if (sum == 4)
+ break;
+ }
+ } while (sum != 4);
+
+ for (i = (len / 2); i < len; i++)
+ s[i] = s[len - i - 1];
+
+ s[len - 1] |= SZ_COEF;
+ } else {
+ /* bilinear scaling for < 2:1 ratios */
+ int v; /* overflow counter */
+ int coeff, nxt; /* table output */
+ int in_pos_inc = 2 * den;
+ int out_pos = num;
+ int out_pos_inc = 2 * num;
+ int init_carry = num - den;
+ int carry = init_carry;
+
+ tmprsz.algo = RESIZE_ALGO_BILINEAR;
+ v = den + in_pos_inc;
+ do {
+ coeff = v - out_pos;
+ out_pos += out_pos_inc;
+ carry += out_pos_inc;
+ for (nxt = 0; v < out_pos; nxt++) {
+ v += in_pos_inc;
+ carry -= in_pos_inc;
+ }
+
+ if (len > RESIZE_NUM_MAX)
+ return -EINVAL;
+
+ coeff = ((coeff << BC_COEF) +
+ (in_pos_inc >> 1)) / in_pos_inc;
+
+ if (coeff >= (SZ_COEF - 1))
+ coeff--;
+
+ coeff |= SZ_COEF;
+ s[len] = (unsigned char)coeff;
+ len++;
+
+ for (i = 1; i < nxt; i++) {
+ if (len >= RESIZE_NUM_MAX)
+ return -EINVAL;
+ s[len] = 0;
+ len++;
+ }
+ } while (carry != init_carry);
+ }
+ tmprsz.len = len;
+ if (dir == RESIZE_DIR_H)
+ mf_in->width = pix_out->width;
+ else
+ mf_in->height = pix_out->height;
+
+ if (apply)
+ memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz));
+ }
+ return 0;
+}
+
static int mx2_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
@@ -1063,6 +1272,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd,
struct v4l2_mbus_framefmt mf;
int ret;
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
if (!xlate) {
dev_warn(icd->parent, "Format %x not found\n",
@@ -1080,6 +1292,22 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd,
if (ret < 0 && ret != -ENOIOCTLCMD)
return ret;
+ /* Store width and height returned by the sensor for resizing */
+ pcdev->s_width = mf.width;
+ pcdev->s_height = mf.height;
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf.width != pix->width || mf.height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
if (mf.code != xlate->code)
return -EINVAL;
@@ -1089,9 +1317,8 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd,
pix->colorspace = mf.colorspace;
icd->current_fmt = xlate;
- if (mx27_camera_emma(pcdev))
- pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
- xlate->host_fmt->fourcc);
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
return 0;
}
@@ -1104,9 +1331,14 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_mbus_framefmt mf;
__u32 pixfmt = pix->pixelformat;
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct mx2_camera_dev *pcdev = ici->priv;
unsigned int width_limit;
int ret;
+ dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
if (pixfmt && !xlate) {
dev_warn(icd->parent, "Format %x not found\n", pixfmt);
@@ -1156,6 +1388,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
if (ret < 0)
return ret;
+ dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n",
+ __func__, pcdev->s_width, pcdev->s_height);
+
+ /* If the sensor does not support image size try PrP resizing */
+ pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code,
+ xlate->host_fmt->fourcc);
+
+ memset(pcdev->resizing, 0, sizeof(pcdev->resizing));
+ if ((mf.width != pix->width || mf.height != pix->height) &&
+ pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) {
+ if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0)
+ dev_dbg(icd->parent, "%s: can't resize\n", __func__);
+ }
+
if (mf.field == V4L2_FIELD_ANY)
mf.field = V4L2_FIELD_NONE;
/*
@@ -1174,6 +1420,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd,
pix->field = mf.field;
pix->colorspace = mf.colorspace;
+ dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n",
+ __func__, pix->width, pix->height);
+
return 0;
}
@@ -1187,136 +1436,11 @@ static int mx2_camera_querycap(struct soc_camera_host *ici,
return 0;
}
-static int mx2_camera_reqbufs(struct soc_camera_device *icd,
- struct v4l2_requestbuffers *p)
-{
- int i;
-
- for (i = 0; i < p->count; i++) {
- struct mx2_buffer *buf = container_of(icd->vb_vidq.bufs[i],
- struct mx2_buffer, vb);
- INIT_LIST_HEAD(&buf->vb.queue);
- }
-
- return 0;
-}
-
-#ifdef CONFIG_MACH_MX27
-static void mx27_camera_frame_done(struct mx2_camera_dev *pcdev, int state)
-{
- struct videobuf_buffer *vb;
- struct mx2_buffer *buf;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&pcdev->lock, flags);
-
- if (!pcdev->active) {
- dev_err(pcdev->dev, "%s called with no active buffer!\n",
- __func__);
- goto out;
- }
-
- vb = &pcdev->active->vb;
- buf = container_of(vb, struct mx2_buffer, vb);
- WARN_ON(list_empty(&vb->queue));
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
- vb, vb->baddr, vb->bsize);
-
- /* _init is used to debug races, see comment in pxa_camera_reqbufs() */
- list_del_init(&vb->queue);
- vb->state = state;
- do_gettimeofday(&vb->ts);
- vb->field_count++;
-
- wake_up(&vb->done);
-
- if (list_empty(&pcdev->capture)) {
- pcdev->active = NULL;
- goto out;
- }
-
- pcdev->active = list_entry(pcdev->capture.next,
- struct mx2_buffer, vb.queue);
-
- vb = &pcdev->active->vb;
- vb->state = VIDEOBUF_ACTIVE;
-
- ret = imx_dma_setup_single(pcdev->dma, videobuf_to_dma_contig(vb),
- vb->size, (u32)pcdev->base_dma + 0x10, DMA_MODE_READ);
-
- if (ret) {
- vb->state = VIDEOBUF_ERROR;
- pcdev->active = NULL;
- wake_up(&vb->done);
- }
-
-out:
- spin_unlock_irqrestore(&pcdev->lock, flags);
-}
-
-static void mx27_camera_dma_err_callback(int channel, void *data, int err)
-{
- struct mx2_camera_dev *pcdev = data;
-
- mx27_camera_frame_done(pcdev, VIDEOBUF_ERROR);
-}
-
-static void mx27_camera_dma_callback(int channel, void *data)
-{
- struct mx2_camera_dev *pcdev = data;
-
- mx27_camera_frame_done(pcdev, VIDEOBUF_DONE);
-}
-
-#define DMA_REQ_CSI_RX 31 /* FIXME: Add this to a resource */
-
-static int __devinit mx27_camera_dma_init(struct platform_device *pdev,
- struct mx2_camera_dev *pcdev)
-{
- int err;
-
- pcdev->dma = imx_dma_request_by_prio("CSI RX DMA", DMA_PRIO_HIGH);
- if (pcdev->dma < 0) {
- dev_err(&pdev->dev, "%s failed to request DMA channel\n",
- __func__);
- return pcdev->dma;
- }
-
- err = imx_dma_setup_handlers(pcdev->dma, mx27_camera_dma_callback,
- mx27_camera_dma_err_callback, pcdev);
- if (err) {
- dev_err(&pdev->dev, "%s failed to set DMA callback\n",
- __func__);
- goto err_out;
- }
-
- err = imx_dma_config_channel(pcdev->dma,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_FIFO,
- IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
- DMA_REQ_CSI_RX, 1);
- if (err) {
- dev_err(&pdev->dev, "%s failed to config DMA channel\n",
- __func__);
- goto err_out;
- }
-
- imx_dma_config_burstlen(pcdev->dma, 64);
-
- return 0;
-
-err_out:
- imx_dma_free(pcdev->dma);
-
- return err;
-}
-#endif /* CONFIG_MACH_MX27 */
-
static unsigned int mx2_camera_poll(struct file *file, poll_table *pt)
{
struct soc_camera_device *icd = file->private_data;
- return videobuf_poll_stream(file, &icd->vb_vidq, pt);
+ return vb2_poll(&icd->vb2_vidq, file, pt);
}
static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
@@ -1327,144 +1451,148 @@ static struct soc_camera_host_ops mx2_soc_camera_host_ops = {
.set_crop = mx2_camera_set_crop,
.get_formats = mx2_camera_get_formats,
.try_fmt = mx2_camera_try_fmt,
- .init_videobuf = mx2_camera_init_videobuf,
- .reqbufs = mx2_camera_reqbufs,
+ .init_videobuf2 = mx2_camera_init_videobuf,
.poll = mx2_camera_poll,
.querycap = mx2_camera_querycap,
.set_bus_param = mx2_camera_set_bus_param,
};
static void mx27_camera_frame_done_emma(struct mx2_camera_dev *pcdev,
- int bufnum, int state)
+ int bufnum, bool err)
{
- u32 imgsize = pcdev->icd->user_height * pcdev->icd->user_width;
+#ifdef DEBUG
struct mx2_fmt_cfg *prp = pcdev->emma_prp;
+#endif
+ struct mx2_buf_internal *ibuf;
struct mx2_buffer *buf;
- struct videobuf_buffer *vb;
+ struct vb2_buffer *vb;
unsigned long phys;
- if (!list_empty(&pcdev->active_bufs)) {
- buf = list_entry(pcdev->active_bufs.next,
- struct mx2_buffer, vb.queue);
+ ibuf = list_first_entry(&pcdev->active_bufs, struct mx2_buf_internal,
+ queue);
+
+ BUG_ON(ibuf->bufnum != bufnum);
- BUG_ON(buf->bufnum != bufnum);
+ if (ibuf->discard) {
+ /*
+ * Discard buffer must not be returned to user space.
+ * Just return it to the discard queue.
+ */
+ list_move_tail(pcdev->active_bufs.next, &pcdev->discard);
+ } else {
+ buf = mx2_ibuf_to_buf(ibuf);
vb = &buf->vb;
#ifdef DEBUG
- phys = videobuf_to_dma_contig(vb);
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
if (prp->cfg.channel == 1) {
if (readl(pcdev->base_emma + PRP_DEST_RGB1_PTR +
4 * bufnum) != phys) {
- dev_err(pcdev->dev, "%p != %p\n", phys,
- readl(pcdev->base_emma +
- PRP_DEST_RGB1_PTR +
- 4 * bufnum));
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_RGB1_PTR + 4 * bufnum));
}
} else {
if (readl(pcdev->base_emma + PRP_DEST_Y_PTR -
0x14 * bufnum) != phys) {
- dev_err(pcdev->dev, "%p != %p\n", phys,
- readl(pcdev->base_emma +
- PRP_DEST_Y_PTR -
- 0x14 * bufnum));
+ dev_err(pcdev->dev, "%lx != %x\n", phys,
+ readl(pcdev->base_emma +
+ PRP_DEST_Y_PTR - 0x14 * bufnum));
}
}
#endif
- dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb,
- vb->baddr, vb->bsize);
+ dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb,
+ vb2_plane_vaddr(vb, 0),
+ vb2_get_plane_payload(vb, 0));
- list_del(&vb->queue);
- vb->state = state;
- do_gettimeofday(&vb->ts);
- vb->field_count = pcdev->frame_count * 2;
- pcdev->frame_count++;
-
- wake_up(&vb->done);
+ list_del_init(&buf->internal.queue);
+ do_gettimeofday(&vb->v4l2_buf.timestamp);
+ vb->v4l2_buf.sequence = pcdev->frame_count;
+ if (err)
+ vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+ else
+ vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
}
+ pcdev->frame_count++;
+
if (list_empty(&pcdev->capture)) {
- if (prp->cfg.channel == 1) {
- writel(pcdev->discard_buffer_dma, pcdev->base_emma +
- PRP_DEST_RGB1_PTR + 4 * bufnum);
- } else {
- writel(pcdev->discard_buffer_dma, pcdev->base_emma +
- PRP_DEST_Y_PTR -
- 0x14 * bufnum);
- if (prp->out_fmt == V4L2_PIX_FMT_YUV420) {
- writel(pcdev->discard_buffer_dma + imgsize,
- pcdev->base_emma + PRP_DEST_CB_PTR -
- 0x14 * bufnum);
- writel(pcdev->discard_buffer_dma +
- ((5 * imgsize) / 4), pcdev->base_emma +
- PRP_DEST_CR_PTR - 0x14 * bufnum);
- }
+ if (list_empty(&pcdev->discard)) {
+ dev_warn(pcdev->dev, "%s: trying to access empty discard list\n",
+ __func__);
+ return;
}
+
+ ibuf = list_first_entry(&pcdev->discard,
+ struct mx2_buf_internal, queue);
+ ibuf->bufnum = bufnum;
+
+ list_move_tail(pcdev->discard.next, &pcdev->active_bufs);
+ mx27_update_emma_buf(pcdev, pcdev->discard_buffer_dma, bufnum);
return;
}
- buf = list_entry(pcdev->capture.next,
- struct mx2_buffer, vb.queue);
+ buf = list_first_entry(&pcdev->capture, struct mx2_buffer,
+ internal.queue);
- buf->bufnum = !bufnum;
+ buf->internal.bufnum = bufnum;
list_move_tail(pcdev->capture.next, &pcdev->active_bufs);
vb = &buf->vb;
- vb->state = VIDEOBUF_ACTIVE;
+ buf->state = MX2_STATE_ACTIVE;
- phys = videobuf_to_dma_contig(vb);
- if (prp->cfg.channel == 1) {
- writel(phys, pcdev->base_emma + PRP_DEST_RGB1_PTR + 4 * bufnum);
- } else {
- writel(phys, pcdev->base_emma +
- PRP_DEST_Y_PTR - 0x14 * bufnum);
- if (prp->cfg.out_fmt == PRP_CNTL_CH2_OUT_YUV420) {
- writel(phys + imgsize, pcdev->base_emma +
- PRP_DEST_CB_PTR - 0x14 * bufnum);
- writel(phys + ((5 * imgsize) / 4), pcdev->base_emma +
- PRP_DEST_CR_PTR - 0x14 * bufnum);
- }
- }
+ phys = vb2_dma_contig_plane_dma_addr(vb, 0);
+ mx27_update_emma_buf(pcdev, phys, bufnum);
}
static irqreturn_t mx27_camera_emma_irq(int irq_emma, void *data)
{
struct mx2_camera_dev *pcdev = data;
unsigned int status = readl(pcdev->base_emma + PRP_INTRSTATUS);
- struct mx2_buffer *buf;
+ struct mx2_buf_internal *ibuf;
+
+ spin_lock(&pcdev->lock);
+
+ if (list_empty(&pcdev->active_bufs)) {
+ dev_warn(pcdev->dev, "%s: called while active list is empty\n",
+ __func__);
+
+ if (!status) {
+ spin_unlock(&pcdev->lock);
+ return IRQ_NONE;
+ }
+ }
if (status & (1 << 7)) { /* overflow */
- u32 cntl;
- /*
- * We only disable channel 1 here since this is the only
- * enabled channel
- *
- * FIXME: the correct DMA overflow handling should be resetting
- * the buffer, returning an error frame, and continuing with
- * the next one.
- */
- cntl = readl(pcdev->base_emma + PRP_CNTL);
+ u32 cntl = readl(pcdev->base_emma + PRP_CNTL);
writel(cntl & ~(PRP_CNTL_CH1EN | PRP_CNTL_CH2EN),
pcdev->base_emma + PRP_CNTL);
writel(cntl, pcdev->base_emma + PRP_CNTL);
- }
- if ((((status & (3 << 5)) == (3 << 5)) ||
- ((status & (3 << 3)) == (3 << 3)))
- && !list_empty(&pcdev->active_bufs)) {
+
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev,
+ ibuf->bufnum, true);
+
+ status &= ~(1 << 7);
+ } else if (((status & (3 << 5)) == (3 << 5)) ||
+ ((status & (3 << 3)) == (3 << 3))) {
/*
* Both buffers have triggered, process the one we're expecting
* to first
*/
- buf = list_entry(pcdev->active_bufs.next,
- struct mx2_buffer, vb.queue);
- mx27_camera_frame_done_emma(pcdev, buf->bufnum, VIDEOBUF_DONE);
- status &= ~(1 << (6 - buf->bufnum)); /* mark processed */
+ ibuf = list_first_entry(&pcdev->active_bufs,
+ struct mx2_buf_internal, queue);
+ mx27_camera_frame_done_emma(pcdev, ibuf->bufnum, false);
+ status &= ~(1 << (6 - ibuf->bufnum)); /* mark processed */
+ } else if ((status & (1 << 6)) || (status & (1 << 4))) {
+ mx27_camera_frame_done_emma(pcdev, 0, false);
+ } else if ((status & (1 << 5)) || (status & (1 << 3))) {
+ mx27_camera_frame_done_emma(pcdev, 1, false);
}
- if ((status & (1 << 6)) || (status & (1 << 4)))
- mx27_camera_frame_done_emma(pcdev, 0, VIDEOBUF_DONE);
- if ((status & (1 << 5)) || (status & (1 << 3)))
- mx27_camera_frame_done_emma(pcdev, 1, VIDEOBUF_DONE);
+ spin_unlock(&pcdev->lock);
writel(status, pcdev->base_emma + PRP_INTRSTATUS);
return IRQ_HANDLED;
@@ -1527,8 +1655,6 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
struct resource *res_csi, *res_emma;
void __iomem *base_csi;
int irq_csi, irq_emma;
- irq_handler_t mx2_cam_irq_handler = cpu_is_mx25() ? mx25_camera_irq
- : mx27_camera_irq;
int err = 0;
dev_dbg(&pdev->dev, "initialising\n");
@@ -1550,22 +1676,11 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
pcdev->clk_csi = clk_get(&pdev->dev, NULL);
if (IS_ERR(pcdev->clk_csi)) {
+ dev_err(&pdev->dev, "Could not get csi clock\n");
err = PTR_ERR(pcdev->clk_csi);
goto exit_kfree;
}
- dev_dbg(&pdev->dev, "Camera clock frequency: %ld\n",
- clk_get_rate(pcdev->clk_csi));
-
- /* Initialize DMA */
-#ifdef CONFIG_MACH_MX27
- if (cpu_is_mx27()) {
- err = mx27_camera_dma_init(pdev, pcdev);
- if (err)
- goto exit_clk_put;
- }
-#endif /* CONFIG_MACH_MX27 */
-
pcdev->res_csi = res_csi;
pcdev->pdata = pdev->dev.platform_data;
if (pcdev->pdata) {
@@ -1585,6 +1700,7 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&pcdev->capture);
INIT_LIST_HEAD(&pcdev->active_bufs);
+ INIT_LIST_HEAD(&pcdev->discard);
spin_lock_init(&pcdev->lock);
/*
@@ -1606,11 +1722,13 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
pcdev->base_dma = res_csi->start;
pcdev->dev = &pdev->dev;
- err = request_irq(pcdev->irq_csi, mx2_cam_irq_handler, 0,
- MX2_CAM_DRV_NAME, pcdev);
- if (err) {
- dev_err(pcdev->dev, "Camera interrupt register failed \n");
- goto exit_iounmap;
+ if (cpu_is_mx25()) {
+ err = request_irq(pcdev->irq_csi, mx25_camera_irq, 0,
+ MX2_CAM_DRV_NAME, pcdev);
+ if (err) {
+ dev_err(pcdev->dev, "Camera interrupt register failed \n");
+ goto exit_iounmap;
+ }
}
if (cpu_is_mx27()) {
@@ -1618,14 +1736,15 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 1);
irq_emma = platform_get_irq(pdev, 1);
- if (res_emma && irq_emma >= 0) {
- dev_info(&pdev->dev, "Using EMMA\n");
- pcdev->use_emma = 1;
- pcdev->res_emma = res_emma;
- pcdev->irq_emma = irq_emma;
- if (mx27_camera_emma_init(pcdev))
- goto exit_free_irq;
+ if (!res_emma || !irq_emma) {
+ dev_err(&pdev->dev, "no EMMA resources\n");
+ goto exit_free_irq;
}
+
+ pcdev->res_emma = res_emma;
+ pcdev->irq_emma = irq_emma;
+ if (mx27_camera_emma_init(pcdev))
+ goto exit_free_irq;
}
pcdev->soc_host.drv_name = MX2_CAM_DRV_NAME,
@@ -1633,6 +1752,12 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
pcdev->soc_host.priv = pcdev;
pcdev->soc_host.v4l2_dev.dev = &pdev->dev;
pcdev->soc_host.nr = pdev->id;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ err = PTR_ERR(pcdev->alloc_ctx);
+ goto eallocctx;
+ }
err = soc_camera_host_register(&pcdev->soc_host);
if (err)
goto exit_free_emma;
@@ -1643,26 +1768,24 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev)
return 0;
exit_free_emma:
- if (mx27_camera_emma(pcdev)) {
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+eallocctx:
+ if (cpu_is_mx27()) {
free_irq(pcdev->irq_emma, pcdev);
clk_disable(pcdev->clk_emma);
clk_put(pcdev->clk_emma);
iounmap(pcdev->base_emma);
- release_mem_region(res_emma->start, resource_size(res_emma));
+ release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma));
}
exit_free_irq:
- free_irq(pcdev->irq_csi, pcdev);
+ if (cpu_is_mx25())
+ free_irq(pcdev->irq_csi, pcdev);
exit_iounmap:
iounmap(base_csi);
exit_release:
release_mem_region(res_csi->start, resource_size(res_csi));
exit_dma_free:
-#ifdef CONFIG_MACH_MX27
- if (cpu_is_mx27())
- imx_dma_free(pcdev->dma);
-exit_clk_put:
clk_put(pcdev->clk_csi);
-#endif /* CONFIG_MACH_MX27 */
exit_kfree:
kfree(pcdev);
exit:
@@ -1677,19 +1800,18 @@ static int __devexit mx2_camera_remove(struct platform_device *pdev)
struct resource *res;
clk_put(pcdev->clk_csi);
-#ifdef CONFIG_MACH_MX27
+ if (cpu_is_mx25())
+ free_irq(pcdev->irq_csi, pcdev);
if (cpu_is_mx27())
- imx_dma_free(pcdev->dma);
-#endif /* CONFIG_MACH_MX27 */
- free_irq(pcdev->irq_csi, pcdev);
- if (mx27_camera_emma(pcdev))
free_irq(pcdev->irq_emma, pcdev);
soc_camera_host_unregister(&pcdev->soc_host);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+
iounmap(pcdev->base_csi);
- if (mx27_camera_emma(pcdev)) {
+ if (cpu_is_mx27()) {
clk_disable(pcdev->clk_emma);
clk_put(pcdev->clk_emma);
iounmap(pcdev->base_emma);
diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c
new file mode 100644
index 000000000000..ba89a7401c8c
--- /dev/null
+++ b/drivers/media/video/mx2_emmaprp.c
@@ -0,0 +1,1008 @@
+/*
+ * Support eMMa-PrP through mem2mem framework.
+ *
+ * eMMa-PrP is a piece of HW that allows fetching buffers
+ * from one memory location and do several operations on
+ * them such as scaling or format conversion giving, as a result
+ * a new processed buffer in another memory location.
+ *
+ * Based on mem2mem_testdev.c by Pawel Osciak.
+ *
+ * Copyright (c) 2011 Vista Silicon S.L.
+ * Javier Martin <javier.martin@vista-silicon.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version
+ */
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <linux/platform_device.h>
+#include <media/v4l2-mem2mem.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-contig.h>
+#include <asm/sizes.h>
+
+#define EMMAPRP_MODULE_NAME "mem2mem-emmaprp"
+
+MODULE_DESCRIPTION("Mem-to-mem device which supports eMMa-PrP present in mx2 SoCs");
+MODULE_AUTHOR("Javier Martin <javier.martin@vista-silicon.com");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.0.1");
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+#define MIN_W 32
+#define MIN_H 32
+#define MAX_W 2040
+#define MAX_H 2046
+
+#define S_ALIGN 1 /* multiple of 2 */
+#define W_ALIGN_YUV420 3 /* multiple of 8 */
+#define W_ALIGN_OTHERS 2 /* multiple of 4 */
+#define H_ALIGN 1 /* multiple of 2 */
+
+/* Flags that indicate a format can be used for capture/output */
+#define MEM2MEM_CAPTURE (1 << 0)
+#define MEM2MEM_OUTPUT (1 << 1)
+
+#define MEM2MEM_NAME "m2m-emmaprp"
+
+/* In bytes, per queue */
+#define MEM2MEM_VID_MEM_LIMIT SZ_16M
+
+#define dprintk(dev, fmt, arg...) \
+ v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
+
+/* EMMA PrP */
+#define PRP_CNTL 0x00
+#define PRP_INTR_CNTL 0x04
+#define PRP_INTRSTATUS 0x08
+#define PRP_SOURCE_Y_PTR 0x0c
+#define PRP_SOURCE_CB_PTR 0x10
+#define PRP_SOURCE_CR_PTR 0x14
+#define PRP_DEST_RGB1_PTR 0x18
+#define PRP_DEST_RGB2_PTR 0x1c
+#define PRP_DEST_Y_PTR 0x20
+#define PRP_DEST_CB_PTR 0x24
+#define PRP_DEST_CR_PTR 0x28
+#define PRP_SRC_FRAME_SIZE 0x2c
+#define PRP_DEST_CH1_LINE_STRIDE 0x30
+#define PRP_SRC_PIXEL_FORMAT_CNTL 0x34
+#define PRP_CH1_PIXEL_FORMAT_CNTL 0x38
+#define PRP_CH1_OUT_IMAGE_SIZE 0x3c
+#define PRP_CH2_OUT_IMAGE_SIZE 0x40
+#define PRP_SRC_LINE_STRIDE 0x44
+#define PRP_CSC_COEF_012 0x48
+#define PRP_CSC_COEF_345 0x4c
+#define PRP_CSC_COEF_678 0x50
+#define PRP_CH1_RZ_HORI_COEF1 0x54
+#define PRP_CH1_RZ_HORI_COEF2 0x58
+#define PRP_CH1_RZ_HORI_VALID 0x5c
+#define PRP_CH1_RZ_VERT_COEF1 0x60
+#define PRP_CH1_RZ_VERT_COEF2 0x64
+#define PRP_CH1_RZ_VERT_VALID 0x68
+#define PRP_CH2_RZ_HORI_COEF1 0x6c
+#define PRP_CH2_RZ_HORI_COEF2 0x70
+#define PRP_CH2_RZ_HORI_VALID 0x74
+#define PRP_CH2_RZ_VERT_COEF1 0x78
+#define PRP_CH2_RZ_VERT_COEF2 0x7c
+#define PRP_CH2_RZ_VERT_VALID 0x80
+
+#define PRP_CNTL_CH1EN (1 << 0)
+#define PRP_CNTL_CH2EN (1 << 1)
+#define PRP_CNTL_CSIEN (1 << 2)
+#define PRP_CNTL_DATA_IN_YUV420 (0 << 3)
+#define PRP_CNTL_DATA_IN_YUV422 (1 << 3)
+#define PRP_CNTL_DATA_IN_RGB16 (2 << 3)
+#define PRP_CNTL_DATA_IN_RGB32 (3 << 3)
+#define PRP_CNTL_CH1_OUT_RGB8 (0 << 5)
+#define PRP_CNTL_CH1_OUT_RGB16 (1 << 5)
+#define PRP_CNTL_CH1_OUT_RGB32 (2 << 5)
+#define PRP_CNTL_CH1_OUT_YUV422 (3 << 5)
+#define PRP_CNTL_CH2_OUT_YUV420 (0 << 7)
+#define PRP_CNTL_CH2_OUT_YUV422 (1 << 7)
+#define PRP_CNTL_CH2_OUT_YUV444 (2 << 7)
+#define PRP_CNTL_CH1_LEN (1 << 9)
+#define PRP_CNTL_CH2_LEN (1 << 10)
+#define PRP_CNTL_SKIP_FRAME (1 << 11)
+#define PRP_CNTL_SWRST (1 << 12)
+#define PRP_CNTL_CLKEN (1 << 13)
+#define PRP_CNTL_WEN (1 << 14)
+#define PRP_CNTL_CH1BYP (1 << 15)
+#define PRP_CNTL_IN_TSKIP(x) ((x) << 16)
+#define PRP_CNTL_CH1_TSKIP(x) ((x) << 19)
+#define PRP_CNTL_CH2_TSKIP(x) ((x) << 22)
+#define PRP_CNTL_INPUT_FIFO_LEVEL(x) ((x) << 25)
+#define PRP_CNTL_RZ_FIFO_LEVEL(x) ((x) << 27)
+#define PRP_CNTL_CH2B1EN (1 << 29)
+#define PRP_CNTL_CH2B2EN (1 << 30)
+#define PRP_CNTL_CH2FEN (1 << 31)
+
+#define PRP_SIZE_HEIGHT(x) (x)
+#define PRP_SIZE_WIDTH(x) ((x) << 16)
+
+/* IRQ Enable and status register */
+#define PRP_INTR_RDERR (1 << 0)
+#define PRP_INTR_CH1WERR (1 << 1)
+#define PRP_INTR_CH2WERR (1 << 2)
+#define PRP_INTR_CH1FC (1 << 3)
+#define PRP_INTR_CH2FC (1 << 5)
+#define PRP_INTR_LBOVF (1 << 7)
+#define PRP_INTR_CH2OVF (1 << 8)
+
+#define PRP_INTR_ST_RDERR (1 << 0)
+#define PRP_INTR_ST_CH1WERR (1 << 1)
+#define PRP_INTR_ST_CH2WERR (1 << 2)
+#define PRP_INTR_ST_CH2B2CI (1 << 3)
+#define PRP_INTR_ST_CH2B1CI (1 << 4)
+#define PRP_INTR_ST_CH1B2CI (1 << 5)
+#define PRP_INTR_ST_CH1B1CI (1 << 6)
+#define PRP_INTR_ST_LBOVF (1 << 7)
+#define PRP_INTR_ST_CH2OVF (1 << 8)
+
+struct emmaprp_fmt {
+ char *name;
+ u32 fourcc;
+ /* Types the format can be used for */
+ u32 types;
+};
+
+static struct emmaprp_fmt formats[] = {
+ {
+ .name = "YUV 4:2:0 Planar",
+ .fourcc = V4L2_PIX_FMT_YUV420,
+ .types = MEM2MEM_CAPTURE,
+ },
+ {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .types = MEM2MEM_OUTPUT,
+ },
+};
+
+/* Per-queue, driver-specific private data */
+struct emmaprp_q_data {
+ unsigned int width;
+ unsigned int height;
+ unsigned int sizeimage;
+ struct emmaprp_fmt *fmt;
+};
+
+enum {
+ V4L2_M2M_SRC = 0,
+ V4L2_M2M_DST = 1,
+};
+
+#define NUM_FORMATS ARRAY_SIZE(formats)
+
+static struct emmaprp_fmt *find_format(struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ unsigned int k;
+
+ for (k = 0; k < NUM_FORMATS; k++) {
+ fmt = &formats[k];
+ if (fmt->fourcc == f->fmt.pix.pixelformat)
+ break;
+ }
+
+ if (k == NUM_FORMATS)
+ return NULL;
+
+ return &formats[k];
+}
+
+struct emmaprp_dev {
+ struct v4l2_device v4l2_dev;
+ struct video_device *vfd;
+
+ struct mutex dev_mutex;
+ spinlock_t irqlock;
+
+ int irq_emma;
+ void __iomem *base_emma;
+ struct clk *clk_emma;
+ struct resource *res_emma;
+
+ struct v4l2_m2m_dev *m2m_dev;
+ struct vb2_alloc_ctx *alloc_ctx;
+};
+
+struct emmaprp_ctx {
+ struct emmaprp_dev *dev;
+ /* Abort requested by m2m */
+ int aborting;
+ struct emmaprp_q_data q_data[2];
+ struct v4l2_m2m_ctx *m2m_ctx;
+};
+
+static struct emmaprp_q_data *get_q_data(struct emmaprp_ctx *ctx,
+ enum v4l2_buf_type type)
+{
+ switch (type) {
+ case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+ return &(ctx->q_data[V4L2_M2M_SRC]);
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return &(ctx->q_data[V4L2_M2M_DST]);
+ default:
+ BUG();
+ }
+ return NULL;
+}
+
+/*
+ * mem2mem callbacks
+ */
+static void emmaprp_job_abort(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+
+ ctx->aborting = 1;
+
+ dprintk(pcdev, "Aborting task\n");
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, ctx->m2m_ctx);
+}
+
+static void emmaprp_lock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_lock(&pcdev->dev_mutex);
+}
+
+static void emmaprp_unlock(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ mutex_unlock(&pcdev->dev_mutex);
+}
+
+static inline void emmaprp_dump_regs(struct emmaprp_dev *pcdev)
+{
+ dprintk(pcdev,
+ "eMMa-PrP Registers:\n"
+ " SOURCE_Y_PTR = 0x%08X\n"
+ " SRC_FRAME_SIZE = 0x%08X\n"
+ " DEST_Y_PTR = 0x%08X\n"
+ " DEST_CR_PTR = 0x%08X\n"
+ " DEST_CB_PTR = 0x%08X\n"
+ " CH2_OUT_IMAGE_SIZE = 0x%08X\n"
+ " CNTL = 0x%08X\n",
+ readl(pcdev->base_emma + PRP_SOURCE_Y_PTR),
+ readl(pcdev->base_emma + PRP_SRC_FRAME_SIZE),
+ readl(pcdev->base_emma + PRP_DEST_Y_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CR_PTR),
+ readl(pcdev->base_emma + PRP_DEST_CB_PTR),
+ readl(pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE),
+ readl(pcdev->base_emma + PRP_CNTL));
+}
+
+static void emmaprp_device_run(void *priv)
+{
+ struct emmaprp_ctx *ctx = priv;
+ struct emmaprp_q_data *s_q_data, *d_q_data;
+ struct vb2_buffer *src_buf, *dst_buf;
+ struct emmaprp_dev *pcdev = ctx->dev;
+ unsigned int s_width, s_height;
+ unsigned int d_width, d_height;
+ unsigned int d_size;
+ dma_addr_t p_in, p_out;
+ u32 tmp;
+
+ src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
+ dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
+
+ s_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
+ s_width = s_q_data->width;
+ s_height = s_q_data->height;
+
+ d_q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE);
+ d_width = d_q_data->width;
+ d_height = d_q_data->height;
+ d_size = d_width * d_height;
+
+ p_in = vb2_dma_contig_plane_dma_addr(src_buf, 0);
+ p_out = vb2_dma_contig_plane_dma_addr(dst_buf, 0);
+ if (!p_in || !p_out) {
+ v4l2_err(&pcdev->v4l2_dev,
+ "Acquiring kernel pointers to buffers failed\n");
+ return;
+ }
+
+ /* Input frame parameters */
+ writel(p_in, pcdev->base_emma + PRP_SOURCE_Y_PTR);
+ writel(PRP_SIZE_WIDTH(s_width) | PRP_SIZE_HEIGHT(s_height),
+ pcdev->base_emma + PRP_SRC_FRAME_SIZE);
+
+ /* Output frame parameters */
+ writel(p_out, pcdev->base_emma + PRP_DEST_Y_PTR);
+ writel(p_out + d_size, pcdev->base_emma + PRP_DEST_CB_PTR);
+ writel(p_out + d_size + (d_size >> 2),
+ pcdev->base_emma + PRP_DEST_CR_PTR);
+ writel(PRP_SIZE_WIDTH(d_width) | PRP_SIZE_HEIGHT(d_height),
+ pcdev->base_emma + PRP_CH2_OUT_IMAGE_SIZE);
+
+ /* IRQ configuration */
+ tmp = readl(pcdev->base_emma + PRP_INTR_CNTL);
+ writel(tmp | PRP_INTR_RDERR |
+ PRP_INTR_CH2WERR |
+ PRP_INTR_CH2FC,
+ pcdev->base_emma + PRP_INTR_CNTL);
+
+ emmaprp_dump_regs(pcdev);
+
+ /* Enable transfer */
+ tmp = readl(pcdev->base_emma + PRP_CNTL);
+ writel(tmp | PRP_CNTL_CH2_OUT_YUV420 |
+ PRP_CNTL_DATA_IN_YUV422 |
+ PRP_CNTL_CH2EN,
+ pcdev->base_emma + PRP_CNTL);
+}
+
+static irqreturn_t emmaprp_irq(int irq_emma, void *data)
+{
+ struct emmaprp_dev *pcdev = data;
+ struct emmaprp_ctx *curr_ctx;
+ struct vb2_buffer *src_vb, *dst_vb;
+ unsigned long flags;
+ u32 irqst;
+
+ /* Check irq flags and clear irq */
+ irqst = readl(pcdev->base_emma + PRP_INTRSTATUS);
+ writel(irqst, pcdev->base_emma + PRP_INTRSTATUS);
+ dprintk(pcdev, "irqst = 0x%08x\n", irqst);
+
+ curr_ctx = v4l2_m2m_get_curr_priv(pcdev->m2m_dev);
+ if (curr_ctx == NULL) {
+ pr_err("Instance released before the end of transaction\n");
+ return IRQ_HANDLED;
+ }
+
+ if (!curr_ctx->aborting) {
+ if ((irqst & PRP_INTR_ST_RDERR) ||
+ (irqst & PRP_INTR_ST_CH2WERR)) {
+ pr_err("PrP bus error ocurred, this transfer is probably corrupted\n");
+ writel(PRP_CNTL_SWRST, pcdev->base_emma + PRP_CNTL);
+ } else if (irqst & PRP_INTR_ST_CH2B1CI) { /* buffer ready */
+ src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
+ dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
+
+ spin_lock_irqsave(&pcdev->irqlock, flags);
+ v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
+ v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
+ spin_unlock_irqrestore(&pcdev->irqlock, flags);
+ }
+ }
+
+ v4l2_m2m_job_finish(pcdev->m2m_dev, curr_ctx->m2m_ctx);
+ return IRQ_HANDLED;
+}
+
+/*
+ * video ioctls
+ */
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
+ strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT
+ | V4L2_CAP_STREAMING;
+
+ return 0;
+}
+
+static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
+{
+ int i, num;
+ struct emmaprp_fmt *fmt;
+
+ num = 0;
+
+ for (i = 0; i < NUM_FORMATS; ++i) {
+ if (formats[i].types & type) {
+ /* index-th format of type type found ? */
+ if (num == f->index)
+ break;
+ /* Correct type but haven't reached our index yet,
+ * just increment per-type index */
+ ++num;
+ }
+ }
+
+ if (i < NUM_FORMATS) {
+ /* Format found */
+ fmt = &formats[i];
+ strlcpy(f->description, fmt->name, sizeof(f->description) - 1);
+ f->pixelformat = fmt->fourcc;
+ return 0;
+ }
+
+ /* Format not found */
+ return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_CAPTURE);
+}
+
+static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ return enum_fmt(f, MEM2MEM_OUTPUT);
+}
+
+static int vidioc_g_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct vb2_queue *vq;
+ struct emmaprp_q_data *q_data;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+
+ f->fmt.pix.width = q_data->width;
+ f->fmt.pix.height = q_data->height;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.pixelformat = q_data->fmt->fourcc;
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420)
+ f->fmt.pix.bytesperline = q_data->width * 3 / 2;
+ else /* YUYV */
+ f->fmt.pix.bytesperline = q_data->width * 2;
+ f->fmt.pix.sizeimage = q_data->sizeimage;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ return vidioc_g_fmt(priv, f);
+}
+
+static int vidioc_try_fmt(struct v4l2_format *f)
+{
+ enum v4l2_field field;
+
+
+ if (!find_format(f))
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ if (field == V4L2_FIELD_ANY)
+ field = V4L2_FIELD_NONE;
+ else if (V4L2_FIELD_NONE != field)
+ return -EINVAL;
+
+ /* V4L2 specification suggests the driver corrects the format struct
+ * if any of the dimensions is unsupported */
+ f->fmt.pix.field = field;
+
+ if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_YUV420, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 3 / 2;
+ } else {
+ v4l_bound_align_image(&f->fmt.pix.width, MIN_W, MAX_W,
+ W_ALIGN_OTHERS, &f->fmt.pix.height,
+ MIN_H, MAX_H, H_ALIGN, S_ALIGN);
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ }
+ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct emmaprp_fmt *fmt;
+ struct emmaprp_ctx *ctx = priv;
+
+ fmt = find_format(f);
+ if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
+ v4l2_err(&ctx->dev->v4l2_dev,
+ "Fourcc format (0x%08x) invalid.\n",
+ f->fmt.pix.pixelformat);
+ return -EINVAL;
+ }
+
+ return vidioc_try_fmt(f);
+}
+
+static int vidioc_s_fmt(struct emmaprp_ctx *ctx, struct v4l2_format *f)
+{
+ struct emmaprp_q_data *q_data;
+ struct vb2_queue *vq;
+ int ret;
+
+ vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
+ if (!vq)
+ return -EINVAL;
+
+ q_data = get_q_data(ctx, f->type);
+ if (!q_data)
+ return -EINVAL;
+
+ if (vb2_is_busy(vq)) {
+ v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
+ return -EBUSY;
+ }
+
+ ret = vidioc_try_fmt(f);
+ if (ret)
+ return ret;
+
+ q_data->fmt = find_format(f);
+ q_data->width = f->fmt.pix.width;
+ q_data->height = f->fmt.pix.height;
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ q_data->sizeimage = q_data->width * q_data->height * 3 / 2;
+ else /* YUYV */
+ q_data->sizeimage = q_data->width * q_data->height * 2;
+
+ dprintk(ctx->dev,
+ "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
+ f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_cap(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ int ret;
+
+ ret = vidioc_try_fmt_vid_out(file, priv, f);
+ if (ret)
+ return ret;
+
+ return vidioc_s_fmt(priv, f);
+}
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *reqbufs)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
+}
+
+static int vidioc_streamoff(struct file *file, void *priv,
+ enum v4l2_buf_type type)
+{
+ struct emmaprp_ctx *ctx = priv;
+
+ return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
+}
+
+static const struct v4l2_ioctl_ops emmaprp_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+
+ .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
+ .vidioc_g_fmt_vid_out = vidioc_g_fmt_vid_out,
+ .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
+ .vidioc_s_fmt_vid_out = vidioc_s_fmt_vid_out,
+
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+};
+
+
+/*
+ * Queue operations
+ */
+static int emmaprp_queue_setup(struct vb2_queue *vq,
+ const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vq);
+ struct emmaprp_q_data *q_data;
+ unsigned int size, count = *nbuffers;
+
+ q_data = get_q_data(ctx, vq->type);
+
+ if (q_data->fmt->fourcc == V4L2_PIX_FMT_YUV420)
+ size = q_data->width * q_data->height * 3 / 2;
+ else
+ size = q_data->width * q_data->height * 2;
+
+ while (size * count > MEM2MEM_VID_MEM_LIMIT)
+ (count)--;
+
+ *nplanes = 1;
+ *nbuffers = count;
+ sizes[0] = size;
+
+ alloc_ctxs[0] = ctx->dev->alloc_ctx;
+
+ dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
+
+ return 0;
+}
+
+static int emmaprp_buf_prepare(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ struct emmaprp_q_data *q_data;
+
+ dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
+
+ q_data = get_q_data(ctx, vb->vb2_queue->type);
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+ dprintk(ctx->dev, "%s data will not fit into plane"
+ "(%lu < %lu)\n", __func__,
+ vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, q_data->sizeimage);
+
+ return 0;
+}
+
+static void emmaprp_buf_queue(struct vb2_buffer *vb)
+{
+ struct emmaprp_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
+ v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
+}
+
+static struct vb2_ops emmaprp_qops = {
+ .queue_setup = emmaprp_queue_setup,
+ .buf_prepare = emmaprp_buf_prepare,
+ .buf_queue = emmaprp_buf_queue,
+};
+
+static int queue_init(void *priv, struct vb2_queue *src_vq,
+ struct vb2_queue *dst_vq)
+{
+ struct emmaprp_ctx *ctx = priv;
+ int ret;
+
+ memset(src_vq, 0, sizeof(*src_vq));
+ src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+ src_vq->io_modes = VB2_MMAP;
+ src_vq->drv_priv = ctx;
+ src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ src_vq->ops = &emmaprp_qops;
+ src_vq->mem_ops = &vb2_dma_contig_memops;
+
+ ret = vb2_queue_init(src_vq);
+ if (ret)
+ return ret;
+
+ memset(dst_vq, 0, sizeof(*dst_vq));
+ dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ dst_vq->io_modes = VB2_MMAP;
+ dst_vq->drv_priv = ctx;
+ dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
+ dst_vq->ops = &emmaprp_qops;
+ dst_vq->mem_ops = &vb2_dma_contig_memops;
+
+ return vb2_queue_init(dst_vq);
+}
+
+/*
+ * File operations
+ */
+static int emmaprp_open(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx;
+
+ ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ file->private_data = ctx;
+ ctx->dev = pcdev;
+
+ ctx->m2m_ctx = v4l2_m2m_ctx_init(pcdev->m2m_dev, ctx, &queue_init);
+
+ if (IS_ERR(ctx->m2m_ctx)) {
+ int ret = PTR_ERR(ctx->m2m_ctx);
+
+ kfree(ctx);
+ return ret;
+ }
+
+ clk_enable(pcdev->clk_emma);
+ ctx->q_data[V4L2_M2M_SRC].fmt = &formats[1];
+ ctx->q_data[V4L2_M2M_DST].fmt = &formats[0];
+
+ dprintk(pcdev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
+
+ return 0;
+}
+
+static int emmaprp_release(struct file *file)
+{
+ struct emmaprp_dev *pcdev = video_drvdata(file);
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ dprintk(pcdev, "Releasing instance %p\n", ctx);
+
+ clk_disable(pcdev->clk_emma);
+ v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ kfree(ctx);
+
+ return 0;
+}
+
+static unsigned int emmaprp_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
+}
+
+static int emmaprp_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct emmaprp_ctx *ctx = file->private_data;
+
+ return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
+}
+
+static const struct v4l2_file_operations emmaprp_fops = {
+ .owner = THIS_MODULE,
+ .open = emmaprp_open,
+ .release = emmaprp_release,
+ .poll = emmaprp_poll,
+ .unlocked_ioctl = video_ioctl2,
+ .mmap = emmaprp_mmap,
+};
+
+static struct video_device emmaprp_videodev = {
+ .name = MEM2MEM_NAME,
+ .fops = &emmaprp_fops,
+ .ioctl_ops = &emmaprp_ioctl_ops,
+ .minor = -1,
+ .release = video_device_release,
+};
+
+static struct v4l2_m2m_ops m2m_ops = {
+ .device_run = emmaprp_device_run,
+ .job_abort = emmaprp_job_abort,
+ .lock = emmaprp_lock,
+ .unlock = emmaprp_unlock,
+};
+
+static int emmaprp_probe(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev;
+ struct video_device *vfd;
+ struct resource *res_emma;
+ int irq_emma;
+ int ret;
+
+ pcdev = kzalloc(sizeof *pcdev, GFP_KERNEL);
+ if (!pcdev)
+ return -ENOMEM;
+
+ spin_lock_init(&pcdev->irqlock);
+
+ pcdev->clk_emma = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(pcdev->clk_emma)) {
+ ret = PTR_ERR(pcdev->clk_emma);
+ goto free_dev;
+ }
+
+ irq_emma = platform_get_irq(pdev, 0);
+ res_emma = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (irq_emma < 0 || res_emma == NULL) {
+ dev_err(&pdev->dev, "Missing platform resources data\n");
+ ret = -ENODEV;
+ goto free_clk;
+ }
+
+ ret = v4l2_device_register(&pdev->dev, &pcdev->v4l2_dev);
+ if (ret)
+ goto free_clk;
+
+ mutex_init(&pcdev->dev_mutex);
+
+ vfd = video_device_alloc();
+ if (!vfd) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to allocate video device\n");
+ ret = -ENOMEM;
+ goto unreg_dev;
+ }
+
+ *vfd = emmaprp_videodev;
+ vfd->lock = &pcdev->dev_mutex;
+
+ video_set_drvdata(vfd, pcdev);
+ snprintf(vfd->name, sizeof(vfd->name), "%s", emmaprp_videodev.name);
+ pcdev->vfd = vfd;
+ v4l2_info(&pcdev->v4l2_dev, EMMAPRP_MODULE_NAME
+ " Device registered as /dev/video%d\n", vfd->num);
+
+ platform_set_drvdata(pdev, pcdev);
+
+ if (devm_request_mem_region(&pdev->dev, res_emma->start,
+ resource_size(res_emma), MEM2MEM_NAME) == NULL)
+ goto rel_vdev;
+
+ pcdev->base_emma = devm_ioremap(&pdev->dev, res_emma->start,
+ resource_size(res_emma));
+ if (!pcdev->base_emma)
+ goto rel_vdev;
+
+ pcdev->irq_emma = irq_emma;
+ pcdev->res_emma = res_emma;
+
+ if (devm_request_irq(&pdev->dev, pcdev->irq_emma, emmaprp_irq,
+ 0, MEM2MEM_NAME, pcdev) < 0)
+ goto rel_vdev;
+
+ pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
+ if (IS_ERR(pcdev->alloc_ctx)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to alloc vb2 context\n");
+ ret = PTR_ERR(pcdev->alloc_ctx);
+ goto rel_vdev;
+ }
+
+ pcdev->m2m_dev = v4l2_m2m_init(&m2m_ops);
+ if (IS_ERR(pcdev->m2m_dev)) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to init mem2mem device\n");
+ ret = PTR_ERR(pcdev->m2m_dev);
+ goto rel_ctx;
+ }
+
+ ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
+ if (ret) {
+ v4l2_err(&pcdev->v4l2_dev, "Failed to register video device\n");
+ goto rel_m2m;
+ }
+
+ return 0;
+
+
+rel_m2m:
+ v4l2_m2m_release(pcdev->m2m_dev);
+rel_ctx:
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+rel_vdev:
+ video_device_release(vfd);
+unreg_dev:
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+free_clk:
+ clk_put(pcdev->clk_emma);
+free_dev:
+ kfree(pcdev);
+
+ return ret;
+}
+
+static int emmaprp_remove(struct platform_device *pdev)
+{
+ struct emmaprp_dev *pcdev = platform_get_drvdata(pdev);
+
+ v4l2_info(&pcdev->v4l2_dev, "Removing " EMMAPRP_MODULE_NAME);
+
+ video_unregister_device(pcdev->vfd);
+ v4l2_m2m_release(pcdev->m2m_dev);
+ vb2_dma_contig_cleanup_ctx(pcdev->alloc_ctx);
+ v4l2_device_unregister(&pcdev->v4l2_dev);
+ clk_put(pcdev->clk_emma);
+ kfree(pcdev);
+
+ return 0;
+}
+
+static struct platform_driver emmaprp_pdrv = {
+ .probe = emmaprp_probe,
+ .remove = emmaprp_remove,
+ .driver = {
+ .name = MEM2MEM_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static void __exit emmaprp_exit(void)
+{
+ platform_driver_unregister(&emmaprp_pdrv);
+}
+
+static int __init emmaprp_init(void)
+{
+ return platform_driver_register(&emmaprp_pdrv);
+}
+
+module_init(emmaprp_init);
+module_exit(emmaprp_exit);
diff --git a/drivers/media/video/noon010pc30.c b/drivers/media/video/noon010pc30.c
index 50838bf84204..440c12962bae 100644
--- a/drivers/media/video/noon010pc30.c
+++ b/drivers/media/video/noon010pc30.c
@@ -725,8 +725,8 @@ static int noon010_probe(struct i2c_client *client,
mutex_init(&info->lock);
sd = &info->sd;
- strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
v4l2_i2c_subdev_init(sd, client, &noon010_ops);
+ strlcpy(sd->name, MODULE_NAME, sizeof(sd->name));
sd->internal_ops = &noon010_subdev_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -844,18 +844,7 @@ static struct i2c_driver noon010_i2c_driver = {
.id_table = noon010_id,
};
-static int __init noon010_init(void)
-{
- return i2c_add_driver(&noon010_i2c_driver);
-}
-
-static void __exit noon010_exit(void)
-{
- i2c_del_driver(&noon010_i2c_driver);
-}
-
-module_init(noon010_init);
-module_exit(noon010_exit);
+module_i2c_driver(noon010_i2c_driver);
MODULE_DESCRIPTION("Siliconfile NOON010PC30 camera driver");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
diff --git a/drivers/media/video/omap/omap_vout.c b/drivers/media/video/omap/omap_vout.c
index 1fb7d5bd5ec2..88cf9d952631 100644
--- a/drivers/media/video/omap/omap_vout.c
+++ b/drivers/media/video/omap/omap_vout.c
@@ -2268,13 +2268,12 @@ static struct platform_driver omap_vout_driver = {
.driver = {
.name = VOUT_NAME,
},
- .probe = omap_vout_probe,
.remove = omap_vout_remove,
};
static int __init omap_vout_init(void)
{
- if (platform_driver_register(&omap_vout_driver) != 0) {
+ if (platform_driver_probe(&omap_vout_driver, omap_vout_probe) != 0) {
printk(KERN_ERR VOUT_NAME ":Could not register Video driver\n");
return -EINVAL;
}
diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c
index b5247cb64fde..3c2c5d3bcc6b 100644
--- a/drivers/media/video/ov2640.c
+++ b/drivers/media/video/ov2640.c
@@ -1103,21 +1103,7 @@ static struct i2c_driver ov2640_i2c_driver = {
.id_table = ov2640_id,
};
-/*
- * Module functions
- */
-static int __init ov2640_module_init(void)
-{
- return i2c_add_driver(&ov2640_i2c_driver);
-}
-
-static void __exit ov2640_module_exit(void)
-{
- i2c_del_driver(&ov2640_i2c_driver);
-}
-
-module_init(ov2640_module_init);
-module_exit(ov2640_module_exit);
+module_i2c_driver(ov2640_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor");
MODULE_AUTHOR("Alberto Panizzo");
diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c
index bb37ec80f274..80e07794ac8e 100644
--- a/drivers/media/video/ov5642.c
+++ b/drivers/media/video/ov5642.c
@@ -1068,18 +1068,7 @@ static struct i2c_driver ov5642_i2c_driver = {
.id_table = ov5642_id,
};
-static int __init ov5642_mod_init(void)
-{
- return i2c_add_driver(&ov5642_i2c_driver);
-}
-
-static void __exit ov5642_mod_exit(void)
-{
- i2c_del_driver(&ov5642_i2c_driver);
-}
-
-module_init(ov5642_mod_init);
-module_exit(ov5642_mod_exit);
+module_i2c_driver(ov5642_i2c_driver);
MODULE_DESCRIPTION("Omnivision OV5642 Camera driver");
MODULE_AUTHOR("Bastian Hecht <hechtb@gmail.com>");
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
index 3627f3225bbb..3e028b1970dd 100644
--- a/drivers/media/video/ov6650.c
+++ b/drivers/media/video/ov6650.c
@@ -1046,18 +1046,7 @@ static struct i2c_driver ov6650_i2c_driver = {
.id_table = ov6650_id,
};
-static int __init ov6650_module_init(void)
-{
- return i2c_add_driver(&ov6650_i2c_driver);
-}
-
-static void __exit ov6650_module_exit(void)
-{
- i2c_del_driver(&ov6650_i2c_driver);
-}
-
-module_init(ov6650_module_init);
-module_exit(ov6650_module_exit);
+module_i2c_driver(ov6650_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV6650");
MODULE_AUTHOR("Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>");
diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
index 6a564964853a..e7c82b297514 100644
--- a/drivers/media/video/ov7670.c
+++ b/drivers/media/video/ov7670.c
@@ -1583,15 +1583,4 @@ static struct i2c_driver ov7670_driver = {
.id_table = ov7670_id,
};
-static __init int init_ov7670(void)
-{
- return i2c_add_driver(&ov7670_driver);
-}
-
-static __exit void exit_ov7670(void)
-{
- i2c_del_driver(&ov7670_driver);
-}
-
-module_init(init_ov7670);
-module_exit(exit_ov7670);
+module_i2c_driver(ov7670_driver);
diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
index 9f6ce3d8a29e..74e77d327ed8 100644
--- a/drivers/media/video/ov772x.c
+++ b/drivers/media/video/ov772x.c
@@ -1123,22 +1123,7 @@ static struct i2c_driver ov772x_i2c_driver = {
.id_table = ov772x_id,
};
-/*
- * module function
- */
-
-static int __init ov772x_module_init(void)
-{
- return i2c_add_driver(&ov772x_i2c_driver);
-}
-
-static void __exit ov772x_module_exit(void)
-{
- i2c_del_driver(&ov772x_i2c_driver);
-}
-
-module_init(ov772x_module_init);
-module_exit(ov772x_module_exit);
+module_i2c_driver(ov772x_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for ov772x");
MODULE_AUTHOR("Kuninori Morimoto");
diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c
index a4f99797eb56..23412debb36b 100644
--- a/drivers/media/video/ov9640.c
+++ b/drivers/media/video/ov9640.c
@@ -738,18 +738,7 @@ static struct i2c_driver ov9640_i2c_driver = {
.id_table = ov9640_id,
};
-static int __init ov9640_module_init(void)
-{
- return i2c_add_driver(&ov9640_i2c_driver);
-}
-
-static void __exit ov9640_module_exit(void)
-{
- i2c_del_driver(&ov9640_i2c_driver);
-}
-
-module_init(ov9640_module_init);
-module_exit(ov9640_module_exit);
+module_i2c_driver(ov9640_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx");
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c
index d9a9f7174f7a..3eb07c22516e 100644
--- a/drivers/media/video/ov9740.c
+++ b/drivers/media/video/ov9740.c
@@ -998,18 +998,7 @@ static struct i2c_driver ov9740_i2c_driver = {
.id_table = ov9740_id,
};
-static int __init ov9740_module_init(void)
-{
- return i2c_add_driver(&ov9740_i2c_driver);
-}
-
-static void __exit ov9740_module_exit(void)
-{
- i2c_del_driver(&ov9740_i2c_driver);
-}
-
-module_init(ov9740_module_init);
-module_exit(ov9740_module_exit);
+module_i2c_driver(ov9740_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV9740");
MODULE_AUTHOR("Andrew Chew <achew@nvidia.com>");
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index c6da8f77e1a2..d8c898278e8c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -320,7 +320,17 @@ static struct tda829x_config tda829x_no_probe = {
.probe_tuner = TDA829X_DONT_PROBE,
};
+static struct tda18271_std_map hauppauge_tda18271_dvbt_std_map = {
+ .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+ .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6,
+ .if_lvl = 1, .rfagc_top = 0x37, },
+};
+
static struct tda18271_config hauppauge_tda18271_dvb_config = {
+ .std_map = &hauppauge_tda18271_dvbt_std_map,
.gate = TDA18271_GATE_ANALOG,
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 6d666174dbb4..e1111d968a3d 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -96,7 +96,6 @@ static struct v4l2_capability pvr_capability ={
.capabilities = (V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
V4L2_CAP_READWRITE),
- .reserved = {0,0,0,0}
};
static struct v4l2_fmtdesc pvr_fmtdesc [] = {
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index f495eeb5403a..2834e3e65b39 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -1146,14 +1146,6 @@ leave:
return ret;
}
-static int pwc_log_status(struct file *file, void *priv)
-{
- struct pwc_device *pdev = video_drvdata(file);
-
- v4l2_ctrl_handler_log_status(&pdev->ctrl_handler, PWC_NAME);
- return 0;
-}
-
const struct v4l2_ioctl_ops pwc_ioctl_ops = {
.vidioc_querycap = pwc_querycap,
.vidioc_enum_input = pwc_enum_input,
@@ -1169,7 +1161,7 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = {
.vidioc_dqbuf = pwc_dqbuf,
.vidioc_streamon = pwc_streamon,
.vidioc_streamoff = pwc_streamoff,
- .vidioc_log_status = pwc_log_status,
+ .vidioc_log_status = v4l2_ctrl_log_status,
.vidioc_enum_framesizes = pwc_enum_framesizes,
.vidioc_enum_frameintervals = pwc_enum_frameintervals,
.vidioc_g_parm = pwc_g_parm,
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
index 0bd7da26d018..5a413f4427e0 100644
--- a/drivers/media/video/pxa_camera.c
+++ b/drivers/media/video/pxa_camera.c
@@ -921,12 +921,12 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev)
/* "Safe default" - 13MHz */
recalculate_fifo_timeout(pcdev, 13000000);
- clk_enable(pcdev->clk);
+ clk_prepare_enable(pcdev->clk);
}
static void pxa_camera_deactivate(struct pxa_camera_dev *pcdev)
{
- clk_disable(pcdev->clk);
+ clk_disable_unprepare(pcdev->clk);
}
static irqreturn_t pxa_camera_irq(int irq, void *data)
diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c
index 9937386a3bae..f6419b22c258 100644
--- a/drivers/media/video/rj54n1cb0c.c
+++ b/drivers/media/video/rj54n1cb0c.c
@@ -1407,18 +1407,7 @@ static struct i2c_driver rj54n1_i2c_driver = {
.id_table = rj54n1_id,
};
-static int __init rj54n1_mod_init(void)
-{
- return i2c_add_driver(&rj54n1_i2c_driver);
-}
-
-static void __exit rj54n1_mod_exit(void)
-{
- i2c_del_driver(&rj54n1_i2c_driver);
-}
-
-module_init(rj54n1_mod_init);
-module_exit(rj54n1_mod_exit);
+module_i2c_driver(rj54n1_i2c_driver);
MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver");
MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
index c1bef6187661..4894cbb1c547 100644
--- a/drivers/media/video/s2255drv.c
+++ b/drivers/media/video/s2255drv.c
@@ -134,7 +134,7 @@
/* usb config commands */
#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de)
-#define CMD_2255 cpu_to_le32(0xc2255000)
+#define CMD_2255 0xc2255000
#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10))
#define CMD_START cpu_to_le32((CMD_2255 | 0x20))
#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30))
@@ -852,15 +852,13 @@ static int vidioc_querycap(struct file *file, void *priv,
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- int index = 0;
- if (f)
- index = f->index;
+ int index = f->index;
if (index >= ARRAY_SIZE(formats))
return -EINVAL;
- if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
- (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
- return -EINVAL;
+ if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) ||
+ (formats[index].fourcc == V4L2_PIX_FMT_MJPEG)))
+ return -EINVAL;
dprintk(4, "name %s\n", formats[index].name);
strlcpy(f->description, formats[index].name, sizeof(f->description));
f->pixelformat = formats[index].fourcc;
@@ -2027,7 +2025,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
pdata[1]);
offset = jj + PREFIX_SIZE;
bframe = 1;
- cc = pdword[1];
+ cc = le32_to_cpu(pdword[1]);
if (cc >= MAX_CHANNELS) {
printk(KERN_ERR
"bad channel\n");
@@ -2036,22 +2034,22 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
/* reverse it */
dev->cc = G_chnmap[cc];
channel = &dev->channel[dev->cc];
- payload = pdword[3];
+ payload = le32_to_cpu(pdword[3]);
if (payload > channel->req_image_size) {
channel->bad_payload++;
/* discard the bad frame */
return -EINVAL;
}
channel->pkt_size = payload;
- channel->jpg_size = pdword[4];
+ channel->jpg_size = le32_to_cpu(pdword[4]);
break;
case S2255_MARKER_RESPONSE:
pdata += DEF_USB_BLOCK;
jj += DEF_USB_BLOCK;
- if (pdword[1] >= MAX_CHANNELS)
+ if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS)
break;
- cc = G_chnmap[pdword[1]];
+ cc = G_chnmap[le32_to_cpu(pdword[1])];
if (cc >= MAX_CHANNELS)
break;
channel = &dev->channel[cc];
@@ -2074,11 +2072,11 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info)
wake_up(&dev->fw_data->wait_fw);
break;
case S2255_RESPONSE_STATUS:
- channel->vidstatus = pdword[3];
+ channel->vidstatus = le32_to_cpu(pdword[3]);
channel->vidstatus_ready = 1;
wake_up(&channel->wait_vidstatus);
dprintk(5, "got vidstatus %x chan %d\n",
- pdword[3], cc);
+ le32_to_cpu(pdword[3]), cc);
break;
default:
printk(KERN_INFO "s2255 unknown resp\n");
@@ -2605,10 +2603,11 @@ static int s2255_probe(struct usb_interface *interface,
__le32 *pRel;
pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4];
printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel);
- dev->dsp_fw_ver = *pRel;
- if (*pRel < S2255_CUR_DSP_FWVER)
+ dev->dsp_fw_ver = le32_to_cpu(*pRel);
+ if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER)
printk(KERN_INFO "s2255: f2255usb.bin out of date.\n");
- if (dev->pid == 0x2257 && *pRel < S2255_MIN_DSP_COLORFILTER)
+ if (dev->pid == 0x2257 &&
+ dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER)
printk(KERN_WARNING "s2255: 2257 requires firmware %d"
" or above.\n", S2255_MIN_DSP_COLORFILTER);
}
diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c
index 0df7f2a41814..6625e46a4638 100644
--- a/drivers/media/video/s5k6aa.c
+++ b/drivers/media/video/s5k6aa.c
@@ -1582,8 +1582,8 @@ static int s5k6aa_probe(struct i2c_client *client,
s5k6aa->inv_vflip = pdata->vert_flip;
sd = &s5k6aa->sd;
- strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops);
+ strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name));
sd->internal_ops = &s5k6aa_subdev_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
@@ -1663,18 +1663,7 @@ static struct i2c_driver s5k6aa_i2c_driver = {
.id_table = s5k6aa_id,
};
-static int __init s5k6aa_init(void)
-{
- return i2c_add_driver(&s5k6aa_i2c_driver);
-}
-
-static void __exit s5k6aa_exit(void)
-{
- i2c_del_driver(&s5k6aa_i2c_driver);
-}
-
-module_init(s5k6aa_init);
-module_exit(s5k6aa_exit);
+module_i2c_driver(s5k6aa_i2c_driver);
MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c
index a9e9653beeb4..b06efd208328 100644
--- a/drivers/media/video/s5p-fimc/fimc-capture.c
+++ b/drivers/media/video/s5p-fimc/fimc-capture.c
@@ -1019,52 +1019,117 @@ static int fimc_cap_dqbuf(struct file *file, void *priv,
return vb2_dqbuf(&fimc->vid_cap.vbq, buf, file->f_flags & O_NONBLOCK);
}
-static int fimc_cap_cropcap(struct file *file, void *fh,
- struct v4l2_cropcap *cr)
+static int fimc_cap_create_bufs(struct file *file, void *priv,
+ struct v4l2_create_buffers *create)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
- if (cr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
- return -EINVAL;
+ return vb2_create_bufs(&fimc->vid_cap.vbq, create);
+}
- cr->bounds.left = 0;
- cr->bounds.top = 0;
- cr->bounds.width = f->o_width;
- cr->bounds.height = f->o_height;
- cr->defrect = cr->bounds;
+static int fimc_cap_prepare_buf(struct file *file, void *priv,
+ struct v4l2_buffer *b)
+{
+ struct fimc_dev *fimc = video_drvdata(file);
- return 0;
+ return vb2_prepare_buf(&fimc->vid_cap.vbq, b);
}
-static int fimc_cap_g_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+static int fimc_cap_g_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
{
struct fimc_dev *fimc = video_drvdata(file);
- struct fimc_frame *f = &fimc->vid_cap.ctx->s_frame;
+ struct fimc_ctx *ctx = fimc->vid_cap.ctx;
+ struct fimc_frame *f = &ctx->s_frame;
- cr->c.left = f->offs_h;
- cr->c.top = f->offs_v;
- cr->c.width = f->width;
- cr->c.height = f->height;
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
- return 0;
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ s->r.left = 0;
+ s->r.top = 0;
+ s->r.width = f->o_width;
+ s->r.height = f->o_height;
+ return 0;
+
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ f = &ctx->d_frame;
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ s->r.left = f->offs_h;
+ s->r.top = f->offs_v;
+ s->r.width = f->width;
+ s->r.height = f->height;
+ return 0;
+ }
+
+ return -EINVAL;
}
-static int fimc_cap_s_crop(struct file *file, void *fh, struct v4l2_crop *cr)
+/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */
+int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b)
+{
+ if (a->left < b->left || a->top < b->top)
+ return 0;
+ if (a->left + a->width > b->left + b->width)
+ return 0;
+ if (a->top + a->height > b->top + b->height)
+ return 0;
+
+ return 1;
+}
+
+static int fimc_cap_s_selection(struct file *file, void *fh,
+ struct v4l2_selection *s)
{
struct fimc_dev *fimc = video_drvdata(file);
struct fimc_ctx *ctx = fimc->vid_cap.ctx;
- struct fimc_frame *ff;
+ struct v4l2_rect rect = s->r;
+ struct fimc_frame *f;
unsigned long flags;
+ unsigned int pad;
- fimc_capture_try_crop(ctx, &cr->c, FIMC_SD_PAD_SINK);
- ff = &ctx->s_frame;
+ if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ return -EINVAL;
+ switch (s->target) {
+ case V4L2_SEL_TGT_COMPOSE_DEFAULT:
+ case V4L2_SEL_TGT_COMPOSE_BOUNDS:
+ case V4L2_SEL_TGT_COMPOSE_ACTIVE:
+ f = &ctx->d_frame;
+ pad = FIMC_SD_PAD_SOURCE;
+ break;
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_ACTIVE:
+ f = &ctx->s_frame;
+ pad = FIMC_SD_PAD_SINK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ fimc_capture_try_crop(ctx, &rect, pad);
+
+ if (s->flags & V4L2_SEL_FLAG_LE &&
+ !enclosed_rectangle(&rect, &s->r))
+ return -ERANGE;
+
+ if (s->flags & V4L2_SEL_FLAG_GE &&
+ !enclosed_rectangle(&s->r, &rect))
+ return -ERANGE;
+
+ s->r = rect;
spin_lock_irqsave(&fimc->slock, flags);
- set_frame_crop(ff, cr->c.left, cr->c.top, cr->c.width, cr->c.height);
- set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
+ set_frame_crop(f, s->r.left, s->r.top, s->r.width,
+ s->r.height);
spin_unlock_irqrestore(&fimc->slock, flags);
+ set_bit(ST_CAPT_APPLY_CFG, &fimc->state);
return 0;
}
@@ -1082,12 +1147,14 @@ static const struct v4l2_ioctl_ops fimc_capture_ioctl_ops = {
.vidioc_qbuf = fimc_cap_qbuf,
.vidioc_dqbuf = fimc_cap_dqbuf,
+ .vidioc_prepare_buf = fimc_cap_prepare_buf,
+ .vidioc_create_bufs = fimc_cap_create_bufs,
+
.vidioc_streamon = fimc_cap_streamon,
.vidioc_streamoff = fimc_cap_streamoff,
- .vidioc_g_crop = fimc_cap_g_crop,
- .vidioc_s_crop = fimc_cap_s_crop,
- .vidioc_cropcap = fimc_cap_cropcap,
+ .vidioc_g_selection = fimc_cap_g_selection,
+ .vidioc_s_selection = fimc_cap_s_selection,
.vidioc_enum_input = fimc_cap_enum_input,
.vidioc_s_input = fimc_cap_s_input,
diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c
index 81bcbb9492ea..e184e650022a 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.c
+++ b/drivers/media/video/s5p-fimc/fimc-core.c
@@ -1602,24 +1602,35 @@ static void fimc_clk_put(struct fimc_dev *fimc)
{
int i;
for (i = 0; i < fimc->num_clocks; i++) {
- if (fimc->clock[i])
- clk_put(fimc->clock[i]);
+ if (IS_ERR_OR_NULL(fimc->clock[i]))
+ continue;
+ clk_unprepare(fimc->clock[i]);
+ clk_put(fimc->clock[i]);
+ fimc->clock[i] = NULL;
}
}
static int fimc_clk_get(struct fimc_dev *fimc)
{
- int i;
+ int i, ret;
+
for (i = 0; i < fimc->num_clocks; i++) {
fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]);
- if (!IS_ERR_OR_NULL(fimc->clock[i]))
- continue;
- dev_err(&fimc->pdev->dev, "failed to get fimc clock: %s\n",
- fimc_clocks[i]);
- return -ENXIO;
+ if (IS_ERR(fimc->clock[i]))
+ goto err;
+ ret = clk_prepare(fimc->clock[i]);
+ if (ret < 0) {
+ clk_put(fimc->clock[i]);
+ fimc->clock[i] = NULL;
+ goto err;
+ }
}
-
return 0;
+err:
+ fimc_clk_put(fimc);
+ dev_err(&fimc->pdev->dev, "failed to get clock: %s\n",
+ fimc_clocks[i]);
+ return -ENXIO;
}
static int fimc_m2m_suspend(struct fimc_dev *fimc)
@@ -1667,8 +1678,6 @@ static int fimc_probe(struct platform_device *pdev)
struct s5p_platform_fimc *pdata;
int ret = 0;
- dev_dbg(&pdev->dev, "%s():\n", __func__);
-
drv_data = (struct samsung_fimc_driverdata *)
platform_get_device_id(pdev)->driver_data;
@@ -1678,7 +1687,7 @@ static int fimc_probe(struct platform_device *pdev)
return -EINVAL;
}
- fimc = kzalloc(sizeof(struct fimc_dev), GFP_KERNEL);
+ fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL);
if (!fimc)
return -ENOMEM;
@@ -1689,51 +1698,35 @@ static int fimc_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data;
fimc->pdata = pdata;
-
init_waitqueue_head(&fimc->irq_queue);
spin_lock_init(&fimc->slock);
mutex_init(&fimc->lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to find the registers\n");
- ret = -ENOENT;
- goto err_info;
- }
-
- fimc->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!fimc->regs_res) {
- dev_err(&pdev->dev, "failed to obtain register region\n");
- ret = -ENOENT;
- goto err_info;
- }
-
- fimc->regs = ioremap(res->start, resource_size(res));
- if (!fimc->regs) {
- dev_err(&pdev->dev, "failed to map registers\n");
- ret = -ENXIO;
- goto err_req_region;
+ fimc->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (fimc->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENOENT;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(&pdev->dev, "failed to get IRQ resource\n");
- ret = -ENXIO;
- goto err_regs_unmap;
+ if (res == NULL) {
+ dev_err(&pdev->dev, "Failed to get IRQ resource\n");
+ return -ENXIO;
}
fimc->irq = res->start;
fimc->num_clocks = MAX_FIMC_CLOCKS;
ret = fimc_clk_get(fimc);
if (ret)
- goto err_regs_unmap;
+ return ret;
clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency);
clk_enable(fimc->clock[CLK_BUS]);
platform_set_drvdata(pdev, fimc);
- ret = request_irq(fimc->irq, fimc_irq_handler, 0, pdev->name, fimc);
+ ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler,
+ 0, pdev->name, fimc);
if (ret) {
dev_err(&pdev->dev, "failed to install irq (%d)\n", ret);
goto err_clk;
@@ -1742,7 +1735,7 @@ static int fimc_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0)
- goto err_irq;
+ goto err_clk;
/* Initialize contiguous memory allocator */
fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev);
if (IS_ERR(fimc->alloc_ctx)) {
@@ -1757,17 +1750,8 @@ static int fimc_probe(struct platform_device *pdev)
err_pm:
pm_runtime_put(&pdev->dev);
-err_irq:
- free_irq(fimc->irq, fimc);
err_clk:
fimc_clk_put(fimc);
-err_regs_unmap:
- iounmap(fimc->regs);
-err_req_region:
- release_resource(fimc->regs_res);
- kfree(fimc->regs_res);
-err_info:
- kfree(fimc);
return ret;
}
@@ -1854,11 +1838,6 @@ static int __devexit fimc_remove(struct platform_device *pdev)
clk_disable(fimc->clock[CLK_BUS]);
fimc_clk_put(fimc);
- free_irq(fimc->irq, fimc);
- iounmap(fimc->regs);
- release_resource(fimc->regs_res);
- kfree(fimc->regs_res);
- kfree(fimc);
dev_info(&pdev->dev, "driver unloaded\n");
return 0;
diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h
index 4e20560c73d4..a18291e648e2 100644
--- a/drivers/media/video/s5p-fimc/fimc-core.h
+++ b/drivers/media/video/s5p-fimc/fimc-core.h
@@ -434,7 +434,6 @@ struct fimc_ctx;
* @num_clocks: the number of clocks managed by this device instance
* @clock: clocks required for FIMC operation
* @regs: the mapped hardware registers
- * @regs_res: the resource claimed for IO registers
* @irq: FIMC interrupt number
* @irq_queue: interrupt handler waitqueue
* @v4l2_dev: root v4l2_device
@@ -454,7 +453,6 @@ struct fimc_dev {
u16 num_clocks;
struct clk *clock[MAX_FIMC_CLOCKS];
void __iomem *regs;
- struct resource *regs_res;
int irq;
wait_queue_head_t irq_queue;
struct v4l2_device *v4l2_dev;
diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c
index 63eccb55728f..62ed37e40149 100644
--- a/drivers/media/video/s5p-fimc/fimc-mdevice.c
+++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c
@@ -750,7 +750,7 @@ static int __devinit fimc_md_probe(struct platform_device *pdev)
struct fimc_md *fmd;
int ret;
- fmd = kzalloc(sizeof(struct fimc_md), GFP_KERNEL);
+ fmd = devm_kzalloc(&pdev->dev, sizeof(*fmd), GFP_KERNEL);
if (!fmd)
return -ENOMEM;
@@ -771,7 +771,7 @@ static int __devinit fimc_md_probe(struct platform_device *pdev)
ret = v4l2_device_register(&pdev->dev, &fmd->v4l2_dev);
if (ret < 0) {
v4l2_err(v4l2_dev, "Failed to register v4l2_device: %d\n", ret);
- goto err1;
+ return ret;
}
ret = media_device_register(&fmd->media_dev);
if (ret < 0) {
@@ -813,8 +813,6 @@ err3:
fimc_md_unregister_entities(fmd);
err2:
v4l2_device_unregister(&fmd->v4l2_dev);
-err1:
- kfree(fmd);
return ret;
}
@@ -828,7 +826,6 @@ static int __devexit fimc_md_remove(struct platform_device *pdev)
fimc_md_unregister_entities(fmd);
media_device_unregister(&fmd->media_dev);
fimc_md_put_clocks(fmd);
- kfree(fmd);
return 0;
}
diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c
index 130335cf62fd..f44f690397f7 100644
--- a/drivers/media/video/s5p-fimc/mipi-csis.c
+++ b/drivers/media/video/s5p-fimc/mipi-csis.c
@@ -1,8 +1,8 @@
/*
* Samsung S5P/EXYNOS4 SoC series MIPI-CSI receiver driver
*
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- * Contact: Sylwester Nawrocki, <s.nawrocki@samsung.com>
+ * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd.
+ * Sylwester Nawrocki, <s.nawrocki@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -100,7 +100,6 @@ enum {
* @pads: CSIS pads array
* @sd: v4l2_subdev associated with CSIS device instance
* @pdev: CSIS platform device
- * @regs_res: requested I/O register memory resource
* @regs: mmaped I/O registers memory
* @clock: CSIS clocks
* @irq: requested s5p-mipi-csis irq number
@@ -113,7 +112,6 @@ struct csis_state {
struct media_pad pads[CSIS_PADS_NUM];
struct v4l2_subdev sd;
struct platform_device *pdev;
- struct resource *regs_res;
void __iomem *regs;
struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
struct clk *clock[NUM_CSIS_CLOCKS];
@@ -258,26 +256,36 @@ static void s5pcsis_clk_put(struct csis_state *state)
{
int i;
- for (i = 0; i < NUM_CSIS_CLOCKS; i++)
- if (!IS_ERR_OR_NULL(state->clock[i]))
- clk_put(state->clock[i]);
+ for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
+ if (IS_ERR_OR_NULL(state->clock[i]))
+ continue;
+ clk_unprepare(state->clock[i]);
+ clk_put(state->clock[i]);
+ state->clock[i] = NULL;
+ }
}
static int s5pcsis_clk_get(struct csis_state *state)
{
struct device *dev = &state->pdev->dev;
- int i;
+ int i, ret;
for (i = 0; i < NUM_CSIS_CLOCKS; i++) {
state->clock[i] = clk_get(dev, csi_clock_name[i]);
- if (IS_ERR(state->clock[i])) {
- s5pcsis_clk_put(state);
- dev_err(dev, "failed to get clock: %s\n",
- csi_clock_name[i]);
- return -ENXIO;
+ if (IS_ERR(state->clock[i]))
+ goto err;
+ ret = clk_prepare(state->clock[i]);
+ if (ret < 0) {
+ clk_put(state->clock[i]);
+ state->clock[i] = NULL;
+ goto err;
}
}
return 0;
+err:
+ s5pcsis_clk_put(state);
+ dev_err(dev, "failed to get clock: %s\n", csi_clock_name[i]);
+ return -ENXIO;
}
static int s5pcsis_s_power(struct v4l2_subdev *sd, int on)
@@ -480,12 +488,11 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
{
struct s5p_platform_mipi_csis *pdata;
struct resource *mem_res;
- struct resource *regs_res;
struct csis_state *state;
int ret = -ENOMEM;
int i;
- state = kzalloc(sizeof(*state), GFP_KERNEL);
+ state = devm_kzalloc(&pdev->dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
@@ -495,52 +502,27 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data;
if (pdata == NULL || pdata->phy_enable == NULL) {
dev_err(&pdev->dev, "Platform data not fully specified\n");
- goto e_free;
+ return -EINVAL;
}
if ((pdev->id == 1 && pdata->lanes > CSIS1_MAX_LANES) ||
pdata->lanes > CSIS0_MAX_LANES) {
- ret = -EINVAL;
dev_err(&pdev->dev, "Unsupported number of data lanes: %d\n",
pdata->lanes);
- goto e_free;
+ return -EINVAL;
}
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem_res) {
- dev_err(&pdev->dev, "Failed to get IO memory region\n");
- goto e_free;
+ state->regs = devm_request_and_ioremap(&pdev->dev, mem_res);
+ if (state->regs == NULL) {
+ dev_err(&pdev->dev, "Failed to request and remap io memory\n");
+ return -ENXIO;
}
- regs_res = request_mem_region(mem_res->start, resource_size(mem_res),
- pdev->name);
- if (!regs_res) {
- dev_err(&pdev->dev, "Failed to request IO memory region\n");
- goto e_free;
- }
- state->regs_res = regs_res;
-
- state->regs = ioremap(mem_res->start, resource_size(mem_res));
- if (!state->regs) {
- dev_err(&pdev->dev, "Failed to remap IO region\n");
- goto e_reqmem;
- }
-
- ret = s5pcsis_clk_get(state);
- if (ret)
- goto e_unmap;
-
- clk_enable(state->clock[CSIS_CLK_MUX]);
- if (pdata->clk_rate)
- clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
- else
- dev_WARN(&pdev->dev, "No clock frequency specified!\n");
-
state->irq = platform_get_irq(pdev, 0);
if (state->irq < 0) {
- ret = state->irq;
dev_err(&pdev->dev, "Failed to get irq\n");
- goto e_clkput;
+ return state->irq;
}
for (i = 0; i < CSIS_NUM_SUPPLIES; i++)
@@ -549,12 +531,22 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
ret = regulator_bulk_get(&pdev->dev, CSIS_NUM_SUPPLIES,
state->supplies);
if (ret)
+ return ret;
+
+ ret = s5pcsis_clk_get(state);
+ if (ret)
goto e_clkput;
- ret = request_irq(state->irq, s5pcsis_irq_handler, 0,
- dev_name(&pdev->dev), state);
+ clk_enable(state->clock[CSIS_CLK_MUX]);
+ if (pdata->clk_rate)
+ clk_set_rate(state->clock[CSIS_CLK_MUX], pdata->clk_rate);
+ else
+ dev_WARN(&pdev->dev, "No clock frequency specified!\n");
+
+ ret = devm_request_irq(&pdev->dev, state->irq, s5pcsis_irq_handler,
+ 0, dev_name(&pdev->dev), state);
if (ret) {
- dev_err(&pdev->dev, "request_irq failed\n");
+ dev_err(&pdev->dev, "Interrupt request failed\n");
goto e_regput;
}
@@ -573,7 +565,7 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
ret = media_entity_init(&state->sd.entity,
CSIS_PADS_NUM, state->pads, 0);
if (ret < 0)
- goto e_irqfree;
+ goto e_clkput;
/* This allows to retrieve the platform device id by the host driver */
v4l2_set_subdevdata(&state->sd, pdev);
@@ -582,22 +574,13 @@ static int __devinit s5pcsis_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, &state->sd);
pm_runtime_enable(&pdev->dev);
-
return 0;
-e_irqfree:
- free_irq(state->irq, state);
e_regput:
regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
e_clkput:
clk_disable(state->clock[CSIS_CLK_MUX]);
s5pcsis_clk_put(state);
-e_unmap:
- iounmap(state->regs);
-e_reqmem:
- release_mem_region(regs_res->start, resource_size(regs_res));
-e_free:
- kfree(state);
return ret;
}
@@ -699,21 +682,15 @@ static int __devexit s5pcsis_remove(struct platform_device *pdev)
{
struct v4l2_subdev *sd = platform_get_drvdata(pdev);
struct csis_state *state = sd_to_csis_state(sd);
- struct resource *res = state->regs_res;
pm_runtime_disable(&pdev->dev);
- s5pcsis_suspend(&pdev->dev);
+ s5pcsis_pm_suspend(&pdev->dev, false);
clk_disable(state->clock[CSIS_CLK_MUX]);
pm_runtime_set_suspended(&pdev->dev);
-
s5pcsis_clk_put(state);
regulator_bulk_free(CSIS_NUM_SUPPLIES, state->supplies);
media_entity_cleanup(&state->sd.entity);
- free_irq(state->irq, state);
- iounmap(state->regs);
- release_mem_region(res->start, resource_size(res));
- kfree(state);
return 0;
}
diff --git a/drivers/media/video/s5p-g2d/g2d-hw.c b/drivers/media/video/s5p-g2d/g2d-hw.c
index 39937cf03c88..5b86cbe408e2 100644
--- a/drivers/media/video/s5p-g2d/g2d-hw.c
+++ b/drivers/media/video/s5p-g2d/g2d-hw.c
@@ -77,6 +77,11 @@ void g2d_set_rop4(struct g2d_dev *d, u32 r)
w(r, ROP4_REG);
}
+void g2d_set_flip(struct g2d_dev *d, u32 r)
+{
+ w(r, SRC_MSK_DIRECT_REG);
+}
+
u32 g2d_cmd_stretch(u32 e)
{
e &= 1;
diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c
index febaa673d363..789de74014e5 100644
--- a/drivers/media/video/s5p-g2d/g2d.c
+++ b/drivers/media/video/s5p-g2d/g2d.c
@@ -178,6 +178,9 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl)
{
struct g2d_ctx *ctx = container_of(ctrl->handler, struct g2d_ctx,
ctrl_handler);
+ unsigned long flags;
+
+ spin_lock_irqsave(&ctx->dev->ctrl_lock, flags);
switch (ctrl->id) {
case V4L2_CID_COLORFX:
if (ctrl->val == V4L2_COLORFX_NEGATIVE)
@@ -185,10 +188,13 @@ static int g2d_s_ctrl(struct v4l2_ctrl *ctrl)
else
ctx->rop = ROP4_COPY;
break;
- default:
- v4l2_err(&ctx->dev->v4l2_dev, "unknown control\n");
- return -EINVAL;
+
+ case V4L2_CID_HFLIP:
+ ctx->flip = ctx->ctrl_hflip->val | (ctx->ctrl_vflip->val << 1);
+ break;
+
}
+ spin_unlock_irqrestore(&ctx->dev->ctrl_lock, flags);
return 0;
}
@@ -200,11 +206,13 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx)
{
struct g2d_dev *dev = ctx->dev;
- v4l2_ctrl_handler_init(&ctx->ctrl_handler, 1);
- if (ctx->ctrl_handler.error) {
- v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n");
- return ctx->ctrl_handler.error;
- }
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+
+ ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &g2d_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std_menu(
&ctx->ctrl_handler,
@@ -215,10 +223,14 @@ int g2d_setup_ctrls(struct g2d_ctx *ctx)
V4L2_COLORFX_NONE);
if (ctx->ctrl_handler.error) {
- v4l2_err(&dev->v4l2_dev, "v4l2_ctrl_handler_init failed\n");
- return ctx->ctrl_handler.error;
+ int err = ctx->ctrl_handler.error;
+ v4l2_err(&dev->v4l2_dev, "g2d_setup_ctrls failed\n");
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ return err;
}
+ v4l2_ctrl_cluster(2, &ctx->ctrl_hflip);
+
return 0;
}
@@ -547,6 +559,7 @@ static void device_run(void *prv)
struct g2d_ctx *ctx = prv;
struct g2d_dev *dev = ctx->dev;
struct vb2_buffer *src, *dst;
+ unsigned long flags;
u32 cmd = 0;
dev->curr = ctx;
@@ -557,6 +570,8 @@ static void device_run(void *prv)
clk_enable(dev->gate);
g2d_reset(dev);
+ spin_lock_irqsave(&dev->ctrl_lock, flags);
+
g2d_set_src_size(dev, &ctx->in);
g2d_set_src_addr(dev, vb2_dma_contig_plane_dma_addr(src, 0));
@@ -564,11 +579,15 @@ static void device_run(void *prv)
g2d_set_dst_addr(dev, vb2_dma_contig_plane_dma_addr(dst, 0));
g2d_set_rop4(dev, ctx->rop);
+ g2d_set_flip(dev, ctx->flip);
+
if (ctx->in.c_width != ctx->out.c_width ||
ctx->in.c_height != ctx->out.c_height)
cmd |= g2d_cmd_stretch(1);
g2d_set_cmd(dev, cmd);
g2d_start(dev);
+
+ spin_unlock_irqrestore(&dev->ctrl_lock, flags);
}
static irqreturn_t g2d_isr(int irq, void *prv)
@@ -658,7 +677,7 @@ static int g2d_probe(struct platform_device *pdev)
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return -ENOMEM;
- spin_lock_init(&dev->irqlock);
+ spin_lock_init(&dev->ctrl_lock);
mutex_init(&dev->mutex);
atomic_set(&dev->num_inst, 0);
init_waitqueue_head(&dev->irq_queue);
@@ -693,18 +712,30 @@ static int g2d_probe(struct platform_device *pdev)
goto unmap_regs;
}
+ ret = clk_prepare(dev->clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare g2d clock\n");
+ goto put_clk;
+ }
+
dev->gate = clk_get(&pdev->dev, "fimg2d");
if (IS_ERR_OR_NULL(dev->gate)) {
dev_err(&pdev->dev, "failed to get g2d clock gate\n");
ret = -ENXIO;
- goto put_clk;
+ goto unprep_clk;
+ }
+
+ ret = clk_prepare(dev->gate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to prepare g2d clock gate\n");
+ goto put_clk_gate;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(&pdev->dev, "failed to find IRQ\n");
ret = -ENXIO;
- goto put_clk_gate;
+ goto unprep_clk_gate;
}
dev->irq = res->start;
@@ -764,8 +795,12 @@ alloc_ctx_cleanup:
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
rel_irq:
free_irq(dev->irq, dev);
+unprep_clk_gate:
+ clk_unprepare(dev->gate);
put_clk_gate:
clk_put(dev->gate);
+unprep_clk:
+ clk_unprepare(dev->clk);
put_clk:
clk_put(dev->clk);
unmap_regs:
@@ -787,7 +822,9 @@ static int g2d_remove(struct platform_device *pdev)
v4l2_device_unregister(&dev->v4l2_dev);
vb2_dma_contig_cleanup_ctx(dev->alloc_ctx);
free_irq(dev->irq, dev);
+ clk_unprepare(dev->gate);
clk_put(dev->gate);
+ clk_unprepare(dev->clk);
clk_put(dev->clk);
iounmap(dev->regs);
release_resource(dev->res_regs);
diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h
index 5eae90107bf8..1b82065aeaef 100644
--- a/drivers/media/video/s5p-g2d/g2d.h
+++ b/drivers/media/video/s5p-g2d/g2d.h
@@ -20,7 +20,7 @@ struct g2d_dev {
struct v4l2_m2m_dev *m2m_dev;
struct video_device *vfd;
struct mutex mutex;
- spinlock_t irqlock;
+ spinlock_t ctrl_lock;
atomic_t num_inst;
struct vb2_alloc_ctx *alloc_ctx;
struct resource *res_regs;
@@ -57,8 +57,11 @@ struct g2d_ctx {
struct v4l2_m2m_ctx *m2m_ctx;
struct g2d_frame in;
struct g2d_frame out;
+ struct v4l2_ctrl *ctrl_hflip;
+ struct v4l2_ctrl *ctrl_vflip;
struct v4l2_ctrl_handler ctrl_handler;
u32 rop;
+ u32 flip;
};
struct g2d_fmt {
@@ -77,6 +80,7 @@ void g2d_set_dst_addr(struct g2d_dev *d, dma_addr_t a);
void g2d_start(struct g2d_dev *d);
void g2d_clear_int(struct g2d_dev *d);
void g2d_set_rop4(struct g2d_dev *d, u32 r);
+void g2d_set_flip(struct g2d_dev *d, u32 r);
u32 g2d_cmd_stretch(u32 e);
void g2d_set_cmd(struct g2d_dev *d, u32 c);
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c
index 1105a8749c8b..5a49c307f9c1 100644
--- a/drivers/media/video/s5p-jpeg/jpeg-core.c
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.c
@@ -32,10 +32,9 @@
static struct s5p_jpeg_fmt formats_enc[] = {
{
- .name = "YUV 4:2:0 planar, YCbCr",
- .fourcc = V4L2_PIX_FMT_YUV420,
- .depth = 12,
- .colplanes = 3,
+ .name = "JPEG JFIF",
+ .fourcc = V4L2_PIX_FMT_JPEG,
+ .colplanes = 1,
.types = MEM2MEM_CAPTURE,
},
{
@@ -43,7 +42,7 @@ static struct s5p_jpeg_fmt formats_enc[] = {
.fourcc = V4L2_PIX_FMT_YUYV,
.depth = 16,
.colplanes = 1,
- .types = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
+ .types = MEM2MEM_OUTPUT,
},
{
.name = "RGB565",
@@ -203,6 +202,16 @@ static const unsigned char hactblg0[162] = {
0xf9, 0xfa
};
+static inline struct s5p_jpeg_ctx *ctrl_to_ctx(struct v4l2_ctrl *c)
+{
+ return container_of(c->handler, struct s5p_jpeg_ctx, ctrl_handler);
+}
+
+static inline struct s5p_jpeg_ctx *fh_to_ctx(struct v4l2_fh *fh)
+{
+ return container_of(fh, struct s5p_jpeg_ctx, fh);
+}
+
static inline void jpeg_set_qtbl(void __iomem *regs, const unsigned char *qtbl,
unsigned long tab, int len)
{
@@ -269,6 +278,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq,
struct vb2_queue *dst_vq);
static struct s5p_jpeg_fmt *s5p_jpeg_find_format(unsigned int mode,
__u32 pixelformat);
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx);
static int s5p_jpeg_open(struct file *file)
{
@@ -276,12 +286,18 @@ static int s5p_jpeg_open(struct file *file)
struct video_device *vfd = video_devdata(file);
struct s5p_jpeg_ctx *ctx;
struct s5p_jpeg_fmt *out_fmt;
+ int ret = 0;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
return -ENOMEM;
- file->private_data = ctx;
+ v4l2_fh_init(&ctx->fh, vfd);
+ /* Use separate control handler per file handle */
+ ctx->fh.ctrl_handler = &ctx->ctrl_handler;
+ file->private_data = &ctx->fh;
+ v4l2_fh_add(&ctx->fh);
+
ctx->jpeg = jpeg;
if (vfd == jpeg->vfd_encoder) {
ctx->mode = S5P_JPEG_ENCODE;
@@ -291,24 +307,35 @@ static int s5p_jpeg_open(struct file *file)
out_fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_JPEG);
}
+ ret = s5p_jpeg_controls_create(ctx);
+ if (ret < 0)
+ goto error;
+
ctx->m2m_ctx = v4l2_m2m_ctx_init(jpeg->m2m_dev, ctx, queue_init);
if (IS_ERR(ctx->m2m_ctx)) {
- int err = PTR_ERR(ctx->m2m_ctx);
- kfree(ctx);
- return err;
+ ret = PTR_ERR(ctx->m2m_ctx);
+ goto error;
}
ctx->out_q.fmt = out_fmt;
ctx->cap_q.fmt = s5p_jpeg_find_format(ctx->mode, V4L2_PIX_FMT_YUYV);
-
return 0;
+
+error:
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
+ kfree(ctx);
+ return ret;
}
static int s5p_jpeg_release(struct file *file)
{
- struct s5p_jpeg_ctx *ctx = file->private_data;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
v4l2_m2m_ctx_release(ctx->m2m_ctx);
+ v4l2_ctrl_handler_free(&ctx->ctrl_handler);
+ v4l2_fh_del(&ctx->fh);
+ v4l2_fh_exit(&ctx->fh);
kfree(ctx);
return 0;
@@ -317,14 +344,14 @@ static int s5p_jpeg_release(struct file *file)
static unsigned int s5p_jpeg_poll(struct file *file,
struct poll_table_struct *wait)
{
- struct s5p_jpeg_ctx *ctx = file->private_data;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
}
static int s5p_jpeg_mmap(struct file *file, struct vm_area_struct *vma)
{
- struct s5p_jpeg_ctx *ctx = file->private_data;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(file->private_data);
return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
}
@@ -448,7 +475,7 @@ static bool s5p_jpeg_parse_hdr(struct s5p_jpeg_q_data *result,
static int s5p_jpeg_querycap(struct file *file, void *priv,
struct v4l2_capability *cap)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE) {
strlcpy(cap->driver, S5P_JPEG_M2M_NAME " encoder",
@@ -497,9 +524,7 @@ static int enum_fmt(struct s5p_jpeg_fmt *formats, int n,
static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- struct s5p_jpeg_ctx *ctx;
-
- ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE)
return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
@@ -511,9 +536,7 @@ static int s5p_jpeg_enum_fmt_vid_cap(struct file *file, void *priv,
static int s5p_jpeg_enum_fmt_vid_out(struct file *file, void *priv,
struct v4l2_fmtdesc *f)
{
- struct s5p_jpeg_ctx *ctx;
-
- ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (ctx->mode == S5P_JPEG_ENCODE)
return enum_fmt(formats_enc, NUM_FORMATS_ENC, f,
@@ -538,7 +561,7 @@ static int s5p_jpeg_g_fmt(struct file *file, void *priv, struct v4l2_format *f)
struct vb2_queue *vq;
struct s5p_jpeg_q_data *q_data = NULL;
struct v4l2_pix_format *pix = &f->fmt.pix;
- struct s5p_jpeg_ctx *ct = priv;
+ struct s5p_jpeg_ctx *ct = fh_to_ctx(priv);
vq = v4l2_m2m_get_vq(ct->m2m_ctx, f->type);
if (!vq)
@@ -659,8 +682,8 @@ static int vidioc_try_fmt(struct v4l2_format *f, struct s5p_jpeg_fmt *fmt,
static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
struct v4l2_format *f)
{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
struct s5p_jpeg_fmt *fmt;
- struct s5p_jpeg_ctx *ctx = priv;
fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
@@ -676,8 +699,8 @@ static int s5p_jpeg_try_fmt_vid_cap(struct file *file, void *priv,
static int s5p_jpeg_try_fmt_vid_out(struct file *file, void *priv,
struct v4l2_format *f)
{
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
struct s5p_jpeg_fmt *fmt;
- struct s5p_jpeg_ctx *ctx = priv;
fmt = s5p_jpeg_find_format(ctx->mode, f->fmt.pix.pixelformat);
if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
@@ -728,7 +751,7 @@ static int s5p_jpeg_s_fmt_vid_cap(struct file *file, void *priv,
if (ret)
return ret;
- return s5p_jpeg_s_fmt(priv, f);
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
}
static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
@@ -740,13 +763,13 @@ static int s5p_jpeg_s_fmt_vid_out(struct file *file, void *priv,
if (ret)
return ret;
- return s5p_jpeg_s_fmt(priv, f);
+ return s5p_jpeg_s_fmt(fh_to_ctx(priv), f);
}
static int s5p_jpeg_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *reqbufs)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
}
@@ -754,14 +777,14 @@ static int s5p_jpeg_reqbufs(struct file *file, void *priv,
static int s5p_jpeg_querybuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
}
static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
}
@@ -769,7 +792,7 @@ static int s5p_jpeg_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
static int s5p_jpeg_dqbuf(struct file *file, void *priv,
struct v4l2_buffer *buf)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
}
@@ -777,7 +800,7 @@ static int s5p_jpeg_dqbuf(struct file *file, void *priv,
static int s5p_jpeg_streamon(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
}
@@ -785,7 +808,7 @@ static int s5p_jpeg_streamon(struct file *file, void *priv,
static int s5p_jpeg_streamoff(struct file *file, void *priv,
enum v4l2_buf_type type)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
}
@@ -793,7 +816,7 @@ static int s5p_jpeg_streamoff(struct file *file, void *priv,
int s5p_jpeg_g_selection(struct file *file, void *priv,
struct v4l2_selection *s)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv);
if (s->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
@@ -822,33 +845,89 @@ int s5p_jpeg_g_selection(struct file *file, void *priv,
return 0;
}
-static int s5p_jpeg_g_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *compr)
+/*
+ * V4L2 controls
+ */
+
+static int s5p_jpeg_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ struct s5p_jpeg *jpeg = ctx->jpeg;
+ unsigned long flags;
- if (ctx->mode == S5P_JPEG_DECODE)
- return -ENOTTY;
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ spin_lock_irqsave(&jpeg->slock, flags);
- memset(compr, 0, sizeof(*compr));
- compr->quality = ctx->compr_quality;
+ WARN_ON(ctx->subsampling > S5P_SUBSAMPLING_MODE_GRAY);
+ if (ctx->subsampling > 2)
+ ctrl->val = V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY;
+ else
+ ctrl->val = ctx->subsampling;
+ spin_unlock_irqrestore(&jpeg->slock, flags);
+ break;
+ }
return 0;
}
-static int s5p_jpeg_s_jpegcomp(struct file *file, void *priv,
- struct v4l2_jpegcompression *compr)
+static int s5p_jpeg_s_ctrl(struct v4l2_ctrl *ctrl)
{
- struct s5p_jpeg_ctx *ctx = priv;
+ struct s5p_jpeg_ctx *ctx = ctrl_to_ctx(ctrl);
+ unsigned long flags;
- if (ctx->mode == S5P_JPEG_DECODE)
- return -ENOTTY;
+ spin_lock_irqsave(&ctx->jpeg->slock, flags);
- compr->quality = clamp(compr->quality, S5P_JPEG_COMPR_QUAL_BEST,
- S5P_JPEG_COMPR_QUAL_WORST);
+ switch (ctrl->id) {
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY:
+ ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - ctrl->val;
+ break;
+ case V4L2_CID_JPEG_RESTART_INTERVAL:
+ ctx->restart_interval = ctrl->val;
+ break;
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ ctx->subsampling = ctrl->val;
+ break;
+ }
- ctx->compr_quality = S5P_JPEG_COMPR_QUAL_WORST - compr->quality;
+ spin_unlock_irqrestore(&ctx->jpeg->slock, flags);
+ return 0;
+}
+
+static const struct v4l2_ctrl_ops s5p_jpeg_ctrl_ops = {
+ .g_volatile_ctrl = s5p_jpeg_g_volatile_ctrl,
+ .s_ctrl = s5p_jpeg_s_ctrl,
+};
+static int s5p_jpeg_controls_create(struct s5p_jpeg_ctx *ctx)
+{
+ unsigned int mask = ~0x27; /* 444, 422, 420, GRAY */
+ struct v4l2_ctrl *ctrl;
+
+ v4l2_ctrl_handler_init(&ctx->ctrl_handler, 3);
+
+ if (ctx->mode == S5P_JPEG_ENCODE) {
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_COMPRESSION_QUALITY,
+ 0, 3, 1, 3);
+
+ v4l2_ctrl_new_std(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_RESTART_INTERVAL,
+ 0, 3, 0xffff, 0);
+ mask = ~0x06; /* 422, 420 */
+ }
+
+ ctrl = v4l2_ctrl_new_std_menu(&ctx->ctrl_handler, &s5p_jpeg_ctrl_ops,
+ V4L2_CID_JPEG_CHROMA_SUBSAMPLING,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_GRAY, mask,
+ V4L2_JPEG_CHROMA_SUBSAMPLING_422);
+
+ if (ctx->ctrl_handler.error)
+ return ctx->ctrl_handler.error;
+
+ if (ctx->mode == S5P_JPEG_DECODE)
+ ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE |
+ V4L2_CTRL_FLAG_READ_ONLY;
return 0;
}
@@ -877,9 +956,6 @@ static const struct v4l2_ioctl_ops s5p_jpeg_ioctl_ops = {
.vidioc_streamoff = s5p_jpeg_streamoff,
.vidioc_g_selection = s5p_jpeg_g_selection,
-
- .vidioc_g_jpegcomp = s5p_jpeg_g_jpegcomp,
- .vidioc_s_jpegcomp = s5p_jpeg_s_jpegcomp,
};
/*
@@ -908,13 +984,8 @@ static void s5p_jpeg_device_run(void *priv)
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_565);
else
jpeg_input_raw_mode(jpeg->regs, S5P_JPEG_RAW_IN_422);
- if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
- jpeg_subsampling_mode(jpeg->regs,
- S5P_JPEG_SUBSAMPLING_422);
- else
- jpeg_subsampling_mode(jpeg->regs,
- S5P_JPEG_SUBSAMPLING_420);
- jpeg_dri(jpeg->regs, 0);
+ jpeg_subsampling_mode(jpeg->regs, ctx->subsampling);
+ jpeg_dri(jpeg->regs, ctx->restart_interval);
jpeg_x(jpeg->regs, ctx->out_q.w);
jpeg_y(jpeg->regs, ctx->out_q.h);
jpeg_imgadr(jpeg->regs, src_addr);
@@ -953,14 +1024,18 @@ static void s5p_jpeg_device_run(void *priv)
jpeg_htbl_dc(jpeg->regs, 2);
jpeg_htbl_ac(jpeg->regs, 3);
jpeg_htbl_dc(jpeg->regs, 3);
- } else {
+ } else { /* S5P_JPEG_DECODE */
jpeg_rst_int_enable(jpeg->regs, true);
jpeg_data_num_int_enable(jpeg->regs, true);
jpeg_final_mcu_num_int_enable(jpeg->regs, true);
- jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
+ if (ctx->cap_q.fmt->fourcc == V4L2_PIX_FMT_YUYV)
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_422);
+ else
+ jpeg_outform_raw(jpeg->regs, S5P_JPEG_RAW_OUT_420);
jpeg_jpgadr(jpeg->regs, src_addr);
jpeg_imgadr(jpeg->regs, dst_addr);
}
+
jpeg_start(jpeg->regs);
}
@@ -1162,6 +1237,8 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
bool timer_elapsed = false;
bool op_completed = false;
+ spin_lock(&jpeg->slock);
+
curr_ctx = v4l2_m2m_get_curr_priv(jpeg->m2m_dev);
src_buf = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
@@ -1192,6 +1269,9 @@ static irqreturn_t s5p_jpeg_irq(int irq, void *dev_id)
v4l2_m2m_buf_done(dst_buf, state);
v4l2_m2m_job_finish(jpeg->m2m_dev, curr_ctx->m2m_ctx);
+ curr_ctx->subsampling = jpeg_get_subsampling_mode(jpeg->regs);
+ spin_unlock(&jpeg->slock);
+
jpeg_clear_int(jpeg->regs);
return IRQ_HANDLED;
@@ -1215,6 +1295,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev)
return -ENOMEM;
mutex_init(&jpeg->lock);
+ spin_lock_init(&jpeg->slock);
jpeg->dev = &pdev->dev;
/* memory-mapped registers */
diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h
index facad6114f5e..38d7367f7a6d 100644
--- a/drivers/media/video/s5p-jpeg/jpeg-core.h
+++ b/drivers/media/video/s5p-jpeg/jpeg-core.h
@@ -14,6 +14,8 @@
#define JPEG_CORE_H_
#include <media/v4l2-device.h>
+#include <media/v4l2-fh.h>
+#include <media/v4l2-ctrls.h>
#define S5P_JPEG_M2M_NAME "s5p-jpeg"
@@ -47,6 +49,7 @@
/**
* struct s5p_jpeg - JPEG IP abstraction
* @lock: the mutex protecting this structure
+ * @slock: spinlock protecting the device contexts
* @v4l2_dev: v4l2 device for mem2mem mode
* @vfd_encoder: video device node for encoder mem2mem mode
* @vfd_decoder: video device node for decoder mem2mem mode
@@ -60,6 +63,7 @@
*/
struct s5p_jpeg {
struct mutex lock;
+ struct spinlock slock;
struct v4l2_device v4l2_dev;
struct video_device *vfd_encoder;
@@ -117,15 +121,20 @@ struct s5p_jpeg_q_data {
* @out_q: source (output) queue information
* @cap_fmt: destination (capture) queue queue information
* @hdr_parsed: set if header has been parsed during decompression
+ * @ctrl_handler: controls handler
*/
struct s5p_jpeg_ctx {
struct s5p_jpeg *jpeg;
unsigned int mode;
- unsigned int compr_quality;
+ unsigned short compr_quality;
+ unsigned short restart_interval;
+ unsigned short subsampling;
struct v4l2_m2m_ctx *m2m_ctx;
struct s5p_jpeg_q_data out_q;
struct s5p_jpeg_q_data cap_q;
+ struct v4l2_fh fh;
bool hdr_parsed;
+ struct v4l2_ctrl_handler ctrl_handler;
};
/**
diff --git a/drivers/media/video/s5p-jpeg/jpeg-hw.h b/drivers/media/video/s5p-jpeg/jpeg-hw.h
index e10c744e9f23..f12f0fdbde7c 100644
--- a/drivers/media/video/s5p-jpeg/jpeg-hw.h
+++ b/drivers/media/video/s5p-jpeg/jpeg-hw.h
@@ -13,6 +13,7 @@
#define JPEG_HW_H_
#include <linux/io.h>
+#include <linux/videodev2.h>
#include "jpeg-hw.h"
#include "jpeg-regs.h"
@@ -25,8 +26,6 @@
#define S5P_JPEG_DECODE 1
#define S5P_JPEG_RAW_IN_565 0
#define S5P_JPEG_RAW_IN_422 1
-#define S5P_JPEG_SUBSAMPLING_422 0
-#define S5P_JPEG_SUBSAMPLING_420 1
#define S5P_JPEG_RAW_OUT_422 0
#define S5P_JPEG_RAW_OUT_420 1
@@ -91,21 +90,26 @@ static inline void jpeg_proc_mode(void __iomem *regs, unsigned long mode)
writel(reg, regs + S5P_JPGMOD);
}
-static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned long mode)
+static inline void jpeg_subsampling_mode(void __iomem *regs, unsigned int mode)
{
unsigned long reg, m;
- m = S5P_SUBSAMPLING_MODE_422;
- if (mode == S5P_JPEG_SUBSAMPLING_422)
- m = S5P_SUBSAMPLING_MODE_422;
- else if (mode == S5P_JPEG_SUBSAMPLING_420)
+ if (mode == V4L2_JPEG_CHROMA_SUBSAMPLING_420)
m = S5P_SUBSAMPLING_MODE_420;
+ else
+ m = S5P_SUBSAMPLING_MODE_422;
+
reg = readl(regs + S5P_JPGMOD);
reg &= ~S5P_SUBSAMPLING_MODE_MASK;
reg |= m;
writel(reg, regs + S5P_JPGMOD);
}
+static inline unsigned int jpeg_get_subsampling_mode(void __iomem *regs)
+{
+ return readl(regs + S5P_JPGMOD) & S5P_SUBSAMPLING_MODE_MASK;
+}
+
static inline void jpeg_dri(void __iomem *regs, unsigned int dri)
{
unsigned long reg;
diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
index f6a3035c4fb7..738a607be43c 100644
--- a/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
+++ b/drivers/media/video/s5p-mfc/s5p_mfc_pm.c
@@ -41,15 +41,29 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
pm->clock_gate = clk_get(&dev->plat_dev->dev, MFC_GATE_CLK_NAME);
if (IS_ERR(pm->clock_gate)) {
mfc_err("Failed to get clock-gating control\n");
- ret = -ENOENT;
+ ret = PTR_ERR(pm->clock_gate);
goto err_g_ip_clk;
}
+
+ ret = clk_prepare(pm->clock_gate);
+ if (ret) {
+ mfc_err("Failed to preapre clock-gating control\n");
+ goto err_p_ip_clk;
+ }
+
pm->clock = clk_get(&dev->plat_dev->dev, MFC_CLKNAME);
if (IS_ERR(pm->clock)) {
mfc_err("Failed to get MFC clock\n");
- ret = -ENOENT;
+ ret = PTR_ERR(pm->clock);
goto err_g_ip_clk_2;
}
+
+ ret = clk_prepare(pm->clock);
+ if (ret) {
+ mfc_err("Failed to prepare MFC clock\n");
+ goto err_p_ip_clk_2;
+ }
+
atomic_set(&pm->power, 0);
#ifdef CONFIG_PM_RUNTIME
pm->device = &dev->plat_dev->dev;
@@ -59,7 +73,11 @@ int s5p_mfc_init_pm(struct s5p_mfc_dev *dev)
atomic_set(&clk_ref, 0);
#endif
return 0;
+err_p_ip_clk_2:
+ clk_put(pm->clock);
err_g_ip_clk_2:
+ clk_unprepare(pm->clock_gate);
+err_p_ip_clk:
clk_put(pm->clock_gate);
err_g_ip_clk:
return ret;
@@ -67,7 +85,9 @@ err_g_ip_clk:
void s5p_mfc_final_pm(struct s5p_mfc_dev *dev)
{
+ clk_unprepare(pm->clock_gate);
clk_put(pm->clock_gate);
+ clk_unprepare(pm->clock);
clk_put(pm->clock);
#ifdef CONFIG_PM_RUNTIME
pm_runtime_disable(pm->device);
diff --git a/drivers/media/video/s5p-tv/Kconfig b/drivers/media/video/s5p-tv/Kconfig
index f2a09779ec8f..f248b2856720 100644
--- a/drivers/media/video/s5p-tv/Kconfig
+++ b/drivers/media/video/s5p-tv/Kconfig
@@ -46,6 +46,16 @@ config VIDEO_SAMSUNG_S5P_HDMIPHY
as module. It is an I2C driver, that exposes a V4L2
subdev for use by other drivers.
+config VIDEO_SAMSUNG_S5P_SII9234
+ tristate "Samsung SII9234 Driver"
+ depends on VIDEO_DEV && VIDEO_V4L2 && I2C
+ depends on VIDEO_SAMSUNG_S5P_TV
+ help
+ Say Y here if you want support for the MHL interface
+ in S5P Samsung SoC. The driver can be compiled
+ as module. It is an I2C driver, that exposes a V4L2
+ subdev for use by other drivers.
+
config VIDEO_SAMSUNG_S5P_SDO
tristate "Samsung Analog TV Driver"
depends on VIDEO_DEV && VIDEO_V4L2
diff --git a/drivers/media/video/s5p-tv/Makefile b/drivers/media/video/s5p-tv/Makefile
index 37e4c17663b4..f49e756a2fde 100644
--- a/drivers/media/video/s5p-tv/Makefile
+++ b/drivers/media/video/s5p-tv/Makefile
@@ -8,6 +8,8 @@
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMIPHY) += s5p-hdmiphy.o
s5p-hdmiphy-y += hdmiphy_drv.o
+obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SII9234) += s5p-sii9234.o
+s5p-sii9234-y += sii9234_drv.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_HDMI) += s5p-hdmi.o
s5p-hdmi-y += hdmi_drv.o
obj-$(CONFIG_VIDEO_SAMSUNG_S5P_SDO) += s5p-sdo.o
diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c
index 8b41a0410ab1..4865d25a0e57 100644
--- a/drivers/media/video/s5p-tv/hdmi_drv.c
+++ b/drivers/media/video/s5p-tv/hdmi_drv.c
@@ -30,6 +30,7 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
+#include <media/s5p_hdmi.h>
#include <media/v4l2-common.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
@@ -66,6 +67,8 @@ struct hdmi_device {
struct v4l2_device v4l2_dev;
/** subdev of HDMIPHY interface */
struct v4l2_subdev *phy_sd;
+ /** subdev of MHL interface */
+ struct v4l2_subdev *mhl_sd;
/** configuration of current graphic mode */
const struct hdmi_preset_conf *cur_conf;
/** current preset */
@@ -74,10 +77,6 @@ struct hdmi_device {
struct hdmi_resources res;
};
-struct hdmi_driver_data {
- int hdmiphy_bus;
-};
-
struct hdmi_tg_regs {
u8 cmd;
u8 h_fsz_l;
@@ -129,23 +128,11 @@ struct hdmi_preset_conf {
struct v4l2_mbus_framefmt mbus_fmt;
};
-/* I2C module and id for HDMIPHY */
-static struct i2c_board_info hdmiphy_info = {
- I2C_BOARD_INFO("hdmiphy", 0x38),
-};
-
-static struct hdmi_driver_data hdmi_driver_data[] = {
- { .hdmiphy_bus = 3 },
- { .hdmiphy_bus = 8 },
-};
-
static struct platform_device_id hdmi_driver_types[] = {
{
.name = "s5pv210-hdmi",
- .driver_data = (unsigned long)&hdmi_driver_data[0],
}, {
.name = "exynos4-hdmi",
- .driver_data = (unsigned long)&hdmi_driver_data[1],
}, {
/* end node */
}
@@ -587,7 +574,15 @@ static int hdmi_streamon(struct hdmi_device *hdev)
if (tries == 0) {
dev_err(dev, "hdmiphy's pll could not reach steady state.\n");
v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
- hdmi_dumpregs(hdev, "s_stream");
+ hdmi_dumpregs(hdev, "hdmiphy - s_stream");
+ return -EIO;
+ }
+
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 1);
+ if (hdev->mhl_sd && ret) {
+ v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
+ hdmi_dumpregs(hdev, "mhl - s_stream");
return -EIO;
}
@@ -618,6 +613,7 @@ static int hdmi_streamoff(struct hdmi_device *hdev)
clk_set_parent(res->sclk_hdmi, res->sclk_pixel);
clk_enable(res->sclk_hdmi);
+ v4l2_subdev_call(hdev->mhl_sd, video, s_stream, 0);
v4l2_subdev_call(hdev->phy_sd, video, s_stream, 0);
hdmi_dumpregs(hdev, "streamoff");
@@ -739,6 +735,7 @@ static int hdmi_runtime_suspend(struct device *dev)
struct hdmi_device *hdev = sd_to_hdmi_dev(sd);
dev_dbg(dev, "%s\n", __func__);
+ v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0);
hdmi_resource_poweroff(&hdev->res);
return 0;
}
@@ -757,6 +754,11 @@ static int hdmi_runtime_resume(struct device *dev)
if (ret)
goto fail;
+ /* starting MHL */
+ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1);
+ if (hdev->mhl_sd && ret)
+ goto fail;
+
dev_dbg(dev, "poweron succeed\n");
return 0;
@@ -867,15 +869,21 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *res;
- struct i2c_adapter *phy_adapter;
+ struct i2c_adapter *adapter;
struct v4l2_subdev *sd;
struct hdmi_device *hdmi_dev = NULL;
- struct hdmi_driver_data *drv_data;
+ struct s5p_hdmi_platform_data *pdata = dev->platform_data;
int ret;
dev_dbg(dev, "probe start\n");
- hdmi_dev = kzalloc(sizeof(*hdmi_dev), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "platform data is missing\n");
+ ret = -ENODEV;
+ goto fail;
+ }
+
+ hdmi_dev = devm_kzalloc(&pdev->dev, sizeof(*hdmi_dev), GFP_KERNEL);
if (!hdmi_dev) {
dev_err(dev, "out of memory\n");
ret = -ENOMEM;
@@ -886,7 +894,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
ret = hdmi_resources_init(hdmi_dev);
if (ret)
- goto fail_hdev;
+ goto fail;
/* mapping HDMI registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -896,24 +904,26 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
goto fail_init;
}
- hdmi_dev->regs = ioremap(res->start, resource_size(res));
+ hdmi_dev->regs = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
if (hdmi_dev->regs == NULL) {
dev_err(dev, "register mapping failed.\n");
ret = -ENXIO;
- goto fail_hdev;
+ goto fail_init;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
ret = -ENXIO;
- goto fail_regs;
+ goto fail_init;
}
- ret = request_irq(res->start, hdmi_irq_handler, 0, "hdmi", hdmi_dev);
+ ret = devm_request_irq(&pdev->dev, res->start, hdmi_irq_handler, 0,
+ "hdmi", hdmi_dev);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
- goto fail_regs;
+ goto fail_init;
}
hdmi_dev->irq = res->start;
@@ -924,28 +934,54 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
ret = v4l2_device_register(NULL, &hdmi_dev->v4l2_dev);
if (ret) {
dev_err(dev, "could not register v4l2 device.\n");
- goto fail_irq;
+ goto fail_init;
}
- drv_data = (struct hdmi_driver_data *)
- platform_get_device_id(pdev)->driver_data;
- phy_adapter = i2c_get_adapter(drv_data->hdmiphy_bus);
- if (phy_adapter == NULL) {
- dev_err(dev, "adapter request failed\n");
+ /* testing if hdmiphy info is present */
+ if (!pdata->hdmiphy_info) {
+ dev_err(dev, "hdmiphy info is missing in platform data\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ adapter = i2c_get_adapter(pdata->hdmiphy_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "hdmiphy adapter request failed\n");
ret = -ENXIO;
goto fail_vdev;
}
hdmi_dev->phy_sd = v4l2_i2c_new_subdev_board(&hdmi_dev->v4l2_dev,
- phy_adapter, &hdmiphy_info, NULL);
+ adapter, pdata->hdmiphy_info, NULL);
/* on failure or not adapter is no longer useful */
- i2c_put_adapter(phy_adapter);
+ i2c_put_adapter(adapter);
if (hdmi_dev->phy_sd == NULL) {
dev_err(dev, "missing subdev for hdmiphy\n");
ret = -ENODEV;
goto fail_vdev;
}
+ /* initialization of MHL interface if present */
+ if (pdata->mhl_info) {
+ adapter = i2c_get_adapter(pdata->mhl_bus);
+ if (adapter == NULL) {
+ dev_err(dev, "MHL adapter request failed\n");
+ ret = -ENXIO;
+ goto fail_vdev;
+ }
+
+ hdmi_dev->mhl_sd = v4l2_i2c_new_subdev_board(
+ &hdmi_dev->v4l2_dev, adapter,
+ pdata->mhl_info, NULL);
+ /* on failure or not adapter is no longer useful */
+ i2c_put_adapter(adapter);
+ if (hdmi_dev->mhl_sd == NULL) {
+ dev_err(dev, "missing subdev for MHL\n");
+ ret = -ENODEV;
+ goto fail_vdev;
+ }
+ }
+
clk_enable(hdmi_dev->res.hdmi);
pm_runtime_enable(dev);
@@ -962,25 +998,16 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
/* storing subdev for call that have only access to struct device */
dev_set_drvdata(dev, sd);
- dev_info(dev, "probe sucessful\n");
+ dev_info(dev, "probe successful\n");
return 0;
fail_vdev:
v4l2_device_unregister(&hdmi_dev->v4l2_dev);
-fail_irq:
- free_irq(hdmi_dev->irq, hdmi_dev);
-
-fail_regs:
- iounmap(hdmi_dev->regs);
-
fail_init:
hdmi_resources_cleanup(hdmi_dev);
-fail_hdev:
- kfree(hdmi_dev);
-
fail:
dev_err(dev, "probe failed\n");
return ret;
@@ -996,11 +1023,8 @@ static int __devexit hdmi_remove(struct platform_device *pdev)
clk_disable(hdmi_dev->res.hdmi);
v4l2_device_unregister(&hdmi_dev->v4l2_dev);
disable_irq(hdmi_dev->irq);
- free_irq(hdmi_dev->irq, hdmi_dev);
- iounmap(hdmi_dev->regs);
hdmi_resources_cleanup(hdmi_dev);
- kfree(hdmi_dev);
- dev_info(dev, "remove sucessful\n");
+ dev_info(dev, "remove successful\n");
return 0;
}
diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c
index 6693f4aff108..0afef77747e5 100644
--- a/drivers/media/video/s5p-tv/hdmiphy_drv.c
+++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c
@@ -175,14 +175,4 @@ static struct i2c_driver hdmiphy_driver = {
.id_table = hdmiphy_id,
};
-static int __init hdmiphy_init(void)
-{
- return i2c_add_driver(&hdmiphy_driver);
-}
-module_init(hdmiphy_init);
-
-static void __exit hdmiphy_exit(void)
-{
- i2c_del_driver(&hdmiphy_driver);
-}
-module_exit(hdmiphy_exit);
+module_i2c_driver(hdmiphy_driver);
diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c
index 00643094b221..a2c0c25ad130 100644
--- a/drivers/media/video/s5p-tv/mixer_drv.c
+++ b/drivers/media/video/s5p-tv/mixer_drv.c
@@ -444,7 +444,7 @@ static int __devexit mxr_remove(struct platform_device *pdev)
kfree(mdev);
- dev_info(dev, "remove sucessful\n");
+ dev_info(dev, "remove successful\n");
return 0;
}
diff --git a/drivers/media/video/s5p-tv/sdo_drv.c b/drivers/media/video/s5p-tv/sdo_drv.c
index 059e7749ce95..f6bca2c20e89 100644
--- a/drivers/media/video/s5p-tv/sdo_drv.c
+++ b/drivers/media/video/s5p-tv/sdo_drv.c
@@ -301,7 +301,7 @@ static int __devinit sdo_probe(struct platform_device *pdev)
struct clk *sclk_vpll;
dev_info(dev, "probe start\n");
- sdev = kzalloc(sizeof *sdev, GFP_KERNEL);
+ sdev = devm_kzalloc(&pdev->dev, sizeof *sdev, GFP_KERNEL);
if (!sdev) {
dev_err(dev, "not enough memory.\n");
ret = -ENOMEM;
@@ -314,14 +314,14 @@ static int __devinit sdo_probe(struct platform_device *pdev)
if (res == NULL) {
dev_err(dev, "get memory resource failed.\n");
ret = -ENXIO;
- goto fail_sdev;
+ goto fail;
}
- sdev->regs = ioremap(res->start, resource_size(res));
+ sdev->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (sdev->regs == NULL) {
dev_err(dev, "register mapping failed.\n");
ret = -ENXIO;
- goto fail_sdev;
+ goto fail;
}
/* acquiring interrupt */
@@ -329,12 +329,13 @@ static int __devinit sdo_probe(struct platform_device *pdev)
if (res == NULL) {
dev_err(dev, "get interrupt resource failed.\n");
ret = -ENXIO;
- goto fail_regs;
+ goto fail;
}
- ret = request_irq(res->start, sdo_irq_handler, 0, "s5p-sdo", sdev);
+ ret = devm_request_irq(&pdev->dev, res->start, sdo_irq_handler, 0,
+ "s5p-sdo", sdev);
if (ret) {
dev_err(dev, "request interrupt failed.\n");
- goto fail_regs;
+ goto fail;
}
sdev->irq = res->start;
@@ -343,7 +344,7 @@ static int __devinit sdo_probe(struct platform_device *pdev)
if (IS_ERR_OR_NULL(sdev->sclk_dac)) {
dev_err(dev, "failed to get clock 'sclk_dac'\n");
ret = -ENXIO;
- goto fail_irq;
+ goto fail;
}
sdev->dac = clk_get(dev, "dac");
if (IS_ERR_OR_NULL(sdev->dac)) {
@@ -415,12 +416,6 @@ fail_dac:
clk_put(sdev->dac);
fail_sclk_dac:
clk_put(sdev->sclk_dac);
-fail_irq:
- free_irq(sdev->irq, sdev);
-fail_regs:
- iounmap(sdev->regs);
-fail_sdev:
- kfree(sdev);
fail:
dev_info(dev, "probe failed\n");
return ret;
@@ -439,9 +434,6 @@ static int __devexit sdo_remove(struct platform_device *pdev)
clk_put(sdev->dacphy);
clk_put(sdev->dac);
clk_put(sdev->sclk_dac);
- free_irq(sdev->irq, sdev);
- iounmap(sdev->regs);
- kfree(sdev);
dev_info(&pdev->dev, "remove successful\n");
return 0;
diff --git a/drivers/media/video/s5p-tv/sii9234_drv.c b/drivers/media/video/s5p-tv/sii9234_drv.c
new file mode 100644
index 000000000000..0f31eccd7b80
--- /dev/null
+++ b/drivers/media/video/s5p-tv/sii9234_drv.c
@@ -0,0 +1,432 @@
+/*
+ * Samsung MHL interface driver
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Tomasz Stanislawski <t.stanislaws@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/freezer.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+
+#include <media/sii9234.h>
+#include <media/v4l2-subdev.h>
+
+MODULE_AUTHOR("Tomasz Stanislawski <t.stanislaws@samsung.com>");
+MODULE_DESCRIPTION("Samsung MHL interface driver");
+MODULE_LICENSE("GPL");
+
+struct sii9234_context {
+ struct i2c_client *client;
+ struct regulator *power;
+ int gpio_n_reset;
+ struct v4l2_subdev sd;
+};
+
+static inline struct sii9234_context *sd_to_context(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct sii9234_context, sd);
+}
+
+static inline int sii9234_readb(struct i2c_client *client, int addr)
+{
+ return i2c_smbus_read_byte_data(client, addr);
+}
+
+static inline int sii9234_writeb(struct i2c_client *client, int addr, int value)
+{
+ return i2c_smbus_write_byte_data(client, addr, value);
+}
+
+static inline int sii9234_writeb_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return i2c_smbus_write_byte_data(client, addr, ret);
+}
+
+static inline int sii9234_readb_idx(struct i2c_client *client, int addr)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ return i2c_smbus_read_byte_data(client, 0xbe);
+}
+
+static inline int sii9234_writeb_idx(struct i2c_client *client, int addr,
+ int value)
+{
+ int ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbc, addr >> 8);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbd, addr & 0xff);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(client, 0xbe, value);
+ return ret;
+}
+
+static inline int sii9234_writeb_idx_mask(struct i2c_client *client, int addr,
+ int value, int mask)
+{
+ int ret;
+
+ ret = sii9234_readb_idx(client, addr);
+ if (ret < 0)
+ return ret;
+ ret = (ret & ~mask) | (value & mask);
+ return sii9234_writeb_idx(client, addr, ret);
+}
+
+static int sii9234_reset(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ struct device *dev = &client->dev;
+ int ret, tries;
+
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 0);
+ mdelay(1);
+ gpio_direction_output(ctx->gpio_n_reset, 1);
+ mdelay(1);
+
+ /* going to TTPI mode */
+ ret = sii9234_writeb(client, 0xc7, 0);
+ if (ret < 0) {
+ dev_err(dev, "failed to set TTPI mode\n");
+ return ret;
+ }
+ for (tries = 0; tries < 100 ; ++tries) {
+ ret = sii9234_readb(client, 0x1b);
+ if (ret > 0)
+ break;
+ if (ret < 0) {
+ dev_err(dev, "failed to reset device\n");
+ return -EIO;
+ }
+ mdelay(1);
+ }
+ if (tries == 100) {
+ dev_err(dev, "maximal number of tries reached\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int sii9234_verify_version(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int family, rev, tpi_rev, dev_id, sub_id, hdcp, id;
+
+ family = sii9234_readb(client, 0x1b);
+ rev = sii9234_readb(client, 0x1c) & 0x0f;
+ tpi_rev = sii9234_readb(client, 0x1d) & 0x7f;
+ dev_id = sii9234_readb_idx(client, 0x0103);
+ sub_id = sii9234_readb_idx(client, 0x0102);
+ hdcp = sii9234_readb(client, 0x30);
+
+ if (family < 0 || rev < 0 || tpi_rev < 0 || dev_id < 0 ||
+ sub_id < 0 || hdcp < 0) {
+ dev_err(dev, "failed to read chip's version\n");
+ return -EIO;
+ }
+
+ id = (dev_id << 8) | sub_id;
+
+ dev_info(dev, "chip: SiL%02x family: %02x, rev: %02x\n",
+ id, family, rev);
+ dev_info(dev, "tpi_rev:%02x, hdcp: %02x\n", tpi_rev, hdcp);
+ if (id != 0x9234) {
+ dev_err(dev, "not supported chip\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static u8 data[][3] = {
+/* setup from driver created by doonsoo45.kim */
+ { 0x01, 0x05, 0x04 }, /* Enable Auto soft reset on SCDT = 0 */
+ { 0x01, 0x08, 0x35 }, /* Power Up TMDS Tx Core */
+ { 0x01, 0x0d, 0x1c }, /* HDMI Transcode mode enable */
+ { 0x01, 0x2b, 0x01 }, /* Enable HDCP Compliance workaround */
+ { 0x01, 0x79, 0x40 }, /* daniel test...MHL_INT */
+ { 0x01, 0x80, 0x34 }, /* Enable Rx PLL Clock Value */
+ { 0x01, 0x90, 0x27 }, /* Enable CBUS discovery */
+ { 0x01, 0x91, 0xe5 }, /* Skip RGND detection */
+ { 0x01, 0x92, 0x46 }, /* Force MHD mode */
+ { 0x01, 0x93, 0xdc }, /* Disable CBUS pull-up during RGND measurement */
+ { 0x01, 0x94, 0x66 }, /* 1.8V CBUS VTH & GND threshold */
+ { 0x01, 0x95, 0x31 }, /* RGND block & single discovery attempt */
+ { 0x01, 0x96, 0x22 }, /* use 1K and 2K setting */
+ { 0x01, 0xa0, 0x10 }, /* SIMG: Term mode */
+ { 0x01, 0xa1, 0xfc }, /* Disable internal Mobile HD driver */
+ { 0x01, 0xa3, 0xfa }, /* SIMG: Output Swing default EB, 3x Clk Mult */
+ { 0x01, 0xa5, 0x80 }, /* SIMG: RGND Hysterisis, 3x mode for Beast */
+ { 0x01, 0xa6, 0x0c }, /* SIMG: Swing Offset */
+ { 0x02, 0x3d, 0x3f }, /* Power up CVCC 1.2V core */
+ { 0x03, 0x00, 0x00 }, /* SIMG: correcting HW default */
+ { 0x03, 0x11, 0x01 }, /* Enable TxPLL Clock */
+ { 0x03, 0x12, 0x15 }, /* Enable Tx Clock Path & Equalizer */
+ { 0x03, 0x13, 0x60 }, /* SIMG: Set termination value */
+ { 0x03, 0x14, 0xf0 }, /* SIMG: Change CKDT level */
+ { 0x03, 0x17, 0x07 }, /* SIMG: PLL Calrefsel */
+ { 0x03, 0x1a, 0x20 }, /* VCO Cal */
+ { 0x03, 0x22, 0xe0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x23, 0xc0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x24, 0xa0 }, /* SIMG: Auto EQ */
+ { 0x03, 0x25, 0x80 }, /* SIMG: Auto EQ */
+ { 0x03, 0x26, 0x60 }, /* SIMG: Auto EQ */
+ { 0x03, 0x27, 0x40 }, /* SIMG: Auto EQ */
+ { 0x03, 0x28, 0x20 }, /* SIMG: Auto EQ */
+ { 0x03, 0x29, 0x00 }, /* SIMG: Auto EQ */
+ { 0x03, 0x31, 0x0b }, /* SIMG: Rx PLL BW value from I2C BW ~ 4MHz */
+ { 0x03, 0x45, 0x06 }, /* SIMG: DPLL Mode */
+ { 0x03, 0x4b, 0x06 }, /* SIMG: Correcting HW default */
+ { 0x03, 0x4c, 0xa0 }, /* Manual zone control */
+ { 0x03, 0x4d, 0x02 }, /* SIMG: PLL Mode Value (order is important) */
+};
+
+static int sii9234_set_internal(struct sii9234_context *ctx)
+{
+ struct i2c_client *client = ctx->client;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(data); ++i) {
+ int addr = (data[i][0] << 8) | data[i][1];
+ ret = sii9234_writeb_idx(client, addr, data[i][2]);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int sii9234_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+
+ dev_info(dev, "suspend start\n");
+
+ sii9234_writeb_mask(client, 0x1e, 3, 3);
+ regulator_disable(ctx->power);
+
+ return 0;
+}
+
+static int sii9234_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct sii9234_context *ctx = sd_to_context(sd);
+ struct i2c_client *client = ctx->client;
+ int ret;
+
+ dev_info(dev, "resume start\n");
+ regulator_enable(ctx->power);
+
+ ret = sii9234_reset(ctx);
+ if (ret)
+ goto fail;
+
+ /* enable tpi */
+ ret = sii9234_writeb_mask(client, 0x1e, 1, 0);
+ if (ret < 0)
+ goto fail;
+ ret = sii9234_set_internal(ctx);
+ if (ret < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ dev_err(dev, "failed to resume\n");
+ regulator_disable(ctx->power);
+
+ return ret;
+}
+
+static const struct dev_pm_ops sii9234_pm_ops = {
+ .runtime_suspend = sii9234_runtime_suspend,
+ .runtime_resume = sii9234_runtime_resume,
+};
+
+static int sii9234_s_power(struct v4l2_subdev *sd, int on)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(&ctx->client->dev);
+ else
+ ret = pm_runtime_put(&ctx->client->dev);
+ /* only values < 0 indicate errors */
+ return IS_ERR_VALUE(ret) ? ret : 0;
+}
+
+static int sii9234_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ /* (dis/en)able TDMS output */
+ sii9234_writeb_mask(ctx->client, 0x1a, enable ? 0 : ~0 , 1 << 4);
+ return 0;
+}
+
+static const struct v4l2_subdev_core_ops sii9234_core_ops = {
+ .s_power = sii9234_s_power,
+};
+
+static const struct v4l2_subdev_video_ops sii9234_video_ops = {
+ .s_stream = sii9234_s_stream,
+};
+
+static const struct v4l2_subdev_ops sii9234_ops = {
+ .core = &sii9234_core_ops,
+ .video = &sii9234_video_ops,
+};
+
+static int __devinit sii9234_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct sii9234_platform_data *pdata = dev->platform_data;
+ struct sii9234_context *ctx;
+ int ret;
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx) {
+ dev_err(dev, "out of memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ }
+ ctx->client = client;
+
+ ctx->power = regulator_get(dev, "hdmi-en");
+ if (IS_ERR(ctx->power)) {
+ dev_err(dev, "failed to acquire regulator hdmi-en\n");
+ ret = PTR_ERR(ctx->power);
+ goto fail_ctx;
+ }
+
+ ctx->gpio_n_reset = pdata->gpio_n_reset;
+ ret = gpio_request(ctx->gpio_n_reset, "MHL_RST");
+ if (ret) {
+ dev_err(dev, "failed to acquire MHL_RST gpio\n");
+ goto fail_power;
+ }
+
+ v4l2_i2c_subdev_init(&ctx->sd, client, &sii9234_ops);
+
+ pm_runtime_enable(dev);
+
+ /* enable device */
+ ret = pm_runtime_get_sync(dev);
+ if (ret)
+ goto fail_pm;
+
+ /* verify chip version */
+ ret = sii9234_verify_version(client);
+ if (ret)
+ goto fail_pm_get;
+
+ /* stop processing */
+ pm_runtime_put(dev);
+
+ dev_info(dev, "probe successful\n");
+
+ return 0;
+
+fail_pm_get:
+ pm_runtime_put_sync(dev);
+
+fail_pm:
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+
+fail_power:
+ regulator_put(ctx->power);
+
+fail_ctx:
+ kfree(ctx);
+
+fail:
+ dev_err(dev, "probe failed\n");
+
+ return ret;
+}
+
+static int __devexit sii9234_remove(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct sii9234_context *ctx = sd_to_context(sd);
+
+ pm_runtime_disable(dev);
+ gpio_free(ctx->gpio_n_reset);
+ regulator_put(ctx->power);
+ kfree(ctx);
+
+ dev_info(dev, "remove successful\n");
+
+ return 0;
+}
+
+
+static const struct i2c_device_id sii9234_id[] = {
+ { "SII9234", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, sii9234_id);
+static struct i2c_driver sii9234_driver = {
+ .driver = {
+ .name = "sii9234",
+ .owner = THIS_MODULE,
+ .pm = &sii9234_pm_ops,
+ },
+ .probe = sii9234_probe,
+ .remove = __devexit_p(sii9234_remove),
+ .id_table = sii9234_id,
+};
+
+static int __init sii9234_init(void)
+{
+ return i2c_add_driver(&sii9234_driver);
+}
+module_init(sii9234_init);
+
+static void __exit sii9234_exit(void)
+{
+ i2c_del_driver(&sii9234_driver);
+}
+module_exit(sii9234_exit);
diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c
index 99a2ac16f9e5..0caac50d7cf4 100644
--- a/drivers/media/video/saa6588.c
+++ b/drivers/media/video/saa6588.c
@@ -539,15 +539,4 @@ static struct i2c_driver saa6588_driver = {
.id_table = saa6588_id,
};
-static __init int init_saa6588(void)
-{
- return i2c_add_driver(&saa6588_driver);
-}
-
-static __exit void exit_saa6588(void)
-{
- i2c_del_driver(&saa6588_driver);
-}
-
-module_init(init_saa6588);
-module_exit(exit_saa6588);
+module_i2c_driver(saa6588_driver);
diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c
index 99664205ef4e..51cd4c8f0520 100644
--- a/drivers/media/video/saa7110.c
+++ b/drivers/media/video/saa7110.c
@@ -491,15 +491,4 @@ static struct i2c_driver saa7110_driver = {
.id_table = saa7110_id,
};
-static __init int init_saa7110(void)
-{
- return i2c_add_driver(&saa7110_driver);
-}
-
-static __exit void exit_saa7110(void)
-{
- i2c_del_driver(&saa7110_driver);
-}
-
-module_init(init_saa7110);
-module_exit(exit_saa7110);
+module_i2c_driver(saa7110_driver);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 0ef5484696b6..2107336cd836 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -1724,15 +1724,4 @@ static struct i2c_driver saa711x_driver = {
.id_table = saa711x_id,
};
-static __init int init_saa711x(void)
-{
- return i2c_add_driver(&saa711x_driver);
-}
-
-static __exit void exit_saa711x(void)
-{
- i2c_del_driver(&saa711x_driver);
-}
-
-module_init(init_saa711x);
-module_exit(exit_saa711x);
+module_i2c_driver(saa711x_driver);
diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
index ad964616c9d2..39c90b08eea8 100644
--- a/drivers/media/video/saa7127.c
+++ b/drivers/media/video/saa7127.c
@@ -852,15 +852,4 @@ static struct i2c_driver saa7127_driver = {
.id_table = saa7127_id,
};
-static __init int init_saa7127(void)
-{
- return i2c_add_driver(&saa7127_driver);
-}
-
-static __exit void exit_saa7127(void)
-{
- i2c_del_driver(&saa7127_driver);
-}
-
-module_init(init_saa7127);
-module_exit(exit_saa7127);
+module_i2c_driver(saa7127_driver);
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index a646ccf51696..da3899329f52 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -10,7 +10,7 @@ obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o
obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/video
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
index f9f29cc93a8a..f147b05bd860 100644
--- a/drivers/media/video/saa7134/saa6752hs.c
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -1001,18 +1001,7 @@ static struct i2c_driver saa6752hs_driver = {
.id_table = saa6752hs_id,
};
-static __init int init_saa6752hs(void)
-{
- return i2c_add_driver(&saa6752hs_driver);
-}
-
-static __exit void exit_saa6752hs(void)
-{
- i2c_del_driver(&saa6752hs_driver);
-}
-
-module_init(init_saa6752hs);
-module_exit(exit_saa6752hs);
+module_i2c_driver(saa6752hs_driver);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 065d0f6be4a0..53aae5968ffb 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -33,6 +33,7 @@
#include "tea5767.h"
#include "tda18271.h"
#include "xc5000.h"
+#include "s5h1411.h"
/* commly used strings */
static char name_mute[] = "mute";
@@ -5712,6 +5713,36 @@ struct saa7134_board saa7134_boards[] = {
.amux = LINE1,
} },
},
+ [SAA7134_BOARD_KWORLD_PC150U] = {
+ .name = "Kworld PC150-U",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .gpiomask = 1 << 21,
+ .ts_type = SAA7134_MPEG_TS_PARALLEL,
+ .inputs = { {
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ }, {
+ .name = name_comp,
+ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+ .amux = TV,
+ .gpio = 0x0000000,
+ },
+ },
};
@@ -6306,6 +6337,12 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_KWORLD_ATSC110, /* ATSC 115 */
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133, /* SAA7135HL */
+ .subvendor = 0x17de,
+ .subdevice = 0xa134,
+ .driver_data = SAA7134_BOARD_KWORLD_PC150U,
+ }, {
+ .vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
.subvendor = 0x1461,
.subdevice = 0x7360,
@@ -7134,6 +7171,23 @@ static inline int saa7134_kworld_sbtvd_toggle_agc(struct saa7134_dev *dev,
return 0;
}
+static int saa7134_kworld_pc150u_toggle_agc(struct saa7134_dev *dev,
+ enum tda18271_mode mode)
+{
+ switch (mode) {
+ case TDA18271_ANALOG:
+ saa7134_set_gpio(dev, 18, 0);
+ break;
+ case TDA18271_DIGITAL:
+ saa7134_set_gpio(dev, 18, 1);
+ msleep(30);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
int command, int arg)
{
@@ -7150,6 +7204,9 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
ret = saa7134_kworld_sbtvd_toggle_agc(dev, arg);
break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ ret = saa7134_kworld_pc150u_toggle_agc(dev, arg);
+ break;
default:
break;
}
@@ -7171,6 +7228,7 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev,
case SAA7134_BOARD_HAUPPAUGE_HVR1120:
case SAA7134_BOARD_AVERMEDIA_M733A:
case SAA7134_BOARD_KWORLD_PCI_SBTVD_FULLSEG:
+ case SAA7134_BOARD_KWORLD_PC150U:
case SAA7134_BOARD_MAGICPRO_PROHDTV_PRO2:
/* tda8290 + tda18271 */
ret = saa7134_tda8290_18271_callback(dev, command, arg);
@@ -7452,6 +7510,7 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_BEHOLD_X7:
case SAA7134_BOARD_BEHOLD_H7:
case SAA7134_BOARD_BEHOLD_A7:
+ case SAA7134_BOARD_KWORLD_PC150U:
dev->has_remote = SAA7134_REMOTE_I2C;
break;
case SAA7134_BOARD_AVERMEDIA_A169_B:
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 089fa0fb5c94..aaa5c97a7216 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -61,6 +61,7 @@
#include "zl10036.h"
#include "zl10039.h"
#include "mt312.h"
+#include "s5h1411.h"
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
@@ -1158,6 +1159,33 @@ static struct tda18271_config prohdtv_pro2_tda18271_config = {
.output_opt = TDA18271_OUTPUT_LT_OFF,
};
+static struct tda18271_std_map kworld_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37 },
+};
+
+static struct tda18271_config kworld_pc150u_tda18271_config = {
+ .std_map = &kworld_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+ .output_opt = TDA18271_OUTPUT_LT_OFF,
+ .config = 3, /* Use tuner callback for AGC */
+ .rf_cal_on_startup = 1
+};
+
+static struct s5h1411_config kworld_s5h1411_config = {
+ .output_mode = S5H1411_PARALLEL_OUTPUT,
+ .gpio = S5H1411_GPIO_OFF,
+ .qam_if = S5H1411_IF_4000,
+ .vsb_if = S5H1411_IF_3250,
+ .inversion = S5H1411_INVERSION_ON,
+ .status_mode = S5H1411_DEMODLOCKING,
+ .mpeg_timing =
+ S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK,
+};
+
+
/* ==================================================================
* Core code
*/
@@ -1438,6 +1466,22 @@ static int dvb_init(struct saa7134_dev *dev)
&dev->i2c_adap, 0x61,
TUNER_PHILIPS_TUV1236D);
break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ saa7134_set_gpio(dev, 18, 1); /* Switch to digital mode */
+ saa7134_tuner_callback(dev, 0,
+ TDA18271_CALLBACK_CMD_AGC_ENABLE, 1);
+ fe0->dvb.frontend = dvb_attach(s5h1411_attach,
+ &kworld_s5h1411_config,
+ &dev->i2c_adap);
+ if (fe0->dvb.frontend != NULL) {
+ dvb_attach(tda829x_attach, fe0->dvb.frontend,
+ &dev->i2c_adap, 0x4b,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, fe0->dvb.frontend,
+ 0x60, &dev->i2c_adap,
+ &kworld_pc150u_tda18271_config);
+ }
+ break;
case SAA7134_BOARD_FLYDVBS_LR300:
fe0->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
&dev->i2c_adap);
diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
index 2d3f6d265bbf..a176ec3285e0 100644
--- a/drivers/media/video/saa7134/saa7134-i2c.c
+++ b/drivers/media/video/saa7134/saa7134-i2c.c
@@ -254,7 +254,9 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
addr = msgs[i].addr << 1;
if (msgs[i].flags & I2C_M_RD)
addr |= 1;
- if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40) {
+ if (i > 0 && msgs[i].flags &
+ I2C_M_RD && msgs[i].addr != 0x40 &&
+ msgs[i].addr != 0x19) {
/* workaround for a saa7134 i2c bug
* needed to talk to the mt352 demux
* thanks to pinnacle for the hint */
@@ -279,6 +281,16 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
d1printk("%02x", rc);
msgs[i].buf[byte] = rc;
}
+ /* discard mysterious extra byte when reading
+ from Samsung S5H1411. i2c bus gets error
+ if we do not. */
+ if (0x19 == msgs[i].addr) {
+ d1printk(" ?");
+ rc = i2c_recv_byte(dev);
+ if (rc < 0)
+ goto err;
+ d1printk("%02x", rc);
+ }
} else {
/* write bytes */
d2printk("write bytes\n");
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 22ecd7297d2d..48d2878699b7 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -210,6 +210,54 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key,
return 1;
}
+/* copied and modified from get_key_msi_tvanywhere_plus() */
+static int get_key_kworld_pc150u(struct IR_i2c *ir, u32 *ir_key,
+ u32 *ir_raw)
+{
+ unsigned char b;
+ unsigned int gpio;
+
+ /* <dev> is needed to access GPIO. Used by the saa_readl macro. */
+ struct saa7134_dev *dev = ir->c->adapter->algo_data;
+ if (dev == NULL) {
+ i2cdprintk("get_key_kworld_pc150u: "
+ "ir->c->adapter->algo_data is NULL!\n");
+ return -EIO;
+ }
+
+ /* rising SAA7134_GPIO_GPRESCAN reads the status */
+
+ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+ saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+ gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
+
+ /* GPIO&0x100 is pulsed low when a button is pressed. Don't do
+ I2C receive if gpio&0x100 is not low. */
+
+ if (gpio & 0x100)
+ return 0; /* No button press */
+
+ /* GPIO says there is a button press. Get it. */
+
+ if (1 != i2c_master_recv(ir->c, &b, 1)) {
+ i2cdprintk("read error\n");
+ return -EIO;
+ }
+
+ /* No button press */
+
+ if (b == 0xff)
+ return 0;
+
+ /* Button pressed */
+
+ dprintk("get_key_kworld_pc150u: Key = 0x%02X\n", b);
+ *ir_key = b;
+ *ir_raw = b;
+ return 1;
+}
+
static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
{
unsigned char b;
@@ -901,6 +949,21 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev)
msg_msi.addr, dev->i2c_adap.name,
(1 == rc) ? "yes" : "no");
break;
+ case SAA7134_BOARD_KWORLD_PC150U:
+ /* copied and modified from MSI TV@nywhere Plus */
+ dev->init_data.name = "Kworld PC150-U";
+ dev->init_data.get_key = get_key_kworld_pc150u;
+ dev->init_data.ir_codes = RC_MAP_KWORLD_PC150U;
+ info.addr = 0x30;
+ /* MSI TV@nywhere Plus controller doesn't seem to
+ respond to probes unless we read something from
+ an existing device. Weird...
+ REVISIT: might no longer be needed */
+ rc = i2c_transfer(&dev->i2c_adap, &msg_msi, 1);
+ dprintk("probe 0x%02x @ %s: %s\n",
+ msg_msi.addr, dev->i2c_adap.name,
+ (1 == rc) ? "yes" : "no");
+ break;
case SAA7134_BOARD_HAUPPAUGE_HVR1110:
dev->init_data.name = "HVR 1110";
dev->init_data.get_key = get_key_hvr1110;
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index 42fba4f93c72..f625060e6a0f 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -126,8 +126,8 @@ struct saa7134_card_ir {
unsigned users;
u32 polling;
- u32 last_gpio;
- u32 mask_keycode, mask_keydown, mask_keyup;
+ u32 last_gpio;
+ u32 mask_keycode, mask_keydown, mask_keyup;
bool running;
bool active;
@@ -331,6 +331,7 @@ struct saa7134_card_ir {
#define SAA7134_BOARD_BEHOLD_501 186
#define SAA7134_BOARD_BEHOLD_503FM 187
#define SAA7134_BOARD_SENSORAY811_911 188
+#define SAA7134_BOARD_KWORLD_PC150U 189
#define SAA7134_MAXBOARDS 32
#define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/saa7164/Makefile b/drivers/media/video/saa7164/Makefile
index ecd5811dc486..068443af30c8 100644
--- a/drivers/media/video/saa7164/Makefile
+++ b/drivers/media/video/saa7164/Makefile
@@ -4,9 +4,9 @@ saa7164-objs := saa7164-cards.o saa7164-core.o saa7164-i2c.o saa7164-dvb.o \
obj-$(CONFIG_VIDEO_SAA7164) += saa7164.o
-ccflags-y += -Idrivers/media/video
-ccflags-y += -Idrivers/media/common/tuners
-ccflags-y += -Idrivers/media/dvb/dvb-core
-ccflags-y += -Idrivers/media/dvb/frontends
+ccflags-y += -I$(srctree)/drivers/media/video
+ccflags-y += -I$(srctree)/drivers/media/common/tuners
+ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core
+ccflags-y += -I$(srctree)/drivers/media/dvb/frontends
ccflags-y += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/saa7164/saa7164-encoder.c b/drivers/media/video/saa7164/saa7164-encoder.c
index 2fd38a01887f..a9ed686ad08a 100644
--- a/drivers/media/video/saa7164/saa7164-encoder.c
+++ b/drivers/media/video/saa7164/saa7164-encoder.c
@@ -791,11 +791,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_log_status(struct file *file, void *priv)
-{
- return 0;
-}
-
static int fill_queryctrl(struct saa7164_encoder_params *params,
struct v4l2_queryctrl *c)
{
@@ -1347,7 +1342,6 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = {
.vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
- .vidioc_log_status = vidioc_log_status,
.vidioc_queryctrl = vidioc_queryctrl,
.vidioc_g_chip_ident = saa7164_g_chip_ident,
#ifdef CONFIG_VIDEO_ADV_DEBUG
diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c
index e2e034158718..273cf807401c 100644
--- a/drivers/media/video/saa7164/saa7164-vbi.c
+++ b/drivers/media/video/saa7164/saa7164-vbi.c
@@ -730,11 +730,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
return 0;
}
-static int vidioc_log_status(struct file *file, void *priv)
-{
- return 0;
-}
-
static int fill_queryctrl(struct saa7164_vbi_params *params,
struct v4l2_queryctrl *c)
{
@@ -1256,7 +1251,6 @@ static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
.vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
.vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
.vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
- .vidioc_log_status = vidioc_log_status,
.vidioc_queryctrl = vidioc_queryctrl,
#if 0
.vidioc_g_chip_ident = saa7164_g_chip_ident,
diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
index b6172c2c517e..1e84466515aa 100644
--- a/drivers/media/video/saa717x.c
+++ b/drivers/media/video/saa717x.c
@@ -1375,15 +1375,4 @@ static struct i2c_driver saa717x_driver = {
.id_table = saa717x_id,
};
-static __init int init_saa717x(void)
-{
- return i2c_add_driver(&saa717x_driver);
-}
-
-static __exit void exit_saa717x(void)
-{
- i2c_del_driver(&saa717x_driver);
-}
-
-module_init(init_saa717x);
-module_exit(exit_saa717x);
+module_i2c_driver(saa717x_driver);
diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c
index 96f56c2f11f3..2c6b65c76e2b 100644
--- a/drivers/media/video/saa7185.c
+++ b/drivers/media/video/saa7185.c
@@ -374,15 +374,4 @@ static struct i2c_driver saa7185_driver = {
.id_table = saa7185_id,
};
-static __init int init_saa7185(void)
-{
- return i2c_add_driver(&saa7185_driver);
-}
-
-static __exit void exit_saa7185(void)
-{
- i2c_del_driver(&saa7185_driver);
-}
-
-module_init(init_saa7185);
-module_exit(exit_saa7185);
+module_i2c_driver(saa7185_driver);
diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c
index 211fa25a1239..d7d1670e0ca3 100644
--- a/drivers/media/video/saa7191.c
+++ b/drivers/media/video/saa7191.c
@@ -656,15 +656,4 @@ static struct i2c_driver saa7191_driver = {
.id_table = saa7191_id,
};
-static __init int init_saa7191(void)
-{
- return i2c_add_driver(&saa7191_driver);
-}
-
-static __exit void exit_saa7191(void)
-{
- i2c_del_driver(&saa7191_driver);
-}
-
-module_init(init_saa7191);
-module_exit(exit_saa7191);
+module_i2c_driver(saa7191_driver);
diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
index f854d85a387c..424dfacd263a 100644
--- a/drivers/media/video/sh_mobile_ceu_camera.c
+++ b/drivers/media/video/sh_mobile_ceu_camera.c
@@ -112,6 +112,10 @@ struct sh_mobile_ceu_dev {
u32 cflcr;
+ /* static max sizes either from platform data or default */
+ int max_width;
+ int max_height;
+
enum v4l2_field field;
int sequence;
@@ -1081,7 +1085,15 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int
if (ret < 0)
return ret;
- while ((mf.width > 2560 || mf.height > 1920) && shift < 4) {
+ /*
+ * All currently existing CEU implementations support 2560x1920
+ * or larger frames. If the sensor is proposing too big a frame,
+ * don't bother with possibly supportred by the CEU larger
+ * sizes, just try VGA multiples. If needed, this can be
+ * adjusted in the future.
+ */
+ while ((mf.width > pcdev->max_width ||
+ mf.height > pcdev->max_height) && shift < 4) {
/* Try 2560x1920, 1280x960, 640x480, 320x240 */
mf.width = 2560 >> shift;
mf.height = 1920 >> shift;
@@ -1377,6 +1389,8 @@ static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop,
static int client_s_fmt(struct soc_camera_device *icd,
struct v4l2_mbus_framefmt *mf, bool ceu_can_scale)
{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
struct sh_mobile_ceu_cam *cam = icd->host_priv;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
struct device *dev = icd->parent;
@@ -1410,8 +1424,8 @@ static int client_s_fmt(struct soc_camera_device *icd,
if (ret < 0)
return ret;
- max_width = min(cap.bounds.width, 2560);
- max_height = min(cap.bounds.height, 1920);
+ max_width = min(cap.bounds.width, pcdev->max_width);
+ max_height = min(cap.bounds.height, pcdev->max_height);
/* Camera set a format, but geometry is not precise, try to improve */
tmp_w = mf->width;
@@ -1551,7 +1565,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
if (ret < 0)
return ret;
- if (mf.width > 2560 || mf.height > 1920)
+ if (mf.width > pcdev->max_width || mf.height > pcdev->max_height)
return -EINVAL;
/* 4. Calculate camera scales */
@@ -1834,6 +1848,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
struct v4l2_format *f)
{
+ struct soc_camera_host *ici = to_soc_camera_host(icd->parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
const struct soc_camera_format_xlate *xlate;
struct v4l2_pix_format *pix = &f->fmt.pix;
struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
@@ -1854,8 +1870,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
/* FIXME: calculate using depth and bus width */
/* CFSZR requires height and width to be 4-pixel aligned */
- v4l_bound_align_image(&pix->width, 2, 2560, 2,
- &pix->height, 4, 1920, 2, 0);
+ v4l_bound_align_image(&pix->width, 2, pcdev->max_width, 2,
+ &pix->height, 4, pcdev->max_height, 2, 0);
width = pix->width;
height = pix->height;
@@ -1890,8 +1906,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
* requested a bigger rectangle, it will not return a
* smaller one.
*/
- mf.width = 2560;
- mf.height = 1920;
+ mf.width = pcdev->max_width;
+ mf.height = pcdev->max_height;
ret = v4l2_device_call_until_err(sd->v4l2_dev,
soc_camera_grp_id(icd), video,
try_mbus_fmt, &mf);
@@ -2082,6 +2098,9 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev)
goto exit_kfree;
}
+ pcdev->max_width = pcdev->pdata->max_width ? : 2560;
+ pcdev->max_height = pcdev->pdata->max_height ? : 1920;
+
base = ioremap_nocache(res->start, resource_size(res));
if (!base) {
err = -ENXIO;
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index b82710745ba8..eb25756a07af 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -526,10 +526,6 @@ static int soc_camera_open(struct file *file)
},
};
- ret = soc_camera_power_on(icd, icl);
- if (ret < 0)
- goto epower;
-
/* The camera could have been already on, try to reset */
if (icl->reset)
icl->reset(icd->pdev);
@@ -540,6 +536,10 @@ static int soc_camera_open(struct file *file)
goto eiciadd;
}
+ ret = soc_camera_power_on(icd, icl);
+ if (ret < 0)
+ goto epower;
+
pm_runtime_enable(&icd->vdev->dev);
ret = pm_runtime_resume(&icd->vdev->dev);
if (ret < 0 && ret != -ENOSYS)
@@ -578,10 +578,10 @@ einitvb:
esfmt:
pm_runtime_disable(&icd->vdev->dev);
eresume:
- ici->ops->remove(icd);
-eiciadd:
soc_camera_power_off(icd, icl);
epower:
+ ici->ops->remove(icd);
+eiciadd:
icd->use_count--;
module_put(ici->ops->owner);
@@ -1050,6 +1050,14 @@ static int soc_camera_probe(struct soc_camera_device *icd)
if (ret < 0)
goto ereg;
+ /* The camera could have been already on, try to reset */
+ if (icl->reset)
+ icl->reset(icd->pdev);
+
+ ret = ici->ops->add(icd);
+ if (ret < 0)
+ goto eadd;
+
/*
* This will not yet call v4l2_subdev_core_ops::s_power(1), because the
* subdevice has not been initialised yet. We'll have to call it once
@@ -1060,14 +1068,6 @@ static int soc_camera_probe(struct soc_camera_device *icd)
if (ret < 0)
goto epower;
- /* The camera could have been already on, try to reset */
- if (icl->reset)
- icl->reset(icd->pdev);
-
- ret = ici->ops->add(icd);
- if (ret < 0)
- goto eadd;
-
/* Must have icd->vdev before registering the device */
ret = video_dev_create(icd);
if (ret < 0)
@@ -1165,10 +1165,10 @@ eadddev:
video_device_release(icd->vdev);
icd->vdev = NULL;
evdc:
- ici->ops->remove(icd);
-eadd:
soc_camera_power_off(icd, icl);
epower:
+ ici->ops->remove(icd);
+eadd:
regulator_bulk_free(icl->num_regulators, icl->regulators);
ereg:
v4l2_ctrl_handler_free(&icd->ctrl_handler);
diff --git a/drivers/media/video/sr030pc30.c b/drivers/media/video/sr030pc30.c
index d1b07aceaf94..e9d95bda2ab1 100644
--- a/drivers/media/video/sr030pc30.c
+++ b/drivers/media/video/sr030pc30.c
@@ -864,18 +864,7 @@ static struct i2c_driver sr030pc30_i2c_driver = {
.id_table = sr030pc30_id,
};
-static int __init sr030pc30_init(void)
-{
- return i2c_add_driver(&sr030pc30_i2c_driver);
-}
-
-static void __exit sr030pc30_exit(void)
-{
- i2c_del_driver(&sr030pc30_i2c_driver);
-}
-
-module_init(sr030pc30_init);
-module_exit(sr030pc30_exit);
+module_i2c_driver(sr030pc30_i2c_driver);
MODULE_DESCRIPTION("Siliconfile SR030PC30 camera driver");
MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
index bd218545da9c..f7707e65761e 100644
--- a/drivers/media/video/tda7432.c
+++ b/drivers/media/video/tda7432.c
@@ -482,15 +482,4 @@ static struct i2c_driver tda7432_driver = {
.id_table = tda7432_id,
};
-static __init int init_tda7432(void)
-{
- return i2c_add_driver(&tda7432_driver);
-}
-
-static __exit void exit_tda7432(void)
-{
- i2c_del_driver(&tda7432_driver);
-}
-
-module_init(init_tda7432);
-module_exit(exit_tda7432);
+module_i2c_driver(tda7432_driver);
diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
index 22fa8202d5ca..465d7086babf 100644
--- a/drivers/media/video/tda9840.c
+++ b/drivers/media/video/tda9840.c
@@ -208,15 +208,4 @@ static struct i2c_driver tda9840_driver = {
.id_table = tda9840_id,
};
-static __init int init_tda9840(void)
-{
- return i2c_add_driver(&tda9840_driver);
-}
-
-static __exit void exit_tda9840(void)
-{
- i2c_del_driver(&tda9840_driver);
-}
-
-module_init(init_tda9840);
-module_exit(exit_tda9840);
+module_i2c_driver(tda9840_driver);
diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
index 827425c5b866..d1d6ea1dd273 100644
--- a/drivers/media/video/tea6415c.c
+++ b/drivers/media/video/tea6415c.c
@@ -184,15 +184,4 @@ static struct i2c_driver tea6415c_driver = {
.id_table = tea6415c_id,
};
-static __init int init_tea6415c(void)
-{
- return i2c_add_driver(&tea6415c_driver);
-}
-
-static __exit void exit_tea6415c(void)
-{
- i2c_del_driver(&tea6415c_driver);
-}
-
-module_init(init_tea6415c);
-module_exit(exit_tea6415c);
+module_i2c_driver(tea6415c_driver);
diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
index f350b6c24500..38757217a074 100644
--- a/drivers/media/video/tea6420.c
+++ b/drivers/media/video/tea6420.c
@@ -166,15 +166,4 @@ static struct i2c_driver tea6420_driver = {
.id_table = tea6420_id,
};
-static __init int init_tea6420(void)
-{
- return i2c_add_driver(&tea6420_driver);
-}
-
-static __exit void exit_tea6420(void)
-{
- i2c_del_driver(&tea6420_driver);
-}
-
-module_init(init_tea6420);
-module_exit(exit_tea6420);
+module_i2c_driver(tea6420_driver);
diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c
index 61b1dd118364..e5c0eedebc58 100644
--- a/drivers/media/video/ths7303.c
+++ b/drivers/media/video/ths7303.c
@@ -137,16 +137,4 @@ static struct i2c_driver ths7303_driver = {
.id_table = ths7303_id,
};
-static int __init ths7303_init(void)
-{
- return i2c_add_driver(&ths7303_driver);
-}
-
-static void __exit ths7303_exit(void)
-{
- i2c_del_driver(&ths7303_driver);
-}
-
-module_init(ths7303_init);
-module_exit(ths7303_exit);
-
+module_i2c_driver(ths7303_driver);
diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
index 286ec7e7062a..809a75a558ee 100644
--- a/drivers/media/video/tlv320aic23b.c
+++ b/drivers/media/video/tlv320aic23b.c
@@ -227,15 +227,4 @@ static struct i2c_driver tlv320aic23b_driver = {
.id_table = tlv320aic23b_id,
};
-static __init int init_tlv320aic23b(void)
-{
- return i2c_add_driver(&tlv320aic23b_driver);
-}
-
-static __exit void exit_tlv320aic23b(void)
-{
- i2c_del_driver(&tlv320aic23b_driver);
-}
-
-module_init(init_tlv320aic23b);
-module_exit(exit_tlv320aic23b);
+module_i2c_driver(tlv320aic23b_driver);
diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c
index 7844607dd45a..859eb90e4d56 100644
--- a/drivers/media/video/tm6000/tm6000-input.c
+++ b/drivers/media/video/tm6000/tm6000-input.c
@@ -481,8 +481,6 @@ int tm6000_ir_fini(struct tm6000_core *dev)
dprintk(2, "%s\n",__func__);
- rc_unregister_device(ir->rc);
-
if (!ir->polling)
__tm6000_ir_int_stop(ir->rc);
@@ -492,6 +490,7 @@ int tm6000_ir_fini(struct tm6000_core *dev)
tm6000_flash_led(dev, 0);
ir->pwled = 0;
+ rc_unregister_device(ir->rc);
kfree(ir);
dev->ir = NULL;
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index 4059ea178c2d..a5c6397ad591 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -380,6 +380,21 @@ static void set_type(struct i2c_client *c, unsigned int type,
tune_now = 0;
break;
}
+ case TUNER_XC5000C:
+ {
+ struct xc5000_config xc5000c_cfg = {
+ .i2c_address = t->i2c->addr,
+ /* if_khz will be set at dvb_attach() */
+ .if_khz = 0,
+ .chip_id = XC5000C,
+ };
+
+ if (!dvb_attach(xc5000_attach,
+ &t->fe, t->i2c->adapter, &xc5000c_cfg))
+ goto attach_failed;
+ tune_now = 0;
+ break;
+ }
case TUNER_NXP_TDA18271:
{
struct tda18271_config cfg = {
@@ -1314,18 +1329,7 @@ static struct i2c_driver tuner_driver = {
.id_table = tuner_id,
};
-static __init int init_tuner(void)
-{
- return i2c_add_driver(&tuner_driver);
-}
-
-static __exit void exit_tuner(void)
-{
- i2c_del_driver(&tuner_driver);
-}
-
-module_init(init_tuner);
-module_exit(exit_tuner);
+module_i2c_driver(tuner_driver);
MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");
MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index f22dbef9b95b..c5b1a7365e4f 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -2078,15 +2078,4 @@ static struct i2c_driver tvaudio_driver = {
.id_table = tvaudio_id,
};
-static __init int init_tvaudio(void)
-{
- return i2c_add_driver(&tvaudio_driver);
-}
-
-static __exit void exit_tvaudio(void)
-{
- i2c_del_driver(&tvaudio_driver);
-}
-
-module_init(init_tvaudio);
-module_exit(exit_tvaudio);
+module_i2c_driver(tvaudio_driver);
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index 6103d1b1081e..3b6cf034976a 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -286,8 +286,16 @@ hauppauge_tuner[] =
{ TUNER_ABSENT, "MaxLinear 301"},
{ TUNER_ABSENT, "Mirics MSi001"},
{ TUNER_ABSENT, "MaxLinear MxL241SF"},
- { TUNER_ABSENT, "Xceive XC5000C"},
+ { TUNER_XC5000C, "Xceive XC5000C"},
{ TUNER_ABSENT, "Montage M68TS2020"},
+ { TUNER_ABSENT, "Siano SMS1530"},
+ { TUNER_ABSENT, "Dibcom 7090"},
+ { TUNER_ABSENT, "Xceive XC5200C"},
+ { TUNER_ABSENT, "NXP 18273"},
+ { TUNER_ABSENT, "Montage M88TS2022"},
+ /* 180-189 */
+ { TUNER_ABSENT, "NXP 18272M"},
+ { TUNER_ABSENT, "NXP 18272S"},
};
/* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are
diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
index dd26cacd0556..cd615c1d6011 100644
--- a/drivers/media/video/tvp514x.c
+++ b/drivers/media/video/tvp514x.c
@@ -1163,15 +1163,4 @@ static struct i2c_driver tvp514x_driver = {
.id_table = tvp514x_id,
};
-static int __init tvp514x_init(void)
-{
- return i2c_add_driver(&tvp514x_driver);
-}
-
-static void __exit tvp514x_exit(void)
-{
- i2c_del_driver(&tvp514x_driver);
-}
-
-module_init(tvp514x_init);
-module_exit(tvp514x_exit);
+module_i2c_driver(tvp514x_driver);
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index 6be9910a6e24..1326e11cf4a9 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -17,6 +17,13 @@
#include "tvp5150_reg.h"
+#define TVP5150_H_MAX 720
+#define TVP5150_V_MAX_525_60 480
+#define TVP5150_V_MAX_OTHERS 576
+#define TVP5150_MAX_CROP_LEFT 511
+#define TVP5150_MAX_CROP_TOP 127
+#define TVP5150_CROP_SHIFT 2
+
MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver");
MODULE_AUTHOR("Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
@@ -29,6 +36,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-2)");
struct tvp5150 {
struct v4l2_subdev sd;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_rect rect;
v4l2_std_id norm; /* Current set standard */
u32 input;
@@ -732,6 +740,13 @@ static int tvp5150_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
if (decoder->norm == std)
return 0;
+ /* Change cropping height limits */
+ if (std & V4L2_STD_525_60)
+ decoder->rect.height = TVP5150_V_MAX_525_60;
+ else
+ decoder->rect.height = TVP5150_V_MAX_OTHERS;
+
+
return tvp5150_set_std(sd, std);
}
@@ -828,11 +843,8 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd,
else
std = decoder->norm;
- f->width = 720;
- if (std & V4L2_STD_525_60)
- f->height = 480;
- else
- f->height = 576;
+ f->width = decoder->rect.width;
+ f->height = decoder->rect.height;
f->code = V4L2_MBUS_FMT_YUYV8_2X8;
f->field = V4L2_FIELD_SEQ_TB;
@@ -843,6 +855,99 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd,
return 0;
}
+static int tvp5150_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct v4l2_rect rect = a->c;
+ struct tvp5150 *decoder = to_tvp5150(sd);
+ v4l2_std_id std;
+ int hmax;
+
+ v4l2_dbg(1, debug, sd, "%s left=%d, top=%d, width=%d, height=%d\n",
+ __func__, rect.left, rect.top, rect.width, rect.height);
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ /* tvp5150 has some special limits */
+ rect.left = clamp(rect.left, 0, TVP5150_MAX_CROP_LEFT);
+ rect.width = clamp(rect.width,
+ TVP5150_H_MAX - TVP5150_MAX_CROP_LEFT - rect.left,
+ TVP5150_H_MAX - rect.left);
+ rect.top = clamp(rect.top, 0, TVP5150_MAX_CROP_TOP);
+
+ /* Calculate height based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ if (std & V4L2_STD_525_60)
+ hmax = TVP5150_V_MAX_525_60;
+ else
+ hmax = TVP5150_V_MAX_OTHERS;
+
+ rect.height = clamp(rect.height,
+ hmax - TVP5150_MAX_CROP_TOP - rect.top,
+ hmax - rect.top);
+
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_START, rect.top);
+ tvp5150_write(sd, TVP5150_VERT_BLANKING_STOP,
+ rect.top + rect.height - hmax);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_MSB,
+ rect.left >> TVP5150_CROP_SHIFT);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_ST_LSB,
+ rect.left | (1 << TVP5150_CROP_SHIFT));
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_MSB,
+ (rect.left + rect.width - TVP5150_MAX_CROP_LEFT) >>
+ TVP5150_CROP_SHIFT);
+ tvp5150_write(sd, TVP5150_ACT_VD_CROP_STP_LSB,
+ rect.left + rect.width - TVP5150_MAX_CROP_LEFT);
+
+ decoder->rect = rect;
+
+ return 0;
+}
+
+static int tvp5150_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+
+ a->c = decoder->rect;
+ a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ return 0;
+}
+
+static int tvp5150_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
+{
+ struct tvp5150 *decoder = container_of(sd, struct tvp5150, sd);
+ v4l2_std_id std;
+
+ if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ a->bounds.left = 0;
+ a->bounds.top = 0;
+ a->bounds.width = TVP5150_H_MAX;
+
+ /* Calculate height based on current standard */
+ if (decoder->norm == V4L2_STD_ALL)
+ std = tvp5150_read_std(sd);
+ else
+ std = decoder->norm;
+
+ if (std & V4L2_STD_525_60)
+ a->bounds.height = TVP5150_V_MAX_525_60;
+ else
+ a->bounds.height = TVP5150_V_MAX_OTHERS;
+
+ a->defrect = a->bounds;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+ return 0;
+}
+
/****************************************************************************
I2C Command
****************************************************************************/
@@ -998,6 +1103,10 @@ static const struct v4l2_subdev_video_ops tvp5150_video_ops = {
.enum_mbus_fmt = tvp5150_enum_mbus_fmt,
.s_mbus_fmt = tvp5150_mbus_fmt,
.try_mbus_fmt = tvp5150_mbus_fmt,
+ .g_mbus_fmt = tvp5150_mbus_fmt,
+ .s_crop = tvp5150_s_crop,
+ .g_crop = tvp5150_g_crop,
+ .cropcap = tvp5150_cropcap,
};
static const struct v4l2_subdev_vbi_ops tvp5150_vbi_ops = {
@@ -1083,6 +1192,15 @@ static int tvp5150_probe(struct i2c_client *c,
}
v4l2_ctrl_handler_setup(&core->hdl);
+ /* Default is no cropping */
+ core->rect.top = 0;
+ if (tvp5150_read_std(sd) & V4L2_STD_525_60)
+ core->rect.height = TVP5150_V_MAX_525_60;
+ else
+ core->rect.height = TVP5150_V_MAX_OTHERS;
+ core->rect.left = 0;
+ core->rect.width = TVP5150_H_MAX;
+
if (debug > 1)
tvp5150_log_status(sd);
return 0;
@@ -1121,15 +1239,4 @@ static struct i2c_driver tvp5150_driver = {
.id_table = tvp5150_id,
};
-static __init int init_tvp5150(void)
-{
- return i2c_add_driver(&tvp5150_driver);
-}
-
-static __exit void exit_tvp5150(void)
-{
- i2c_del_driver(&tvp5150_driver);
-}
-
-module_init(init_tvp5150);
-module_exit(exit_tvp5150);
+module_i2c_driver(tvp5150_driver);
diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c
index 236c559d5f51..d7676d85c4df 100644
--- a/drivers/media/video/tvp7002.c
+++ b/drivers/media/video/tvp7002.c
@@ -1069,27 +1069,4 @@ static struct i2c_driver tvp7002_driver = {
.id_table = tvp7002_id,
};
-/*
- * tvp7002_init - Initialize driver via I2C interface
- *
- * Register the TVP7002 driver.
- * Return 0 on success or error code on failure.
- */
-static int __init tvp7002_init(void)
-{
- return i2c_add_driver(&tvp7002_driver);
-}
-
-/*
- * tvp7002_exit - Remove driver via I2C interface
- *
- * Unregister the TVP7002 driver.
- * Returns nothing.
- */
-static void __exit tvp7002_exit(void)
-{
- i2c_del_driver(&tvp7002_driver);
-}
-
-module_init(tvp7002_init);
-module_exit(tvp7002_exit);
+module_i2c_driver(tvp7002_driver);
diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
index a514fa61116c..8768efb8508a 100644
--- a/drivers/media/video/tw9910.c
+++ b/drivers/media/video/tw9910.c
@@ -951,21 +951,7 @@ static struct i2c_driver tw9910_i2c_driver = {
.id_table = tw9910_id,
};
-/*
- * module function
- */
-static int __init tw9910_module_init(void)
-{
- return i2c_add_driver(&tw9910_i2c_driver);
-}
-
-static void __exit tw9910_module_exit(void)
-{
- i2c_del_driver(&tw9910_i2c_driver);
-}
-
-module_init(tw9910_module_init);
-module_exit(tw9910_module_exit);
+module_i2c_driver(tw9910_i2c_driver);
MODULE_DESCRIPTION("SoC Camera driver for tw9910");
MODULE_AUTHOR("Kuninori Morimoto");
diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
index 1aab96a88203..1e7446542091 100644
--- a/drivers/media/video/upd64031a.c
+++ b/drivers/media/video/upd64031a.c
@@ -271,15 +271,4 @@ static struct i2c_driver upd64031a_driver = {
.id_table = upd64031a_id,
};
-static __init int init_upd64031a(void)
-{
- return i2c_add_driver(&upd64031a_driver);
-}
-
-static __exit void exit_upd64031a(void)
-{
- i2c_del_driver(&upd64031a_driver);
-}
-
-module_init(init_upd64031a);
-module_exit(exit_upd64031a);
+module_i2c_driver(upd64031a_driver);
diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
index 65d065aa6091..75d6acc62018 100644
--- a/drivers/media/video/upd64083.c
+++ b/drivers/media/video/upd64083.c
@@ -243,15 +243,4 @@ static struct i2c_driver upd64083_driver = {
.id_table = upd64083_id,
};
-static __init int init_upd64083(void)
-{
- return i2c_add_driver(&upd64083_driver);
-}
-
-static __exit void exit_upd64083(void)
-{
- i2c_del_driver(&upd64083_driver);
-}
-
-module_init(init_upd64083);
-module_exit(exit_upd64083);
+module_i2c_driver(upd64083_driver);
diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
index a240d43d15d1..1d131720b6d7 100644
--- a/drivers/media/video/uvc/uvc_driver.c
+++ b/drivers/media/video/uvc/uvc_driver.c
@@ -23,6 +23,7 @@
* codec can't handle MJPEG data.
*/
+#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
@@ -32,7 +33,6 @@
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/version.h>
-#include <asm/atomic.h>
#include <asm/unaligned.h>
#include <media/v4l2-common.h>
@@ -2139,6 +2139,15 @@ static struct usb_device_id uvc_ids[] = {
.bInterfaceSubClass = 1,
.bInterfaceProtocol = 0,
.driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Dell XPS m1530 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x05a9,
+ .idProduct = 0x2640,
+ .bInterfaceClass = USB_CLASS_VIDEO,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_PROBE_DEF },
/* Apple Built-In iSight */
{ .match_flags = USB_DEVICE_ID_MATCH_DEVICE
| USB_DEVICE_ID_MATCH_INT_INFO,
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 518f77d3a4d8..8f54e24e3f35 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -126,7 +126,7 @@ void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
int drop_corrupted)
{
queue->queue.type = type;
- queue->queue.io_modes = VB2_MMAP;
+ queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
queue->queue.drv_priv = queue;
queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
queue->queue.ops = &uvc_queue_qops;
diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
index 2ae4f880ea05..ff2cdddf9bc6 100644
--- a/drivers/media/video/uvc/uvc_v4l2.c
+++ b/drivers/media/video/uvc/uvc_v4l2.c
@@ -11,6 +11,7 @@
*
*/
+#include <linux/compat.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/list.h>
@@ -1012,7 +1013,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
default:
uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd);
- return -EINVAL;
+ return -ENOTTY;
}
return ret;
@@ -1030,6 +1031,207 @@ static long uvc_v4l2_ioctl(struct file *file,
return video_usercopy(file, cmd, arg, uvc_v4l2_do_ioctl);
}
+#ifdef CONFIG_COMPAT
+struct uvc_xu_control_mapping32 {
+ __u32 id;
+ __u8 name[32];
+ __u8 entity[16];
+ __u8 selector;
+
+ __u8 size;
+ __u8 offset;
+ __u32 v4l2_type;
+ __u32 data_type;
+
+ compat_caddr_t menu_info;
+ __u32 menu_count;
+
+ __u32 reserved[4];
+};
+
+static int uvc_v4l2_get_xu_mapping(struct uvc_xu_control_mapping *kp,
+ const struct uvc_xu_control_mapping32 __user *up)
+{
+ struct uvc_menu_info __user *umenus;
+ struct uvc_menu_info __user *kmenus;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+ __copy_from_user(kp, up, offsetof(typeof(*up), menu_info)) ||
+ __get_user(kp->menu_count, &up->menu_count))
+ return -EFAULT;
+
+ memset(kp->reserved, 0, sizeof(kp->reserved));
+
+ if (kp->menu_count == 0) {
+ kp->menu_info = NULL;
+ return 0;
+ }
+
+ if (__get_user(p, &up->menu_info))
+ return -EFAULT;
+ umenus = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ kmenus = compat_alloc_user_space(kp->menu_count * sizeof(*kmenus));
+ if (kmenus == NULL)
+ return -EFAULT;
+ kp->menu_info = kmenus;
+
+ if (copy_in_user(kmenus, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp,
+ struct uvc_xu_control_mapping32 __user *up)
+{
+ struct uvc_menu_info __user *umenus;
+ struct uvc_menu_info __user *kmenus = kp->menu_info;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __copy_to_user(up, kp, offsetof(typeof(*up), menu_info)) ||
+ __put_user(kp->menu_count, &up->menu_count))
+ return -EFAULT;
+
+ __clear_user(up->reserved, sizeof(up->reserved));
+
+ if (kp->menu_count == 0)
+ return 0;
+
+ if (get_user(p, &up->menu_info))
+ return -EFAULT;
+ umenus = compat_ptr(p);
+ if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus)))
+ return -EFAULT;
+
+ return 0;
+}
+
+struct uvc_xu_control_query32 {
+ __u8 unit;
+ __u8 selector;
+ __u8 query;
+ __u16 size;
+ compat_caddr_t data;
+};
+
+static int uvc_v4l2_get_xu_query(struct uvc_xu_control_query *kp,
+ const struct uvc_xu_control_query32 __user *up)
+{
+ u8 __user *udata;
+ u8 __user *kdata;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_READ, up, sizeof(*up)) ||
+ __copy_from_user(kp, up, offsetof(typeof(*up), data)))
+ return -EFAULT;
+
+ if (kp->size == 0) {
+ kp->data = NULL;
+ return 0;
+ }
+
+ if (__get_user(p, &up->data))
+ return -EFAULT;
+ udata = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, udata, kp->size))
+ return -EFAULT;
+
+ kdata = compat_alloc_user_space(kp->size);
+ if (kdata == NULL)
+ return -EFAULT;
+ kp->data = kdata;
+
+ if (copy_in_user(kdata, udata, kp->size))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uvc_v4l2_put_xu_query(const struct uvc_xu_control_query *kp,
+ struct uvc_xu_control_query32 __user *up)
+{
+ u8 __user *udata;
+ u8 __user *kdata = kp->data;
+ compat_caddr_t p;
+
+ if (!access_ok(VERIFY_WRITE, up, sizeof(*up)) ||
+ __copy_to_user(up, kp, offsetof(typeof(*up), data)))
+ return -EFAULT;
+
+ if (kp->size == 0)
+ return 0;
+
+ if (get_user(p, &up->data))
+ return -EFAULT;
+ udata = compat_ptr(p);
+ if (!access_ok(VERIFY_READ, udata, kp->size))
+ return -EFAULT;
+
+ if (copy_in_user(udata, kdata, kp->size))
+ return -EFAULT;
+
+ return 0;
+}
+
+#define UVCIOC_CTRL_MAP32 _IOWR('u', 0x20, struct uvc_xu_control_mapping32)
+#define UVCIOC_CTRL_QUERY32 _IOWR('u', 0x21, struct uvc_xu_control_query32)
+
+static long uvc_v4l2_compat_ioctl32(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ union {
+ struct uvc_xu_control_mapping xmap;
+ struct uvc_xu_control_query xqry;
+ } karg;
+ void __user *up = compat_ptr(arg);
+ mm_segment_t old_fs;
+ long ret;
+
+ switch (cmd) {
+ case UVCIOC_CTRL_MAP32:
+ cmd = UVCIOC_CTRL_MAP;
+ ret = uvc_v4l2_get_xu_mapping(&karg.xmap, up);
+ break;
+
+ case UVCIOC_CTRL_QUERY32:
+ cmd = UVCIOC_CTRL_QUERY;
+ ret = uvc_v4l2_get_xu_query(&karg.xqry, up);
+ break;
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ ret = uvc_v4l2_ioctl(file, cmd, (unsigned long)&karg);
+ set_fs(old_fs);
+
+ if (ret < 0)
+ return ret;
+
+ switch (cmd) {
+ case UVCIOC_CTRL_MAP:
+ ret = uvc_v4l2_put_xu_mapping(&karg.xmap, up);
+ break;
+
+ case UVCIOC_CTRL_QUERY:
+ ret = uvc_v4l2_put_xu_query(&karg.xqry, up);
+ break;
+ }
+
+ return ret;
+}
+#endif
+
static ssize_t uvc_v4l2_read(struct file *file, char __user *data,
size_t count, loff_t *ppos)
{
@@ -1076,6 +1278,9 @@ const struct v4l2_file_operations uvc_fops = {
.open = uvc_v4l2_open,
.release = uvc_v4l2_release,
.unlocked_ioctl = uvc_v4l2_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl32 = uvc_v4l2_compat_ioctl32,
+#endif
.read = uvc_v4l2_read,
.mmap = uvc_v4l2_mmap,
.poll = uvc_v4l2_poll,
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 5c6100fb4072..1baec8393306 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-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
index af4419e6c658..2829d256e4b7 100644
--- a/drivers/media/video/v4l2-compat-ioctl32.c
+++ b/drivers/media/video/v4l2-compat-ioctl32.c
@@ -14,12 +14,11 @@
*/
#include <linux/compat.h>
-#include <linux/videodev2.h>
#include <linux/module.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
#include <media/v4l2-ioctl.h>
-#ifdef CONFIG_COMPAT
-
static long native_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
long ret = -ENOIOCTLCMD;
@@ -937,6 +936,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar
long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
{
+ struct video_device *vdev = video_devdata(file);
long ret = -ENOIOCTLCMD;
if (!file->f_op->unlocked_ioctl)
@@ -1005,6 +1005,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOC_G_ENC_INDEX:
case VIDIOC_ENCODER_CMD:
case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_DECODER_CMD:
+ case VIDIOC_TRY_DECODER_CMD:
case VIDIOC_DBG_S_REGISTER:
case VIDIOC_DBG_G_REGISTER:
case VIDIOC_DBG_G_CHIP_IDENT:
@@ -1025,14 +1027,16 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
break;
default:
- printk(KERN_WARNING "compat_ioctl32: "
- "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
- _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd), cmd);
+ if (vdev->fops->compat_ioctl32)
+ ret = vdev->fops->compat_ioctl32(file, cmd, arg);
+
+ if (ret == -ENOIOCTLCMD)
+ printk(KERN_WARNING "compat_ioctl32: "
+ "unknown ioctl '%c', dir=%d, #%d (0x%08x)\n",
+ _IOC_TYPE(cmd), _IOC_DIR(cmd), _IOC_NR(cmd),
+ cmd);
break;
}
return ret;
}
EXPORT_SYMBOL_GPL(v4l2_compat_ioctl32);
-#endif
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index cccd42be718a..18015c0a8d31 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -175,6 +175,15 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
"16-bit CRC",
NULL
};
+ static const char * const mpeg_audio_dec_playback[] = {
+ "Auto",
+ "Stereo",
+ "Left",
+ "Right",
+ "Mono",
+ "Swapped Stereo",
+ NULL
+ };
static const char * const mpeg_video_encoding[] = {
"MPEG-1",
"MPEG-2",
@@ -236,8 +245,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
};
static const char * const tune_preemphasis[] = {
"No Preemphasis",
- "50 useconds",
- "75 useconds",
+ "50 Microseconds",
+ "75 Microseconds",
NULL,
};
static const char * const header_mode[] = {
@@ -334,7 +343,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
};
static const char * const mpeg4_profile[] = {
"Simple",
- "Adcanved Simple",
+ "Advanced Simple",
"Core",
"Simple Scalable",
"Advanced Coding Efficency",
@@ -353,6 +362,16 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
NULL,
};
+ static const char * const jpeg_chroma_subsampling[] = {
+ "4:4:4",
+ "4:2:2",
+ "4:2:0",
+ "4:1:1",
+ "4:1:0",
+ "Gray",
+ NULL,
+ };
+
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
return mpeg_audio_sampling_freq;
@@ -374,6 +393,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return mpeg_audio_emphasis;
case V4L2_CID_MPEG_AUDIO_CRC:
return mpeg_audio_crc;
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
+ return mpeg_audio_dec_playback;
case V4L2_CID_MPEG_VIDEO_ENCODING:
return mpeg_video_encoding;
case V4L2_CID_MPEG_VIDEO_ASPECT:
@@ -414,6 +436,9 @@ const char * const *v4l2_ctrl_get_menu(u32 id)
return mpeg_mpeg4_level;
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
return mpeg4_profile;
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
+ return jpeg_chroma_subsampling;
+
default:
return NULL;
}
@@ -492,6 +517,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_AUDIO_MUTE: return "Audio Mute";
case V4L2_CID_MPEG_AUDIO_AAC_BITRATE: return "Audio AAC Bitrate";
case V4L2_CID_MPEG_AUDIO_AC3_BITRATE: return "Audio AC-3 Bitrate";
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK: return "Audio Playback";
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK: return "Audio Multilingual Playback";
case V4L2_CID_MPEG_VIDEO_ENCODING: return "Video Encoding";
case V4L2_CID_MPEG_VIDEO_ASPECT: return "Video Aspect";
case V4L2_CID_MPEG_VIDEO_B_FRAMES: return "Video B Frames";
@@ -546,6 +573,8 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB: return "Number of MBs in a Slice";
case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: return "Slice Partitioning Method";
case V4L2_CID_MPEG_VIDEO_VBV_SIZE: return "VBV Buffer Size";
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS: return "Video Decoder PTS";
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME: return "Video Decoder Frame Count";
/* CAMERA controls */
/* Keep the order of the 'case's the same as in videodev2.h! */
@@ -607,6 +636,14 @@ const char *v4l2_ctrl_get_name(u32 id)
case V4L2_CID_FLASH_CHARGE: return "Charge";
case V4L2_CID_FLASH_READY: return "Ready to Strobe";
+ /* JPEG encoder controls */
+ /* Keep the order of the 'case's the same as in videodev2.h! */
+ case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls";
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling";
+ case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval";
+ case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality";
+ case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers";
+
default:
return NULL;
}
@@ -674,6 +711,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
case V4L2_CID_MPEG_AUDIO_EMPHASIS:
case V4L2_CID_MPEG_AUDIO_CRC:
+ case V4L2_CID_MPEG_AUDIO_DEC_PLAYBACK:
+ case V4L2_CID_MPEG_AUDIO_DEC_MULTILINGUAL_PLAYBACK:
case V4L2_CID_MPEG_VIDEO_ENCODING:
case V4L2_CID_MPEG_VIDEO_ASPECT:
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
@@ -693,6 +732,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_IDC:
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+ case V4L2_CID_JPEG_CHROMA_SUBSAMPLING:
*type = V4L2_CTRL_TYPE_MENU;
break;
case V4L2_CID_RDS_TX_PS_NAME:
@@ -704,6 +744,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
case V4L2_CID_MPEG_CLASS:
case V4L2_CID_FM_TX_CLASS:
case V4L2_CID_FLASH_CLASS:
+ case V4L2_CID_JPEG_CLASS:
*type = V4L2_CTRL_TYPE_CTRL_CLASS;
/* You can neither read not write these */
*flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -717,6 +758,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
*max = 0xFFFFFF;
break;
case V4L2_CID_FLASH_FAULT:
+ case V4L2_CID_JPEG_ACTIVE_MARKER:
*type = V4L2_CTRL_TYPE_BITMASK;
break;
case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE:
@@ -724,6 +766,11 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type,
*type = V4L2_CTRL_TYPE_INTEGER;
*flags |= V4L2_CTRL_FLAG_READ_ONLY;
break;
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ *type = V4L2_CTRL_TYPE_INTEGER64;
+ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE;
+ break;
default:
*type = V4L2_CTRL_TYPE_INTEGER;
break;
@@ -1470,7 +1517,7 @@ EXPORT_SYMBOL(v4l2_ctrl_add_ctrl);
int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
struct v4l2_ctrl_handler *add)
{
- struct v4l2_ctrl *ctrl;
+ struct v4l2_ctrl_ref *ref;
int ret = 0;
/* Do nothing if either handler is NULL or if they are the same */
@@ -1479,7 +1526,9 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl,
if (hdl->error)
return hdl->error;
mutex_lock(&add->lock);
- list_for_each_entry(ctrl, &add->ctrls, node) {
+ list_for_each_entry(ref, &add->ctrl_refs, node) {
+ struct v4l2_ctrl *ctrl = ref->ctrl;
+
/* Skip handler-private controls. */
if (ctrl->is_private)
continue;
@@ -2359,3 +2408,35 @@ void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
v4l2_ctrl_unlock(ctrl);
}
EXPORT_SYMBOL(v4l2_ctrl_del_event);
+
+int v4l2_ctrl_log_status(struct file *file, void *fh)
+{
+ struct video_device *vfd = video_devdata(file);
+ struct v4l2_fh *vfh = file->private_data;
+
+ if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) && vfd->v4l2_dev)
+ v4l2_ctrl_handler_log_status(vfh->ctrl_handler,
+ vfd->v4l2_dev->name);
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_log_status);
+
+int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+{
+ if (sub->type == V4L2_EVENT_CTRL)
+ return v4l2_event_subscribe(fh, sub, 0);
+ return -EINVAL;
+}
+EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
+
+unsigned int v4l2_ctrl_poll(struct file *file, struct poll_table_struct *wait)
+{
+ struct v4l2_fh *fh = file->private_data;
+
+ if (v4l2_event_pending(fh))
+ return POLLPRI;
+ poll_wait(file, &fh->wait, wait);
+ return 0;
+}
+EXPORT_SYMBOL(v4l2_ctrl_poll);
diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
index 96e9615663e9..70bec548d904 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>
@@ -788,7 +787,7 @@ static void __exit videodev_exit(void)
unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);
}
-module_init(videodev_init)
+subsys_initcall(videodev_init);
module_exit(videodev_exit)
MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index 3f623859a337..5b2ec1fd2d0a 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -260,6 +260,8 @@ static const char *v4l2_ioctls[] = {
[_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD",
[_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD",
+ [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD",
+ [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD",
[_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER",
[_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER",
@@ -540,10 +542,12 @@ static long __video_do_ioctl(struct file *file,
if (!ret)
dbgarg(cmd, "driver=%s, card=%s, bus=%s, "
"version=0x%08x, "
- "capabilities=0x%08x\n",
+ "capabilities=0x%08x, "
+ "device_caps=0x%08x\n",
cap->driver, cap->card, cap->bus_info,
cap->version,
- cap->capabilities);
+ cap->capabilities,
+ cap->device_caps);
break;
}
@@ -1762,6 +1766,32 @@ static long __video_do_ioctl(struct file *file,
dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
break;
}
+ case VIDIOC_DECODER_CMD:
+ {
+ struct v4l2_decoder_cmd *p = arg;
+
+ if (!ops->vidioc_decoder_cmd)
+ break;
+ if (ret_prio) {
+ ret = ret_prio;
+ break;
+ }
+ ret = ops->vidioc_decoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+ break;
+ }
+ case VIDIOC_TRY_DECODER_CMD:
+ {
+ struct v4l2_decoder_cmd *p = arg;
+
+ if (!ops->vidioc_try_decoder_cmd)
+ break;
+ ret = ops->vidioc_try_decoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+ break;
+ }
case VIDIOC_G_PARM:
{
struct v4l2_streamparm *p = arg;
@@ -1909,7 +1939,13 @@ static long __video_do_ioctl(struct file *file,
{
if (!ops->vidioc_log_status)
break;
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================= START STATUS =================\n",
+ vfd->v4l2_dev->name);
ret = ops->vidioc_log_status(file, fh);
+ if (vfd->v4l2_dev)
+ pr_info("%s: ================== END STATUS ==================\n",
+ vfd->v4l2_dev->name);
break;
}
#ifdef CONFIG_VIDEO_ADV_DEBUG
@@ -2419,7 +2455,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
/* Handles IOCTL */
err = func(file, cmd, parg);
if (err == -ENOIOCTLCMD)
- err = -EINVAL;
+ err = -ENOTTY;
if (has_array_args) {
*kernel_ptr = user_ptr;
diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
index 41d118ee2de6..6fe88e965a8c 100644
--- a/drivers/media/video/v4l2-subdev.c
+++ b/drivers/media/video/v4l2-subdev.c
@@ -194,8 +194,16 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg)
}
#endif
- case VIDIOC_LOG_STATUS:
- return v4l2_subdev_call(sd, core, log_status);
+ case VIDIOC_LOG_STATUS: {
+ int ret;
+
+ pr_info("%s: ================= START STATUS =================\n",
+ sd->name);
+ ret = v4l2_subdev_call(sd, core, log_status);
+ pr_info("%s: ================== END STATUS ==================\n",
+ sd->name);
+ return ret;
+ }
#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API)
case VIDIOC_SUBDEV_G_FMT: {
diff --git a/drivers/media/video/videobuf2-vmalloc.c b/drivers/media/video/videobuf2-vmalloc.c
index 4e789a178f8a..6b5ca6c70a46 100644
--- a/drivers/media/video/videobuf2-vmalloc.c
+++ b/drivers/media/video/videobuf2-vmalloc.c
@@ -10,6 +10,7 @@
* the Free Software Foundation.
*/
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/sched.h>
@@ -22,6 +23,7 @@
struct vb2_vmalloc_buf {
void *vaddr;
struct page **pages;
+ struct vm_area_struct *vma;
int write;
unsigned long size;
unsigned int n_pages;
@@ -71,6 +73,8 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
struct vb2_vmalloc_buf *buf;
unsigned long first, last;
int n_pages, offset;
+ struct vm_area_struct *vma;
+ dma_addr_t physp;
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
if (!buf)
@@ -80,23 +84,37 @@ static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
offset = vaddr & ~PAGE_MASK;
buf->size = size;
- first = vaddr >> PAGE_SHIFT;
- last = (vaddr + size - 1) >> PAGE_SHIFT;
- buf->n_pages = last - first + 1;
- buf->pages = kzalloc(buf->n_pages * sizeof(struct page *), GFP_KERNEL);
- if (!buf->pages)
- goto fail_pages_array_alloc;
- /* current->mm->mmap_sem is taken by videobuf2 core */
- n_pages = get_user_pages(current, current->mm, vaddr & PAGE_MASK,
- buf->n_pages, write, 1, /* force */
- buf->pages, NULL);
- if (n_pages != buf->n_pages)
- goto fail_get_user_pages;
-
- buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1, PAGE_KERNEL);
- if (!buf->vaddr)
- goto fail_get_user_pages;
+ vma = find_vma(current->mm, vaddr);
+ if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
+ if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
+ goto fail_pages_array_alloc;
+ buf->vma = vma;
+ buf->vaddr = ioremap_nocache(physp, size);
+ if (!buf->vaddr)
+ goto fail_pages_array_alloc;
+ } else {
+ first = vaddr >> PAGE_SHIFT;
+ last = (vaddr + size - 1) >> PAGE_SHIFT;
+ buf->n_pages = last - first + 1;
+ buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
+ GFP_KERNEL);
+ if (!buf->pages)
+ goto fail_pages_array_alloc;
+
+ /* current->mm->mmap_sem is taken by videobuf2 core */
+ n_pages = get_user_pages(current, current->mm,
+ vaddr & PAGE_MASK, buf->n_pages,
+ write, 1, /* force */
+ buf->pages, NULL);
+ if (n_pages != buf->n_pages)
+ goto fail_get_user_pages;
+
+ buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
+ PAGE_KERNEL);
+ if (!buf->vaddr)
+ goto fail_get_user_pages;
+ }
buf->vaddr += offset;
return buf;
@@ -120,14 +138,20 @@ static void vb2_vmalloc_put_userptr(void *buf_priv)
unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
unsigned int i;
- if (vaddr)
- vm_unmap_ram((void *)vaddr, buf->n_pages);
- for (i = 0; i < buf->n_pages; ++i) {
- if (buf->write)
- set_page_dirty_lock(buf->pages[i]);
- put_page(buf->pages[i]);
+ if (buf->pages) {
+ if (vaddr)
+ vm_unmap_ram((void *)vaddr, buf->n_pages);
+ for (i = 0; i < buf->n_pages; ++i) {
+ if (buf->write)
+ set_page_dirty_lock(buf->pages[i]);
+ put_page(buf->pages[i]);
+ }
+ kfree(buf->pages);
+ } else {
+ if (buf->vma)
+ vb2_put_vma(buf->vma);
+ iounmap(buf->vaddr);
}
- kfree(buf->pages);
kfree(buf);
}
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 7d754fbcccbf..5e8b0710105b 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -819,8 +819,9 @@ static int vidioc_querycap(struct file *file, void *priv,
strcpy(cap->driver, "vivi");
strcpy(cap->card, "vivi");
strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | \
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING |
V4L2_CAP_READWRITE;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
@@ -958,14 +959,6 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
return vb2_streamoff(&dev->vb_vidq, i);
}
-static int vidioc_log_status(struct file *file, void *priv)
-{
- struct vivi_dev *dev = video_drvdata(file);
-
- v4l2_ctrl_handler_log_status(&dev->ctrl_handler, dev->v4l2_dev.name);
- return 0;
-}
-
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
return 0;
@@ -1008,17 +1001,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
return 0;
}
-static int vidioc_subscribe_event(struct v4l2_fh *fh,
- struct v4l2_event_subscription *sub)
-{
- switch (sub->type) {
- case V4L2_EVENT_CTRL:
- return v4l2_event_subscribe(fh, sub, 0);
- default:
- return -EINVAL;
- }
-}
-
/* --- controls ---------------------------------------------- */
static int vivi_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
@@ -1209,8 +1191,8 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
.vidioc_s_input = vidioc_s_input,
.vidioc_streamon = vidioc_streamon,
.vidioc_streamoff = vidioc_streamoff,
- .vidioc_log_status = vidioc_log_status,
- .vidioc_subscribe_event = vidioc_subscribe_event,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
};
diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
index c15efb6e7771..7cfbc9d94a48 100644
--- a/drivers/media/video/vp27smpx.c
+++ b/drivers/media/video/vp27smpx.c
@@ -208,15 +208,4 @@ static struct i2c_driver vp27smpx_driver = {
.id_table = vp27smpx_id,
};
-static __init int init_vp27smpx(void)
-{
- return i2c_add_driver(&vp27smpx_driver);
-}
-
-static __exit void exit_vp27smpx(void)
-{
- i2c_del_driver(&vp27smpx_driver);
-}
-
-module_init(init_vp27smpx);
-module_exit(exit_vp27smpx);
+module_i2c_driver(vp27smpx_driver);
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index e5cad6ff64a1..2f67b4c5c823 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -588,15 +588,4 @@ static struct i2c_driver vpx3220_driver = {
.id_table = vpx3220_id,
};
-static __init int init_vpx3220(void)
-{
- return i2c_add_driver(&vpx3220_driver);
-}
-
-static __exit void exit_vpx3220(void)
-{
- i2c_del_driver(&vpx3220_driver);
-}
-
-module_init(init_vpx3220);
-module_exit(exit_vpx3220);
+module_i2c_driver(vpx3220_driver);
diff --git a/drivers/media/video/vs6624.c b/drivers/media/video/vs6624.c
new file mode 100644
index 000000000000..42ae9dc9c574
--- /dev/null
+++ b/drivers/media/video/vs6624.c
@@ -0,0 +1,928 @@
+/*
+ * vs6624.c ST VS6624 CMOS image sensor driver
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-chip-ident.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-mediabus.h>
+
+#include "vs6624_regs.h"
+
+#define VGA_WIDTH 640
+#define VGA_HEIGHT 480
+#define QVGA_WIDTH 320
+#define QVGA_HEIGHT 240
+#define QQVGA_WIDTH 160
+#define QQVGA_HEIGHT 120
+#define CIF_WIDTH 352
+#define CIF_HEIGHT 288
+#define QCIF_WIDTH 176
+#define QCIF_HEIGHT 144
+#define QQCIF_WIDTH 88
+#define QQCIF_HEIGHT 72
+
+#define MAX_FRAME_RATE 30
+
+struct vs6624 {
+ struct v4l2_subdev sd;
+ struct v4l2_ctrl_handler hdl;
+ struct v4l2_fract frame_rate;
+ struct v4l2_mbus_framefmt fmt;
+ unsigned ce_pin;
+};
+
+static const struct vs6624_format {
+ enum v4l2_mbus_pixelcode mbus_code;
+ enum v4l2_colorspace colorspace;
+} vs6624_formats[] = {
+ {
+ .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ },
+ {
+ .mbus_code = V4L2_MBUS_FMT_RGB565_2X8_LE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ },
+};
+
+static struct v4l2_mbus_framefmt vs6624_default_fmt = {
+ .width = VGA_WIDTH,
+ .height = VGA_HEIGHT,
+ .code = V4L2_MBUS_FMT_UYVY8_2X8,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+};
+
+static const u16 vs6624_p1[] = {
+ 0x8104, 0x03,
+ 0x8105, 0x01,
+ 0xc900, 0x03,
+ 0xc904, 0x47,
+ 0xc905, 0x10,
+ 0xc906, 0x80,
+ 0xc907, 0x3a,
+ 0x903a, 0x02,
+ 0x903b, 0x47,
+ 0x903c, 0x15,
+ 0xc908, 0x31,
+ 0xc909, 0xdc,
+ 0xc90a, 0x80,
+ 0xc90b, 0x44,
+ 0x9044, 0x02,
+ 0x9045, 0x31,
+ 0x9046, 0xe2,
+ 0xc90c, 0x07,
+ 0xc90d, 0xe0,
+ 0xc90e, 0x80,
+ 0xc90f, 0x47,
+ 0x9047, 0x90,
+ 0x9048, 0x83,
+ 0x9049, 0x81,
+ 0x904a, 0xe0,
+ 0x904b, 0x60,
+ 0x904c, 0x08,
+ 0x904d, 0x90,
+ 0x904e, 0xc0,
+ 0x904f, 0x43,
+ 0x9050, 0x74,
+ 0x9051, 0x01,
+ 0x9052, 0xf0,
+ 0x9053, 0x80,
+ 0x9054, 0x05,
+ 0x9055, 0xE4,
+ 0x9056, 0x90,
+ 0x9057, 0xc0,
+ 0x9058, 0x43,
+ 0x9059, 0xf0,
+ 0x905a, 0x02,
+ 0x905b, 0x07,
+ 0x905c, 0xec,
+ 0xc910, 0x5d,
+ 0xc911, 0xca,
+ 0xc912, 0x80,
+ 0xc913, 0x5d,
+ 0x905d, 0xa3,
+ 0x905e, 0x04,
+ 0x905f, 0xf0,
+ 0x9060, 0xa3,
+ 0x9061, 0x04,
+ 0x9062, 0xf0,
+ 0x9063, 0x22,
+ 0xc914, 0x72,
+ 0xc915, 0x92,
+ 0xc916, 0x80,
+ 0xc917, 0x64,
+ 0x9064, 0x74,
+ 0x9065, 0x01,
+ 0x9066, 0x02,
+ 0x9067, 0x72,
+ 0x9068, 0x95,
+ 0xc918, 0x47,
+ 0xc919, 0xf2,
+ 0xc91a, 0x81,
+ 0xc91b, 0x69,
+ 0x9169, 0x74,
+ 0x916a, 0x02,
+ 0x916b, 0xf0,
+ 0x916c, 0xec,
+ 0x916d, 0xb4,
+ 0x916e, 0x10,
+ 0x916f, 0x0a,
+ 0x9170, 0x90,
+ 0x9171, 0x80,
+ 0x9172, 0x16,
+ 0x9173, 0xe0,
+ 0x9174, 0x70,
+ 0x9175, 0x04,
+ 0x9176, 0x90,
+ 0x9177, 0xd3,
+ 0x9178, 0xc4,
+ 0x9179, 0xf0,
+ 0x917a, 0x22,
+ 0xc91c, 0x0a,
+ 0xc91d, 0xbe,
+ 0xc91e, 0x80,
+ 0xc91f, 0x73,
+ 0x9073, 0xfc,
+ 0x9074, 0xa3,
+ 0x9075, 0xe0,
+ 0x9076, 0xf5,
+ 0x9077, 0x82,
+ 0x9078, 0x8c,
+ 0x9079, 0x83,
+ 0x907a, 0xa3,
+ 0x907b, 0xa3,
+ 0x907c, 0xe0,
+ 0x907d, 0xfc,
+ 0x907e, 0xa3,
+ 0x907f, 0xe0,
+ 0x9080, 0xc3,
+ 0x9081, 0x9f,
+ 0x9082, 0xff,
+ 0x9083, 0xec,
+ 0x9084, 0x9e,
+ 0x9085, 0xfe,
+ 0x9086, 0x02,
+ 0x9087, 0x0a,
+ 0x9088, 0xea,
+ 0xc920, 0x47,
+ 0xc921, 0x38,
+ 0xc922, 0x80,
+ 0xc923, 0x89,
+ 0x9089, 0xec,
+ 0x908a, 0xd3,
+ 0x908b, 0x94,
+ 0x908c, 0x20,
+ 0x908d, 0x40,
+ 0x908e, 0x01,
+ 0x908f, 0x1c,
+ 0x9090, 0x90,
+ 0x9091, 0xd3,
+ 0x9092, 0xd4,
+ 0x9093, 0xec,
+ 0x9094, 0xf0,
+ 0x9095, 0x02,
+ 0x9096, 0x47,
+ 0x9097, 0x3d,
+ 0xc924, 0x45,
+ 0xc925, 0xca,
+ 0xc926, 0x80,
+ 0xc927, 0x98,
+ 0x9098, 0x12,
+ 0x9099, 0x77,
+ 0x909a, 0xd6,
+ 0x909b, 0x02,
+ 0x909c, 0x45,
+ 0x909d, 0xcd,
+ 0xc928, 0x20,
+ 0xc929, 0xd5,
+ 0xc92a, 0x80,
+ 0xc92b, 0x9e,
+ 0x909e, 0x90,
+ 0x909f, 0x82,
+ 0x90a0, 0x18,
+ 0x90a1, 0xe0,
+ 0x90a2, 0xb4,
+ 0x90a3, 0x03,
+ 0x90a4, 0x0e,
+ 0x90a5, 0x90,
+ 0x90a6, 0x83,
+ 0x90a7, 0xbf,
+ 0x90a8, 0xe0,
+ 0x90a9, 0x60,
+ 0x90aa, 0x08,
+ 0x90ab, 0x90,
+ 0x90ac, 0x81,
+ 0x90ad, 0xfc,
+ 0x90ae, 0xe0,
+ 0x90af, 0xff,
+ 0x90b0, 0xc3,
+ 0x90b1, 0x13,
+ 0x90b2, 0xf0,
+ 0x90b3, 0x90,
+ 0x90b4, 0x81,
+ 0x90b5, 0xfc,
+ 0x90b6, 0xe0,
+ 0x90b7, 0xff,
+ 0x90b8, 0x02,
+ 0x90b9, 0x20,
+ 0x90ba, 0xda,
+ 0xc92c, 0x70,
+ 0xc92d, 0xbc,
+ 0xc92e, 0x80,
+ 0xc92f, 0xbb,
+ 0x90bb, 0x90,
+ 0x90bc, 0x82,
+ 0x90bd, 0x18,
+ 0x90be, 0xe0,
+ 0x90bf, 0xb4,
+ 0x90c0, 0x03,
+ 0x90c1, 0x06,
+ 0x90c2, 0x90,
+ 0x90c3, 0xc1,
+ 0x90c4, 0x06,
+ 0x90c5, 0x74,
+ 0x90c6, 0x05,
+ 0x90c7, 0xf0,
+ 0x90c8, 0x90,
+ 0x90c9, 0xd3,
+ 0x90ca, 0xa0,
+ 0x90cb, 0x02,
+ 0x90cc, 0x70,
+ 0x90cd, 0xbf,
+ 0xc930, 0x72,
+ 0xc931, 0x21,
+ 0xc932, 0x81,
+ 0xc933, 0x3b,
+ 0x913b, 0x7d,
+ 0x913c, 0x02,
+ 0x913d, 0x7f,
+ 0x913e, 0x7b,
+ 0x913f, 0x02,
+ 0x9140, 0x72,
+ 0x9141, 0x25,
+ 0xc934, 0x28,
+ 0xc935, 0xae,
+ 0xc936, 0x80,
+ 0xc937, 0xd2,
+ 0x90d2, 0xf0,
+ 0x90d3, 0x90,
+ 0x90d4, 0xd2,
+ 0x90d5, 0x0a,
+ 0x90d6, 0x02,
+ 0x90d7, 0x28,
+ 0x90d8, 0xb4,
+ 0xc938, 0x28,
+ 0xc939, 0xb1,
+ 0xc93a, 0x80,
+ 0xc93b, 0xd9,
+ 0x90d9, 0x90,
+ 0x90da, 0x83,
+ 0x90db, 0xba,
+ 0x90dc, 0xe0,
+ 0x90dd, 0xff,
+ 0x90de, 0x90,
+ 0x90df, 0xd2,
+ 0x90e0, 0x08,
+ 0x90e1, 0xe0,
+ 0x90e2, 0xe4,
+ 0x90e3, 0xef,
+ 0x90e4, 0xf0,
+ 0x90e5, 0xa3,
+ 0x90e6, 0xe0,
+ 0x90e7, 0x74,
+ 0x90e8, 0xff,
+ 0x90e9, 0xf0,
+ 0x90ea, 0x90,
+ 0x90eb, 0xd2,
+ 0x90ec, 0x0a,
+ 0x90ed, 0x02,
+ 0x90ee, 0x28,
+ 0x90ef, 0xb4,
+ 0xc93c, 0x29,
+ 0xc93d, 0x79,
+ 0xc93e, 0x80,
+ 0xc93f, 0xf0,
+ 0x90f0, 0xf0,
+ 0x90f1, 0x90,
+ 0x90f2, 0xd2,
+ 0x90f3, 0x0e,
+ 0x90f4, 0x02,
+ 0x90f5, 0x29,
+ 0x90f6, 0x7f,
+ 0xc940, 0x29,
+ 0xc941, 0x7c,
+ 0xc942, 0x80,
+ 0xc943, 0xf7,
+ 0x90f7, 0x90,
+ 0x90f8, 0x83,
+ 0x90f9, 0xba,
+ 0x90fa, 0xe0,
+ 0x90fb, 0xff,
+ 0x90fc, 0x90,
+ 0x90fd, 0xd2,
+ 0x90fe, 0x0c,
+ 0x90ff, 0xe0,
+ 0x9100, 0xe4,
+ 0x9101, 0xef,
+ 0x9102, 0xf0,
+ 0x9103, 0xa3,
+ 0x9104, 0xe0,
+ 0x9105, 0x74,
+ 0x9106, 0xff,
+ 0x9107, 0xf0,
+ 0x9108, 0x90,
+ 0x9109, 0xd2,
+ 0x910a, 0x0e,
+ 0x910b, 0x02,
+ 0x910c, 0x29,
+ 0x910d, 0x7f,
+ 0xc944, 0x2a,
+ 0xc945, 0x42,
+ 0xc946, 0x81,
+ 0xc947, 0x0e,
+ 0x910e, 0xf0,
+ 0x910f, 0x90,
+ 0x9110, 0xd2,
+ 0x9111, 0x12,
+ 0x9112, 0x02,
+ 0x9113, 0x2a,
+ 0x9114, 0x48,
+ 0xc948, 0x2a,
+ 0xc949, 0x45,
+ 0xc94a, 0x81,
+ 0xc94b, 0x15,
+ 0x9115, 0x90,
+ 0x9116, 0x83,
+ 0x9117, 0xba,
+ 0x9118, 0xe0,
+ 0x9119, 0xff,
+ 0x911a, 0x90,
+ 0x911b, 0xd2,
+ 0x911c, 0x10,
+ 0x911d, 0xe0,
+ 0x911e, 0xe4,
+ 0x911f, 0xef,
+ 0x9120, 0xf0,
+ 0x9121, 0xa3,
+ 0x9122, 0xe0,
+ 0x9123, 0x74,
+ 0x9124, 0xff,
+ 0x9125, 0xf0,
+ 0x9126, 0x90,
+ 0x9127, 0xd2,
+ 0x9128, 0x12,
+ 0x9129, 0x02,
+ 0x912a, 0x2a,
+ 0x912b, 0x48,
+ 0xc900, 0x01,
+ 0x0000, 0x00,
+};
+
+static const u16 vs6624_p2[] = {
+ 0x806f, 0x01,
+ 0x058c, 0x01,
+ 0x0000, 0x00,
+};
+
+static const u16 vs6624_run_setup[] = {
+ 0x1d18, 0x00, /* Enableconstrainedwhitebalance */
+ VS6624_PEAK_MIN_OUT_G_MSB, 0x3c, /* Damper PeakGain Output MSB */
+ VS6624_PEAK_MIN_OUT_G_LSB, 0x66, /* Damper PeakGain Output LSB */
+ VS6624_CM_LOW_THR_MSB, 0x65, /* Damper Low MSB */
+ VS6624_CM_LOW_THR_LSB, 0xd1, /* Damper Low LSB */
+ VS6624_CM_HIGH_THR_MSB, 0x66, /* Damper High MSB */
+ VS6624_CM_HIGH_THR_LSB, 0x62, /* Damper High LSB */
+ VS6624_CM_MIN_OUT_MSB, 0x00, /* Damper Min output MSB */
+ VS6624_CM_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */
+ VS6624_NORA_DISABLE, 0x00, /* Nora fDisable */
+ VS6624_NORA_USAGE, 0x04, /* Nora usage */
+ VS6624_NORA_LOW_THR_MSB, 0x63, /* Damper Low MSB Changed 0x63 to 0x65 */
+ VS6624_NORA_LOW_THR_LSB, 0xd1, /* Damper Low LSB */
+ VS6624_NORA_HIGH_THR_MSB, 0x68, /* Damper High MSB */
+ VS6624_NORA_HIGH_THR_LSB, 0xdd, /* Damper High LSB */
+ VS6624_NORA_MIN_OUT_MSB, 0x3a, /* Damper Min output MSB */
+ VS6624_NORA_MIN_OUT_LSB, 0x00, /* Damper Min output LSB */
+ VS6624_F2B_DISABLE, 0x00, /* Disable */
+ 0x1d8a, 0x30, /* MAXWeightHigh */
+ 0x1d91, 0x62, /* fpDamperLowThresholdHigh MSB */
+ 0x1d92, 0x4a, /* fpDamperLowThresholdHigh LSB */
+ 0x1d95, 0x65, /* fpDamperHighThresholdHigh MSB */
+ 0x1d96, 0x0e, /* fpDamperHighThresholdHigh LSB */
+ 0x1da1, 0x3a, /* fpMinimumDamperOutputLow MSB */
+ 0x1da2, 0xb8, /* fpMinimumDamperOutputLow LSB */
+ 0x1e08, 0x06, /* MAXWeightLow */
+ 0x1e0a, 0x0a, /* MAXWeightHigh */
+ 0x1601, 0x3a, /* Red A MSB */
+ 0x1602, 0x14, /* Red A LSB */
+ 0x1605, 0x3b, /* Blue A MSB */
+ 0x1606, 0x85, /* BLue A LSB */
+ 0x1609, 0x3b, /* RED B MSB */
+ 0x160a, 0x85, /* RED B LSB */
+ 0x160d, 0x3a, /* Blue B MSB */
+ 0x160e, 0x14, /* Blue B LSB */
+ 0x1611, 0x30, /* Max Distance from Locus MSB */
+ 0x1612, 0x8f, /* Max Distance from Locus MSB */
+ 0x1614, 0x01, /* Enable constrainer */
+ 0x0000, 0x00,
+};
+
+static const u16 vs6624_default[] = {
+ VS6624_CONTRAST0, 0x84,
+ VS6624_SATURATION0, 0x75,
+ VS6624_GAMMA0, 0x11,
+ VS6624_CONTRAST1, 0x84,
+ VS6624_SATURATION1, 0x75,
+ VS6624_GAMMA1, 0x11,
+ VS6624_MAN_RG, 0x80,
+ VS6624_MAN_GG, 0x80,
+ VS6624_MAN_BG, 0x80,
+ VS6624_WB_MODE, 0x1,
+ VS6624_EXPO_COMPENSATION, 0xfe,
+ VS6624_EXPO_METER, 0x0,
+ VS6624_LIGHT_FREQ, 0x64,
+ VS6624_PEAK_GAIN, 0xe,
+ VS6624_PEAK_LOW_THR, 0x28,
+ VS6624_HMIRROR0, 0x0,
+ VS6624_VFLIP0, 0x0,
+ VS6624_ZOOM_HSTEP0_MSB, 0x0,
+ VS6624_ZOOM_HSTEP0_LSB, 0x1,
+ VS6624_ZOOM_VSTEP0_MSB, 0x0,
+ VS6624_ZOOM_VSTEP0_LSB, 0x1,
+ VS6624_PAN_HSTEP0_MSB, 0x0,
+ VS6624_PAN_HSTEP0_LSB, 0xf,
+ VS6624_PAN_VSTEP0_MSB, 0x0,
+ VS6624_PAN_VSTEP0_LSB, 0xf,
+ VS6624_SENSOR_MODE, 0x1,
+ VS6624_SYNC_CODE_SETUP, 0x21,
+ VS6624_DISABLE_FR_DAMPER, 0x0,
+ VS6624_FR_DEN, 0x1,
+ VS6624_FR_NUM_LSB, 0xf,
+ VS6624_INIT_PIPE_SETUP, 0x0,
+ VS6624_IMG_FMT0, 0x0,
+ VS6624_YUV_SETUP, 0x1,
+ VS6624_IMAGE_SIZE0, 0x2,
+ 0x0000, 0x00,
+};
+
+static inline struct vs6624 *to_vs6624(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct vs6624, sd);
+}
+static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
+{
+ return &container_of(ctrl->handler, struct vs6624, hdl)->sd;
+}
+
+static int vs6624_read(struct v4l2_subdev *sd, u16 index)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[2];
+
+ buf[0] = index >> 8;
+ buf[1] = index;
+ i2c_master_send(client, buf, 2);
+ i2c_master_recv(client, buf, 1);
+
+ return buf[0];
+}
+
+static int vs6624_write(struct v4l2_subdev *sd, u16 index,
+ u8 value)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 buf[3];
+
+ buf[0] = index >> 8;
+ buf[1] = index;
+ buf[2] = value;
+
+ return i2c_master_send(client, buf, 3);
+}
+
+static int vs6624_writeregs(struct v4l2_subdev *sd, const u16 *regs)
+{
+ u16 reg;
+ u8 data;
+
+ while (*regs != 0x00) {
+ reg = *regs++;
+ data = *regs++;
+
+ vs6624_write(sd, reg, data);
+ }
+ return 0;
+}
+
+static int vs6624_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct v4l2_subdev *sd = to_sd(ctrl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_CONTRAST:
+ vs6624_write(sd, VS6624_CONTRAST0, ctrl->val);
+ break;
+ case V4L2_CID_SATURATION:
+ vs6624_write(sd, VS6624_SATURATION0, ctrl->val);
+ break;
+ case V4L2_CID_HFLIP:
+ vs6624_write(sd, VS6624_HMIRROR0, ctrl->val);
+ break;
+ case V4L2_CID_VFLIP:
+ vs6624_write(sd, VS6624_VFLIP0, ctrl->val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int vs6624_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index,
+ enum v4l2_mbus_pixelcode *code)
+{
+ if (index >= ARRAY_SIZE(vs6624_formats))
+ return -EINVAL;
+
+ *code = vs6624_formats[index].mbus_code;
+ return 0;
+}
+
+static int vs6624_try_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ int index;
+
+ for (index = 0; index < ARRAY_SIZE(vs6624_formats); index++)
+ if (vs6624_formats[index].mbus_code == fmt->code)
+ break;
+ if (index >= ARRAY_SIZE(vs6624_formats)) {
+ /* default to first format */
+ index = 0;
+ fmt->code = vs6624_formats[0].mbus_code;
+ }
+
+ /* sensor mode is VGA */
+ if (fmt->width > VGA_WIDTH)
+ fmt->width = VGA_WIDTH;
+ if (fmt->height > VGA_HEIGHT)
+ fmt->height = VGA_HEIGHT;
+ fmt->width = fmt->width & (~3);
+ fmt->height = fmt->height & (~3);
+ fmt->field = V4L2_FIELD_NONE;
+ fmt->colorspace = vs6624_formats[index].colorspace;
+ return 0;
+}
+
+static int vs6624_s_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+ int ret;
+
+ ret = vs6624_try_mbus_fmt(sd, fmt);
+ if (ret)
+ return ret;
+
+ /* set image format */
+ switch (fmt->code) {
+ case V4L2_MBUS_FMT_UYVY8_2X8:
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
+ vs6624_write(sd, VS6624_YUV_SETUP, 0x1);
+ break;
+ case V4L2_MBUS_FMT_YUYV8_2X8:
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x0);
+ vs6624_write(sd, VS6624_YUV_SETUP, 0x3);
+ break;
+ case V4L2_MBUS_FMT_RGB565_2X8_LE:
+ vs6624_write(sd, VS6624_IMG_FMT0, 0x4);
+ vs6624_write(sd, VS6624_RGB_SETUP, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set image size */
+ if ((fmt->width == VGA_WIDTH) && (fmt->height == VGA_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x2);
+ else if ((fmt->width == QVGA_WIDTH) && (fmt->height == QVGA_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x4);
+ else if ((fmt->width == QQVGA_WIDTH) && (fmt->height == QQVGA_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x6);
+ else if ((fmt->width == CIF_WIDTH) && (fmt->height == CIF_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x3);
+ else if ((fmt->width == QCIF_WIDTH) && (fmt->height == QCIF_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x5);
+ else if ((fmt->width == QQCIF_WIDTH) && (fmt->height == QQCIF_HEIGHT))
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x7);
+ else {
+ vs6624_write(sd, VS6624_IMAGE_SIZE0, 0x8);
+ vs6624_write(sd, VS6624_MAN_HSIZE0_MSB, fmt->width >> 8);
+ vs6624_write(sd, VS6624_MAN_HSIZE0_LSB, fmt->width & 0xFF);
+ vs6624_write(sd, VS6624_MAN_VSIZE0_MSB, fmt->height >> 8);
+ vs6624_write(sd, VS6624_MAN_VSIZE0_LSB, fmt->height & 0xFF);
+ vs6624_write(sd, VS6624_CROP_CTRL0, 0x1);
+ }
+
+ sensor->fmt = *fmt;
+
+ return 0;
+}
+
+static int vs6624_g_mbus_fmt(struct v4l2_subdev *sd,
+ struct v4l2_mbus_framefmt *fmt)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+
+ *fmt = sensor->fmt;
+ return 0;
+}
+
+static int vs6624_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ memset(cp, 0, sizeof(*cp));
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = sensor->frame_rate.denominator;
+ cp->timeperframe.denominator = sensor->frame_rate.numerator;
+ return 0;
+}
+
+static int vs6624_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+{
+ struct vs6624 *sensor = to_vs6624(sd);
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (cp->extendedmode != 0)
+ return -EINVAL;
+
+ if (tpf->numerator == 0 || tpf->denominator == 0
+ || (tpf->denominator > tpf->numerator * MAX_FRAME_RATE)) {
+ /* reset to max frame rate */
+ tpf->numerator = 1;
+ tpf->denominator = MAX_FRAME_RATE;
+ }
+ sensor->frame_rate.numerator = tpf->denominator;
+ sensor->frame_rate.denominator = tpf->numerator;
+ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
+ vs6624_write(sd, VS6624_FR_NUM_MSB,
+ sensor->frame_rate.numerator >> 8);
+ vs6624_write(sd, VS6624_FR_NUM_LSB,
+ sensor->frame_rate.numerator & 0xFF);
+ vs6624_write(sd, VS6624_FR_DEN,
+ sensor->frame_rate.denominator & 0xFF);
+ return 0;
+}
+
+static int vs6624_s_stream(struct v4l2_subdev *sd, int enable)
+{
+ if (enable)
+ vs6624_write(sd, VS6624_USER_CMD, 0x2);
+ else
+ vs6624_write(sd, VS6624_USER_CMD, 0x4);
+ udelay(100);
+ return 0;
+}
+
+static int vs6624_g_chip_ident(struct v4l2_subdev *sd,
+ struct v4l2_dbg_chip_ident *chip)
+{
+ int rev;
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ rev = (vs6624_read(sd, VS6624_FW_VSN_MAJOR) << 8)
+ | vs6624_read(sd, VS6624_FW_VSN_MINOR);
+
+ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_VS6624, rev);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vs6624_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ reg->val = vs6624_read(sd, reg->reg & 0xffff);
+ reg->size = 1;
+ return 0;
+}
+
+static int vs6624_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+{
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ vs6624_write(sd, reg->reg & 0xffff, reg->val & 0xff);
+ return 0;
+}
+#endif
+
+static const struct v4l2_ctrl_ops vs6624_ctrl_ops = {
+ .s_ctrl = vs6624_s_ctrl,
+};
+
+static const struct v4l2_subdev_core_ops vs6624_core_ops = {
+ .g_chip_ident = vs6624_g_chip_ident,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .g_register = vs6624_g_register,
+ .s_register = vs6624_s_register,
+#endif
+};
+
+static const struct v4l2_subdev_video_ops vs6624_video_ops = {
+ .enum_mbus_fmt = vs6624_enum_mbus_fmt,
+ .try_mbus_fmt = vs6624_try_mbus_fmt,
+ .s_mbus_fmt = vs6624_s_mbus_fmt,
+ .g_mbus_fmt = vs6624_g_mbus_fmt,
+ .s_parm = vs6624_s_parm,
+ .g_parm = vs6624_g_parm,
+ .s_stream = vs6624_s_stream,
+};
+
+static const struct v4l2_subdev_ops vs6624_ops = {
+ .core = &vs6624_core_ops,
+ .video = &vs6624_video_ops,
+};
+
+static int __devinit vs6624_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct vs6624 *sensor;
+ struct v4l2_subdev *sd;
+ struct v4l2_ctrl_handler *hdl;
+ const unsigned *ce;
+ int ret;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ ce = client->dev.platform_data;
+ if (ce == NULL)
+ return -EINVAL;
+
+ ret = gpio_request(*ce, "VS6624 Chip Enable");
+ if (ret) {
+ v4l_err(client, "failed to request GPIO %d\n", *ce);
+ return ret;
+ }
+ gpio_direction_output(*ce, 1);
+ /* wait 100ms before any further i2c writes are performed */
+ mdelay(100);
+
+ sensor = kzalloc(sizeof(*sensor), GFP_KERNEL);
+ if (sensor == NULL) {
+ gpio_free(*ce);
+ return -ENOMEM;
+ }
+
+ sd = &sensor->sd;
+ v4l2_i2c_subdev_init(sd, client, &vs6624_ops);
+
+ vs6624_writeregs(sd, vs6624_p1);
+ vs6624_write(sd, VS6624_MICRO_EN, 0x2);
+ vs6624_write(sd, VS6624_DIO_EN, 0x1);
+ mdelay(10);
+ vs6624_writeregs(sd, vs6624_p2);
+
+ vs6624_writeregs(sd, vs6624_default);
+ vs6624_write(sd, VS6624_HSYNC_SETUP, 0xF);
+ vs6624_writeregs(sd, vs6624_run_setup);
+
+ /* set frame rate */
+ sensor->frame_rate.numerator = MAX_FRAME_RATE;
+ sensor->frame_rate.denominator = 1;
+ vs6624_write(sd, VS6624_DISABLE_FR_DAMPER, 0x0);
+ vs6624_write(sd, VS6624_FR_NUM_MSB,
+ sensor->frame_rate.numerator >> 8);
+ vs6624_write(sd, VS6624_FR_NUM_LSB,
+ sensor->frame_rate.numerator & 0xFF);
+ vs6624_write(sd, VS6624_FR_DEN,
+ sensor->frame_rate.denominator & 0xFF);
+
+ sensor->fmt = vs6624_default_fmt;
+ sensor->ce_pin = *ce;
+
+ v4l_info(client, "chip found @ 0x%02x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
+ hdl = &sensor->hdl;
+ v4l2_ctrl_handler_init(hdl, 4);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_CONTRAST, 0, 0xFF, 1, 0x87);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_SATURATION, 0, 0xFF, 1, 0x78);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 0);
+ v4l2_ctrl_new_std(hdl, &vs6624_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 0);
+ /* hook the control handler into the driver */
+ sd->ctrl_handler = hdl;
+ if (hdl->error) {
+ int err = hdl->error;
+
+ v4l2_ctrl_handler_free(hdl);
+ kfree(sensor);
+ gpio_free(*ce);
+ return err;
+ }
+
+ /* initialize the hardware to the default control values */
+ ret = v4l2_ctrl_handler_setup(hdl);
+ if (ret) {
+ v4l2_ctrl_handler_free(hdl);
+ kfree(sensor);
+ gpio_free(*ce);
+ }
+ return ret;
+}
+
+static int __devexit vs6624_remove(struct i2c_client *client)
+{
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct vs6624 *sensor = to_vs6624(sd);
+
+ v4l2_device_unregister_subdev(sd);
+ v4l2_ctrl_handler_free(sd->ctrl_handler);
+ gpio_free(sensor->ce_pin);
+ kfree(sensor);
+ return 0;
+}
+
+static const struct i2c_device_id vs6624_id[] = {
+ {"vs6624", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, vs6624_id);
+
+static struct i2c_driver vs6624_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "vs6624",
+ },
+ .probe = vs6624_probe,
+ .remove = __devexit_p(vs6624_remove),
+ .id_table = vs6624_id,
+};
+
+static __init int vs6624_init(void)
+{
+ return i2c_add_driver(&vs6624_driver);
+}
+
+static __exit void vs6624_exit(void)
+{
+ i2c_del_driver(&vs6624_driver);
+}
+
+module_init(vs6624_init);
+module_exit(vs6624_exit);
+
+MODULE_DESCRIPTION("VS6624 sensor driver");
+MODULE_AUTHOR("Scott Jiang <Scott.Jiang.Linux@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/vs6624_regs.h b/drivers/media/video/vs6624_regs.h
new file mode 100644
index 000000000000..6ba2ee25827e
--- /dev/null
+++ b/drivers/media/video/vs6624_regs.h
@@ -0,0 +1,337 @@
+/*
+ * vs6624 - ST VS6624 CMOS image sensor registers
+ *
+ * Copyright (c) 2011 Analog Devices Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _VS6624_REGS_H_
+#define _VS6624_REGS_H_
+
+/* low level control registers */
+#define VS6624_MICRO_EN 0xC003 /* power enable for all MCU clock */
+#define VS6624_DIO_EN 0xC044 /* enable digital I/O */
+/* device parameters */
+#define VS6624_DEV_ID_MSB 0x0001 /* device id MSB */
+#define VS6624_DEV_ID_LSB 0x0002 /* device id LSB */
+#define VS6624_FW_VSN_MAJOR 0x0004 /* firmware version major */
+#define VS6624_FW_VSN_MINOR 0x0006 /* firmware version minor */
+#define VS6624_PATCH_VSN_MAJOR 0x0008 /* patch version major */
+#define VS6624_PATCH_VSN_MINOR 0x000A /* patch version minor */
+/* host interface manager control */
+#define VS6624_USER_CMD 0x0180 /* user level control of operating states */
+/* host interface manager status */
+#define VS6624_STATE 0x0202 /* current state of the mode manager */
+/* run mode control */
+#define VS6624_METER_ON 0x0280 /* if false AE and AWB are disabled */
+/* mode setup */
+#define VS6624_ACTIVE_PIPE_SETUP 0x0302 /* select the active bank for non view live mode */
+#define VS6624_SENSOR_MODE 0x0308 /* select the different sensor mode */
+/* pipe setup bank0 */
+#define VS6624_IMAGE_SIZE0 0x0380 /* required output dimension */
+#define VS6624_MAN_HSIZE0_MSB 0x0383 /* input required manual H size MSB */
+#define VS6624_MAN_HSIZE0_LSB 0x0384 /* input required manual H size LSB */
+#define VS6624_MAN_VSIZE0_MSB 0x0387 /* input required manual V size MSB */
+#define VS6624_MAN_VSIZE0_LSB 0x0388 /* input required manual V size LSB */
+#define VS6624_ZOOM_HSTEP0_MSB 0x038B /* set the zoom H step MSB */
+#define VS6624_ZOOM_HSTEP0_LSB 0x038C /* set the zoom H step LSB */
+#define VS6624_ZOOM_VSTEP0_MSB 0x038F /* set the zoom V step MSB */
+#define VS6624_ZOOM_VSTEP0_LSB 0x0390 /* set the zoom V step LSB */
+#define VS6624_ZOOM_CTRL0 0x0392 /* control zoon in, out and stop */
+#define VS6624_PAN_HSTEP0_MSB 0x0395 /* set the pan H step MSB */
+#define VS6624_PAN_HSTEP0_LSB 0x0396 /* set the pan H step LSB */
+#define VS6624_PAN_VSTEP0_MSB 0x0399 /* set the pan V step MSB */
+#define VS6624_PAN_VSTEP0_LSB 0x039A /* set the pan V step LSB */
+#define VS6624_PAN_CTRL0 0x039C /* control pan operation */
+#define VS6624_CROP_CTRL0 0x039E /* select cropping mode */
+#define VS6624_CROP_HSTART0_MSB 0x03A1 /* set the cropping H start address MSB */
+#define VS6624_CROP_HSTART0_LSB 0x03A2 /* set the cropping H start address LSB */
+#define VS6624_CROP_HSIZE0_MSB 0x03A5 /* set the cropping H size MSB */
+#define VS6624_CROP_HSIZE0_LSB 0x03A6 /* set the cropping H size LSB */
+#define VS6624_CROP_VSTART0_MSB 0x03A9 /* set the cropping V start address MSB */
+#define VS6624_CROP_VSTART0_LSB 0x03AA /* set the cropping V start address LSB */
+#define VS6624_CROP_VSIZE0_MSB 0x03AD /* set the cropping V size MSB */
+#define VS6624_CROP_VSIZE0_LSB 0x03AE /* set the cropping V size LSB */
+#define VS6624_IMG_FMT0 0x03B0 /* select required output image format */
+#define VS6624_BAYER_OUT_ALIGN0 0x03B2 /* set bayer output alignment */
+#define VS6624_CONTRAST0 0x03B4 /* contrast control for output */
+#define VS6624_SATURATION0 0x03B6 /* saturation control for output */
+#define VS6624_GAMMA0 0x03B8 /* gamma settings */
+#define VS6624_HMIRROR0 0x03BA /* horizontal image orientation flip */
+#define VS6624_VFLIP0 0x03BC /* vertical image orientation flip */
+#define VS6624_CHANNEL_ID0 0x03BE /* logical DMA channel number */
+/* pipe setup bank1 */
+#define VS6624_IMAGE_SIZE1 0x0400 /* required output dimension */
+#define VS6624_MAN_HSIZE1_MSB 0x0403 /* input required manual H size MSB */
+#define VS6624_MAN_HSIZE1_LSB 0x0404 /* input required manual H size LSB */
+#define VS6624_MAN_VSIZE1_MSB 0x0407 /* input required manual V size MSB */
+#define VS6624_MAN_VSIZE1_LSB 0x0408 /* input required manual V size LSB */
+#define VS6624_ZOOM_HSTEP1_MSB 0x040B /* set the zoom H step MSB */
+#define VS6624_ZOOM_HSTEP1_LSB 0x040C /* set the zoom H step LSB */
+#define VS6624_ZOOM_VSTEP1_MSB 0x040F /* set the zoom V step MSB */
+#define VS6624_ZOOM_VSTEP1_LSB 0x0410 /* set the zoom V step LSB */
+#define VS6624_ZOOM_CTRL1 0x0412 /* control zoon in, out and stop */
+#define VS6624_PAN_HSTEP1_MSB 0x0415 /* set the pan H step MSB */
+#define VS6624_PAN_HSTEP1_LSB 0x0416 /* set the pan H step LSB */
+#define VS6624_PAN_VSTEP1_MSB 0x0419 /* set the pan V step MSB */
+#define VS6624_PAN_VSTEP1_LSB 0x041A /* set the pan V step LSB */
+#define VS6624_PAN_CTRL1 0x041C /* control pan operation */
+#define VS6624_CROP_CTRL1 0x041E /* select cropping mode */
+#define VS6624_CROP_HSTART1_MSB 0x0421 /* set the cropping H start address MSB */
+#define VS6624_CROP_HSTART1_LSB 0x0422 /* set the cropping H start address LSB */
+#define VS6624_CROP_HSIZE1_MSB 0x0425 /* set the cropping H size MSB */
+#define VS6624_CROP_HSIZE1_LSB 0x0426 /* set the cropping H size LSB */
+#define VS6624_CROP_VSTART1_MSB 0x0429 /* set the cropping V start address MSB */
+#define VS6624_CROP_VSTART1_LSB 0x042A /* set the cropping V start address LSB */
+#define VS6624_CROP_VSIZE1_MSB 0x042D /* set the cropping V size MSB */
+#define VS6624_CROP_VSIZE1_LSB 0x042E /* set the cropping V size LSB */
+#define VS6624_IMG_FMT1 0x0430 /* select required output image format */
+#define VS6624_BAYER_OUT_ALIGN1 0x0432 /* set bayer output alignment */
+#define VS6624_CONTRAST1 0x0434 /* contrast control for output */
+#define VS6624_SATURATION1 0x0436 /* saturation control for output */
+#define VS6624_GAMMA1 0x0438 /* gamma settings */
+#define VS6624_HMIRROR1 0x043A /* horizontal image orientation flip */
+#define VS6624_VFLIP1 0x043C /* vertical image orientation flip */
+#define VS6624_CHANNEL_ID1 0x043E /* logical DMA channel number */
+/* view live control */
+#define VS6624_VIEW_LIVE_EN 0x0480 /* enable view live mode */
+#define VS6624_INIT_PIPE_SETUP 0x0482 /* select initial pipe setup bank */
+/* view live status */
+#define VS6624_CUR_PIPE_SETUP 0x0500 /* indicates most recently applied setup bank */
+/* power management */
+#define VS6624_TIME_TO_POWER_DOWN 0x0580 /* automatically transition time to stop mode */
+/* video timing parameter host inputs */
+#define VS6624_EXT_CLK_FREQ_NUM_MSB 0x0605 /* external clock frequency numerator MSB */
+#define VS6624_EXT_CLK_FREQ_NUM_LSB 0x0606 /* external clock frequency numerator LSB */
+#define VS6624_EXT_CLK_FREQ_DEN 0x0608 /* external clock frequency denominator */
+/* video timing control */
+#define VS6624_SYS_CLK_MODE 0x0880 /* decides system clock frequency */
+/* frame dimension parameter host inputs */
+#define VS6624_LIGHT_FREQ 0x0C80 /* AC frequency used for flicker free time */
+#define VS6624_FLICKER_COMPAT 0x0C82 /* flicker compatible frame length */
+/* static frame rate control */
+#define VS6624_FR_NUM_MSB 0x0D81 /* desired frame rate numerator MSB */
+#define VS6624_FR_NUM_LSB 0x0D82 /* desired frame rate numerator LSB */
+#define VS6624_FR_DEN 0x0D84 /* desired frame rate denominator */
+/* automatic frame rate control */
+#define VS6624_DISABLE_FR_DAMPER 0x0E80 /* defines frame rate mode */
+#define VS6624_MIN_DAMPER_OUT_MSB 0x0E8C /* minimum frame rate MSB */
+#define VS6624_MIN_DAMPER_OUT_LSB 0x0E8A /* minimum frame rate LSB */
+/* exposure controls */
+#define VS6624_EXPO_MODE 0x1180 /* exposure mode */
+#define VS6624_EXPO_METER 0x1182 /* weights to be associated with the zones */
+#define VS6624_EXPO_TIME_NUM 0x1184 /* exposure time numerator */
+#define VS6624_EXPO_TIME_DEN 0x1186 /* exposure time denominator */
+#define VS6624_EXPO_TIME_MSB 0x1189 /* exposure time for the Manual Mode MSB */
+#define VS6624_EXPO_TIME_LSB 0x118A /* exposure time for the Manual Mode LSB */
+#define VS6624_EXPO_COMPENSATION 0x1190 /* exposure compensation */
+#define VS6624_DIRECT_COARSE_MSB 0x1195 /* coarse integration lines for Direct Mode MSB */
+#define VS6624_DIRECT_COARSE_LSB 0x1196 /* coarse integration lines for Direct Mode LSB */
+#define VS6624_DIRECT_FINE_MSB 0x1199 /* fine integration pixels for Direct Mode MSB */
+#define VS6624_DIRECT_FINE_LSB 0x119A /* fine integration pixels for Direct Mode LSB */
+#define VS6624_DIRECT_ANAL_GAIN_MSB 0x119D /* analog gain for Direct Mode MSB */
+#define VS6624_DIRECT_ANAL_GAIN_LSB 0x119E /* analog gain for Direct Mode LSB */
+#define VS6624_DIRECT_DIGI_GAIN_MSB 0x11A1 /* digital gain for Direct Mode MSB */
+#define VS6624_DIRECT_DIGI_GAIN_LSB 0x11A2 /* digital gain for Direct Mode LSB */
+#define VS6624_FLASH_COARSE_MSB 0x11A5 /* coarse integration lines for Flash Gun Mode MSB */
+#define VS6624_FLASH_COARSE_LSB 0x11A6 /* coarse integration lines for Flash Gun Mode LSB */
+#define VS6624_FLASH_FINE_MSB 0x11A9 /* fine integration pixels for Flash Gun Mode MSB */
+#define VS6624_FLASH_FINE_LSB 0x11AA /* fine integration pixels for Flash Gun Mode LSB */
+#define VS6624_FLASH_ANAL_GAIN_MSB 0x11AD /* analog gain for Flash Gun Mode MSB */
+#define VS6624_FLASH_ANAL_GAIN_LSB 0x11AE /* analog gain for Flash Gun Mode LSB */
+#define VS6624_FLASH_DIGI_GAIN_MSB 0x11B1 /* digital gain for Flash Gun Mode MSB */
+#define VS6624_FLASH_DIGI_GAIN_LSB 0x11B2 /* digital gain for Flash Gun Mode LSB */
+#define VS6624_FREEZE_AE 0x11B4 /* freeze auto exposure */
+#define VS6624_MAX_INT_TIME_MSB 0x11B7 /* user maximum integration time MSB */
+#define VS6624_MAX_INT_TIME_LSB 0x11B8 /* user maximum integration time LSB */
+#define VS6624_FLASH_AG_THR_MSB 0x11BB /* recommend flash gun analog gain threshold MSB */
+#define VS6624_FLASH_AG_THR_LSB 0x11BC /* recommend flash gun analog gain threshold LSB */
+#define VS6624_ANTI_FLICKER_MODE 0x11C0 /* anti flicker mode */
+/* white balance control */
+#define VS6624_WB_MODE 0x1480 /* set white balance mode */
+#define VS6624_MAN_RG 0x1482 /* user setting for red channel gain */
+#define VS6624_MAN_GG 0x1484 /* user setting for green channel gain */
+#define VS6624_MAN_BG 0x1486 /* user setting for blue channel gain */
+#define VS6624_FLASH_RG_MSB 0x148B /* red gain for Flash Gun MSB */
+#define VS6624_FLASH_RG_LSB 0x148C /* red gain for Flash Gun LSB */
+#define VS6624_FLASH_GG_MSB 0x148F /* green gain for Flash Gun MSB */
+#define VS6624_FLASH_GG_LSB 0x1490 /* green gain for Flash Gun LSB */
+#define VS6624_FLASH_BG_MSB 0x1493 /* blue gain for Flash Gun MSB */
+#define VS6624_FLASH_BG_LSB 0x1494 /* blue gain for Flash Gun LSB */
+/* sensor setup */
+#define VS6624_BC_OFFSET 0x1990 /* Black Correction Offset */
+/* image stability */
+#define VS6624_STABLE_WB 0x1900 /* white balance stable */
+#define VS6624_STABLE_EXPO 0x1902 /* exposure stable */
+#define VS6624_STABLE 0x1906 /* system stable */
+/* flash control */
+#define VS6624_FLASH_MODE 0x1A80 /* flash mode */
+#define VS6624_FLASH_OFF_LINE_MSB 0x1A83 /* off line at flash pulse mode MSB */
+#define VS6624_FLASH_OFF_LINE_LSB 0x1A84 /* off line at flash pulse mode LSB */
+/* flash status */
+#define VS6624_FLASH_RECOM 0x1B00 /* flash gun is recommended */
+#define VS6624_FLASH_GRAB_COMPLETE 0x1B02 /* flash gun image has been grabbed */
+/* scythe filter controls */
+#define VS6624_SCYTHE_FILTER 0x1D80 /* disable scythe defect correction */
+/* jack filter controls */
+#define VS6624_JACK_FILTER 0x1E00 /* disable jack defect correction */
+/* demosaic control */
+#define VS6624_ANTI_ALIAS_FILTER 0x1E80 /* anti alias filter suppress */
+/* color matrix dampers */
+#define VS6624_CM_DISABLE 0x1F00 /* disable color matrix damper */
+#define VS6624_CM_LOW_THR_MSB 0x1F03 /* low threshold for exposure MSB */
+#define VS6624_CM_LOW_THR_LSB 0x1F04 /* low threshold for exposure LSB */
+#define VS6624_CM_HIGH_THR_MSB 0x1F07 /* high threshold for exposure MSB */
+#define VS6624_CM_HIGH_THR_LSB 0x1F08 /* high threshold for exposure LSB */
+#define VS6624_CM_MIN_OUT_MSB 0x1F0B /* minimum possible damper output MSB */
+#define VS6624_CM_MIN_OUT_LSB 0x1F0C /* minimum possible damper output LSB */
+/* peaking control */
+#define VS6624_PEAK_GAIN 0x2000 /* controls peaking gain */
+#define VS6624_PEAK_G_DISABLE 0x2002 /* disable peak gain damping */
+#define VS6624_PEAK_LOW_THR_G_MSB 0x2005 /* low threshold for exposure for gain MSB */
+#define VS6624_PEAK_LOW_THR_G_LSB 0x2006 /* low threshold for exposure for gain LSB */
+#define VS6624_PEAK_HIGH_THR_G_MSB 0x2009 /* high threshold for exposure for gain MSB */
+#define VS6624_PEAK_HIGH_THR_G_LSB 0x200A /* high threshold for exposure for gain LSB */
+#define VS6624_PEAK_MIN_OUT_G_MSB 0x200D /* minimum damper output for gain MSB */
+#define VS6624_PEAK_MIN_OUT_G_LSB 0x200E /* minimum damper output for gain LSB */
+#define VS6624_PEAK_LOW_THR 0x2010 /* adjust degree of coring */
+#define VS6624_PEAK_C_DISABLE 0x2012 /* disable coring damping */
+#define VS6624_PEAK_HIGH_THR 0x2014 /* adjust maximum gain */
+#define VS6624_PEAK_LOW_THR_C_MSB 0x2017 /* low threshold for exposure for coring MSB */
+#define VS6624_PEAK_LOW_THR_C_LSB 0x2018 /* low threshold for exposure for coring LSB */
+#define VS6624_PEAK_HIGH_THR_C_MSB 0x201B /* high threshold for exposure for coring MSB */
+#define VS6624_PEAK_HIGH_THR_C_LSB 0x201C /* high threshold for exposure for coring LSB */
+#define VS6624_PEAK_MIN_OUT_C_MSB 0x201F /* minimum damper output for coring MSB */
+#define VS6624_PEAK_MIN_OUT_C_LSB 0x2020 /* minimum damper output for coring LSB */
+/* pipe 0 RGB to YUV matrix manual control */
+#define VS6624_RYM0_MAN_CTRL 0x2180 /* enable manual RGB to YUV matrix */
+#define VS6624_RYM0_W00_MSB 0x2183 /* row 0 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W00_LSB 0x2184 /* row 0 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W01_MSB 0x2187 /* row 0 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W01_LSB 0x2188 /* row 0 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W02_MSB 0x218C /* row 0 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W02_LSB 0x218D /* row 0 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_W10_MSB 0x2190 /* row 1 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W10_LSB 0x218F /* row 1 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W11_MSB 0x2193 /* row 1 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W11_LSB 0x2194 /* row 1 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W12_MSB 0x2197 /* row 1 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W12_LSB 0x2198 /* row 1 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_W20_MSB 0x219B /* row 2 column 0 of YUV matrix MSB */
+#define VS6624_RYM0_W20_LSB 0x219C /* row 2 column 0 of YUV matrix LSB */
+#define VS6624_RYM0_W21_MSB 0x21A0 /* row 2 column 1 of YUV matrix MSB */
+#define VS6624_RYM0_W21_LSB 0x219F /* row 2 column 1 of YUV matrix LSB */
+#define VS6624_RYM0_W22_MSB 0x21A3 /* row 2 column 2 of YUV matrix MSB */
+#define VS6624_RYM0_W22_LSB 0x21A4 /* row 2 column 2 of YUV matrix LSB */
+#define VS6624_RYM0_YINY_MSB 0x21A7 /* Y in Y MSB */
+#define VS6624_RYM0_YINY_LSB 0x21A8 /* Y in Y LSB */
+#define VS6624_RYM0_YINCB_MSB 0x21AB /* Y in Cb MSB */
+#define VS6624_RYM0_YINCB_LSB 0x21AC /* Y in Cb LSB */
+#define VS6624_RYM0_YINCR_MSB 0x21B0 /* Y in Cr MSB */
+#define VS6624_RYM0_YINCR_LSB 0x21AF /* Y in Cr LSB */
+/* pipe 1 RGB to YUV matrix manual control */
+#define VS6624_RYM1_MAN_CTRL 0x2200 /* enable manual RGB to YUV matrix */
+#define VS6624_RYM1_W00_MSB 0x2203 /* row 0 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W00_LSB 0x2204 /* row 0 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W01_MSB 0x2207 /* row 0 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W01_LSB 0x2208 /* row 0 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W02_MSB 0x220C /* row 0 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W02_LSB 0x220D /* row 0 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_W10_MSB 0x2210 /* row 1 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W10_LSB 0x220F /* row 1 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W11_MSB 0x2213 /* row 1 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W11_LSB 0x2214 /* row 1 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W12_MSB 0x2217 /* row 1 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W12_LSB 0x2218 /* row 1 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_W20_MSB 0x221B /* row 2 column 0 of YUV matrix MSB */
+#define VS6624_RYM1_W20_LSB 0x221C /* row 2 column 0 of YUV matrix LSB */
+#define VS6624_RYM1_W21_MSB 0x2220 /* row 2 column 1 of YUV matrix MSB */
+#define VS6624_RYM1_W21_LSB 0x221F /* row 2 column 1 of YUV matrix LSB */
+#define VS6624_RYM1_W22_MSB 0x2223 /* row 2 column 2 of YUV matrix MSB */
+#define VS6624_RYM1_W22_LSB 0x2224 /* row 2 column 2 of YUV matrix LSB */
+#define VS6624_RYM1_YINY_MSB 0x2227 /* Y in Y MSB */
+#define VS6624_RYM1_YINY_LSB 0x2228 /* Y in Y LSB */
+#define VS6624_RYM1_YINCB_MSB 0x222B /* Y in Cb MSB */
+#define VS6624_RYM1_YINCB_LSB 0x222C /* Y in Cb LSB */
+#define VS6624_RYM1_YINCR_MSB 0x2220 /* Y in Cr MSB */
+#define VS6624_RYM1_YINCR_LSB 0x222F /* Y in Cr LSB */
+/* pipe 0 gamma manual control */
+#define VS6624_GAMMA_MAN_CTRL0 0x2280 /* enable manual gamma setup */
+#define VS6624_GAMMA_PEAK_R0 0x2282 /* peaked red channel gamma value */
+#define VS6624_GAMMA_PEAK_G0 0x2284 /* peaked green channel gamma value */
+#define VS6624_GAMMA_PEAK_B0 0x2286 /* peaked blue channel gamma value */
+#define VS6624_GAMMA_UNPEAK_R0 0x2288 /* unpeaked red channel gamma value */
+#define VS6624_GAMMA_UNPEAK_G0 0x228A /* unpeaked green channel gamma value */
+#define VS6624_GAMMA_UNPEAK_B0 0x228C /* unpeaked blue channel gamma value */
+/* pipe 1 gamma manual control */
+#define VS6624_GAMMA_MAN_CTRL1 0x2300 /* enable manual gamma setup */
+#define VS6624_GAMMA_PEAK_R1 0x2302 /* peaked red channel gamma value */
+#define VS6624_GAMMA_PEAK_G1 0x2304 /* peaked green channel gamma value */
+#define VS6624_GAMMA_PEAK_B1 0x2306 /* peaked blue channel gamma value */
+#define VS6624_GAMMA_UNPEAK_R1 0x2308 /* unpeaked red channel gamma value */
+#define VS6624_GAMMA_UNPEAK_G1 0x230A /* unpeaked green channel gamma value */
+#define VS6624_GAMMA_UNPEAK_B1 0x230C /* unpeaked blue channel gamma value */
+/* fade to black */
+#define VS6624_F2B_DISABLE 0x2480 /* disable fade to black */
+#define VS6624_F2B_BLACK_VAL_MSB 0x2483 /* black value MSB */
+#define VS6624_F2B_BLACK_VAL_LSB 0x2484 /* black value LSB */
+#define VS6624_F2B_LOW_THR_MSB 0x2487 /* low threshold for exposure MSB */
+#define VS6624_F2B_LOW_THR_LSB 0x2488 /* low threshold for exposure LSB */
+#define VS6624_F2B_HIGH_THR_MSB 0x248B /* high threshold for exposure MSB */
+#define VS6624_F2B_HIGH_THR_LSB 0x248C /* high threshold for exposure LSB */
+#define VS6624_F2B_MIN_OUT_MSB 0x248F /* minimum damper output MSB */
+#define VS6624_F2B_MIN_OUT_LSB 0x2490 /* minimum damper output LSB */
+/* output formatter control */
+#define VS6624_CODE_CK_EN 0x2580 /* code check enable */
+#define VS6624_BLANK_FMT 0x2582 /* blank format */
+#define VS6624_SYNC_CODE_SETUP 0x2584 /* sync code setup */
+#define VS6624_HSYNC_SETUP 0x2586 /* H sync setup */
+#define VS6624_VSYNC_SETUP 0x2588 /* V sync setup */
+#define VS6624_PCLK_SETUP 0x258A /* PCLK setup */
+#define VS6624_PCLK_EN 0x258C /* PCLK enable */
+#define VS6624_OPF_SP_SETUP 0x258E /* output formatter sp setup */
+#define VS6624_BLANK_DATA_MSB 0x2590 /* blank data MSB */
+#define VS6624_BLANK_DATA_LSB 0x2592 /* blank data LSB */
+#define VS6624_RGB_SETUP 0x2594 /* RGB setup */
+#define VS6624_YUV_SETUP 0x2596 /* YUV setup */
+#define VS6624_VSYNC_RIS_COARSE_H 0x2598 /* V sync rising coarse high */
+#define VS6624_VSYNC_RIS_COARSE_L 0x259A /* V sync rising coarse low */
+#define VS6624_VSYNC_RIS_FINE_H 0x259C /* V sync rising fine high */
+#define VS6624_VSYNC_RIS_FINE_L 0x259E /* V sync rising fine low */
+#define VS6624_VSYNC_FALL_COARSE_H 0x25A0 /* V sync falling coarse high */
+#define VS6624_VSYNC_FALL_COARSE_L 0x25A2 /* V sync falling coarse low */
+#define VS6624_VSYNC_FALL_FINE_H 0x25A4 /* V sync falling fine high */
+#define VS6624_VSYNC_FALL_FINE_L 0x25A6 /* V sync falling fine low */
+#define VS6624_HSYNC_RIS_H 0x25A8 /* H sync rising high */
+#define VS6624_HSYNC_RIS_L 0x25AA /* H sync rising low */
+#define VS6624_HSYNC_FALL_H 0x25AC /* H sync falling high */
+#define VS6624_HSYNC_FALL_L 0x25AE /* H sync falling low */
+#define VS6624_OUT_IF 0x25B0 /* output interface */
+#define VS6624_CCP_EXT_DATA 0x25B2 /* CCP extra data */
+/* NoRA controls */
+#define VS6624_NORA_DISABLE 0x2600 /* NoRA control mode */
+#define VS6624_NORA_USAGE 0x2602 /* usage */
+#define VS6624_NORA_SPLIT_KN 0x2604 /* split kn */
+#define VS6624_NORA_SPLIT_NI 0x2606 /* split ni */
+#define VS6624_NORA_TIGHT_G 0x2608 /* tight green */
+#define VS6624_NORA_DISABLE_NP 0x260A /* disable noro promoting */
+#define VS6624_NORA_LOW_THR_MSB 0x260D /* low threshold for exposure MSB */
+#define VS6624_NORA_LOW_THR_LSB 0x260E /* low threshold for exposure LSB */
+#define VS6624_NORA_HIGH_THR_MSB 0x2611 /* high threshold for exposure MSB */
+#define VS6624_NORA_HIGH_THR_LSB 0x2612 /* high threshold for exposure LSB */
+#define VS6624_NORA_MIN_OUT_MSB 0x2615 /* minimum damper output MSB */
+#define VS6624_NORA_MIN_OUT_LSB 0x2616 /* minimum damper output LSB */
+
+#endif
diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
index 453dbbd1e6e8..7fd7ac567e1a 100644
--- a/drivers/media/video/w9966.c
+++ b/drivers/media/video/w9966.c
@@ -129,9 +129,9 @@ MODULE_LICENSE("GPL");
MODULE_VERSION("0.33.1");
#ifdef MODULE
-static const char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = ""};
#else
-static const char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
+static char *pardev[] = {[0 ... W9966_MAXCAMS] = "aggressive"};
#endif
module_param_array(pardev, charp, NULL, 0);
MODULE_PARM_DESC(pardev, "pardev: where to search for\n"
diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
index a22f765e968a..3bb99e93febe 100644
--- a/drivers/media/video/wm8739.c
+++ b/drivers/media/video/wm8739.c
@@ -291,15 +291,4 @@ static struct i2c_driver wm8739_driver = {
.id_table = wm8739_id,
};
-static __init int init_wm8739(void)
-{
- return i2c_add_driver(&wm8739_driver);
-}
-
-static __exit void exit_wm8739(void)
-{
- i2c_del_driver(&wm8739_driver);
-}
-
-module_init(init_wm8739);
-module_exit(exit_wm8739);
+module_i2c_driver(wm8739_driver);
diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
index 9cedb1e69b58..bee77ea9f49e 100644
--- a/drivers/media/video/wm8775.c
+++ b/drivers/media/video/wm8775.c
@@ -339,15 +339,4 @@ static struct i2c_driver wm8775_driver = {
.id_table = wm8775_id,
};
-static __init int init_wm8775(void)
-{
- return i2c_add_driver(&wm8775_driver);
-}
-
-static __exit void exit_wm8775(void)
-{
- i2c_del_driver(&wm8775_driver);
-}
-
-module_init(init_wm8775);
-module_exit(exit_wm8775);
+module_i2c_driver(wm8775_driver);
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c
index a7dc4672d996..a5c591ffe395 100644
--- a/drivers/message/fusion/mptbase.c
+++ b/drivers/message/fusion/mptbase.c
@@ -346,7 +346,7 @@ static int mpt_remove_dead_ioc_func(void *arg)
if ((pdev == NULL))
return -1;
- pci_remove_bus_device(pdev);
+ pci_stop_and_remove_bus_device(pdev);
return 0;
}
diff --git a/drivers/message/i2o/i2o_scsi.c b/drivers/message/i2o/i2o_scsi.c
index c8ed7b63fdf5..1d31d7284cbd 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 17dfe9bb6d27..87bd5ba38d5b 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 f93dd9571c3c..b2cfdc458561 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 1489c3540f96..29f463cc09cb 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 b953bab934f7..05fa538c5efe 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 d295941c9a3d..1f08704f7ae8 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 087fecd71ce0..b83045f102be 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 000000000000..2af42480635e
--- /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 b85bbd7f0d19..1895cf9fab8c 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 5ddde2a9176a..7ff313fe9fb1 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 44b97c70a61f..36b88e395499 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 cdbc7cad326f..6faf149e8d94 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 af8e0efedbe4..ebc1e8658226 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 ec22e9f15d32..3a0bf91d7780 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 7122386b4e3c..9fd4f63c45cc 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 86cc3f7841cd..62e5e3617eb0 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 02c53a0766c4..c54e244ca0cf 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 411f523d4878..ffc3d48676ae 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 68ac2c55d5ae..95a2e546a489 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 ff1a7e741ecd..189c2f07b83f 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 9ab19a8f669d..d02ddf2ebd63 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 048a3b903b01..498286cbb530 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 000000000000..fa6f80fad5f1
--- /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 000000000000..99ef944c621d
--- /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 caadabeed8e9..48949d998d10 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 de76dfb6f0ad..0236676085cf 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 f4d86117f44a..d927dd49acb3 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 e07947e56b2a..2dd8d49cb30b 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 000000000000..a66d4df51293
--- /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 000000000000..f7d854e4cc62
--- /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 95c0d7978bec..c9ed5c00a621 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 4392f6bca156..bf2b25ebf2ca 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 806680d1bbb4..7c2267e71f8b 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 8c50a556e986..6ff99dce714f 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 b69bb517b102..5d656e814358 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 c6b456ad7342..b76902f1e44a 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 cea9da60850d..b63c0756a669 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 febc90cdef7e..70f02daeb22a 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 63a3cbdfa3f3..1e0e20c0e082 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-core.c b/drivers/mfd/wm831x-core.c
index f5e54fae8ada..838056c3493a 100644
--- a/drivers/mfd/wm831x-core.c
+++ b/drivers/mfd/wm831x-core.c
@@ -1631,7 +1631,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
- goto err_regmap;
+ goto err;
}
switch (ret) {
case 0x6204:
@@ -1640,20 +1640,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
default:
dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
ret = -EINVAL;
- goto err_regmap;
+ goto err;
}
ret = wm831x_reg_read(wm831x, WM831X_REVISION);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
- goto err_regmap;
+ goto err;
}
rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;
ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
- goto err_regmap;
+ goto err;
}
/* Some engineering samples do not have the ID set, rely on
@@ -1728,7 +1728,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
default:
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
ret = -EINVAL;
- goto err_regmap;
+ goto err;
}
/* This will need revisiting in future but is OK for all
@@ -1742,7 +1742,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
- goto err_regmap;
+ goto err;
}
if (ret != 0) {
dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
@@ -1755,7 +1755,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = pdata->pre_init(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
- goto err_regmap;
+ goto err;
}
}
@@ -1778,7 +1778,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = wm831x_irq_init(wm831x, irq);
if (ret != 0)
- goto err_regmap;
+ goto err;
wm831x_auxadc_init(wm831x);
@@ -1874,9 +1874,8 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
err_irq:
wm831x_irq_exit(wm831x);
-err_regmap:
+err:
mfd_remove_devices(wm831x->dev);
- regmap_exit(wm831x->regmap);
return ret;
}
@@ -1887,7 +1886,6 @@ void wm831x_device_exit(struct wm831x *wm831x)
if (wm831x->irq_base)
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
wm831x_irq_exit(wm831x);
- regmap_exit(wm831x->regmap);
}
int wm831x_device_suspend(struct wm831x *wm831x)
diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c
index cb15609b0a48..2b29caebc9cf 100644
--- a/drivers/mfd/wm831x-i2c.c
+++ b/drivers/mfd/wm831x-i2c.c
@@ -37,7 +37,7 @@ static int wm831x_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm831x);
wm831x->dev = &i2c->dev;
- wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config);
+ wm831x->regmap = devm_regmap_init_i2c(i2c, &wm831x_regmap_config);
if (IS_ERR(wm831x->regmap)) {
ret = PTR_ERR(wm831x->regmap);
dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c
index 62ef3254105f..4bceee98f0a4 100644
--- a/drivers/mfd/wm831x-spi.c
+++ b/drivers/mfd/wm831x-spi.c
@@ -40,7 +40,7 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi)
dev_set_drvdata(&spi->dev, wm831x);
wm831x->dev = &spi->dev;
- wm831x->regmap = regmap_init_spi(spi, &wm831x_regmap_config);
+ wm831x->regmap = devm_regmap_init_spi(spi, &wm831x_regmap_config);
if (IS_ERR(wm831x->regmap)) {
ret = PTR_ERR(wm831x->regmap);
dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
@@ -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 2204893444a6..1189a17f0f25 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;
}
@@ -350,7 +349,7 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
goto err;
}
- wm8400->regmap = regmap_init_i2c(i2c, &wm8400_regmap_config);
+ wm8400->regmap = devm_regmap_init_i2c(i2c, &wm8400_regmap_config);
if (IS_ERR(wm8400->regmap)) {
ret = PTR_ERR(wm8400->regmap);
goto err;
@@ -361,12 +360,10 @@ static int wm8400_i2c_probe(struct i2c_client *i2c,
ret = wm8400_init(wm8400, i2c->dev.platform_data);
if (ret != 0)
- goto map_err;
+ goto err;
return 0;
-map_err:
- regmap_exit(wm8400->regmap);
err:
return ret;
}
@@ -376,7 +373,6 @@ static int wm8400_i2c_remove(struct i2c_client *i2c)
struct wm8400 *wm8400 = i2c_get_clientdata(i2c);
wm8400_release(wm8400);
- regmap_exit(wm8400->regmap);
return 0;
}
diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c
index a04b3c108c8c..9d7ca1e978fa 100644
--- a/drivers/mfd/wm8994-core.c
+++ b/drivers/mfd/wm8994-core.c
@@ -359,15 +359,38 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo)
}
#endif
+static const __devinitdata struct reg_default wm8994_revc_patch[] = {
+ { 0x102, 0x3 },
+ { 0x56, 0x3 },
+ { 0x817, 0x0 },
+ { 0x102, 0x0 },
+};
+
+static const __devinitdata struct reg_default wm8958_reva_patch[] = {
+ { 0x102, 0x3 },
+ { 0xcb, 0x81 },
+ { 0x817, 0x0 },
+ { 0x102, 0x0 },
+};
+
+static const __devinitdata struct reg_default wm1811_reva_patch[] = {
+ { 0x102, 0x3 },
+ { 0x56, 0x7 },
+ { 0x5d, 0x7e },
+ { 0x5e, 0x0 },
+ { 0x102, 0x0 },
+};
+
/*
* Instantiate the generic non-control parts of the device.
*/
-static int wm8994_device_init(struct wm8994 *wm8994, int irq)
+static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)
{
struct wm8994_pdata *pdata = wm8994->dev->platform_data;
struct regmap_config *regmap_config;
+ const struct reg_default *regmap_patch = NULL;
const char *devname;
- int ret, i;
+ int ret, i, patch_regs;
int pulls = 0;
dev_set_drvdata(wm8994->dev, wm8994);
@@ -379,7 +402,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
NULL, 0);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to add children: %d\n", ret);
- goto err_regmap;
+ goto err;
}
switch (wm8994->type) {
@@ -394,7 +417,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
- goto err_regmap;
+ goto err;
}
wm8994->supplies = devm_kzalloc(wm8994->dev,
@@ -402,7 +425,7 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
wm8994->num_supplies, GFP_KERNEL);
if (!wm8994->supplies) {
ret = -ENOMEM;
- goto err_regmap;
+ goto err;
}
switch (wm8994->type) {
@@ -420,14 +443,14 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
break;
default:
BUG();
- goto err_regmap;
+ goto err;
}
ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies,
wm8994->supplies);
if (ret != 0) {
dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret);
- goto err_regmap;
+ goto err;
}
ret = regulator_bulk_enable(wm8994->num_supplies,
@@ -488,15 +511,44 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
"revision %c not fully supported\n",
'A' + wm8994->revision);
break;
+ case 2:
+ case 3:
+ regmap_patch = wm8994_revc_patch;
+ patch_regs = ARRAY_SIZE(wm8994_revc_patch);
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case WM8958:
+ switch (wm8994->revision) {
+ case 0:
+ regmap_patch = wm8958_reva_patch;
+ patch_regs = ARRAY_SIZE(wm8958_reva_patch);
+ break;
default:
break;
}
break;
+
case WM1811:
/* Revision C did not change the relevant layer */
if (wm8994->revision > 1)
wm8994->revision++;
+ switch (wm8994->revision) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ regmap_patch = wm1811_reva_patch;
+ patch_regs = ARRAY_SIZE(wm1811_reva_patch);
+ break;
+ default:
+ break;
+ }
break;
+
default:
break;
}
@@ -526,6 +578,16 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq)
return ret;
}
+ if (regmap_patch) {
+ ret = regmap_register_patch(wm8994->regmap, regmap_patch,
+ patch_regs);
+ if (ret != 0) {
+ dev_err(wm8994->dev, "Failed to register patch: %d\n",
+ ret);
+ goto err;
+ }
+ }
+
if (pdata) {
wm8994->irq_base = pdata->irq_base;
wm8994->gpio_base = pdata->gpio_base;
@@ -577,7 +639,7 @@ static 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;
@@ -588,13 +650,12 @@ err_enable:
wm8994->supplies);
err_get:
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
-err_regmap:
- regmap_exit(wm8994->regmap);
+err:
mfd_remove_devices(wm8994->dev);
return ret;
}
-static void wm8994_device_exit(struct wm8994 *wm8994)
+static __devexit void wm8994_device_exit(struct wm8994 *wm8994)
{
pm_runtime_disable(wm8994->dev);
mfd_remove_devices(wm8994->dev);
@@ -602,7 +663,6 @@ static void wm8994_device_exit(struct wm8994 *wm8994)
regulator_bulk_disable(wm8994->num_supplies,
wm8994->supplies);
regulator_bulk_free(wm8994->num_supplies, wm8994->supplies);
- regmap_exit(wm8994->regmap);
}
static const struct of_device_id wm8994_of_match[] = {
@@ -613,8 +673,8 @@ static const struct of_device_id wm8994_of_match[] = {
};
MODULE_DEVICE_TABLE(of, wm8994_of_match);
-static int wm8994_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+static __devinit int wm8994_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
{
struct wm8994 *wm8994;
int ret;
@@ -628,7 +688,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
wm8994->irq = i2c->irq;
wm8994->type = id->driver_data;
- wm8994->regmap = regmap_init_i2c(i2c, &wm8994_base_regmap_config);
+ wm8994->regmap = devm_regmap_init_i2c(i2c, &wm8994_base_regmap_config);
if (IS_ERR(wm8994->regmap)) {
ret = PTR_ERR(wm8994->regmap);
dev_err(wm8994->dev, "Failed to allocate register map: %d\n",
@@ -639,7 +699,7 @@ static int wm8994_i2c_probe(struct i2c_client *i2c,
return wm8994_device_init(wm8994, i2c->irq);
}
-static int wm8994_i2c_remove(struct i2c_client *i2c)
+static __devexit int wm8994_i2c_remove(struct i2c_client *i2c)
{
struct wm8994 *wm8994 = i2c_get_clientdata(i2c);
@@ -668,7 +728,7 @@ static struct i2c_driver wm8994_i2c_driver = {
.of_match_table = wm8994_of_match,
},
.probe = wm8994_i2c_probe,
- .remove = wm8994_i2c_remove,
+ .remove = __devexit_p(wm8994_i2c_remove),
.id_table = wm8994_i2c_id,
};
diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c
index bc0c5096539a..bfd25af6ecb1 100644
--- a/drivers/mfd/wm8994-regmap.c
+++ b/drivers/mfd/wm8994-regmap.c
@@ -15,11 +15,11 @@
#include <linux/mfd/wm8994/core.h>
#include <linux/mfd/wm8994/registers.h>
#include <linux/regmap.h>
+#include <linux/device.h>
#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) */
@@ -60,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 */
@@ -68,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) */
@@ -87,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) */
@@ -217,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 */
@@ -227,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) */
@@ -274,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) */
@@ -291,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) */
@@ -444,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 */
@@ -454,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) */
@@ -969,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/atmel_tclib.c b/drivers/misc/atmel_tclib.c
index 4bcfc3759734..c8d8e38d0d8a 100644
--- a/drivers/misc/atmel_tclib.c
+++ b/drivers/misc/atmel_tclib.c
@@ -6,12 +6,10 @@
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/export.h>
-
-/* Number of bytes to reserve for the iomem resource */
-#define ATMEL_TC_IOMEM_SIZE 256
-
+#include <linux/of.h>
/*
* This is a thin library to solve the problem of how to portably allocate
@@ -48,10 +46,17 @@ struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name)
struct atmel_tc *tc;
struct platform_device *pdev = NULL;
struct resource *r;
+ size_t size;
spin_lock(&tc_list_lock);
list_for_each_entry(tc, &tc_list, node) {
- if (tc->pdev->id == block) {
+ if (tc->pdev->dev.of_node) {
+ if (of_alias_get_id(tc->pdev->dev.of_node, "tcb")
+ == block) {
+ pdev = tc->pdev;
+ break;
+ }
+ } else if (tc->pdev->id == block) {
pdev = tc->pdev;
break;
}
@@ -61,11 +66,15 @@ struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name)
goto fail;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- r = request_mem_region(r->start, ATMEL_TC_IOMEM_SIZE, name);
if (!r)
goto fail;
- tc->regs = ioremap(r->start, ATMEL_TC_IOMEM_SIZE);
+ size = resource_size(r);
+ r = request_mem_region(r->start, size, name);
+ if (!r)
+ goto fail;
+
+ tc->regs = ioremap(r->start, size);
if (!tc->regs)
goto fail_ioremap;
@@ -76,7 +85,7 @@ out:
return tc;
fail_ioremap:
- release_mem_region(r->start, ATMEL_TC_IOMEM_SIZE);
+ release_mem_region(r->start, size);
fail:
tc = NULL;
goto out;
@@ -96,7 +105,7 @@ void atmel_tc_free(struct atmel_tc *tc)
spin_lock(&tc_list_lock);
if (tc->regs) {
iounmap(tc->regs);
- release_mem_region(tc->iomem->start, ATMEL_TC_IOMEM_SIZE);
+ release_mem_region(tc->iomem->start, resource_size(tc->iomem));
tc->regs = NULL;
tc->iomem = NULL;
}
@@ -104,6 +113,30 @@ void atmel_tc_free(struct atmel_tc *tc)
}
EXPORT_SYMBOL_GPL(atmel_tc_free);
+#if defined(CONFIG_OF)
+static struct atmel_tcb_config tcb_rm9200_config = {
+ .counter_width = 16,
+};
+
+static struct atmel_tcb_config tcb_sam9x5_config = {
+ .counter_width = 32,
+};
+
+static const struct of_device_id atmel_tcb_dt_ids[] = {
+ {
+ .compatible = "atmel,at91rm9200-tcb",
+ .data = &tcb_rm9200_config,
+ }, {
+ .compatible = "atmel,at91sam9x5-tcb",
+ .data = &tcb_sam9x5_config,
+ }, {
+ /* sentinel */
+ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_tcb_dt_ids);
+#endif
+
static int __init tc_probe(struct platform_device *pdev)
{
struct atmel_tc *tc;
@@ -129,6 +162,14 @@ static int __init tc_probe(struct platform_device *pdev)
return -EINVAL;
}
+ /* Now take SoC information if available */
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node);
+ if (match)
+ tc->tcb_config = match->data;
+ }
+
tc->clk[0] = clk;
tc->clk[1] = clk_get(&pdev->dev, "t1_clk");
if (IS_ERR(tc->clk[1]))
@@ -153,7 +194,10 @@ static int __init tc_probe(struct platform_device *pdev)
}
static struct platform_driver tc_driver = {
- .driver.name = "atmel_tcb",
+ .driver = {
+ .name = "atmel_tcb",
+ .of_match_table = of_match_ptr(atmel_tcb_dt_ids),
+ },
};
static int __init tc_init(void)
diff --git a/drivers/misc/sgi-gru/gru_instructions.h b/drivers/misc/sgi-gru/gru_instructions.h
index d95587cc794c..04d5170ac149 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 851b2f25ce0e..c862cd4583cc 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 e5a3c7b6dedb..eed213a5c8cb 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 082202ae4a03..29de31e260dd 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 132378b89d76..14f262e9246d 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 c3704e293a7b..91c84c7a1829 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 08a7852ade44..f2ab9e578126 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 2b9ed1401dc4..02914d609a91 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 4d41fa984c93..69370f494e05 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 00fcbed1afd2..2bc06e7344db 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -395,7 +395,7 @@ config MMC_SPI
config MMC_S3C
tristate "Samsung S3C SD/MMC Card Interface support"
- depends on ARCH_S3C2410
+ depends on ARCH_S3C24XX
help
This selects a driver for the MCI interface found in
Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs.
@@ -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 745f8fce2519..3e7e26d08073 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/at91_mci.c b/drivers/mmc/host/at91_mci.c
index 947faa5d2ce4..efdb81d21c44 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -86,7 +86,6 @@ static inline int at91mci_is_mci1rev2xx(void)
{
return ( cpu_is_at91sam9260()
|| cpu_is_at91sam9263()
- || cpu_is_at91cap9()
|| cpu_is_at91sam9rl()
|| cpu_is_at91sam9g10()
|| cpu_is_at91sam9g20()
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index e4449a54ae8f..390863e7efbd 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1975,7 +1975,7 @@ 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));
return true;
}
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 64a8325a4a8a..c1f3673ae1ef 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 000000000000..dc0d25a013e0
--- /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 000000000000..92ec3eb3aae7
--- /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 8bec1c36b159..bf3c9b456aaf 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 df392a1143f2..15c27e17c23f 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 11e589cd8233..983e244eca76 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -53,6 +53,8 @@ static unsigned int fmax = 515633;
* @sdio: variant supports SDIO
* @st_clkdiv: true if using a ST-specific clock divider algorithm
* @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
+ * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @signal_direction: input/out direction of bus signals can be indicated
*/
struct variant_data {
unsigned int clkreg;
@@ -63,18 +65,22 @@ struct variant_data {
bool sdio;
bool st_clkdiv;
bool blksz_datactrl16;
+ u32 pwrreg_powerup;
+ bool signal_direction;
};
static struct variant_data variant_arm = {
.fifosize = 16 * 4,
.fifohalfsize = 8 * 4,
.datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
};
static struct variant_data variant_arm_extended_fifo = {
.fifosize = 128 * 4,
.fifohalfsize = 64 * 4,
.datalength_bits = 16,
+ .pwrreg_powerup = MCI_PWR_UP,
};
static struct variant_data variant_u300 = {
@@ -83,6 +89,8 @@ static struct variant_data variant_u300 = {
.clkreg_enable = MCI_ST_U300_HWFCEN,
.datalength_bits = 16,
.sdio = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
};
static struct variant_data variant_ux500 = {
@@ -93,6 +101,8 @@ static struct variant_data variant_ux500 = {
.datalength_bits = 24,
.sdio = true,
.st_clkdiv = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
};
static struct variant_data variant_ux500v2 = {
@@ -104,11 +114,35 @@ static struct variant_data variant_ux500v2 = {
.sdio = true,
.st_clkdiv = true,
.blksz_datactrl16 = true,
+ .pwrreg_powerup = MCI_PWR_ON,
+ .signal_direction = true,
};
/*
* This must be called with host->lock held
*/
+static void mmci_write_clkreg(struct mmci_host *host, u32 clk)
+{
+ if (host->clk_reg != clk) {
+ host->clk_reg = clk;
+ writel(clk, host->base + MMCICLOCK);
+ }
+}
+
+/*
+ * This must be called with host->lock held
+ */
+static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
+{
+ if (host->pwr_reg != pwr) {
+ host->pwr_reg = pwr;
+ writel(pwr, host->base + MMCIPOWER);
+ }
+}
+
+/*
+ * This must be called with host->lock held
+ */
static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
{
struct variant_data *variant = host->variant;
@@ -153,7 +187,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
clk |= MCI_ST_8BIT_BUS;
- writel(clk, host->base + MMCICLOCK);
+ mmci_write_clkreg(host, clk);
}
static void
@@ -166,14 +200,10 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
host->mrq = NULL;
host->cmd = NULL;
- /*
- * Need to drop the host lock here; mmc_request_done may call
- * back into the driver...
- */
- spin_unlock(&host->lock);
- pm_runtime_put(mmc_dev(host->mmc));
mmc_request_done(host->mmc, mrq);
- spin_lock(&host->lock);
+
+ pm_runtime_mark_last_busy(mmc_dev(host->mmc));
+ pm_runtime_put_autosuspend(mmc_dev(host->mmc));
}
static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
@@ -607,6 +637,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
if (data->flags & MMC_DATA_READ)
datactrl |= MCI_DPSM_DIRECTION;
+ /* The ST Micro variants has a special bit to enable SDIO */
+ if (variant->sdio && host->mmc->card)
+ if (mmc_card_sdio(host->mmc->card))
+ datactrl |= MCI_ST_DPSM_SDIOEN;
+
/*
* Attempt to use DMA operation mode, if this
* should fail, fall back to PIO mode
@@ -635,11 +670,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
irqmask = MCI_TXFIFOHALFEMPTYMASK;
}
- /* The ST Micro variants has a special bit to enable SDIO */
- if (variant->sdio && host->mmc->card)
- if (mmc_card_sdio(host->mmc->card))
- datactrl |= MCI_ST_DPSM_SDIOEN;
-
writel(datactrl, base + MMCIDATACTRL);
writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
mmci_set_mask1(host, irqmask);
@@ -786,7 +816,24 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
if (count <= 0)
break;
- readsl(base + MMCIFIFO, ptr, count >> 2);
+ /*
+ * SDIO especially may want to send something that is
+ * not divisible by 4 (as opposed to card sectors
+ * etc). Therefore make sure to always read the last bytes
+ * while only doing full 32-bit reads towards the FIFO.
+ */
+ if (unlikely(count & 0x3)) {
+ if (count < 4) {
+ unsigned char buf[4];
+ readsl(base + MMCIFIFO, buf, 1);
+ memcpy(ptr, buf, count);
+ } else {
+ readsl(base + MMCIFIFO, ptr, count >> 2);
+ count &= ~0x3;
+ }
+ } else {
+ readsl(base + MMCIFIFO, ptr, count >> 2);
+ }
ptr += count;
remain -= count;
@@ -821,14 +868,13 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
*/
if (variant->sdio &&
mmc_card_sdio(host->mmc->card)) {
+ u32 clk;
if (count < 8)
- writel(readl(host->base + MMCICLOCK) &
- ~variant->clkreg_enable,
- host->base + MMCICLOCK);
+ clk = host->clk_reg & ~variant->clkreg_enable;
else
- writel(readl(host->base + MMCICLOCK) |
- variant->clkreg_enable,
- host->base + MMCICLOCK);
+ clk = host->clk_reg | variant->clkreg_enable;
+
+ mmci_write_clkreg(host, clk);
}
/*
@@ -1015,10 +1061,17 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct mmci_host *host = mmc_priv(mmc);
+ struct variant_data *variant = host->variant;
u32 pwr = 0;
unsigned long flags;
int ret;
+ pm_runtime_get_sync(mmc_dev(mmc));
+
+ if (host->plat->ios_handler &&
+ host->plat->ios_handler(mmc_dev(mmc), ios))
+ dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
+
switch (ios->power_mode) {
case MMC_POWER_OFF:
if (host->vcc)
@@ -1035,22 +1088,38 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* power should be rare so we print an error
* and return here.
*/
- return;
+ goto out;
}
}
- if (host->plat->vdd_handler)
- pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd,
- ios->power_mode);
- /* The ST version does not have this, fall through to POWER_ON */
- if (host->hw_designer != AMBA_VENDOR_ST) {
- pwr |= MCI_PWR_UP;
- break;
- }
+ /*
+ * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
+ * and instead uses MCI_PWR_ON so apply whatever value is
+ * configured in the variant data.
+ */
+ pwr |= variant->pwrreg_powerup;
+
+ break;
case MMC_POWER_ON:
pwr |= MCI_PWR_ON;
break;
}
+ if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
+ /*
+ * The ST Micro variant has some additional bits
+ * indicating signal direction for the signals in
+ * the SD/MMC bus and feedback-clock usage.
+ */
+ pwr |= host->plat->sigdir;
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ pwr &= ~MCI_ST_DATA74DIREN;
+ else if (ios->bus_width == MMC_BUS_WIDTH_1)
+ pwr &= (~MCI_ST_DATA74DIREN &
+ ~MCI_ST_DATA31DIREN &
+ ~MCI_ST_DATA2DIREN);
+ }
+
if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
if (host->hw_designer != AMBA_VENDOR_ST)
pwr |= MCI_ROD;
@@ -1066,13 +1135,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_lock_irqsave(&host->lock, flags);
mmci_set_clkreg(host, ios->clock);
-
- if (host->pwr != pwr) {
- host->pwr = pwr;
- writel(pwr, host->base + MMCIPOWER);
- }
+ mmci_write_pwrreg(host, pwr);
spin_unlock_irqrestore(&host->lock, flags);
+
+ out:
+ pm_runtime_mark_last_busy(mmc_dev(mmc));
+ pm_runtime_put_autosuspend(mmc_dev(mmc));
}
static int mmci_get_ro(struct mmc_host *mmc)
@@ -1326,7 +1395,7 @@ static int __devinit mmci_probe(struct amba_device *dev,
if (ret)
goto unmap;
- if (dev->irq[1] == NO_IRQ)
+ if (dev->irq[1] == NO_IRQ || !dev->irq[1])
host->singleirq = true;
else {
ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED,
@@ -1346,6 +1415,8 @@ static int __devinit mmci_probe(struct amba_device *dev,
mmci_dma_setup(host);
+ pm_runtime_set_autosuspend_delay(&dev->dev, 50);
+ pm_runtime_use_autosuspend(&dev->dev);
pm_runtime_put(&dev->dev);
mmc_add_host(mmc);
@@ -1430,43 +1501,49 @@ static int __devexit mmci_remove(struct amba_device *dev)
return 0;
}
-#ifdef CONFIG_PM
-static int mmci_suspend(struct amba_device *dev, pm_message_t state)
+#ifdef CONFIG_SUSPEND
+static int mmci_suspend(struct device *dev)
{
- struct mmc_host *mmc = amba_get_drvdata(dev);
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
ret = mmc_suspend_host(mmc);
- if (ret == 0)
+ if (ret == 0) {
+ pm_runtime_get_sync(dev);
writel(0, host->base + MMCIMASK0);
+ }
}
return ret;
}
-static int mmci_resume(struct amba_device *dev)
+static int mmci_resume(struct device *dev)
{
- struct mmc_host *mmc = amba_get_drvdata(dev);
+ struct amba_device *adev = to_amba_device(dev);
+ struct mmc_host *mmc = amba_get_drvdata(adev);
int ret = 0;
if (mmc) {
struct mmci_host *host = mmc_priv(mmc);
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+ pm_runtime_put(dev);
ret = mmc_resume_host(mmc);
}
return ret;
}
-#else
-#define mmci_suspend NULL
-#define mmci_resume NULL
#endif
+static const struct dev_pm_ops mmci_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume)
+};
+
static struct amba_id mmci_ids[] = {
{
.id = 0x00041180,
@@ -1512,26 +1589,15 @@ MODULE_DEVICE_TABLE(amba, mmci_ids);
static struct amba_driver mmci_driver = {
.drv = {
.name = DRIVER_NAME,
+ .pm = &mmci_dev_pm_ops,
},
.probe = mmci_probe,
.remove = __devexit_p(mmci_remove),
- .suspend = mmci_suspend,
- .resume = mmci_resume,
.id_table = mmci_ids,
};
-static int __init mmci_init(void)
-{
- return amba_driver_register(&mmci_driver);
-}
-
-static void __exit mmci_exit(void)
-{
- amba_driver_unregister(&mmci_driver);
-}
+module_amba_driver(mmci_driver);
-module_init(mmci_init);
-module_exit(mmci_exit);
module_param(fmax, uint, 0444);
MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 79e4143ab9df..d437ccf62d6b 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -13,16 +13,6 @@
#define MCI_PWR_ON 0x03
#define MCI_OD (1 << 6)
#define MCI_ROD (1 << 7)
-/*
- * The ST Micro version does not have ROD and reuse the voltage registers
- * for direction settings
- */
-#define MCI_ST_DATA2DIREN (1 << 2)
-#define MCI_ST_CMDDIREN (1 << 3)
-#define MCI_ST_DATA0DIREN (1 << 4)
-#define MCI_ST_DATA31DIREN (1 << 5)
-#define MCI_ST_FBCLKEN (1 << 7)
-#define MCI_ST_DATA74DIREN (1 << 8)
#define MMCICLOCK 0x004
#define MCI_CLK_ENABLE (1 << 8)
@@ -160,7 +150,7 @@
(MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \
MCI_TXFIFOHALFEMPTYMASK)
-#define NR_SG 16
+#define NR_SG 128
struct clk;
struct variant_data;
@@ -189,7 +179,8 @@ struct mmci_host {
unsigned int mclk;
unsigned int cclk;
- u32 pwr;
+ u32 pwr_reg;
+ u32 clk_reg;
struct mmci_platform_data *plat;
struct variant_data *variant;
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index fd0c661bbad3..47adb161d3ad 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-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 0be4e2013632..6193a0d7bde5 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -464,7 +464,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev)
err = PTR_ERR(clk);
goto err_clk_get;
}
- clk_enable(clk);
+ clk_prepare_enable(clk);
pltfm_host->clk = clk;
if (!is_imx25_esdhc(imx_data))
@@ -559,7 +559,7 @@ no_card_detect_irq:
gpio_free(boarddata->wp_gpio);
no_card_detect_pin:
no_board_data:
- clk_disable(pltfm_host->clk);
+ clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
err_clk_get:
kfree(imx_data);
@@ -586,7 +586,7 @@ static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev)
gpio_free(boarddata->cd_gpio);
}
- clk_disable(pltfm_host->clk);
+ clk_disable_unprepare(pltfm_host->clk);
clk_put(pltfm_host->clk);
kfree(imx_data);
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index 5d876ff86f37..f8eb1fb0c921 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 6ebdc4010e7c..fbbebe251e01 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-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 1af756ee0f9a..b19e7d435f8d 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -518,9 +518,6 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
if (pdata->cd_type == S3C_SDHCI_CD_PERMANENT)
host->mmc->caps = MMC_CAP_NONREMOVABLE;
- if (pdata->host_caps)
- host->mmc->caps |= pdata->host_caps;
-
if (pdata->pm_caps)
host->mmc->pm_caps |= pdata->pm_caps;
@@ -544,6 +541,9 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev)
if (pdata->host_caps)
host->mmc->caps |= pdata->host_caps;
+ if (pdata->host_caps2)
+ host->mmc->caps2 |= pdata->host_caps2;
+
ret = sdhci_add_host(host);
if (ret) {
dev_err(dev, "sdhci_add_host() failed\n");
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index b7f8b33c5f19..6dfa82e03c7e 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 78a36eba4df0..53b26502f6e2 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -19,11 +19,11 @@
#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>
#include <linux/mmc/host.h>
-#include <linux/module.h>
#include <asm/gpio.h>
@@ -32,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;
@@ -47,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;
}
@@ -57,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.
@@ -66,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)
@@ -77,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;
@@ -99,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);
@@ -125,7 +150,8 @@ 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 |
@@ -133,8 +159,35 @@ static struct sdhci_pltfm_data sdhci_tegra_pdata = {
.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);
@@ -165,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);
@@ -188,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");
@@ -284,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);
@@ -327,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 8d66706824a6..8262cadfdab7 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 ad265b96b75b..f761f23d2a28 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 75a485448796..60f205708f54 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -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 58da3c44acc5..934b68e9efc3 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 f96c536d130a..d857f5c6e7d9 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_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index e21988901c36..9a7996ade58e 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 1be621841400..284cf3433720 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
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index e1e122f2f929..9bcd1f415f43 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -2526,12 +2526,10 @@ static void cfi_intelext_restore_locks(struct mtd_info *mtd)
if (!region->lockmap)
continue;
- for (block = 0; block < region->numblocks; block++) {
+ for_each_clear_bit(block, region->lockmap, region->numblocks) {
len = region->erasesize;
adr = region->offset + block * len;
-
- if (!test_bit(block, region->lockmap))
- cfi_intelext_unlock(mtd, adr, len);
+ cfi_intelext_unlock(mtd, adr, len);
}
}
}
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 37b05c3f2792..8d3dac40d7e6 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"
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index ecff765579dd..5d53c5760a6c 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -93,7 +93,6 @@
#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>
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index e585263161b9..288594163c22 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>
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 6c5c431c64af..8af67cfd671a 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/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index e8e9fec23553..0259cf583022 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>
diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c
index 502821997707..cbc3b7867910 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;
@@ -373,21 +273,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/mtdchar.c b/drivers/mtd/mtdchar.c
index 50c6a1e7f675..c57ae92ebda4 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -31,13 +31,13 @@
#include <linux/compat.h>
#include <linux/mount.h>
#include <linux/blkpg.h>
+#include <linux/magic.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/map.h>
#include <asm/uaccess.h>
-#define MTD_INODE_FS_MAGIC 0x11307854
static DEFINE_MUTEX(mtd_mutex);
static struct vfsmount *mtd_inode_mnt __read_mostly;
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 3b1d6da874e0..a3c4de551ebe 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -187,7 +187,7 @@ config MTD_NAND_PPCHAMELEONEVB
config MTD_NAND_S3C2410
tristate "NAND Flash support for Samsung S3C SoCs"
- depends on ARCH_S3C2410 || ARCH_S3C64XX
+ depends on ARCH_S3C24XX || ARCH_S3C64XX
help
This enables the NAND flash controller on the S3C24xx and S3C64xx
SoCs
@@ -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
@@ -431,6 +432,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/ams-delta.c b/drivers/mtd/nand/ams-delta.c
index 3197e9764fcd..73416951f4c1 100644
--- a/drivers/mtd/nand/ams-delta.c
+++ b/drivers/mtd/nand/ams-delta.c
@@ -26,7 +26,7 @@
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/sizes.h>
-#include <asm/gpio.h>
+#include <linux/gpio.h>
#include <plat/board-ams-delta.h>
/*
@@ -34,8 +34,6 @@
*/
static struct mtd_info *ams_delta_mtd = NULL;
-#define NAND_MASK (AMS_DELTA_LATCH2_NAND_NRE | AMS_DELTA_LATCH2_NAND_NWE | AMS_DELTA_LATCH2_NAND_CLE | AMS_DELTA_LATCH2_NAND_ALE | AMS_DELTA_LATCH2_NAND_NCE | AMS_DELTA_LATCH2_NAND_NWP)
-
/*
* Define partitions for flash devices
*/
@@ -68,10 +66,9 @@ static void ams_delta_write_byte(struct mtd_info *mtd, u_char byte)
writew(0, io_base + OMAP_MPUIO_IO_CNTL);
writew(byte, this->IO_ADDR_W);
- ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE, 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 0);
ndelay(40);
- ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NWE,
- AMS_DELTA_LATCH2_NAND_NWE);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NWE, 1);
}
static u_char ams_delta_read_byte(struct mtd_info *mtd)
@@ -80,12 +77,11 @@ static u_char ams_delta_read_byte(struct mtd_info *mtd)
struct nand_chip *this = mtd->priv;
void __iomem *io_base = this->priv;
- ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE, 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 0);
ndelay(40);
writew(~0, io_base + OMAP_MPUIO_IO_CNTL);
res = readw(this->IO_ADDR_R);
- ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_NRE,
- AMS_DELTA_LATCH2_NAND_NRE);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NRE, 1);
return res;
}
@@ -132,15 +128,12 @@ static void ams_delta_hwcontrol(struct mtd_info *mtd, int cmd,
{
if (ctrl & NAND_CTRL_CHANGE) {
- unsigned long bits;
-
- bits = (~ctrl & NAND_NCE) ? AMS_DELTA_LATCH2_NAND_NCE : 0;
- bits |= (ctrl & NAND_CLE) ? AMS_DELTA_LATCH2_NAND_CLE : 0;
- bits |= (ctrl & NAND_ALE) ? AMS_DELTA_LATCH2_NAND_ALE : 0;
-
- ams_delta_latch2_write(AMS_DELTA_LATCH2_NAND_CLE |
- AMS_DELTA_LATCH2_NAND_ALE |
- AMS_DELTA_LATCH2_NAND_NCE, bits);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_NCE,
+ (ctrl & NAND_NCE) == 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_CLE,
+ (ctrl & NAND_CLE) != 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_NAND_ALE,
+ (ctrl & NAND_ALE) != 0);
}
if (cmd != NAND_CMD_NONE)
@@ -152,6 +145,39 @@ static int ams_delta_nand_ready(struct mtd_info *mtd)
return gpio_get_value(AMS_DELTA_GPIO_PIN_NAND_RB);
}
+static const struct gpio _mandatory_gpio[] = {
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NCE,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nce",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NRE,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nre",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NWP,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nwp",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_NWE,
+ .flags = GPIOF_OUT_INIT_HIGH,
+ .label = "nand_nwe",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_ALE,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "nand_ale",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_NAND_CLE,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "nand_cle",
+ },
+};
+
/*
* Main initialization routine
*/
@@ -223,10 +249,9 @@ static int __devinit ams_delta_init(struct platform_device *pdev)
platform_set_drvdata(pdev, io_base);
/* Set chip enabled, but */
- ams_delta_latch2_write(NAND_MASK, AMS_DELTA_LATCH2_NAND_NRE |
- AMS_DELTA_LATCH2_NAND_NWE |
- AMS_DELTA_LATCH2_NAND_NCE |
- AMS_DELTA_LATCH2_NAND_NWP);
+ err = gpio_request_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
+ if (err)
+ goto out_gpio;
/* Scan to find existence of the device */
if (nand_scan(ams_delta_mtd, 1)) {
@@ -241,7 +266,10 @@ static int __devinit ams_delta_init(struct platform_device *pdev)
goto out;
out_mtd:
+ gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
+out_gpio:
platform_set_drvdata(pdev, NULL);
+ gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
iounmap(io_base);
out_release_io:
release_mem_region(res->start, resource_size(res));
@@ -262,6 +290,8 @@ static int __devexit ams_delta_cleanup(struct platform_device *pdev)
/* Release resources, unregister device */
nand_release(ams_delta_mtd);
+ gpio_free_array(_mandatory_gpio, ARRAY_SIZE(_mandatory_gpio));
+ gpio_free(AMS_DELTA_GPIO_PIN_NAND_RB);
iounmap(io_base);
release_mem_region(res->start, resource_size(res));
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index 35b4fb55dbd6..ae7e37d9ac17 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,26 +579,25 @@ 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;
@@ -558,7 +607,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
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 +616,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 +699,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 +745,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 50387fd4009b..64c9cbaf86a1 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>
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 772ad2966619..91467bb03634 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/ubi/build.c b/drivers/mtd/ubi/build.c
index 115749f20f9e..0fde9fc7d2e5 100644
--- a/drivers/mtd/ubi/build.c
+++ b/drivers/mtd/ubi/build.c
@@ -945,12 +945,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset)
goto out_free;
err = -ENOMEM;
- ubi->peb_buf1 = vmalloc(ubi->peb_size);
- if (!ubi->peb_buf1)
- goto out_free;
-
- ubi->peb_buf2 = vmalloc(ubi->peb_size);
- if (!ubi->peb_buf2)
+ ubi->peb_buf = vmalloc(ubi->peb_size);
+ if (!ubi->peb_buf)
goto out_free;
err = ubi_debugging_init_dev(ubi);
@@ -1029,8 +1025,7 @@ out_detach:
out_debugging:
ubi_debugging_exit_dev(ubi);
out_free:
- vfree(ubi->peb_buf1);
- vfree(ubi->peb_buf2);
+ vfree(ubi->peb_buf);
if (ref)
put_device(&ubi->dev);
else
@@ -1101,8 +1096,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway)
vfree(ubi->vtbl);
put_mtd_device(ubi->mtd);
ubi_debugging_exit_dev(ubi);
- vfree(ubi->peb_buf1);
- vfree(ubi->peb_buf2);
+ vfree(ubi->peb_buf);
ubi_msg("mtd%d is detached from ubi%d", ubi->mtd->index, ubi->ubi_num);
put_device(&ubi->dev);
return 0;
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index cd26da8ad225..2455d620d96b 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -529,18 +529,18 @@ retry:
data_size = offset + len;
mutex_lock(&ubi->buf_mutex);
- memset(ubi->peb_buf1 + offset, 0xFF, len);
+ memset(ubi->peb_buf + offset, 0xFF, len);
/* Read everything before the area where the write failure happened */
if (offset > 0) {
- err = ubi_io_read_data(ubi, ubi->peb_buf1, pnum, 0, offset);
+ err = ubi_io_read_data(ubi, ubi->peb_buf, pnum, 0, offset);
if (err && err != UBI_IO_BITFLIPS)
goto out_unlock;
}
- memcpy(ubi->peb_buf1 + offset, buf, len);
+ memcpy(ubi->peb_buf + offset, buf, len);
- err = ubi_io_write_data(ubi, ubi->peb_buf1, new_pnum, 0, data_size);
+ err = ubi_io_write_data(ubi, ubi->peb_buf, new_pnum, 0, data_size);
if (err) {
mutex_unlock(&ubi->buf_mutex);
goto write_error;
@@ -979,7 +979,7 @@ static int is_error_sane(int err)
* physical eraseblock @to. The @vid_hdr buffer may be changed by this
* function. Returns:
* o %0 in case of success;
- * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_CANCEL_BITFLIPS, etc;
+ * o %MOVE_CANCEL_RACE, %MOVE_TARGET_WR_ERR, %MOVE_TARGET_BITFLIPS, etc;
* o a negative error code in case of failure.
*/
int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
@@ -1053,13 +1053,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
/*
* OK, now the LEB is locked and we can safely start moving it. Since
- * this function utilizes the @ubi->peb_buf1 buffer which is shared
+ * this function utilizes the @ubi->peb_buf buffer which is shared
* with some other functions - we lock the buffer by taking the
* @ubi->buf_mutex.
*/
mutex_lock(&ubi->buf_mutex);
dbg_wl("read %d bytes of data", aldata_size);
- err = ubi_io_read_data(ubi, ubi->peb_buf1, from, 0, aldata_size);
+ err = ubi_io_read_data(ubi, ubi->peb_buf, from, 0, aldata_size);
if (err && err != UBI_IO_BITFLIPS) {
ubi_warn("error %d while reading data from PEB %d",
err, from);
@@ -1079,10 +1079,10 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
*/
if (vid_hdr->vol_type == UBI_VID_DYNAMIC)
aldata_size = data_size =
- ubi_calc_data_len(ubi, ubi->peb_buf1, data_size);
+ ubi_calc_data_len(ubi, ubi->peb_buf, data_size);
cond_resched();
- crc = crc32(UBI_CRC32_INIT, ubi->peb_buf1, data_size);
+ crc = crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size);
cond_resched();
/*
@@ -1116,12 +1116,12 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
if (is_error_sane(err))
err = MOVE_TARGET_RD_ERR;
} else
- err = MOVE_CANCEL_BITFLIPS;
+ err = MOVE_TARGET_BITFLIPS;
goto out_unlock_buf;
}
if (data_size > 0) {
- err = ubi_io_write_data(ubi, ubi->peb_buf1, to, 0, aldata_size);
+ err = ubi_io_write_data(ubi, ubi->peb_buf, to, 0, aldata_size);
if (err) {
if (err == -EIO)
err = MOVE_TARGET_WR_ERR;
@@ -1134,8 +1134,8 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
* We've written the data and are going to read it back to make
* sure it was written correctly.
*/
-
- err = ubi_io_read_data(ubi, ubi->peb_buf2, to, 0, aldata_size);
+ memset(ubi->peb_buf, 0xFF, aldata_size);
+ err = ubi_io_read_data(ubi, ubi->peb_buf, to, 0, aldata_size);
if (err) {
if (err != UBI_IO_BITFLIPS) {
ubi_warn("error %d while reading data back "
@@ -1143,13 +1143,13 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
if (is_error_sane(err))
err = MOVE_TARGET_RD_ERR;
} else
- err = MOVE_CANCEL_BITFLIPS;
+ err = MOVE_TARGET_BITFLIPS;
goto out_unlock_buf;
}
cond_resched();
- if (memcmp(ubi->peb_buf1, ubi->peb_buf2, aldata_size)) {
+ if (crc != crc32(UBI_CRC32_INIT, ubi->peb_buf, data_size)) {
ubi_warn("read data back from PEB %d and it is "
"different", to);
err = -EINVAL;
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index 5cde4e5ca3e5..43f1a0011a55 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -431,11 +431,11 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
goto out;
/* Make sure the PEB contains only 0xFF bytes */
- err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
+ err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
if (err)
goto out;
- err = ubi_check_pattern(ubi->peb_buf1, 0xFF, ubi->peb_size);
+ err = ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->peb_size);
if (err == 0) {
ubi_err("erased PEB %d, but a non-0xFF byte found",
pnum);
@@ -444,17 +444,17 @@ static int torture_peb(struct ubi_device *ubi, int pnum)
}
/* Write a pattern and check it */
- memset(ubi->peb_buf1, patterns[i], ubi->peb_size);
- err = ubi_io_write(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
+ memset(ubi->peb_buf, patterns[i], ubi->peb_size);
+ err = ubi_io_write(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
if (err)
goto out;
- memset(ubi->peb_buf1, ~patterns[i], ubi->peb_size);
- err = ubi_io_read(ubi, ubi->peb_buf1, pnum, 0, ubi->peb_size);
+ memset(ubi->peb_buf, ~patterns[i], ubi->peb_size);
+ err = ubi_io_read(ubi, ubi->peb_buf, pnum, 0, ubi->peb_size);
if (err)
goto out;
- err = ubi_check_pattern(ubi->peb_buf1, patterns[i],
+ err = ubi_check_pattern(ubi->peb_buf, patterns[i],
ubi->peb_size);
if (err == 0) {
ubi_err("pattern %x checking failed for PEB %d",
diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c
index 0cb17d936b5a..12c43b44f815 100644
--- a/drivers/mtd/ubi/scan.c
+++ b/drivers/mtd/ubi/scan.c
@@ -789,9 +789,9 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr,
int err;
mutex_lock(&ubi->buf_mutex);
- memset(ubi->peb_buf1, 0x00, ubi->leb_size);
+ memset(ubi->peb_buf, 0x00, ubi->leb_size);
- err = ubi_io_read(ubi, ubi->peb_buf1, pnum, ubi->leb_start,
+ err = ubi_io_read(ubi, ubi->peb_buf, pnum, ubi->leb_start,
ubi->leb_size);
if (err == UBI_IO_BITFLIPS || mtd_is_eccerr(err)) {
/*
@@ -808,7 +808,7 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr,
if (err)
goto out_unlock;
- if (ubi_check_pattern(ubi->peb_buf1, 0xFF, ubi->leb_size))
+ if (ubi_check_pattern(ubi->peb_buf, 0xFF, ubi->leb_size))
goto out_unlock;
ubi_err("PEB %d contains corrupted VID header, and the data does not "
@@ -818,7 +818,7 @@ static int check_corruption(struct ubi_device *ubi, struct ubi_vid_hdr *vid_hdr,
dbg_msg("hexdump of PEB %d offset %d, length %d",
pnum, ubi->leb_start, ubi->leb_size);
ubi_dbg_print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1,
- ubi->peb_buf1, ubi->leb_size, 1);
+ ubi->peb_buf, ubi->leb_size, 1);
err = 1;
out_unlock:
@@ -1174,7 +1174,7 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi)
ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL);
if (!ech)
- goto out_slab;
+ goto out_si;
vidh = ubi_zalloc_vid_hdr(ubi, GFP_KERNEL);
if (!vidh)
@@ -1235,8 +1235,6 @@ out_vidh:
ubi_free_vid_hdr(ubi, vidh);
out_ech:
kfree(ech);
-out_slab:
- kmem_cache_destroy(si->scan_leb_slab);
out_si:
ubi_scan_destroy_si(si);
return ERR_PTR(err);
@@ -1325,7 +1323,9 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si)
}
}
- kmem_cache_destroy(si->scan_leb_slab);
+ if (si->scan_leb_slab)
+ kmem_cache_destroy(si->scan_leb_slab);
+
kfree(si);
}
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index d51d75d34446..b162790790a9 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -118,7 +118,7 @@ enum {
* PEB
* MOVE_TARGET_WR_ERR: canceled because there was a write error to the target
* PEB
- * MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the
+ * MOVE_TARGET_BITFLIPS: canceled because a bit-flip was detected in the
* target PEB
* MOVE_RETRY: retry scrubbing the PEB
*/
@@ -127,7 +127,7 @@ enum {
MOVE_SOURCE_RD_ERR,
MOVE_TARGET_RD_ERR,
MOVE_TARGET_WR_ERR,
- MOVE_CANCEL_BITFLIPS,
+ MOVE_TARGET_BITFLIPS,
MOVE_RETRY,
};
@@ -387,9 +387,8 @@ struct ubi_wl_entry;
* time (MTD write buffer size)
* @mtd: MTD device descriptor
*
- * @peb_buf1: a buffer of PEB size used for different purposes
- * @peb_buf2: another buffer of PEB size used for different purposes
- * @buf_mutex: protects @peb_buf1 and @peb_buf2
+ * @peb_buf: a buffer of PEB size used for different purposes
+ * @buf_mutex: protects @peb_buf
* @ckvol_mutex: serializes static volume checking when opening
*
* @dbg: debugging information for this UBI device
@@ -471,8 +470,7 @@ struct ubi_device {
int max_write_size;
struct mtd_info *mtd;
- void *peb_buf1;
- void *peb_buf2;
+ void *peb_buf;
struct mutex buf_mutex;
struct mutex ckvol_mutex;
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 0696e36b0539..7c1a9bf8ac86 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -350,18 +350,19 @@ static void prot_queue_add(struct ubi_device *ubi, struct ubi_wl_entry *e)
/**
* find_wl_entry - find wear-leveling entry closest to certain erase counter.
* @root: the RB-tree where to look for
- * @max: highest possible erase counter
+ * @diff: maximum possible difference from the smallest erase counter
*
* This function looks for a wear leveling entry with erase counter closest to
- * @max and less than @max.
+ * min + @diff, where min is the smallest erase counter.
*/
-static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max)
+static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int diff)
{
struct rb_node *p;
struct ubi_wl_entry *e;
+ int max;
e = rb_entry(rb_first(root), struct ubi_wl_entry, u.rb);
- max += e->ec;
+ max = e->ec + diff;
p = root->rb_node;
while (p) {
@@ -389,7 +390,7 @@ static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max)
*/
int ubi_wl_get_peb(struct ubi_device *ubi, int dtype)
{
- int err, medium_ec;
+ int err;
struct ubi_wl_entry *e, *first, *last;
ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM ||
@@ -427,7 +428,7 @@ retry:
* For unknown data we pick a physical eraseblock with medium
* erase counter. But we by no means can pick a physical
* eraseblock with erase counter greater or equivalent than the
- * lowest erase counter plus %WL_FREE_MAX_DIFF.
+ * lowest erase counter plus %WL_FREE_MAX_DIFF/2.
*/
first = rb_entry(rb_first(&ubi->free), struct ubi_wl_entry,
u.rb);
@@ -436,10 +437,8 @@ retry:
if (last->ec - first->ec < WL_FREE_MAX_DIFF)
e = rb_entry(ubi->free.rb_node,
struct ubi_wl_entry, u.rb);
- else {
- medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2;
- e = find_wl_entry(&ubi->free, medium_ec);
- }
+ else
+ e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF/2);
break;
case UBI_SHORTTERM:
/*
@@ -799,7 +798,7 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
scrubbing = 1;
goto out_not_moved;
}
- if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR ||
+ if (err == MOVE_TARGET_BITFLIPS || err == MOVE_TARGET_WR_ERR ||
err == MOVE_TARGET_RD_ERR) {
/*
* Target PEB had bit-flips or write error - torture it.
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index 068c3563e00f..88bbd8ffa7fe 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -190,8 +190,10 @@ static struct devprobe2 isa_probes[] __initdata = {
{seeq8005_probe, 0},
#endif
#ifdef CONFIG_CS89x0
+#ifndef CONFIG_CS89x0_PLATFORM
{cs89x0_probe, 0},
#endif
+#endif
#ifdef CONFIG_AT1700
{at1700_probe, 0},
#endif
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index 9abd4eb86dc1..dd5e04813b76 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 6057b30417a2..0910dce3996d 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 980e65c14936..5bed4c4e2508 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 0730203a19f2..0c76186bb9e7 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>
@@ -2573,12 +2572,16 @@ re_arm:
static int bond_has_this_ip(struct bonding *bond, __be32 ip)
{
struct vlan_entry *vlan;
+ struct net_device *vlan_dev;
- if (ip == bond->master_ip)
+ if (ip == bond_confirm_addr(bond->dev, 0, ip))
return 1;
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- if (ip == vlan->vlan_ip)
+ rcu_read_lock();
+ vlan_dev = __vlan_find_dev_deep(bond->dev, vlan->vlan_id);
+ rcu_read_unlock();
+ if (vlan_dev && ip == bond_confirm_addr(vlan_dev, 0, ip))
return 1;
}
@@ -2620,17 +2623,19 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
int i, vlan_id;
__be32 *targets = bond->params.arp_targets;
struct vlan_entry *vlan;
- struct net_device *vlan_dev;
+ struct net_device *vlan_dev = NULL;
struct rtable *rt;
for (i = 0; (i < BOND_MAX_ARP_TARGETS); i++) {
+ __be32 addr;
if (!targets[i])
break;
pr_debug("basa: target %x\n", targets[i]);
if (!bond_vlan_used(bond)) {
pr_debug("basa: empty vlan: arp_send\n");
+ addr = bond_confirm_addr(bond->dev, targets[i], 0);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- bond->master_ip, 0);
+ addr, 0);
continue;
}
@@ -2655,8 +2660,9 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
if (rt->dst.dev == bond->dev) {
ip_rt_put(rt);
pr_debug("basa: rtdev == bond->dev: arp_send\n");
+ addr = bond_confirm_addr(bond->dev, targets[i], 0);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- bond->master_ip, 0);
+ addr, 0);
continue;
}
@@ -2674,10 +2680,11 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave)
}
}
- if (vlan_id) {
+ if (vlan_id && vlan_dev) {
ip_rt_put(rt);
+ addr = bond_confirm_addr(vlan_dev, targets[i], 0);
bond_arp_send(slave->dev, ARPOP_REQUEST, targets[i],
- vlan->vlan_ip, vlan_id);
+ addr, vlan_id);
continue;
}
@@ -3299,68 +3306,10 @@ static int bond_netdev_event(struct notifier_block *this,
return NOTIFY_DONE;
}
-/*
- * bond_inetaddr_event: handle inetaddr notifier chain events.
- *
- * We keep track of device IPs primarily to use as source addresses in
- * ARP monitor probes (rather than spewing out broadcasts all the time).
- *
- * We track one IP for the main device (if it has one), plus one per VLAN.
- */
-static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, void *ptr)
-{
- struct in_ifaddr *ifa = ptr;
- struct net_device *vlan_dev, *event_dev = ifa->ifa_dev->dev;
- struct bond_net *bn = net_generic(dev_net(event_dev), bond_net_id);
- struct bonding *bond;
- struct vlan_entry *vlan;
-
- /* we only care about primary address */
- if(ifa->ifa_flags & IFA_F_SECONDARY)
- return NOTIFY_DONE;
-
- list_for_each_entry(bond, &bn->dev_list, bond_list) {
- if (bond->dev == event_dev) {
- switch (event) {
- case NETDEV_UP:
- bond->master_ip = ifa->ifa_local;
- return NOTIFY_OK;
- case NETDEV_DOWN:
- bond->master_ip = 0;
- return NOTIFY_OK;
- default:
- return NOTIFY_DONE;
- }
- }
-
- list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
- vlan_dev = __vlan_find_dev_deep(bond->dev,
- vlan->vlan_id);
- if (vlan_dev == event_dev) {
- switch (event) {
- case NETDEV_UP:
- vlan->vlan_ip = ifa->ifa_local;
- return NOTIFY_OK;
- case NETDEV_DOWN:
- vlan->vlan_ip = 0;
- return NOTIFY_OK;
- default:
- return NOTIFY_DONE;
- }
- }
- }
- }
- return NOTIFY_DONE;
-}
-
static struct notifier_block bond_netdev_notifier = {
.notifier_call = bond_netdev_event,
};
-static struct notifier_block bond_inetaddr_notifier = {
- .notifier_call = bond_inetaddr_event,
-};
-
/*---------------------------- Hashing Policies -----------------------------*/
/*
@@ -4929,7 +4878,6 @@ static int __init bonding_init(void)
}
register_netdevice_notifier(&bond_netdev_notifier);
- register_inetaddr_notifier(&bond_inetaddr_notifier);
out:
return res;
err:
@@ -4943,7 +4891,6 @@ err_link:
static void __exit bonding_exit(void)
{
unregister_netdevice_notifier(&bond_netdev_notifier);
- unregister_inetaddr_notifier(&bond_inetaddr_notifier);
bond_destroy_debugfs();
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 1aecc37e5b4d..9f2bae6616d3 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -21,6 +21,7 @@
#include <linux/cpumask.h>
#include <linux/in6.h>
#include <linux/netpoll.h>
+#include <linux/inetdevice.h>
#include "bond_3ad.h"
#include "bond_alb.h"
@@ -166,7 +167,6 @@ struct bond_parm_tbl {
struct vlan_entry {
struct list_head vlan_list;
- __be32 vlan_ip;
unsigned short vlan_id;
};
@@ -232,7 +232,6 @@ struct bonding {
struct list_head bond_list;
struct netdev_hw_addr_list mc_list;
int (*xmit_hash_policy)(struct sk_buff *, int);
- __be32 master_ip;
u16 rr_tx_counter;
struct ad_bond_info ad_info;
struct alb_bond_info alb_info;
@@ -378,6 +377,21 @@ static inline bool bond_is_slave_inactive(struct slave *slave)
return slave->inactive;
}
+static inline __be32 bond_confirm_addr(struct net_device *dev, __be32 dst, __be32 local)
+{
+ struct in_device *in_dev;
+ __be32 addr = 0;
+
+ rcu_read_lock();
+ in_dev = __in_dev_get_rcu(dev);
+
+ if (in_dev)
+ addr = inet_confirm_addr(in_dev, dst, local, RT_SCOPE_HOST);
+
+ rcu_read_unlock();
+ return addr;
+}
+
struct bond_net;
struct vlan_entry *bond_next_vlan(struct bonding *bond, struct vlan_entry *curr);
diff --git a/drivers/net/can/slcan.c b/drivers/net/can/slcan.c
index 98a5a7d867f5..034c16b60e96 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 7cb2785e209d..ec03b401620a 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 e61b2f82ba3a..66df93638085 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 b23253b9f742..a556c01e011b 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 fbab1367505f..49d76bd0dc86 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 5337dd0a59b0..ccf07942ff6e 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 3ad5d2f9a49c..923959275a82 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 c30adcc9828a..11476ca95e93 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 c5bd8eb7a9f5..e1b3941bd149 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 d16dc53c1813..ed55ce85ebbf 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 6428f9e7a554..ba1b5c95531f 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 48c4948750d1..dbefd5658c14 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 d42938b6b596..52f70f999c00 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 113f1e075a26..37fa89aa4578 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 e77f624e8194..b329f5c0d62b 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 69490ae018ea..479409bf2e3c 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 af5d9822cad9..88ccc8b14f0a 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 9b9c77d5a65c..7fc28f2d28a6 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 f92ea2a65a57..d04911d33b64 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 922b32036c63..ef85839f43d8 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 3fab04a0034a..5e8845febfb8 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 2a3e8057feae..a2f8b2b8e27c 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 f2a4e5de18c4..de1af0bfed4c 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 77efec44fea0..7a68590f2804 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 1cc306a83ff7..b0fbce39661a 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 bb87053eb3da..923e42aedcfd 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 3b903759980a..8df4c4157230 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 c175fadb597b..03eb3eed49fa 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 bcd27323b203..7818e6397e91 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 6c3b1c0adaa0..7219123fa0a4 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 1b046f58d58f..6e722dc37db7 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 cc7b9e46780c..e10ffad525a7 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 9f62504d0086..64d0d9c1afa2 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 7dc508e5c72e..75299f500ee5 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 4e2d68a4de8a..8baff4e5d964 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 56bc47a94186..9af3c307862c 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 ebdb9e238a8d..9f59bf63514b 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 e3fe3504e198..d7a3533d990b 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/cnic.c b/drivers/net/ethernet/broadcom/cnic.c
index 7b65716b8734..c95e7b5e2b85 100644
--- a/drivers/net/ethernet/broadcom/cnic.c
+++ b/drivers/net/ethernet/broadcom/cnic.c
@@ -47,6 +47,7 @@
#include "bnx2x/bnx2x_hsi.h"
#include "../../../scsi/bnx2i/57xx_iscsi_constants.h"
#include "../../../scsi/bnx2i/57xx_iscsi_hsi.h"
+#include "../../../scsi/bnx2fc/bnx2fc_constants.h"
#include "cnic.h"
#include "cnic_defs.h"
@@ -2547,7 +2548,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe)
}
kcqe.kcqe_op_flag = kcqe_op << KCQE_FLAGS_OPCODE_SHIFT;
kcqe.kcqe_op_flag |= KCQE_FLAGS_LAYER_MASK_L5_FCOE;
- kcqe.kcqe_info1 = FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR;
+ kcqe.kcqe_info1 = FCOE_KCQE_COMPLETION_STATUS_PARITY_ERROR;
kcqe.kcqe_info2 = cid;
kcqe.kcqe_info0 = l5_cid;
@@ -2558,7 +2559,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe)
kcqe.kcqe_op_flag = (opcode + 0x10) << KCQE_FLAGS_OPCODE_SHIFT;
kcqe.kcqe_op_flag |= KCQE_FLAGS_LAYER_MASK_L5_ISCSI;
- kcqe.kcqe_info1 = ISCSI_KCQE_COMPLETION_STATUS_NIC_ERROR;
+ kcqe.kcqe_info1 = ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR;
kcqe.kcqe_info2 = cid;
cnic_get_l5_cid(cp, BNX2X_SW_CID(cid), &kcqe.kcqe_info0);
@@ -2577,7 +2578,7 @@ static void cnic_bnx2x_kwqe_err(struct cnic_dev *dev, struct kwqe *kwqe)
kcqe.kcqe_op_flag = (kcqe_op << KCQE_FLAGS_OPCODE_SHIFT) |
KCQE_FLAGS_LAYER_MASK_L4;
- l4kcqe->status = L4_KCQE_COMPLETION_STATUS_NIC_ERROR;
+ l4kcqe->status = L4_KCQE_COMPLETION_STATUS_PARITY_ERROR;
l4kcqe->cid = cid;
cnic_get_l5_cid(cp, BNX2X_SW_CID(cid), &l4kcqe->conn_id);
} else {
@@ -3933,7 +3934,8 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
case L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE:
if (l4kcqe->status == 0)
set_bit(SK_F_OFFLD_COMPLETE, &csk->flags);
- else if (l4kcqe->status == L4_KCQE_COMPLETION_STATUS_NIC_ERROR)
+ else if (l4kcqe->status ==
+ L4_KCQE_COMPLETION_STATUS_PARITY_ERROR)
set_bit(SK_F_HW_ERR, &csk->flags);
smp_mb__before_clear_bit();
@@ -3946,7 +3948,7 @@ static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
case L4_KCQE_OPCODE_VALUE_RESET_COMP:
case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE:
case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD:
- if (l4kcqe->status == L4_KCQE_COMPLETION_STATUS_NIC_ERROR)
+ if (l4kcqe->status == L4_KCQE_COMPLETION_STATUS_PARITY_ERROR)
set_bit(SK_F_HW_ERR, &csk->flags);
cp->close_conn(csk, opcode);
diff --git a/drivers/net/ethernet/broadcom/cnic_defs.h b/drivers/net/ethernet/broadcom/cnic_defs.h
index 06ca00266d70..382c98b0cc0c 100644
--- a/drivers/net/ethernet/broadcom/cnic_defs.h
+++ b/drivers/net/ethernet/broadcom/cnic_defs.h
@@ -35,16 +35,6 @@
#define L5CM_RAMROD_CMD_ID_SEARCHER_DELETE (L5CM_RAMROD_CMD_ID_BASE + 14)
#define L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD (L5CM_RAMROD_CMD_ID_BASE + 15)
-#define FCOE_KCQE_OPCODE_INIT_FUNC (0x10)
-#define FCOE_KCQE_OPCODE_DESTROY_FUNC (0x11)
-#define FCOE_KCQE_OPCODE_STAT_FUNC (0x12)
-#define FCOE_KCQE_OPCODE_OFFLOAD_CONN (0x15)
-#define FCOE_KCQE_OPCODE_ENABLE_CONN (0x16)
-#define FCOE_KCQE_OPCODE_DISABLE_CONN (0x17)
-#define FCOE_KCQE_OPCODE_DESTROY_CONN (0x18)
-#define FCOE_KCQE_OPCODE_CQ_EVENT_NOTIFICATION (0x20)
-#define FCOE_KCQE_OPCODE_FCOE_ERROR (0x21)
-
#define FCOE_RAMROD_CMD_ID_INIT_FUNC (FCOE_KCQE_OPCODE_INIT_FUNC)
#define FCOE_RAMROD_CMD_ID_DESTROY_FUNC (FCOE_KCQE_OPCODE_DESTROY_FUNC)
#define FCOE_RAMROD_CMD_ID_STAT_FUNC (FCOE_KCQE_OPCODE_STAT_FUNC)
@@ -54,23 +44,6 @@
#define FCOE_RAMROD_CMD_ID_DESTROY_CONN (FCOE_KCQE_OPCODE_DESTROY_CONN)
#define FCOE_RAMROD_CMD_ID_TERMINATE_CONN (0x81)
-#define FCOE_KWQE_OPCODE_INIT1 (0)
-#define FCOE_KWQE_OPCODE_INIT2 (1)
-#define FCOE_KWQE_OPCODE_INIT3 (2)
-#define FCOE_KWQE_OPCODE_OFFLOAD_CONN1 (3)
-#define FCOE_KWQE_OPCODE_OFFLOAD_CONN2 (4)
-#define FCOE_KWQE_OPCODE_OFFLOAD_CONN3 (5)
-#define FCOE_KWQE_OPCODE_OFFLOAD_CONN4 (6)
-#define FCOE_KWQE_OPCODE_ENABLE_CONN (7)
-#define FCOE_KWQE_OPCODE_DISABLE_CONN (8)
-#define FCOE_KWQE_OPCODE_DESTROY_CONN (9)
-#define FCOE_KWQE_OPCODE_DESTROY (10)
-#define FCOE_KWQE_OPCODE_STAT (11)
-
-#define FCOE_KCQE_COMPLETION_STATUS_ERROR (0x1)
-#define FCOE_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE (0x3)
-#define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5)
-
/* KCQ (kernel completion queue) response op codes */
#define L4_KCQE_OPCODE_VALUE_CLOSE_COMP (53)
#define L4_KCQE_OPCODE_VALUE_RESET_COMP (54)
@@ -87,6 +60,7 @@
/* KCQ (kernel completion queue) completion status */
#define L4_KCQE_COMPLETION_STATUS_SUCCESS (0)
#define L4_KCQE_COMPLETION_STATUS_NIC_ERROR (4)
+#define L4_KCQE_COMPLETION_STATUS_PARITY_ERROR (0x81)
#define L4_KCQE_COMPLETION_STATUS_TIMEOUT (0x93)
#define L4_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAIL (0x83)
diff --git a/drivers/net/ethernet/broadcom/cnic_if.h b/drivers/net/ethernet/broadcom/cnic_if.h
index 60deb84d36bd..289274e546be 100644
--- a/drivers/net/ethernet/broadcom/cnic_if.h
+++ b/drivers/net/ethernet/broadcom/cnic_if.h
@@ -12,8 +12,8 @@
#ifndef CNIC_IF_H
#define CNIC_IF_H
-#define CNIC_MODULE_VERSION "2.5.9"
-#define CNIC_MODULE_RELDATE "Feb 8, 2012"
+#define CNIC_MODULE_VERSION "2.5.10"
+#define CNIC_MODULE_RELDATE "March 21, 2012"
#define CNIC_ULP_RDMA 0
#define CNIC_ULP_ISCSI 1
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index b0657466041d..4e4bb3875868 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>
@@ -89,10 +88,10 @@ static inline void _tg3_flag_clear(enum TG3_FLAGS flag, unsigned long *bits)
#define DRV_MODULE_NAME "tg3"
#define TG3_MAJ_NUM 3
-#define TG3_MIN_NUM 122
+#define TG3_MIN_NUM 123
#define DRV_MODULE_VERSION \
__stringify(TG3_MAJ_NUM) "." __stringify(TG3_MIN_NUM)
-#define DRV_MODULE_RELDATE "December 7, 2011"
+#define DRV_MODULE_RELDATE "March 21, 2012"
#define RESET_KIND_SHUTDOWN 0
#define RESET_KIND_INIT 1
@@ -5953,8 +5952,10 @@ next_pkt_nopost:
tpr->rx_std_prod_idx = std_prod_idx & tp->rx_std_ring_mask;
tpr->rx_jmb_prod_idx = jmb_prod_idx & tp->rx_jmb_ring_mask;
- if (tnapi != &tp->napi[1])
+ if (tnapi != &tp->napi[1]) {
+ tp->rx_refill = true;
napi_schedule(&tp->napi[1].napi);
+ }
}
return received;
@@ -6134,6 +6135,7 @@ static int tg3_poll_work(struct tg3_napi *tnapi, int work_done, int budget)
u32 std_prod_idx = dpr->rx_std_prod_idx;
u32 jmb_prod_idx = dpr->rx_jmb_prod_idx;
+ tp->rx_refill = false;
for (i = 1; i < tp->irq_cnt; i++)
err |= tg3_rx_prodring_xfer(tp, dpr,
&tp->napi[i].prodring);
@@ -6197,9 +6199,25 @@ static int tg3_poll_msix(struct napi_struct *napi, int budget)
/* check for RX/TX work to do */
if (likely(sblk->idx[0].tx_consumer == tnapi->tx_cons &&
*(tnapi->rx_rcb_prod_idx) == tnapi->rx_rcb_ptr)) {
+
+ /* This test here is not race free, but will reduce
+ * the number of interrupts by looping again.
+ */
+ if (tnapi == &tp->napi[1] && tp->rx_refill)
+ continue;
+
napi_complete(napi);
/* Reenable interrupts. */
tw32_mailbox(tnapi->int_mbox, tnapi->last_tag << 24);
+
+ /* This test here is synchronized by napi_schedule()
+ * and napi_complete() to close the race condition.
+ */
+ if (unlikely(tnapi == &tp->napi[1] && tp->rx_refill)) {
+ tw32(HOSTCC_MODE, tp->coalesce_mode |
+ HOSTCC_MODE_ENABLE |
+ tnapi->coal_now);
+ }
mmiowb();
break;
}
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h
index 66bcfca55261..93865f899a4f 100644
--- a/drivers/net/ethernet/broadcom/tg3.h
+++ b/drivers/net/ethernet/broadcom/tg3.h
@@ -3007,6 +3007,7 @@ struct tg3 {
u32 rx_std_max_post;
u32 rx_offset;
u32 rx_pkt_map_sz;
+ bool rx_refill;
/* begin "everything else" cacheline(s) section */
diff --git a/drivers/net/ethernet/cirrus/Kconfig b/drivers/net/ethernet/cirrus/Kconfig
index 1f8648f099c7..8388e36cf08f 100644
--- a/drivers/net/ethernet/cirrus/Kconfig
+++ b/drivers/net/ethernet/cirrus/Kconfig
@@ -5,8 +5,7 @@
config NET_VENDOR_CIRRUS
bool "Cirrus devices"
default y
- depends on ISA || EISA || MACH_IXDP2351 || ARCH_IXDP2X01 \
- || MACH_MX31ADS || MACH_QQ2440 || (ARM && ARCH_EP93XX) || MAC
+ depends on ISA || EISA || ARM || MAC
---help---
If you have a network (Ethernet) card belonging to this class, say Y
and read the Ethernet-HOWTO, available from
@@ -21,8 +20,7 @@ if NET_VENDOR_CIRRUS
config CS89x0
tristate "CS89x0 support"
- depends on (ISA || EISA || MACH_IXDP2351 \
- || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440)
+ depends on ISA || EISA || ARM
---help---
Support for CS89x0 chipset based Ethernet cards. If you have a
network (Ethernet) card of this type, say Y and read the
@@ -33,10 +31,15 @@ config CS89x0
To compile this driver as a module, choose M here. The module
will be called cs89x0.
-config CS89x0_NONISA_IRQ
- def_bool y
- depends on CS89x0 != n
- depends on MACH_IXDP2351 || ARCH_IXDP2X01 || MACH_MX31ADS || MACH_QQ2440
+config CS89x0_PLATFORM
+ bool "CS89x0 platform driver support"
+ depends on CS89x0
+ help
+ Say Y to compile the cs89x0 driver as a platform driver. This
+ makes this driver suitable for use on certain evaluation boards
+ such as the iMX21ADS.
+
+ If you are unsure, say N.
config EP93XX_ETH
tristate "EP93xx Ethernet support"
diff --git a/drivers/net/ethernet/cirrus/cs89x0.c b/drivers/net/ethernet/cirrus/cs89x0.c
index d5ff93653e4c..b9406cbfc180 100644
--- a/drivers/net/ethernet/cirrus/cs89x0.c
+++ b/drivers/net/ethernet/cirrus/cs89x0.c
@@ -100,9 +100,6 @@
*/
-/* Always include 'config.h' first in case the user wants to turn on
- or override something. */
-#include <linux/module.h>
/*
* Set this to zero to disable DMA code
@@ -131,9 +128,12 @@
*/
+#include <linux/module.h>
+#include <linux/printk.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
@@ -148,9 +148,9 @@
#include <linux/delay.h>
#include <linux/gfp.h>
-#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <linux/atomic.h>
#if ALLOW_DMA
#include <asm/dma.h>
#endif
@@ -174,26 +174,20 @@ static char version[] __initdata =
them to system IRQ numbers. This mapping is card specific and is set to
the configuration of the Cirrus Eval board for this chip. */
#if defined(CONFIG_MACH_IXDP2351)
+#define CS89x0_NONISA_IRQ
static unsigned int netcard_portlist[] __used __initdata = {IXDP2351_VIRT_CS8900_BASE, 0};
static unsigned int cs8900_irq_map[] = {IRQ_IXDP2351_CS8900, 0, 0, 0};
#elif defined(CONFIG_ARCH_IXDP2X01)
+#define CS89x0_NONISA_IRQ
static unsigned int netcard_portlist[] __used __initdata = {IXDP2X01_CS8900_VIRT_BASE, 0};
static unsigned int cs8900_irq_map[] = {IRQ_IXDP2X01_CS8900, 0, 0, 0};
-#elif defined(CONFIG_MACH_QQ2440)
-#include <mach/qq2440.h>
-static unsigned int netcard_portlist[] __used __initdata = { QQ2440_CS8900_VIRT_BASE + 0x300, 0 };
-static unsigned int cs8900_irq_map[] = { QQ2440_CS8900_IRQ, 0, 0, 0 };
-#elif defined(CONFIG_MACH_MX31ADS)
-#include <mach/board-mx31ads.h>
-static unsigned int netcard_portlist[] __used __initdata = {
- PBC_BASE_ADDRESS + PBC_CS8900A_IOBASE + 0x300, 0
-};
-static unsigned cs8900_irq_map[] = {EXPIO_INT_ENET_INT, 0, 0, 0};
#else
+#ifndef CONFIG_CS89x0_PLATFORM
static unsigned int netcard_portlist[] __used __initdata =
{ 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0};
static unsigned int cs8900_irq_map[] = {10,11,12,5};
#endif
+#endif
#if DEBUGGING
static unsigned int net_debug = DEBUGGING;
@@ -236,11 +230,16 @@ struct net_local {
unsigned char *end_dma_buff; /* points to the end of the buffer */
unsigned char *rx_dma_ptr; /* points to the next packet */
#endif
+#ifdef CONFIG_CS89x0_PLATFORM
+ void __iomem *virt_addr;/* Virtual address for accessing the CS89x0. */
+ unsigned long phys_addr;/* Physical address for accessing the CS89x0. */
+ unsigned long size; /* Length of CS89x0 memory region. */
+#endif
};
/* Index to functions, as function prototypes. */
-static int cs89x0_probe1(struct net_device *dev, int ioaddr, int modular);
+static int cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular);
static int net_open(struct net_device *dev);
static netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev);
static irqreturn_t net_interrupt(int irq, void *dev_id);
@@ -294,6 +293,7 @@ static int __init media_fn(char *str)
__setup("cs89x0_media=", media_fn);
+#ifndef CONFIG_CS89x0_PLATFORM
/* Check for a network adaptor of this type, and return '0' iff one exists.
If dev->base_addr == 0, probe all likely locations.
If dev->base_addr == 1, always return failure.
@@ -343,6 +343,7 @@ out:
return ERR_PTR(err);
}
#endif
+#endif
#if defined(CONFIG_MACH_IXDP2351)
static u16
@@ -504,7 +505,7 @@ static const struct net_device_ops net_ops = {
*/
static int __init
-cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
+cs89x0_probe1(struct net_device *dev, unsigned long ioaddr, int modular)
{
struct net_local *lp = netdev_priv(dev);
static unsigned version_printed;
@@ -529,15 +530,12 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
lp->force = g_cs89x0_media__force;
#endif
-#if defined(CONFIG_MACH_QQ2440)
- lp->force |= FORCE_RJ45 | FORCE_FULL;
-#endif
}
/* Grab the region so we can find another board if autoIRQ fails. */
/* WTF is going on here? */
if (!request_region(ioaddr & ~3, NETCARD_IO_EXTENT, DRV_NAME)) {
- printk(KERN_ERR "%s: request_region(0x%x, 0x%x) failed\n",
+ printk(KERN_ERR "%s: request_region(0x%lx, 0x%x) failed\n",
DRV_NAME, ioaddr, NETCARD_IO_EXTENT);
retval = -EBUSY;
goto out1;
@@ -549,7 +547,7 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
will skip the test for the ADD_PORT. */
if (ioaddr & 1) {
if (net_debug > 1)
- printk(KERN_INFO "%s: odd ioaddr 0x%x\n", dev->name, ioaddr);
+ printk(KERN_INFO "%s: odd ioaddr 0x%lx\n", dev->name, ioaddr);
if ((ioaddr & 2) != 2)
if ((readword(ioaddr & ~3, ADD_PORT) & ADD_MASK) != ADD_SIG) {
printk(KERN_ERR "%s: bad signature 0x%x\n",
@@ -560,13 +558,13 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
}
ioaddr &= ~3;
- printk(KERN_DEBUG "PP_addr at %x[%x]: 0x%x\n",
+ printk(KERN_DEBUG "PP_addr at %lx[%x]: 0x%x\n",
ioaddr, ADD_PORT, readword(ioaddr, ADD_PORT));
writeword(ioaddr, ADD_PORT, PP_ChipID);
tmp = readword(ioaddr, DATA_PORT);
if (tmp != CHIP_EISA_ID_SIG) {
- printk(KERN_DEBUG "%s: incorrect signature at %x[%x]: 0x%x!="
+ printk(KERN_DEBUG "%s: incorrect signature at %lx[%x]: 0x%x!="
CHIP_EISA_ID_SIG_STR "\n",
dev->name, ioaddr, DATA_PORT, tmp);
retval = -ENODEV;
@@ -736,8 +734,9 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
dev->irq = i;
} else {
i = lp->isa_config & INT_NO_MASK;
+#ifndef CONFIG_CS89x0_PLATFORM
if (lp->chip_type == CS8900) {
-#ifdef CONFIG_CS89x0_NONISA_IRQ
+#ifdef CS89x0_NONISA_IRQ
i = cs8900_irq_map[0];
#else
/* Translate the IRQ using the IRQ mapping table. */
@@ -758,6 +757,7 @@ cs89x0_probe1(struct net_device *dev, int ioaddr, int modular)
}
#endif
}
+#endif
if (!dev->irq)
dev->irq = i;
}
@@ -1168,6 +1168,7 @@ write_irq(struct net_device *dev, int chip_type, int irq)
int i;
if (chip_type == CS8900) {
+#ifndef CONFIG_CS89x0_PLATFORM
/* Search the mapping table for the corresponding IRQ pin. */
for (i = 0; i != ARRAY_SIZE(cs8900_irq_map); i++)
if (cs8900_irq_map[i] == irq)
@@ -1175,6 +1176,10 @@ write_irq(struct net_device *dev, int chip_type, int irq)
/* Not found */
if (i == ARRAY_SIZE(cs8900_irq_map))
i = 3;
+#else
+ /* INTRQ0 pin is used for interrupt generation. */
+ i = 0;
+#endif
writereg(dev, PP_CS8900_ISAINT, i);
} else {
writereg(dev, PP_CS8920_ISAINT, irq);
@@ -1228,7 +1233,7 @@ net_open(struct net_device *dev)
}
else
{
-#ifndef CONFIG_CS89x0_NONISA_IRQ
+#if !defined(CS89x0_NONISA_IRQ) && !defined(CONFIG_CS89x0_PLATFORM)
if (((1 << dev->irq) & lp->irq_map) == 0) {
printk(KERN_ERR "%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
dev->name, dev->irq, lp->irq_map);
@@ -1746,7 +1751,7 @@ static int set_mac_address(struct net_device *dev, void *p)
return 0;
}
-#ifdef MODULE
+#if defined(MODULE) && !defined(CONFIG_CS89x0_PLATFORM)
static struct net_device *dev_cs89x0;
@@ -1900,7 +1905,97 @@ cleanup_module(void)
release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT);
free_netdev(dev_cs89x0);
}
-#endif /* MODULE */
+#endif /* MODULE && !CONFIG_CS89x0_PLATFORM */
+
+#ifdef CONFIG_CS89x0_PLATFORM
+static int __init cs89x0_platform_probe(struct platform_device *pdev)
+{
+ struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
+ struct net_local *lp;
+ struct resource *mem_res;
+ int err;
+
+ if (!dev)
+ return -ENOMEM;
+
+ lp = netdev_priv(dev);
+
+ mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ dev->irq = platform_get_irq(pdev, 0);
+ if (mem_res == NULL || dev->irq <= 0) {
+ dev_warn(&dev->dev, "memory/interrupt resource missing.\n");
+ err = -ENXIO;
+ goto free;
+ }
+
+ lp->phys_addr = mem_res->start;
+ lp->size = resource_size(mem_res);
+ if (!request_mem_region(lp->phys_addr, lp->size, DRV_NAME)) {
+ dev_warn(&dev->dev, "request_mem_region() failed.\n");
+ err = -EBUSY;
+ goto free;
+ }
+
+ lp->virt_addr = ioremap(lp->phys_addr, lp->size);
+ if (!lp->virt_addr) {
+ dev_warn(&dev->dev, "ioremap() failed.\n");
+ err = -ENOMEM;
+ goto release;
+ }
+
+ err = cs89x0_probe1(dev, (unsigned long)lp->virt_addr, 0);
+ if (err) {
+ dev_warn(&dev->dev, "no cs8900 or cs8920 detected.\n");
+ goto unmap;
+ }
+
+ platform_set_drvdata(pdev, dev);
+ return 0;
+
+unmap:
+ iounmap(lp->virt_addr);
+release:
+ release_mem_region(lp->phys_addr, lp->size);
+free:
+ free_netdev(dev);
+ return err;
+}
+
+static int cs89x0_platform_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct net_local *lp = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ iounmap(lp->virt_addr);
+ release_mem_region(lp->phys_addr, lp->size);
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver cs89x0_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .remove = cs89x0_platform_remove,
+};
+
+static int __init cs89x0_init(void)
+{
+ return platform_driver_probe(&cs89x0_driver, cs89x0_platform_probe);
+}
+
+module_init(cs89x0_init);
+
+static void __exit cs89x0_cleanup(void)
+{
+ platform_driver_unregister(&cs89x0_driver);
+}
+
+module_exit(cs89x0_cleanup);
+
+#endif /* CONFIG_CS89x0_PLATFORM */
/*
* Local variables:
diff --git a/drivers/net/ethernet/cirrus/mac89x0.c b/drivers/net/ethernet/cirrus/mac89x0.c
index 932fdccc339a..e285f384b096 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 682750c052c8..414f0eea1049 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 afc5aaac6b60..2e2bc60ee811 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/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c
index d9428f0e738a..e7bed5303997 100644
--- a/drivers/net/ethernet/freescale/gianfar.c
+++ b/drivers/net/ethernet/freescale/gianfar.c
@@ -968,7 +968,6 @@ static int gfar_probe(struct platform_device *ofdev)
struct gfar_private *priv = NULL;
struct gfar __iomem *regs = NULL;
int err = 0, i, grp_idx = 0;
- int len_devname;
u32 rstat = 0, tstat = 0, rqueue = 0, tqueue = 0;
u32 isrg = 0;
u32 __iomem *baddr;
@@ -1169,40 +1168,16 @@ static int gfar_probe(struct platform_device *ofdev)
priv->device_flags & FSL_GIANFAR_DEV_HAS_MAGIC_PACKET);
/* fill out IRQ number and name fields */
- len_devname = strlen(dev->name);
for (i = 0; i < priv->num_grps; i++) {
- strncpy(&priv->gfargrp[i].int_name_tx[0], dev->name,
- len_devname);
if (priv->device_flags & FSL_GIANFAR_DEV_HAS_MULTI_INTR) {
- strncpy(&priv->gfargrp[i].int_name_tx[len_devname],
- "_g", sizeof("_g"));
- priv->gfargrp[i].int_name_tx[
- strlen(priv->gfargrp[i].int_name_tx)] = i+48;
- strncpy(&priv->gfargrp[i].int_name_tx[strlen(
- priv->gfargrp[i].int_name_tx)],
- "_tx", sizeof("_tx") + 1);
-
- strncpy(&priv->gfargrp[i].int_name_rx[0], dev->name,
- len_devname);
- strncpy(&priv->gfargrp[i].int_name_rx[len_devname],
- "_g", sizeof("_g"));
- priv->gfargrp[i].int_name_rx[
- strlen(priv->gfargrp[i].int_name_rx)] = i+48;
- strncpy(&priv->gfargrp[i].int_name_rx[strlen(
- priv->gfargrp[i].int_name_rx)],
- "_rx", sizeof("_rx") + 1);
-
- strncpy(&priv->gfargrp[i].int_name_er[0], dev->name,
- len_devname);
- strncpy(&priv->gfargrp[i].int_name_er[len_devname],
- "_g", sizeof("_g"));
- priv->gfargrp[i].int_name_er[strlen(
- priv->gfargrp[i].int_name_er)] = i+48;
- strncpy(&priv->gfargrp[i].int_name_er[strlen(\
- priv->gfargrp[i].int_name_er)],
- "_er", sizeof("_er") + 1);
+ sprintf(priv->gfargrp[i].int_name_tx, "%s%s%c%s",
+ dev->name, "_g", '0' + i, "_tx");
+ sprintf(priv->gfargrp[i].int_name_rx, "%s%s%c%s",
+ dev->name, "_g", '0' + i, "_rx");
+ sprintf(priv->gfargrp[i].int_name_er, "%s%s%c%s",
+ dev->name, "_g", '0' + i, "_er");
} else
- priv->gfargrp[i].int_name_tx[len_devname] = '\0';
+ strcpy(priv->gfargrp[i].int_name_tx, dev->name);
}
/* Initialize the filer table */
diff --git a/drivers/net/ethernet/freescale/gianfar.h b/drivers/net/ethernet/freescale/gianfar.h
index fc2488adca36..4c9f8d487dbb 100644
--- a/drivers/net/ethernet/freescale/gianfar.h
+++ b/drivers/net/ethernet/freescale/gianfar.h
@@ -517,7 +517,7 @@ extern const char gfar_driver_version[];
#define RXFCB_PERR_MASK 0x000c
#define RXFCB_PERR_BADL3 0x0008
-#define GFAR_INT_NAME_MAX IFNAMSIZ + 4
+#define GFAR_INT_NAME_MAX (IFNAMSIZ + 6) /* '_g#_xx' */
struct txbd8
{
diff --git a/drivers/net/ethernet/fujitsu/at1700.c b/drivers/net/ethernet/fujitsu/at1700.c
index 586b46fd4eed..3d94797c8f9b 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 c3f0178fb5cb..a992d1f7e0d2 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 0230319ddb59..2418faf2251a 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 ed6925f11479..e8984b059905 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 ef43f3e951c5..278e791afe00 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 7a4ad4a07917..7f49fd54c521 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 3fc649e54a32..cc2e66ad4436 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 406a12b46404..067db3f13e91 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 a43649735a04..bd1f1ef91e19 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 e877371680a9..9010cea68bc3 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 f30db1c46600..bc58f1dc22f5 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 75af1afe46c8..5e1ca0f05090 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 45a6333588e6..efec6b60b327 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/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c
index 82c2c86a1951..423a1a2a702e 100644
--- a/drivers/net/ethernet/marvell/sky2.c
+++ b/drivers/net/ethernet/marvell/sky2.c
@@ -95,6 +95,10 @@ static int disable_msi = 0;
module_param(disable_msi, int, 0);
MODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)");
+static int legacy_pme = 0;
+module_param(legacy_pme, int, 0);
+MODULE_PARM_DESC(legacy_pme, "Legacy power management");
+
static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9000) }, /* SK-9Sxx */
{ PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, 0x9E00) }, /* SK-9Exx */
@@ -867,6 +871,13 @@ static void sky2_wol_init(struct sky2_port *sky2)
/* Disable PiG firmware */
sky2_write16(hw, B0_CTST, Y2_HW_WOL_OFF);
+ /* Needed by some broken BIOSes, use PCI rather than PCI-e for WOL */
+ if (legacy_pme) {
+ u32 reg1 = sky2_pci_read32(hw, PCI_DEV_REG1);
+ reg1 |= PCI_Y2_PME_LEGACY;
+ sky2_pci_write32(hw, PCI_DEV_REG1, reg1);
+ }
+
/* block receiver */
sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
sky2_read32(hw, B0_CTST);
diff --git a/drivers/net/ethernet/natsemi/jazzsonic.c b/drivers/net/ethernet/natsemi/jazzsonic.c
index 5b89fd377ae3..95dd39ffb230 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 e640e23460de..b9680ba5a325 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 c24b46cbfe27..d52728b3c436 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 22a8de00bf02..6338ef8606ae 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 8561dd25db66..aca13046e432 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/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 2b5af22419a5..385a4d5c7c25 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -36,8 +36,8 @@
#define _QLCNIC_LINUX_MAJOR 5
#define _QLCNIC_LINUX_MINOR 0
-#define _QLCNIC_LINUX_SUBVERSION 25
-#define QLCNIC_LINUX_VERSIONID "5.0.26"
+#define _QLCNIC_LINUX_SUBVERSION 27
+#define QLCNIC_LINUX_VERSIONID "5.0.27"
#define QLCNIC_DRV_IDC_VER 0x01
#define QLCNIC_DRIVER_VERSION ((_QLCNIC_LINUX_MAJOR << 16) |\
(_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION))
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 81bb1a69e69f..75c32e875fef 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -1458,8 +1458,10 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter)
if (netif_running(netdev)) {
err = qlcnic_attach(adapter);
- if (!err)
+ if (!err) {
__qlcnic_up(adapter, netdev);
+ qlcnic_restore_indev_addr(netdev, NETDEV_UP);
+ }
}
netif_device_attach(netdev);
diff --git a/drivers/net/ethernet/realtek/atp.c b/drivers/net/ethernet/realtek/atp.c
index 46c1932048cb..e02f04d7f3ad 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 27c358c8f4dc..7b23554f80b6 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 7b819bd8c416..df808ac8cb65 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 798990774446..698edbbfc149 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/smsc/smc91c92_cs.c b/drivers/net/ethernet/smsc/smc91c92_cs.c
index d12e48a7861d..04393b5fef71 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 1dc4fad593e7..fee449355014 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 3c2295560732..ce4df61b4b56 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 f359863b5340..2a83fc57edba 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 ba041596e046..558409ff4058 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 8b627e2f798d..b95e7e681b38 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 139d6b410d68..7d4a040d84a2 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 92a037a8228a..38e3ae9155b7 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 840e0e9031f5..277c93e9ff4d 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 5c69c6f93fb8..94a1f94f74b8 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 2a5a34d2d67b..64783a0d545a 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 f1aea0c98333..acb636963e90 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 18d8affecd1b..76d54774ba82 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 bc02968cee16..aed1a6105b24 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 33655814448e..efc6c97163a7 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 696327773fbe..5a6412ecce73 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 2a51363d9fed..168c8f41d09f 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 e535137eb2d0..468047866c8c 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 617a446d126c..4351296dde32 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 da2705061a60..a0d1913a58d3 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 b71998d0b5b4..32eb94ece6c1 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 1a5a316cc968..bed62d9c53c8 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 0a0a6643cf3a..1252d9c726a7 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 69345dfae0fd..d4c9db3da22a 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 d7c292aa76b1..b15ac81d46fa 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 515f122777ab..b715e6b444da 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 91b684630fc5..356e28e4881b 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 8d71e0d29062..3e4b4f091113 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 1cdc034f6aec..28adcdf3b14c 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 fd8dce90c957..0e234741cc79 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 8d362e64a40e..62d90e40f9ec 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 46db5c5395b2..ee11e93dc30e 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 029846a98636..cb35fb79e016 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 102f896bbc58..be4813e0366c 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 d3e788a9cd1c..fb9918da5792 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 74d7f76d14a3..bb8c72c79c6f 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/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index 790cbdea7392..3886b30ed373 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -164,12 +164,14 @@ static void rx_complete(struct urb *req)
/* Can't use pskb_pull() on page in IRQ */
memcpy(skb_put(skb, 1), page_address(page), 1);
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- page, 1, req->actual_length);
+ page, 1, req->actual_length,
+ req->actual_length);
page = NULL;
}
} else {
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
- page, 0, req->actual_length);
+ page, 0, req->actual_length,
+ req->actual_length);
page = NULL;
}
if (req->actual_length < PAGE_SIZE)
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index aac68f5195c0..552d24bf862e 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -409,6 +409,42 @@ static const struct usb_device_id products[] = {
.bInterfaceProtocol = 0xff,
.driver_info = (unsigned long)&qmi_wwan_force_int4,
},
+ { /* ZTE (Vodafone) K3565-Z */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19d2,
+ .idProduct = 0x0063,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ .driver_info = (unsigned long)&qmi_wwan_force_int4,
+ },
+ { /* ZTE (Vodafone) K3570-Z */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19d2,
+ .idProduct = 0x1008,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ .driver_info = (unsigned long)&qmi_wwan_force_int4,
+ },
+ { /* ZTE (Vodafone) K3571-Z */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19d2,
+ .idProduct = 0x1010,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ .driver_info = (unsigned long)&qmi_wwan_force_int4,
+ },
+ { /* ZTE (Vodafone) K4505-Z */
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x19d2,
+ .idProduct = 0x0104,
+ .bInterfaceClass = 0xff,
+ .bInterfaceSubClass = 0xff,
+ .bInterfaceProtocol = 0xff,
+ .driver_info = (unsigned long)&qmi_wwan_force_int4,
+ },
{QMI_GOBI_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{QMI_GOBI_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
{QMI_GOBI_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 4b8b52ca09d8..b7b3f5b0d406 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -493,6 +493,7 @@ block:
if (netif_running (dev->net) &&
!test_bit (EVENT_RX_HALT, &dev->flags)) {
rx_submit (dev, urb, GFP_ATOMIC);
+ usb_mark_last_busy(dev->udev);
return;
}
usb_free_urb (urb);
@@ -589,6 +590,14 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
entry = (struct skb_data *) skb->cb;
urb = entry->urb;
+ /*
+ * Get reference count of the URB to avoid it to be
+ * freed during usb_unlink_urb, which may trigger
+ * use-after-free problem inside usb_unlink_urb since
+ * usb_unlink_urb is always racing with .complete
+ * handler(include defer_bh).
+ */
+ usb_get_urb(urb);
spin_unlock_irqrestore(&q->lock, flags);
// during some PM-driven resume scenarios,
// these (async) unlinks complete immediately
@@ -597,6 +606,7 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
netdev_dbg(dev->net, "unlink urb err, %d\n", retval);
else
count++;
+ usb_put_urb(urb);
spin_lock_irqsave(&q->lock, flags);
}
spin_unlock_irqrestore (&q->lock, flags);
@@ -1028,7 +1038,6 @@ static void tx_complete (struct urb *urb)
}
usb_autopm_put_interface_async(dev->intf);
- urb->dev = NULL;
entry->state = tx_done;
defer_bh(dev, skb, &dev->txq);
}
diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c
index 48ab38a34c5a..147614ed86aa 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 fe8d060d8fff..c676de7de024 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 33b67d88fceb..cf4903355a34 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 efc0db101183..e2779faa6c4f 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 7beeb9b88a3b..a73b49eb87e3 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 c8531612eea9..de3bbf43fc5a 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 e862369b4a6d..d7a65e141d1a 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 ddc061dd150c..520a4b2eb9cc 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 c983c10e0f6a..630577dd3a7a 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 3010cee7b95a..6c87a823f5a9 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 ec295c4f677d..ded03d226a71 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/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c
index c5b1d199e0bc..b25c01be0d90 100644
--- a/drivers/net/wireless/iwlegacy/3945.c
+++ b/drivers/net/wireless/iwlegacy/3945.c
@@ -499,7 +499,8 @@ il3945_pass_packet_to_mac80211(struct il_priv *il, struct il_rx_buf *rxb,
le32_to_cpu(rx_end->status), stats);
skb_add_rx_frag(skb, 0, rxb->page,
- (void *)rx_hdr->payload - (void *)pkt, len);
+ (void *)rx_hdr->payload - (void *)pkt, len,
+ len);
il_update_stats(il, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c
index 7b54dbb338be..17f1c6853182 100644
--- a/drivers/net/wireless/iwlegacy/4965-mac.c
+++ b/drivers/net/wireless/iwlegacy/4965-mac.c
@@ -596,7 +596,8 @@ il4965_pass_packet_to_mac80211(struct il_priv *il, struct ieee80211_hdr *hdr,
return;
}
- skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len);
+ skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len,
+ len);
il_update_stats(il, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
index 44c6f712b77d..f4b84d1596e3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
@@ -796,7 +796,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
offset = (void *)hdr - rxb_addr(rxb);
p = rxb_steal_page(rxb);
- skb_add_rx_frag(skb, 0, p, offset, len);
+ skb_add_rx_frag(skb, 0, p, offset, len, len);
iwl_update_stats(priv, false, fc, len);
diff --git a/drivers/net/wireless/prism54/islpci_mgt.c b/drivers/net/wireless/prism54/islpci_mgt.c
index 851fa10241e1..c5404cb59e08 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 04fec1fa6e0b..86a738bf591c 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 98fbf54f6004..00f6e69c1dcd 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/net/xen-netfront.c b/drivers/net/xen-netfront.c
index b16175032327..663b32c2e931 100644
--- a/drivers/net/xen-netfront.c
+++ b/drivers/net/xen-netfront.c
@@ -47,6 +47,7 @@
#include <xen/xenbus.h>
#include <xen/events.h>
#include <xen/page.h>
+#include <xen/platform_pci.h>
#include <xen/grant_table.h>
#include <xen/interface/io/netif.h>
@@ -1964,6 +1965,9 @@ static int __init netif_init(void)
if (xen_initial_domain())
return 0;
+ if (!xen_platform_pci_unplug)
+ return -ENODEV;
+
printk(KERN_INFO "Initialising Xen virtual ethernet driver.\n");
return xenbus_register_frontend(&netfront_driver);
diff --git a/drivers/nubus/nubus.c b/drivers/nubus/nubus.c
index b764ac22d523..44d01afafe9c 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 6ea51dcbc728..8e84ce9765a9 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 a73f5a51ff4c..aa90e602c8a7 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 7e62d15d60f6..bba81216b4db 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 000000000000..e7cad627a5d1
--- /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/of/platform.c b/drivers/of/platform.c
index 20fbebd49db3..343ad29e211c 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -253,7 +253,7 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
if (!of_device_is_available(node))
return NULL;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = amba_device_alloc(NULL, 0, 0);
if (!dev)
return NULL;
@@ -283,14 +283,14 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
if (ret)
goto err_free;
- ret = amba_device_register(dev, &iomem_resource);
+ ret = amba_device_add(dev, &iomem_resource);
if (ret)
goto err_free;
return dev;
err_free:
- kfree(dev);
+ amba_device_put(dev);
return NULL;
}
#else /* CONFIG_ARM_AMBA */
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index 7ff10c1e8664..432d4bbcc62a 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>
@@ -553,7 +552,6 @@ dino_fixup_bus(struct pci_bus *bus)
struct list_head *ln;
struct pci_dev *dev;
struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge));
- int port_base = HBA_PORT_BASE(dino_dev->hba.hba_num);
DBG(KERN_WARNING "%s(0x%p) bus %d platform_data 0x%p\n",
__func__, bus, bus->secondary,
@@ -599,8 +597,6 @@ dino_fixup_bus(struct pci_bus *bus)
list_for_each(ln, &bus->devices) {
- int i;
-
dev = pci_dev_b(ln);
if (is_card_dino(&dino_dev->hba.dev->id))
dino_card_fixup(dev);
@@ -612,21 +608,6 @@ dino_fixup_bus(struct pci_bus *bus)
if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI)
continue;
- /* Adjust the I/O Port space addresses */
- for (i = 0; i < PCI_NUM_RESOURCES; i++) {
- struct resource *res = &dev->resource[i];
- if (res->flags & IORESOURCE_IO) {
- res->start |= port_base;
- res->end |= port_base;
- }
-#ifdef __LP64__
- /* Sign Extend MMIO addresses */
- else if (res->flags & IORESOURCE_MEM) {
- res->start |= F_EXTEND(0UL);
- res->end |= F_EXTEND(0UL);
- }
-#endif
- }
/* null out the ROM resource if there is one (we don't
* care about an expansion rom on parisc, since it
* usually contains (x86) bios code) */
@@ -991,11 +972,14 @@ static int __init dino_probe(struct parisc_device *dev)
dev->dev.platform_data = dino_dev;
- pci_add_resource(&resources, &dino_dev->hba.io_space);
+ pci_add_resource_offset(&resources, &dino_dev->hba.io_space,
+ HBA_PORT_BASE(dino_dev->hba.hba_num));
if (dino_dev->hba.lmmio_space.flags)
- pci_add_resource(&resources, &dino_dev->hba.lmmio_space);
+ pci_add_resource_offset(&resources, &dino_dev->hba.lmmio_space,
+ dino_dev->hba.lmmio_space_offset);
if (dino_dev->hba.elmmio_space.flags)
- pci_add_resource(&resources, &dino_dev->hba.elmmio_space);
+ pci_add_resource_offset(&resources, &dino_dev->hba.elmmio_space,
+ dino_dev->hba.lmmio_space_offset);
if (dino_dev->hba.gmmio_space.flags)
pci_add_resource(&resources, &dino_dev->hba.gmmio_space);
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
index 95930d016235..1f9e9fefb8e7 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 d5f3d753a108..052fa230bc77 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 */
@@ -635,7 +634,6 @@ lba_fixup_bus(struct pci_bus *bus)
u16 status;
#endif
struct lba_device *ldev = LBA_DEV(parisc_walk_tree(bus->bridge));
- int lba_portbase = HBA_PORT_BASE(ldev->hba.hba_num);
DBG("lba_fixup_bus(0x%p) bus %d platform_data 0x%p\n",
bus, bus->secondary, bus->bridge->platform_data);
@@ -726,27 +724,6 @@ lba_fixup_bus(struct pci_bus *bus)
if (!res->start)
continue;
- if (res->flags & IORESOURCE_IO) {
- DBG("lba_fixup_bus() I/O Ports [%lx/%lx] -> ",
- res->start, res->end);
- res->start |= lba_portbase;
- res->end |= lba_portbase;
- DBG("[%lx/%lx]\n", res->start, res->end);
- } else if (res->flags & IORESOURCE_MEM) {
- /*
- ** Convert PCI (IO_VIEW) addresses to
- ** processor (PA_VIEW) addresses
- */
- DBG("lba_fixup_bus() MMIO [%lx/%lx] -> ",
- res->start, res->end);
- res->start = PCI_HOST_ADDR(HBA_DATA(ldev), res->start);
- res->end = PCI_HOST_ADDR(HBA_DATA(ldev), res->end);
- DBG("[%lx/%lx]\n", res->start, res->end);
- } else {
- DBG("lba_fixup_bus() WTF? 0x%lx [%lx/%lx] XXX",
- res->flags, res->start, res->end);
- }
-
/*
** FIXME: this will result in whinging for devices
** that share expansion ROMs (think quad tulip), but
@@ -1514,11 +1491,14 @@ lba_driver_probe(struct parisc_device *dev)
lba_dev->hba.lmmio_space.flags = 0;
}
- pci_add_resource(&resources, &lba_dev->hba.io_space);
+ pci_add_resource_offset(&resources, &lba_dev->hba.io_space,
+ HBA_PORT_BASE(lba_dev->hba.hba_num));
if (lba_dev->hba.elmmio_space.start)
- pci_add_resource(&resources, &lba_dev->hba.elmmio_space);
+ pci_add_resource_offset(&resources, &lba_dev->hba.elmmio_space,
+ lba_dev->hba.lmmio_space_offset);
if (lba_dev->hba.lmmio_space.flags)
- pci_add_resource(&resources, &lba_dev->hba.lmmio_space);
+ pci_add_resource_offset(&resources, &lba_dev->hba.lmmio_space,
+ lba_dev->hba.lmmio_space_offset);
if (lba_dev->hba.gmmio_space.flags)
pci_add_resource(&resources, &lba_dev->hba.gmmio_space);
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 37856f7c7781..848bfb84c04c 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -31,6 +31,19 @@ config PCI_DEBUG
When in doubt, say N.
+config PCI_REALLOC_ENABLE_AUTO
+ bool "Enable PCI resource re-allocation detection"
+ depends on PCI
+ help
+ Say Y here if you want the PCI core to detect if PCI resource
+ re-allocation needs to be enabled. You can always use pci=realloc=on
+ or pci=realloc=off to override it. Note this feature is a no-op
+ unless PCI_IOV support is also enabled; in that case it will
+ automatically re-allocate PCI resources if SR-IOV BARs have not
+ been allocated by the BIOS.
+
+ When in doubt, say N.
+
config PCI_STUB
tristate "PCI Stub driver"
depends on PCI
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 398f5d859791..4ce5ef2f2826 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -18,28 +18,36 @@
#include "pci.h"
-void pci_add_resource(struct list_head *resources, struct resource *res)
+void pci_add_resource_offset(struct list_head *resources, struct resource *res,
+ resource_size_t offset)
{
- struct pci_bus_resource *bus_res;
+ struct pci_host_bridge_window *window;
- bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL);
- if (!bus_res) {
- printk(KERN_ERR "PCI: can't add bus resource %pR\n", res);
+ window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL);
+ if (!window) {
+ printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res);
return;
}
- bus_res->res = res;
- list_add_tail(&bus_res->list, resources);
+ window->res = res;
+ window->offset = offset;
+ list_add_tail(&window->list, resources);
+}
+EXPORT_SYMBOL(pci_add_resource_offset);
+
+void pci_add_resource(struct list_head *resources, struct resource *res)
+{
+ pci_add_resource_offset(resources, res, 0);
}
EXPORT_SYMBOL(pci_add_resource);
void pci_free_resource_list(struct list_head *resources)
{
- struct pci_bus_resource *bus_res, *tmp;
+ struct pci_host_bridge_window *window, *tmp;
- list_for_each_entry_safe(bus_res, tmp, resources, list) {
- list_del(&bus_res->list);
- kfree(bus_res);
+ list_for_each_entry_safe(window, tmp, resources, list) {
+ list_del(&window->list);
+ kfree(window);
}
}
EXPORT_SYMBOL(pci_free_resource_list);
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 9ddf69e3bbef..806c44fa645a 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -800,20 +800,10 @@ static int __ref enable_device(struct acpiphp_slot *slot)
if (slot->flags & SLOT_ENABLED)
goto err_exit;
- /* sanity check: dev should be NULL when hot-plugged in */
- dev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0));
- if (dev) {
- /* This case shouldn't happen */
- err("pci_dev structure already exists.\n");
- pci_dev_put(dev);
- retval = -1;
- goto err_exit;
- }
-
num = pci_scan_slot(bus, PCI_DEVFN(slot->device, 0));
if (num == 0) {
- err("No new device found\n");
- retval = -1;
+ /* Maybe only part of funcs are added. */
+ dbg("No new device found\n");
goto err_exit;
}
@@ -848,11 +838,16 @@ static int __ref enable_device(struct acpiphp_slot *slot)
pci_bus_add_devices(bus);
+ slot->flags |= SLOT_ENABLED;
list_for_each_entry(func, &slot->funcs, sibling) {
dev = pci_get_slot(bus, PCI_DEVFN(slot->device,
func->function));
- if (!dev)
+ if (!dev) {
+ /* Do not set SLOT_ENABLED flag if some funcs
+ are not added. */
+ slot->flags &= (~SLOT_ENABLED);
continue;
+ }
if (dev->hdr_type != PCI_HEADER_TYPE_BRIDGE &&
dev->hdr_type != PCI_HEADER_TYPE_CARDBUS) {
@@ -867,7 +862,6 @@ static int __ref enable_device(struct acpiphp_slot *slot)
pci_dev_put(dev);
}
- slot->flags |= SLOT_ENABLED;
err_exit:
return retval;
@@ -892,9 +886,12 @@ static int disable_device(struct acpiphp_slot *slot)
{
struct acpiphp_func *func;
struct pci_dev *pdev;
+ struct pci_bus *bus = slot->bridge->pci_bus;
- /* is this slot already disabled? */
- if (!(slot->flags & SLOT_ENABLED))
+ /* The slot will be enabled when func 0 is added, so check
+ func 0 before disable the slot. */
+ pdev = pci_get_slot(bus, PCI_DEVFN(slot->device, 0));
+ if (!pdev)
goto err_exit;
list_for_each_entry(func, &slot->funcs, sibling) {
@@ -913,7 +910,7 @@ static int disable_device(struct acpiphp_slot *slot)
disable_bridges(pdev->subordinate);
pci_disable_device(pdev);
}
- pci_remove_bus_device(pdev);
+ __pci_remove_bus_device(pdev);
pci_dev_put(pdev);
}
}
@@ -1070,7 +1067,7 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus)
res->end) {
/* Could not assign a required resources
* for this device, remove it */
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
break;
}
}
diff --git a/drivers/pci/hotplug/cpci_hotplug_pci.c b/drivers/pci/hotplug/cpci_hotplug_pci.c
index 829c327cfb5e..ae853ccd0cd5 100644
--- a/drivers/pci/hotplug/cpci_hotplug_pci.c
+++ b/drivers/pci/hotplug/cpci_hotplug_pci.c
@@ -341,7 +341,7 @@ int cpci_unconfigure_slot(struct slot* slot)
dev = pci_get_slot(slot->bus,
PCI_DEVFN(PCI_SLOT(slot->devfn), i));
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
index fb3f84661bdc..81af764c629b 100644
--- a/drivers/pci/hotplug/cpcihp_generic.c
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -62,7 +62,7 @@
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
/* local variables */
-static int debug;
+static bool debug;
static char *bridge;
static u8 bridge_busnr;
static u8 bridge_slot;
diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c
index 6173b9a4544e..1c8494021a42 100644
--- a/drivers/pci/hotplug/cpqphp_pci.c
+++ b/drivers/pci/hotplug/cpqphp_pci.c
@@ -127,7 +127,7 @@ int cpqhp_unconfigure_device(struct pci_func* func)
struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
if (temp) {
pci_dev_put(temp);
- pci_remove_bus_device(temp);
+ pci_stop_and_remove_bus_device(temp);
}
}
return 0;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index 17d10e2e8fb6..a019c9a712be 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -40,7 +40,7 @@ static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
static void remove_callback(void *data)
{
- pci_remove_bus_device((struct pci_dev *)data);
+ pci_stop_and_remove_bus_device((struct pci_dev *)data);
}
static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c
index 5506e0e8fbc0..4fda7e6a86a7 100644
--- a/drivers/pci/hotplug/ibmphp_core.c
+++ b/drivers/pci/hotplug/ibmphp_core.c
@@ -721,7 +721,7 @@ static void ibm_unconfigure_device(struct pci_func *func)
for (j = 0; j < 0x08; j++) {
temp = pci_get_bus_and_slot(func->busno, (func->device << 3) | j);
if (temp) {
- pci_remove_bus_device(temp);
+ pci_stop_and_remove_bus_device(temp);
pci_dev_put(temp);
}
}
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c
index 2850e64dedae..714ca5c4ed50 100644
--- a/drivers/pci/hotplug/ibmphp_ebda.c
+++ b/drivers/pci/hotplug/ibmphp_ebda.c
@@ -368,8 +368,10 @@ int __init ibmphp_access_ebda (void)
debug ("rio blk id: %x\n", blk_id);
rio_table_ptr = kzalloc(sizeof(struct rio_table_hdr), GFP_KERNEL);
- if (!rio_table_ptr)
- return -ENOMEM;
+ if (!rio_table_ptr) {
+ rc = -ENOMEM;
+ goto out;
+ }
rio_table_ptr->ver_num = readb (io_mem + offset);
rio_table_ptr->scal_count = readb (io_mem + offset + 1);
rio_table_ptr->riodev_count = readb (io_mem + offset + 2);
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index bcdbb1643621..a960faec1021 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -241,34 +241,79 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask)
return retval;
}
-static inline int check_link_active(struct controller *ctrl)
+static bool check_link_active(struct controller *ctrl)
{
- u16 link_status;
+ bool ret = false;
+ u16 lnk_status;
- if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &link_status))
- return 0;
- return !!(link_status & PCI_EXP_LNKSTA_DLLLA);
+ if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status))
+ return ret;
+
+ ret = !!(lnk_status & PCI_EXP_LNKSTA_DLLLA);
+
+ if (ret)
+ ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status);
+
+ return ret;
}
-static void pcie_wait_link_active(struct controller *ctrl)
+static void __pcie_wait_link_active(struct controller *ctrl, bool active)
{
int timeout = 1000;
- if (check_link_active(ctrl))
+ if (check_link_active(ctrl) == active)
return;
while (timeout > 0) {
msleep(10);
timeout -= 10;
- if (check_link_active(ctrl))
+ if (check_link_active(ctrl) == active)
return;
}
- ctrl_dbg(ctrl, "Data Link Layer Link Active not set in 1000 msec\n");
+ ctrl_dbg(ctrl, "Data Link Layer Link Active not %s in 1000 msec\n",
+ active ? "set" : "cleared");
+}
+
+static void pcie_wait_link_active(struct controller *ctrl)
+{
+ __pcie_wait_link_active(ctrl, true);
+}
+
+static void pcie_wait_link_not_active(struct controller *ctrl)
+{
+ __pcie_wait_link_active(ctrl, false);
+}
+
+static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
+{
+ u32 l;
+ int count = 0;
+ int delay = 1000, step = 20;
+ bool found = false;
+
+ do {
+ found = pci_bus_read_dev_vendor_id(bus, devfn, &l, 0);
+ count++;
+
+ if (found)
+ break;
+
+ msleep(step);
+ delay -= step;
+ } while (delay > 0);
+
+ if (count > 1 && pciehp_debug)
+ printk(KERN_DEBUG "pci %04x:%02x:%02x.%d id reading try %d times with interval %d ms to get %08x\n",
+ pci_domain_nr(bus), bus->number, PCI_SLOT(devfn),
+ PCI_FUNC(devfn), count, step, l);
+
+ return found;
}
int pciehp_check_link_status(struct controller *ctrl)
{
u16 lnk_status;
int retval = 0;
+ bool found = false;
/*
* Data Link Layer Link Active Reporting must be capable for
@@ -280,13 +325,10 @@ int pciehp_check_link_status(struct controller *ctrl)
else
msleep(1000);
- /*
- * Need to wait for 1000 ms after Data Link Layer Link Active
- * (DLLLA) bit reads 1b before sending configuration request.
- * We need it before checking Link Training (LT) bit becuase
- * LT is still set even after DLLLA bit is set on some platform.
- */
- msleep(1000);
+ /* wait 100ms before read pci conf, and try in 1s */
+ msleep(100);
+ found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
+ PCI_DEVFN(0, 0));
retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status);
if (retval) {
@@ -302,19 +344,50 @@ int pciehp_check_link_status(struct controller *ctrl)
return retval;
}
- /*
- * If the port supports Link speeds greater than 5.0 GT/s, we
- * must wait for 100 ms after Link training completes before
- * sending configuration request.
- */
- if (ctrl->pcie->port->subordinate->max_bus_speed > PCIE_SPEED_5_0GT)
- msleep(100);
-
pcie_update_link_speed(ctrl->pcie->port->subordinate, lnk_status);
+ if (!found && !retval)
+ retval = -1;
+
return retval;
}
+static int __pciehp_link_set(struct controller *ctrl, bool enable)
+{
+ u16 lnk_ctrl;
+ int retval = 0;
+
+ retval = pciehp_readw(ctrl, PCI_EXP_LNKCTL, &lnk_ctrl);
+ if (retval) {
+ ctrl_err(ctrl, "Cannot read LNKCTRL register\n");
+ return retval;
+ }
+
+ if (enable)
+ lnk_ctrl &= ~PCI_EXP_LNKCTL_LD;
+ else
+ lnk_ctrl |= PCI_EXP_LNKCTL_LD;
+
+ retval = pciehp_writew(ctrl, PCI_EXP_LNKCTL, lnk_ctrl);
+ if (retval) {
+ ctrl_err(ctrl, "Cannot write LNKCTRL register\n");
+ return retval;
+ }
+ ctrl_dbg(ctrl, "%s: lnk_ctrl = %x\n", __func__, lnk_ctrl);
+
+ return retval;
+}
+
+static int pciehp_link_enable(struct controller *ctrl)
+{
+ return __pciehp_link_set(ctrl, true);
+}
+
+static int pciehp_link_disable(struct controller *ctrl)
+{
+ return __pciehp_link_set(ctrl, false);
+}
+
int pciehp_get_attention_status(struct slot *slot, u8 *status)
{
struct controller *ctrl = slot->ctrl;
@@ -533,6 +606,10 @@ int pciehp_power_on_slot(struct slot * slot)
ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", __func__,
pci_pcie_cap(ctrl->pcie->port) + PCI_EXP_SLTCTL, slot_cmd);
+ retval = pciehp_link_enable(ctrl);
+ if (retval)
+ ctrl_err(ctrl, "%s: Can not enable the link!\n", __func__);
+
return retval;
}
@@ -543,6 +620,14 @@ int pciehp_power_off_slot(struct slot * slot)
u16 cmd_mask;
int retval;
+ /* Disable the link at first */
+ pciehp_link_disable(ctrl);
+ /* wait the link is down */
+ if (ctrl->link_active_reporting)
+ pcie_wait_link_not_active(ctrl);
+ else
+ msleep(1000);
+
slot_cmd = POWER_OFF;
cmd_mask = PCI_EXP_SLTCTL_PCC;
retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask);
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c
index a4031dfe938e..47d9dc06b109 100644
--- a/drivers/pci/hotplug/pciehp_pci.c
+++ b/drivers/pci/hotplug/pciehp_pci.c
@@ -141,7 +141,7 @@ int pciehp_unconfigure_device(struct slot *p_slot)
break;
}
}
- pci_remove_bus_device(temp);
+ pci_stop_and_remove_bus_device(temp);
/*
* Ensure that no new Requests will be generated from
* the device.
diff --git a/drivers/pci/hotplug/rpadlpar_core.c b/drivers/pci/hotplug/rpadlpar_core.c
index c56a9413e1af..1e117c2a3cad 100644
--- a/drivers/pci/hotplug/rpadlpar_core.c
+++ b/drivers/pci/hotplug/rpadlpar_core.c
@@ -389,7 +389,7 @@ int dlpar_remove_pci_slot(char *drc_name, struct device_node *dn)
BUG_ON(!bus->self);
pr_debug("PCI: Now removing bridge device %s\n", pci_name(bus->self));
eeh_remove_bus_device(bus->self);
- pci_remove_bus_device(bus->self);
+ pci_stop_and_remove_bus_device(bus->self);
return 0;
}
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index 72d507b6a2aa..de573113c102 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -554,7 +554,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
PCI_FUNC(func)));
if (dev) {
sn_bus_free_data(dev);
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c
index a2ccfcd3c298..df7e4bfadae3 100644
--- a/drivers/pci/hotplug/shpchp_pci.c
+++ b/drivers/pci/hotplug/shpchp_pci.c
@@ -124,7 +124,7 @@ int shpchp_unconfigure_device(struct slot *p_slot)
break;
}
}
- pci_remove_bus_device(temp);
+ pci_stop_and_remove_bus_device(temp);
pci_dev_put(temp);
}
return rc;
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 0dab5ecf61bb..6554e1a0f634 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -142,7 +142,7 @@ failed2:
failed1:
pci_dev_put(dev);
mutex_lock(&iov->dev->sriov->lock);
- pci_remove_bus_device(virtfn);
+ pci_stop_and_remove_bus_device(virtfn);
virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
mutex_unlock(&iov->dev->sriov->lock);
@@ -173,10 +173,16 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset)
sprintf(buf, "virtfn%u", id);
sysfs_remove_link(&dev->dev.kobj, buf);
- sysfs_remove_link(&virtfn->dev.kobj, "physfn");
+ /*
+ * pci_stop_dev() could have been called for this virtfn already,
+ * so the directory for the virtfn may have been removed before.
+ * Double check to avoid spurious sysfs warnings.
+ */
+ if (virtfn->dev.kobj.sd)
+ sysfs_remove_link(&virtfn->dev.kobj, "physfn");
mutex_lock(&iov->dev->sriov->lock);
- pci_remove_bus_device(virtfn);
+ pci_stop_and_remove_bus_device(virtfn);
virtfn_remove_bus(dev->bus, virtfn_bus(dev, id));
mutex_unlock(&iov->dev->sriov->lock);
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 8d9616b821ca..6b54b23b990b 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -419,6 +419,16 @@ static void pci_device_shutdown(struct device *dev)
drv->shutdown(pci_dev);
pci_msi_shutdown(pci_dev);
pci_msix_shutdown(pci_dev);
+
+ /*
+ * Devices may be enabled to wake up by runtime PM, but they need not
+ * be supposed to wake up the system from its "power off" state (e.g.
+ * ACPI S5). Therefore disable wakeup for all devices that aren't
+ * supposed to wake up the system at this point. The state argument
+ * will be ignored by pci_enable_wake().
+ */
+ if (!device_may_wakeup(dev))
+ pci_enable_wake(pci_dev, PCI_UNKNOWN, false);
}
#ifdef CONFIG_PM
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index a3cd8cad532a..a55e248618cd 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -330,7 +330,7 @@ static void remove_callback(struct device *dev)
struct pci_dev *pdev = to_pci_dev(dev);
mutex_lock(&pci_remove_rescan_mutex);
- pci_remove_bus_device(pdev);
+ pci_stop_and_remove_bus_device(pdev);
mutex_unlock(&pci_remove_rescan_mutex);
}
@@ -366,7 +366,10 @@ dev_bus_rescan_store(struct device *dev, struct device_attribute *attr,
if (val) {
mutex_lock(&pci_remove_rescan_mutex);
- pci_rescan_bus(bus);
+ if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
+ pci_rescan_bus_bridge_resize(bus->self);
+ else
+ pci_rescan_bus(bus);
mutex_unlock(&pci_remove_rescan_mutex);
}
return count;
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index af295bb21d62..815674415267 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -94,6 +94,9 @@ u8 pci_cache_line_size;
*/
unsigned int pcibios_max_latency = 255;
+/* If set, the PCIe ARI capability will not be used. */
+static bool pcie_ari_disabled;
+
/**
* pci_bus_max_busnr - returns maximum PCI bus number of given bus' children
* @bus: pointer to PCI bus structure to search
@@ -825,6 +828,19 @@ EXPORT_SYMBOL(pci_choose_state);
#define pcie_cap_has_sltctl2(type, flags) \
((flags & PCI_EXP_FLAGS_VERS) > 1)
+static struct pci_cap_saved_state *pci_find_saved_cap(
+ struct pci_dev *pci_dev, char cap)
+{
+ struct pci_cap_saved_state *tmp;
+ struct hlist_node *pos;
+
+ hlist_for_each_entry(tmp, pos, &pci_dev->saved_cap_space, next) {
+ if (tmp->cap.cap_nr == cap)
+ return tmp;
+ }
+ return NULL;
+}
+
static int pci_save_pcie_state(struct pci_dev *dev)
{
int pos, i = 0;
@@ -959,6 +975,7 @@ void pci_restore_state(struct pci_dev *dev)
{
int i;
u32 val;
+ int tries;
if (!dev->state_saved)
return;
@@ -973,12 +990,16 @@ void pci_restore_state(struct pci_dev *dev)
*/
for (i = 15; i >= 0; i--) {
pci_read_config_dword(dev, i * 4, &val);
- if (val != dev->saved_config_space[i]) {
+ tries = 10;
+ while (tries && val != dev->saved_config_space[i]) {
dev_dbg(&dev->dev, "restoring config "
"space at offset %#x (was %#x, writing %#x)\n",
i, val, (int)dev->saved_config_space[i]);
pci_write_config_dword(dev,i * 4,
dev->saved_config_space[i]);
+ pci_read_config_dword(dev, i * 4, &val);
+ mdelay(10);
+ tries--;
}
}
pci_restore_pcix_state(dev);
@@ -1864,6 +1885,12 @@ void platform_pci_wakeup_init(struct pci_dev *dev)
platform_pci_sleep_wake(dev, false);
}
+static void pci_add_saved_cap(struct pci_dev *pci_dev,
+ struct pci_cap_saved_state *new_cap)
+{
+ hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space);
+}
+
/**
* pci_add_save_buffer - allocate buffer for saving given capability registers
* @dev: the PCI device
@@ -1911,6 +1938,15 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev)
"unable to preallocate PCI-X save buffer\n");
}
+void pci_free_cap_save_buffers(struct pci_dev *dev)
+{
+ struct pci_cap_saved_state *tmp;
+ struct hlist_node *pos, *n;
+
+ hlist_for_each_entry_safe(tmp, pos, n, &dev->saved_cap_space, next)
+ kfree(tmp);
+}
+
/**
* pci_enable_ari - enable ARI forwarding if hardware support it
* @dev: the PCI device
@@ -1922,7 +1958,7 @@ void pci_enable_ari(struct pci_dev *dev)
u16 flags, ctrl;
struct pci_dev *bridge;
- if (!pci_is_pcie(dev) || dev->devfn)
+ if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)
return;
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
@@ -3163,6 +3199,31 @@ int __pci_reset_function(struct pci_dev *dev)
EXPORT_SYMBOL_GPL(__pci_reset_function);
/**
+ * __pci_reset_function_locked - reset a PCI device function while holding
+ * the @dev mutex lock.
+ * @dev: PCI device to reset
+ *
+ * Some devices allow an individual function to be reset without affecting
+ * other functions in the same device. The PCI device must be responsive
+ * to PCI config space in order to use this function.
+ *
+ * The device function is presumed to be unused and the caller is holding
+ * the device mutex lock when this function is called.
+ * Resetting the device will make the contents of PCI configuration space
+ * random, so any caller of this must be prepared to reinitialise the
+ * device including MSI, bus mastering, BARs, decoding IO and memory spaces,
+ * etc.
+ *
+ * Returns 0 if the device function was successfully reset or negative if the
+ * device doesn't support resetting a single function.
+ */
+int __pci_reset_function_locked(struct pci_dev *dev)
+{
+ return pci_dev_reset(dev, 1);
+}
+EXPORT_SYMBOL_GPL(__pci_reset_function_locked);
+
+/**
* pci_probe_reset_function - check whether the device can be safely reset
* @dev: PCI device to reset
*
@@ -3636,6 +3697,68 @@ int pci_is_reassigndev(struct pci_dev *dev)
return (pci_specified_resource_alignment(dev) != 0);
}
+/*
+ * This function disables memory decoding and releases memory resources
+ * of the device specified by kernel's boot parameter 'pci=resource_alignment='.
+ * It also rounds up size to specified alignment.
+ * Later on, the kernel will assign page-aligned memory resource back
+ * to the device.
+ */
+void pci_reassigndev_resource_alignment(struct pci_dev *dev)
+{
+ int i;
+ struct resource *r;
+ resource_size_t align, size;
+ u16 command;
+
+ if (!pci_is_reassigndev(dev))
+ return;
+
+ if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
+ dev_warn(&dev->dev,
+ "Can't reassign resources to host bridge.\n");
+ return;
+ }
+
+ dev_info(&dev->dev,
+ "Disabling memory decoding and releasing memory resources.\n");
+ pci_read_config_word(dev, PCI_COMMAND, &command);
+ command &= ~PCI_COMMAND_MEMORY;
+ pci_write_config_word(dev, PCI_COMMAND, command);
+
+ align = pci_specified_resource_alignment(dev);
+ for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) {
+ r = &dev->resource[i];
+ if (!(r->flags & IORESOURCE_MEM))
+ continue;
+ size = resource_size(r);
+ if (size < align) {
+ size = align;
+ dev_info(&dev->dev,
+ "Rounding up size of resource #%d to %#llx.\n",
+ i, (unsigned long long)size);
+ }
+ r->end = size - 1;
+ r->start = 0;
+ }
+ /* Need to disable bridge's resource window,
+ * to enable the kernel to reassign new resource
+ * window later on.
+ */
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
+ (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+ for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
+ r = &dev->resource[i];
+ if (!(r->flags & IORESOURCE_MEM))
+ continue;
+ r->end = resource_size(r) - 1;
+ r->start = 0;
+ }
+ pci_disable_bridge_window(dev);
+ }
+}
+
ssize_t pci_set_resource_alignment_param(const char *buf, size_t count)
{
if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1)
@@ -3714,10 +3837,14 @@ static int __init pci_setup(char *str)
pci_no_msi();
} else if (!strcmp(str, "noaer")) {
pci_no_aer();
+ } else if (!strncmp(str, "realloc=", 8)) {
+ pci_realloc_get_opt(str + 8);
} else if (!strncmp(str, "realloc", 7)) {
- pci_realloc();
+ pci_realloc_get_opt("on");
} else if (!strcmp(str, "nodomains")) {
pci_no_domains();
+ } else if (!strncmp(str, "noari", 5)) {
+ pcie_ari_disabled = true;
} else if (!strncmp(str, "cbiosize=", 9)) {
pci_cardbus_io_size = memparse(str + 9, &str);
} else if (!strncmp(str, "cbmemsize=", 10)) {
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 1009a5e88e53..e4943479b234 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -73,6 +73,7 @@ extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign);
extern void pci_pm_init(struct pci_dev *dev);
extern void platform_pci_wakeup_init(struct pci_dev *dev);
extern void pci_allocate_cap_save_buffers(struct pci_dev *dev);
+void pci_free_cap_save_buffers(struct pci_dev *dev);
static inline void pci_wakeup_event(struct pci_dev *dev)
{
@@ -148,7 +149,7 @@ static inline void pci_no_msi(void) { }
static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
#endif
-extern void pci_realloc(void);
+void pci_realloc_get_opt(char *);
static inline int pci_no_d1d2(struct pci_dev *dev)
{
@@ -207,6 +208,8 @@ enum pci_bar_type {
pci_bar_mem64, /* A 64-bit memory BAR */
};
+bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl,
+ int crs_timeout);
extern int pci_setup_device(struct pci_dev *dev);
extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
struct resource *res, unsigned int reg);
@@ -225,11 +228,8 @@ static inline int pci_ari_enabled(struct pci_bus *bus)
return bus->self && bus->self->ari_enabled;
}
-#ifdef CONFIG_PCI_QUIRKS
-extern int pci_is_reassigndev(struct pci_dev *dev);
-resource_size_t pci_specified_resource_alignment(struct pci_dev *dev);
+void pci_reassigndev_resource_alignment(struct pci_dev *dev);
extern void pci_disable_bridge_window(struct pci_dev *dev);
-#endif
/* Single Root I/O Virtualization */
struct pci_sriov {
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 72962cc92e0a..6c8bc5809787 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -55,6 +55,31 @@ config PCIEASPM_DEBUG
This enables PCI Express ASPM debug support. It will add per-device
interface to control ASPM.
+choice
+ prompt "Default ASPM policy"
+ default PCIEASPM_DEFAULT
+ depends on PCIEASPM
+
+config PCIEASPM_DEFAULT
+ bool "BIOS default"
+ depends on PCIEASPM
+ help
+ Use the BIOS defaults for PCI Express ASPM.
+
+config PCIEASPM_POWERSAVE
+ bool "Powersave"
+ depends on PCIEASPM
+ help
+ Enable PCI Express ASPM L0s and L1 where possible, even if the
+ BIOS did not.
+
+config PCIEASPM_PERFORMANCE
+ bool "Performance"
+ depends on PCIEASPM
+ help
+ Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them.
+endchoice
+
config PCIE_PME
def_bool y
depends on PCIEPORTBUS && PM_RUNTIME && EXPERIMENTAL && ACPI
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 24f049e73952..4bdef24cd412 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -76,7 +76,15 @@ static LIST_HEAD(link_list);
#define POLICY_DEFAULT 0 /* BIOS default setting */
#define POLICY_PERFORMANCE 1 /* high performance */
#define POLICY_POWERSAVE 2 /* high power saving */
+
+#ifdef CONFIG_PCIEASPM_PERFORMANCE
+static int aspm_policy = POLICY_PERFORMANCE;
+#elif defined CONFIG_PCIEASPM_POWERSAVE
+static int aspm_policy = POLICY_POWERSAVE;
+#else
static int aspm_policy;
+#endif
+
static const char *policy_str[] = {
[POLICY_DEFAULT] = "default",
[POLICY_PERFORMANCE] = "performance",
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index bd00a01aef14..eea2ca2375e6 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -34,6 +34,18 @@ struct pci_dev;
extern void pcie_clear_root_pme_status(struct pci_dev *dev);
+#ifdef CONFIG_HOTPLUG_PCI_PCIE
+extern bool pciehp_msi_disabled;
+
+static inline bool pciehp_no_msi(void)
+{
+ return pciehp_msi_disabled;
+}
+
+#else /* !CONFIG_HOTPLUG_PCI_PCIE */
+static inline bool pciehp_no_msi(void) { return false; }
+#endif /* !CONFIG_HOTPLUG_PCI_PCIE */
+
#ifdef CONFIG_PCIE_PME
extern bool pcie_pme_msi_disabled;
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 595654a1a6a6..2f589a54f9bd 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -19,6 +19,17 @@
#include "../pci.h"
#include "portdrv.h"
+bool pciehp_msi_disabled;
+
+static int __init pciehp_setup(char *str)
+{
+ if (!strncmp(str, "nomsi", 5))
+ pciehp_msi_disabled = true;
+
+ return 1;
+}
+__setup("pcie_hp=", pciehp_setup);
+
/**
* release_pcie_device - free PCI Express port service device structure
* @dev: Port service device to release
@@ -189,8 +200,9 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
int i, irq = -1;
- /* We have to use INTx if MSI cannot be used for PCIe PME. */
- if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) {
+ /* We have to use INTx if MSI cannot be used for PCIe PME or pciehp. */
+ if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) ||
+ ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) {
if (dev->pin)
irq = dev->irq;
goto no_msi;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 71eac9cd724d..5e1ca3c58a7d 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -15,6 +15,8 @@
#define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */
#define CARDBUS_RESERVE_BUSNR 3
+static LIST_HEAD(pci_host_bridges);
+
/* Ugh. Need to stop exporting this to modules. */
LIST_HEAD(pci_root_buses);
EXPORT_SYMBOL(pci_root_buses);
@@ -42,6 +44,82 @@ int no_pci_devices(void)
}
EXPORT_SYMBOL(no_pci_devices);
+static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev)
+{
+ struct pci_bus *bus;
+ struct pci_host_bridge *bridge;
+
+ bus = dev->bus;
+ while (bus->parent)
+ bus = bus->parent;
+
+ list_for_each_entry(bridge, &pci_host_bridges, list) {
+ if (bridge->bus == bus)
+ return bridge;
+ }
+
+ return NULL;
+}
+
+static bool resource_contains(struct resource *res1, struct resource *res2)
+{
+ return res1->start <= res2->start && res1->end >= res2->end;
+}
+
+void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
+ struct resource *res)
+{
+ struct pci_host_bridge *bridge = pci_host_bridge(dev);
+ struct pci_host_bridge_window *window;
+ resource_size_t offset = 0;
+
+ list_for_each_entry(window, &bridge->windows, list) {
+ if (resource_type(res) != resource_type(window->res))
+ continue;
+
+ if (resource_contains(window->res, res)) {
+ offset = window->offset;
+ break;
+ }
+ }
+
+ region->start = res->start - offset;
+ region->end = res->end - offset;
+}
+EXPORT_SYMBOL(pcibios_resource_to_bus);
+
+static bool region_contains(struct pci_bus_region *region1,
+ struct pci_bus_region *region2)
+{
+ return region1->start <= region2->start && region1->end >= region2->end;
+}
+
+void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
+ struct pci_bus_region *region)
+{
+ struct pci_host_bridge *bridge = pci_host_bridge(dev);
+ struct pci_host_bridge_window *window;
+ struct pci_bus_region bus_region;
+ resource_size_t offset = 0;
+
+ list_for_each_entry(window, &bridge->windows, list) {
+ if (resource_type(res) != resource_type(window->res))
+ continue;
+
+ bus_region.start = window->res->start - window->offset;
+ bus_region.end = window->res->end - window->offset;
+
+ if (region_contains(&bus_region, region)) {
+ offset = window->offset;
+ break;
+ }
+ }
+
+ res->start = region->start + offset;
+ res->end = region->end + offset;
+}
+EXPORT_SYMBOL(pcibios_bus_to_resource);
+
/*
* PCI Bus Class
*/
@@ -135,6 +213,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
{
u32 l, sz, mask;
u16 orig_cmd;
+ struct pci_bus_region region;
mask = type ? PCI_ROM_ADDRESS_MASK : ~0;
@@ -214,11 +293,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
/* Address above 32-bit boundary; disable the BAR */
pci_write_config_dword(dev, pos, 0);
pci_write_config_dword(dev, pos + 4, 0);
- res->start = 0;
- res->end = sz64;
+ region.start = 0;
+ region.end = sz64;
+ pcibios_bus_to_resource(dev, res, &region);
} else {
- res->start = l64;
- res->end = l64 + sz64;
+ region.start = l64;
+ region.end = l64 + sz64;
+ pcibios_bus_to_resource(dev, res, &region);
dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n",
pos, res);
}
@@ -228,8 +309,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type,
if (!sz)
goto fail;
- res->start = l;
- res->end = l + sz;
+ region.start = l;
+ region.end = l + sz;
+ pcibios_bus_to_resource(dev, res, &region);
dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res);
}
@@ -266,7 +348,8 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child)
struct pci_dev *dev = child->self;
u8 io_base_lo, io_limit_lo;
unsigned long base, limit;
- struct resource *res;
+ struct pci_bus_region region;
+ struct resource *res, res2;
res = child->resource[0];
pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo);
@@ -284,10 +367,14 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child)
if (base && base <= limit) {
res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO;
+ res2.flags = res->flags;
+ region.start = base;
+ region.end = limit + 0xfff;
+ pcibios_bus_to_resource(dev, &res2, &region);
if (!res->start)
- res->start = base;
+ res->start = res2.start;
if (!res->end)
- res->end = limit + 0xfff;
+ res->end = res2.end;
dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
}
}
@@ -297,6 +384,7 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child)
struct pci_dev *dev = child->self;
u16 mem_base_lo, mem_limit_lo;
unsigned long base, limit;
+ struct pci_bus_region region;
struct resource *res;
res = child->resource[1];
@@ -306,8 +394,9 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child)
limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16;
if (base && base <= limit) {
res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM;
- res->start = base;
- res->end = limit + 0xfffff;
+ region.start = base;
+ region.end = limit + 0xfffff;
+ pcibios_bus_to_resource(dev, res, &region);
dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
}
}
@@ -317,6 +406,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child)
struct pci_dev *dev = child->self;
u16 mem_base_lo, mem_limit_lo;
unsigned long base, limit;
+ struct pci_bus_region region;
struct resource *res;
res = child->resource[2];
@@ -353,8 +443,9 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child)
IORESOURCE_MEM | IORESOURCE_PREFETCH;
if (res->flags & PCI_PREF_RANGE_TYPE_64)
res->flags |= IORESOURCE_MEM_64;
- res->start = base;
- res->end = limit + 0xfffff;
+ region.start = base;
+ region.end = limit + 0xfffff;
+ pcibios_bus_to_resource(dev, res, &region);
dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res);
}
}
@@ -900,6 +991,8 @@ int pci_setup_device(struct pci_dev *dev)
u8 hdr_type;
struct pci_slot *slot;
int pos = 0;
+ struct pci_bus_region region;
+ struct resource *res;
if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type))
return -EIO;
@@ -926,12 +1019,10 @@ int pci_setup_device(struct pci_dev *dev)
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class);
dev->revision = class & 0xff;
- class >>= 8; /* upper 3 bytes */
- dev->class = class;
- class >>= 8;
+ dev->class = class >> 8; /* upper 3 bytes */
- dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %d class %#08x\n",
- dev->vendor, dev->device, dev->hdr_type, class);
+ dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %02x class %#08x\n",
+ dev->vendor, dev->device, dev->hdr_type, dev->class);
/* need to have dev->class ready */
dev->cfg_size = pci_cfg_space_size(dev);
@@ -963,20 +1054,28 @@ int pci_setup_device(struct pci_dev *dev)
u8 progif;
pci_read_config_byte(dev, PCI_CLASS_PROG, &progif);
if ((progif & 1) == 0) {
- dev->resource[0].start = 0x1F0;
- dev->resource[0].end = 0x1F7;
- dev->resource[0].flags = LEGACY_IO_RESOURCE;
- dev->resource[1].start = 0x3F6;
- dev->resource[1].end = 0x3F6;
- dev->resource[1].flags = LEGACY_IO_RESOURCE;
+ region.start = 0x1F0;
+ region.end = 0x1F7;
+ res = &dev->resource[0];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev, res, &region);
+ region.start = 0x3F6;
+ region.end = 0x3F6;
+ res = &dev->resource[1];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev, res, &region);
}
if ((progif & 4) == 0) {
- dev->resource[2].start = 0x170;
- dev->resource[2].end = 0x177;
- dev->resource[2].flags = LEGACY_IO_RESOURCE;
- dev->resource[3].start = 0x376;
- dev->resource[3].end = 0x376;
- dev->resource[3].flags = LEGACY_IO_RESOURCE;
+ region.start = 0x170;
+ region.end = 0x177;
+ res = &dev->resource[2];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev, res, &region);
+ region.start = 0x376;
+ region.end = 0x376;
+ res = &dev->resource[3];
+ res->flags = LEGACY_IO_RESOURCE;
+ pcibios_bus_to_resource(dev, res, &region);
}
}
break;
@@ -1013,8 +1112,8 @@ int pci_setup_device(struct pci_dev *dev)
return -EIO;
bad:
- dev_err(&dev->dev, "ignoring class %02x (doesn't match header "
- "type %02x)\n", class, dev->hdr_type);
+ dev_err(&dev->dev, "ignoring class %#08x (doesn't match header "
+ "type %02x)\n", dev->class, dev->hdr_type);
dev->class = PCI_CLASS_NOT_DEFINED;
}
@@ -1026,6 +1125,7 @@ static void pci_release_capabilities(struct pci_dev *dev)
{
pci_vpd_release(dev);
pci_iov_release(dev);
+ pci_free_cap_save_buffers(dev);
}
/**
@@ -1118,40 +1218,54 @@ struct pci_dev *alloc_pci_dev(void)
}
EXPORT_SYMBOL(alloc_pci_dev);
-/*
- * Read the config data for a PCI device, sanity-check it
- * and fill in the dev structure...
- */
-static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
+bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l,
+ int crs_timeout)
{
- struct pci_dev *dev;
- u32 l;
int delay = 1;
- if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
- return NULL;
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
/* some broken boards return 0 or ~0 if a slot is empty: */
- if (l == 0xffffffff || l == 0x00000000 ||
- l == 0x0000ffff || l == 0xffff0000)
- return NULL;
+ if (*l == 0xffffffff || *l == 0x00000000 ||
+ *l == 0x0000ffff || *l == 0xffff0000)
+ return false;
/* Configuration request Retry Status */
- while (l == 0xffff0001) {
+ while (*l == 0xffff0001) {
+ if (!crs_timeout)
+ return false;
+
msleep(delay);
delay *= 2;
- if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l))
- return NULL;
+ if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l))
+ return false;
/* Card hasn't responded in 60 seconds? Must be stuck. */
- if (delay > 60 * 1000) {
+ if (delay > crs_timeout) {
printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not "
"responding\n", pci_domain_nr(bus),
bus->number, PCI_SLOT(devfn),
PCI_FUNC(devfn));
- return NULL;
+ return false;
}
}
+ return true;
+}
+EXPORT_SYMBOL(pci_bus_read_dev_vendor_id);
+
+/*
+ * Read the config data for a PCI device, sanity-check it
+ * and fill in the dev structure...
+ */
+static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
+{
+ struct pci_dev *dev;
+ u32 l;
+
+ if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000))
+ return NULL;
+
dev = alloc_pci_dev();
if (!dev)
return NULL;
@@ -1212,6 +1326,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus)
/* Fix up broken headers */
pci_fixup_device(pci_fixup_header, dev);
+ /* moved out from quirk header fixup code */
+ pci_reassigndev_resource_alignment(dev);
+
/* Clear the state_saved flag. */
dev->state_saved = false;
@@ -1530,21 +1647,27 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus)
struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
struct pci_ops *ops, void *sysdata, struct list_head *resources)
{
- int error, i;
+ int error;
+ struct pci_host_bridge *bridge;
struct pci_bus *b, *b2;
struct device *dev;
- struct pci_bus_resource *bus_res, *n;
+ struct pci_host_bridge_window *window, *n;
struct resource *res;
+ resource_size_t offset;
+ char bus_addr[64];
+ char *fmt;
+
+ bridge = kzalloc(sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return NULL;
b = pci_alloc_bus();
if (!b)
- return NULL;
+ goto err_bus;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- kfree(b);
- return NULL;
- }
+ if (!dev)
+ goto err_dev;
b->sysdata = sysdata;
b->ops = ops;
@@ -1556,10 +1679,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
goto err_out;
}
- down_write(&pci_bus_sem);
- list_add_tail(&b->node, &pci_root_buses);
- up_write(&pci_bus_sem);
-
dev->parent = parent;
dev->release = pci_release_bus_bridge_dev;
dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus);
@@ -1585,31 +1704,53 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus,
b->number = b->secondary = bus;
- /* Add initial resources to the bus */
- list_for_each_entry_safe(bus_res, n, resources, list)
- list_move_tail(&bus_res->list, &b->resources);
+ bridge->bus = b;
+ INIT_LIST_HEAD(&bridge->windows);
if (parent)
dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev));
else
printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev));
- pci_bus_for_each_resource(b, res, i) {
- if (res)
- dev_info(&b->dev, "root bus resource %pR\n", res);
+ /* Add initial resources to the bus */
+ list_for_each_entry_safe(window, n, resources, list) {
+ list_move_tail(&window->list, &bridge->windows);
+ res = window->res;
+ offset = window->offset;
+ pci_bus_add_resource(b, res, 0);
+ if (offset) {
+ if (resource_type(res) == IORESOURCE_IO)
+ fmt = " (bus address [%#06llx-%#06llx])";
+ else
+ fmt = " (bus address [%#010llx-%#010llx])";
+ snprintf(bus_addr, sizeof(bus_addr), fmt,
+ (unsigned long long) (res->start - offset),
+ (unsigned long long) (res->end - offset));
+ } else
+ bus_addr[0] = '\0';
+ dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr);
}
+ down_write(&pci_bus_sem);
+ list_add_tail(&bridge->list, &pci_host_bridges);
+ list_add_tail(&b->node, &pci_root_buses);
+ up_write(&pci_bus_sem);
+
return b;
class_dev_reg_err:
device_unregister(dev);
dev_reg_err:
down_write(&pci_bus_sem);
+ list_del(&bridge->list);
list_del(&b->node);
up_write(&pci_bus_sem);
err_out:
kfree(dev);
+err_dev:
kfree(b);
+err_bus:
+ kfree(bridge);
return NULL;
}
@@ -1667,36 +1808,29 @@ EXPORT_SYMBOL(pci_scan_bus);
#ifdef CONFIG_HOTPLUG
/**
- * pci_rescan_bus - scan a PCI bus for devices.
- * @bus: PCI bus to scan
+ * pci_rescan_bus_bridge_resize - scan a PCI bus for devices.
+ * @bridge: PCI bridge for the bus to scan
*
- * Scan a PCI bus and child buses for new devices, adds them,
- * and enables them.
+ * Scan a PCI bus and child buses for new devices, add them,
+ * and enable them, resizing bridge mmio/io resource if necessary
+ * and possible. The caller must ensure the child devices are already
+ * removed for resizing to occur.
*
* Returns the max number of subordinate bus discovered.
*/
-unsigned int __ref pci_rescan_bus(struct pci_bus *bus)
+unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge)
{
unsigned int max;
- struct pci_dev *dev;
+ struct pci_bus *bus = bridge->subordinate;
max = pci_scan_child_bus(bus);
- down_read(&pci_bus_sem);
- list_for_each_entry(dev, &bus->devices, bus_list)
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
- dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
- if (dev->subordinate)
- pci_bus_size_bridges(dev->subordinate);
- up_read(&pci_bus_sem);
+ pci_assign_unassigned_bridge_resources(bridge);
- pci_bus_assign_resources(bus);
- pci_enable_bridges(bus);
pci_bus_add_devices(bus);
return max;
}
-EXPORT_SYMBOL_GPL(pci_rescan_bus);
EXPORT_SYMBOL(pci_add_new_bus);
EXPORT_SYMBOL(pci_scan_slot);
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index f722c5f6951a..4bf71028556b 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -26,73 +26,12 @@
#include <linux/dmi.h>
#include <linux/pci-aspm.h>
#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/ktime.h>
#include <asm/dma.h> /* isa_dma_bridge_buggy */
#include "pci.h"
/*
- * This quirk function disables memory decoding and releases memory resources
- * of the device specified by kernel's boot parameter 'pci=resource_alignment='.
- * It also rounds up size to specified alignment.
- * Later on, the kernel will assign page-aligned memory resource back
- * to the device.
- */
-static void __devinit quirk_resource_alignment(struct pci_dev *dev)
-{
- int i;
- struct resource *r;
- resource_size_t align, size;
- u16 command;
-
- if (!pci_is_reassigndev(dev))
- return;
-
- if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
- (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
- dev_warn(&dev->dev,
- "Can't reassign resources to host bridge.\n");
- return;
- }
-
- dev_info(&dev->dev,
- "Disabling memory decoding and releasing memory resources.\n");
- pci_read_config_word(dev, PCI_COMMAND, &command);
- command &= ~PCI_COMMAND_MEMORY;
- pci_write_config_word(dev, PCI_COMMAND, command);
-
- align = pci_specified_resource_alignment(dev);
- for (i=0; i < PCI_BRIDGE_RESOURCES; i++) {
- r = &dev->resource[i];
- if (!(r->flags & IORESOURCE_MEM))
- continue;
- size = resource_size(r);
- if (size < align) {
- size = align;
- dev_info(&dev->dev,
- "Rounding up size of resource #%d to %#llx.\n",
- i, (unsigned long long)size);
- }
- r->end = size - 1;
- r->start = 0;
- }
- /* Need to disable bridge's resource window,
- * to enable the kernel to reassign new resource
- * window later on.
- */
- if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
- (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
- for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
- r = &dev->resource[i];
- if (!(r->flags & IORESOURCE_MEM))
- continue;
- r->end = resource_size(r) - 1;
- r->start = 0;
- }
- pci_disable_bridge_window(dev);
- }
-}
-DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment);
-
-/*
* Decoding should be disabled for a PCI device during BAR sizing to avoid
* conflict. But doing so may cause problems on host bridge and perhaps other
* key system devices. For devices that need to have mmio decoding always-on,
@@ -100,10 +39,10 @@ DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment);
*/
static void __devinit quirk_mmio_always_on(struct pci_dev *dev)
{
- if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST)
- dev->mmio_always_on = 1;
+ dev->mmio_always_on = 1;
}
-DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_mmio_always_on);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_HOST, 8, quirk_mmio_always_on);
/* The Mellanox Tavor device gives false positive parity errors
* Mark this device with a broken_parity_status, to allow
@@ -1002,12 +941,12 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C597_0, quirk_vt
*/
static void quirk_cardbus_legacy(struct pci_dev *dev)
{
- if ((PCI_CLASS_BRIDGE_CARDBUS << 8) ^ dev->class)
- return;
pci_write_config_dword(dev, PCI_CB_LEGACY_MODE_BASE, 0);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
-DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID, quirk_cardbus_legacy);
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_CARDBUS, 8, quirk_cardbus_legacy);
+DECLARE_PCI_FIXUP_CLASS_RESUME_EARLY(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_BRIDGE_CARDBUS, 8, quirk_cardbus_legacy);
/*
* Following the PCI ordering rules is optional on the AMD762. I'm not
@@ -1164,17 +1103,20 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10, qui
static void __devinit quirk_no_ata_d3(struct pci_dev *pdev)
{
- /* Quirk the legacy ATA devices only. The AHCI ones are ok */
- if ((pdev->class >> 8) == PCI_CLASS_STORAGE_IDE)
- pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
+ pdev->dev_flags |= PCI_DEV_FLAGS_NO_D3;
}
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID, quirk_no_ata_d3);
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID, quirk_no_ata_d3);
+/* Quirk the legacy ATA devices only. The AHCI ones are ok */
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_SERVERWORKS, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
/* ALi loses some register settings that we cannot then restore */
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, quirk_no_ata_d3);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
/* VIA comes back fine but we need to keep it alive or ACPI GTM failures
occur when mode detecting */
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID, quirk_no_ata_d3);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_VIA, PCI_ANY_ID,
+ PCI_CLASS_STORAGE_IDE, 8, quirk_no_ata_d3);
/* This was originally an Alpha specific thing, but it really fits here.
* The i82375 PCI/EISA bridge appears as non-classified. Fix that.
@@ -1873,8 +1815,7 @@ static void __devinit quirk_netmos(struct pci_dev *dev)
case PCI_DEVICE_ID_NETMOS_9745:
case PCI_DEVICE_ID_NETMOS_9845:
case PCI_DEVICE_ID_NETMOS_9855:
- if ((dev->class >> 8) == PCI_CLASS_COMMUNICATION_SERIAL &&
- num_parallel) {
+ if (num_parallel) {
dev_info(&dev->dev, "Netmos %04x (%u parallel, "
"%u serial); changing class SERIAL to OTHER "
"(use parport_serial)\n",
@@ -1884,7 +1825,8 @@ static void __devinit quirk_netmos(struct pci_dev *dev)
}
}
}
-DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID, quirk_netmos);
+DECLARE_PCI_FIXUP_CLASS_HEADER(PCI_VENDOR_ID_NETMOS, PCI_ANY_ID,
+ PCI_CLASS_COMMUNICATION_SERIAL, 8, quirk_netmos);
static void __devinit quirk_e100_interrupt(struct pci_dev *dev)
{
@@ -1952,7 +1894,8 @@ static void __devinit quirk_e100_interrupt(struct pci_dev *dev)
iounmap(csr);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_e100_interrupt);
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID,
+ PCI_CLASS_NETWORK_ETHERNET, 8, quirk_e100_interrupt);
/*
* The 82575 and 82598 may experience data corruption issues when transitioning
@@ -2834,12 +2777,11 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x3c28, vtd_mask_spec_errors);
static void __devinit fixup_ti816x_class(struct pci_dev* dev)
{
/* TI 816x devices do not have class code set when in PCIe boot mode */
- if (dev->class == PCI_CLASS_NOT_DEFINED) {
- dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n");
- dev->class = PCI_CLASS_MULTIMEDIA_VIDEO;
- }
+ dev_info(&dev->dev, "Setting PCI class for 816x PCIe device\n");
+ dev->class = PCI_CLASS_MULTIMEDIA_VIDEO;
}
-DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_TI, 0xb800, fixup_ti816x_class);
+DECLARE_PCI_FIXUP_CLASS_EARLY(PCI_VENDOR_ID_TI, 0xb800,
+ PCI_CLASS_NOT_DEFINED, 0, fixup_ti816x_class);
/* Some PCIe devices do not work reliably with the claimed maximum
* payload size supported.
@@ -2924,17 +2866,73 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f8, quirk_intel_mc_errata);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65f9, quirk_intel_mc_errata);
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x65fa, quirk_intel_mc_errata);
+
+static void do_one_fixup_debug(void (*fn)(struct pci_dev *dev), struct pci_dev *dev)
+{
+ ktime_t calltime, delta, rettime;
+ unsigned long long duration;
+
+ printk(KERN_DEBUG "calling %pF @ %i for %s\n",
+ fn, task_pid_nr(current), dev_name(&dev->dev));
+ calltime = ktime_get();
+ fn(dev);
+ rettime = ktime_get();
+ delta = ktime_sub(rettime, calltime);
+ duration = (unsigned long long) ktime_to_ns(delta) >> 10;
+ printk(KERN_DEBUG "pci fixup %pF returned after %lld usecs for %s\n",
+ fn, duration, dev_name(&dev->dev));
+}
+
+/*
+ * Some BIOS implementations leave the Intel GPU interrupts enabled,
+ * even though no one is handling them (f.e. i915 driver is never loaded).
+ * Additionally the interrupt destination is not set up properly
+ * and the interrupt ends up -somewhere-.
+ *
+ * These spurious interrupts are "sticky" and the kernel disables
+ * the (shared) interrupt line after 100.000+ generated interrupts.
+ *
+ * Fix it by disabling the still enabled interrupts.
+ * This resolves crashes often seen on monitor unplug.
+ */
+#define I915_DEIER_REG 0x4400c
+static void __devinit disable_igfx_irq(struct pci_dev *dev)
+{
+ void __iomem *regs = pci_iomap(dev, 0, 0);
+ if (regs == NULL) {
+ dev_warn(&dev->dev, "igfx quirk: Can't iomap PCI device\n");
+ return;
+ }
+
+ /* Check if any interrupt line is still enabled */
+ if (readl(regs + I915_DEIER_REG) != 0) {
+ dev_warn(&dev->dev, "BIOS left Intel GPU interrupts enabled; "
+ "disabling\n");
+
+ writel(0, regs + I915_DEIER_REG);
+ }
+
+ pci_iounmap(dev, regs);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x0102, disable_igfx_irq);
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, 0x010a, disable_igfx_irq);
+
static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f,
struct pci_fixup *end)
{
- while (f < end) {
- if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) &&
- (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) {
+ for (; f < end; f++)
+ if ((f->class == (u32) (dev->class >> f->class_shift) ||
+ f->class == (u32) PCI_ANY_ID) &&
+ (f->vendor == dev->vendor ||
+ f->vendor == (u16) PCI_ANY_ID) &&
+ (f->device == dev->device ||
+ f->device == (u16) PCI_ANY_ID)) {
dev_dbg(&dev->dev, "calling %pF\n", f->hook);
- f->hook(dev);
+ if (initcall_debug)
+ do_one_fixup_debug(f->hook, dev);
+ else
+ f->hook(dev);
}
- f++;
- }
}
extern struct pci_fixup __start_pci_fixups_early[];
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index ef8b18c48f26..fd77e2bde2e8 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -79,7 +79,7 @@ EXPORT_SYMBOL(pci_remove_bus);
static void __pci_remove_behind_bridge(struct pci_dev *dev);
/**
- * pci_remove_bus_device - remove a PCI device and any children
+ * pci_stop_and_remove_bus_device - remove a PCI device and any children
* @dev: the device to remove
*
* Remove a PCI device from the device lists, informing the drivers
@@ -90,7 +90,7 @@ static void __pci_remove_behind_bridge(struct pci_dev *dev);
* device lists, remove the /proc entry, and notify userspace
* (/sbin/hotplug).
*/
-static void __pci_remove_bus_device(struct pci_dev *dev)
+void __pci_remove_bus_device(struct pci_dev *dev)
{
if (dev->subordinate) {
struct pci_bus *b = dev->subordinate;
@@ -102,7 +102,9 @@ static void __pci_remove_bus_device(struct pci_dev *dev)
pci_destroy_dev(dev);
}
-void pci_remove_bus_device(struct pci_dev *dev)
+EXPORT_SYMBOL(__pci_remove_bus_device);
+
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
pci_stop_bus_device(dev);
__pci_remove_bus_device(dev);
@@ -127,14 +129,15 @@ static void pci_stop_behind_bridge(struct pci_dev *dev)
}
/**
- * pci_remove_behind_bridge - remove all devices behind a PCI bridge
+ * pci_stop_and_remove_behind_bridge - stop and remove all devices behind
+ * a PCI bridge
* @dev: PCI bridge device
*
* Remove all devices on the bus, except for the parent bridge.
* This also removes any child buses, and any devices they may
* contain in a depth-first manner.
*/
-void pci_remove_behind_bridge(struct pci_dev *dev)
+void pci_stop_and_remove_behind_bridge(struct pci_dev *dev)
{
pci_stop_behind_bridge(dev);
__pci_remove_behind_bridge(dev);
@@ -144,7 +147,15 @@ static void pci_stop_bus_devices(struct pci_bus *bus)
{
struct list_head *l, *n;
- list_for_each_safe(l, n, &bus->devices) {
+ /*
+ * VFs could be removed by pci_stop_and_remove_bus_device() in the
+ * pci_stop_bus_devices() code path for PF.
+ * aka, bus->devices get updated in the process.
+ * but VFs are inserted after PFs when SRIOV is enabled for PF,
+ * We can iterate the list backwards to get prev valid PF instead
+ * of removed VF.
+ */
+ list_for_each_prev_safe(l, n, &bus->devices) {
struct pci_dev *dev = pci_dev_b(l);
pci_stop_bus_device(dev);
}
@@ -166,6 +177,6 @@ void pci_stop_bus_device(struct pci_dev *dev)
pci_stop_dev(dev);
}
-EXPORT_SYMBOL(pci_remove_bus_device);
-EXPORT_SYMBOL(pci_remove_behind_bridge);
+EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
+EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge);
EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 86b69f85f900..8fa2d4be88de 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -25,10 +25,13 @@
#include <linux/ioport.h>
#include <linux/cache.h>
#include <linux/slab.h>
+#include <asm-generic/pci-bridge.h>
#include "pci.h"
-struct resource_list_x {
- struct resource_list_x *next;
+unsigned int pci_flags;
+
+struct pci_dev_resource {
+ struct list_head list;
struct resource *res;
struct pci_dev *dev;
resource_size_t start;
@@ -38,21 +41,14 @@ struct resource_list_x {
unsigned long flags;
};
-#define free_list(type, head) do { \
- struct type *list, *tmp; \
- for (list = (head)->next; list;) { \
- tmp = list; \
- list = list->next; \
- kfree(tmp); \
- } \
- (head)->next = NULL; \
-} while (0)
-
-int pci_realloc_enable = 0;
-#define pci_realloc_enabled() pci_realloc_enable
-void pci_realloc(void)
+static void free_list(struct list_head *head)
{
- pci_realloc_enable = 1;
+ struct pci_dev_resource *dev_res, *tmp;
+
+ list_for_each_entry_safe(dev_res, tmp, head, list) {
+ list_del(&dev_res->list);
+ kfree(dev_res);
+ }
}
/**
@@ -64,21 +60,18 @@ void pci_realloc(void)
* @add_size: additional size to be optionally added
* to the resource
*/
-static void add_to_list(struct resource_list_x *head,
+static int add_to_list(struct list_head *head,
struct pci_dev *dev, struct resource *res,
resource_size_t add_size, resource_size_t min_align)
{
- struct resource_list_x *list = head;
- struct resource_list_x *ln = list->next;
- struct resource_list_x *tmp;
+ struct pci_dev_resource *tmp;
- tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
if (!tmp) {
pr_warning("add_to_list: kmalloc() failed!\n");
- return;
+ return -ENOMEM;
}
- tmp->next = ln;
tmp->res = res;
tmp->dev = dev;
tmp->start = res->start;
@@ -86,19 +79,100 @@ static void add_to_list(struct resource_list_x *head,
tmp->flags = res->flags;
tmp->add_size = add_size;
tmp->min_align = min_align;
- list->next = tmp;
+
+ list_add(&tmp->list, head);
+
+ return 0;
}
-static void add_to_failed_list(struct resource_list_x *head,
- struct pci_dev *dev, struct resource *res)
+static void remove_from_list(struct list_head *head,
+ struct resource *res)
{
- add_to_list(head, dev, res,
- 0 /* dont care */,
- 0 /* dont care */);
+ struct pci_dev_resource *dev_res, *tmp;
+
+ list_for_each_entry_safe(dev_res, tmp, head, list) {
+ if (dev_res->res == res) {
+ list_del(&dev_res->list);
+ kfree(dev_res);
+ break;
+ }
+ }
+}
+
+static resource_size_t get_res_add_size(struct list_head *head,
+ struct resource *res)
+{
+ struct pci_dev_resource *dev_res;
+
+ list_for_each_entry(dev_res, head, list) {
+ if (dev_res->res == res) {
+ int idx = res - &dev_res->dev->resource[0];
+
+ dev_printk(KERN_DEBUG, &dev_res->dev->dev,
+ "res[%d]=%pR get_res_add_size add_size %llx\n",
+ idx, dev_res->res,
+ (unsigned long long)dev_res->add_size);
+
+ return dev_res->add_size;
+ }
+ }
+
+ return 0;
+}
+
+/* Sort resources by alignment */
+static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head)
+{
+ int i;
+
+ for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+ struct resource *r;
+ struct pci_dev_resource *dev_res, *tmp;
+ resource_size_t r_align;
+ struct list_head *n;
+
+ r = &dev->resource[i];
+
+ if (r->flags & IORESOURCE_PCI_FIXED)
+ continue;
+
+ if (!(r->flags) || r->parent)
+ continue;
+
+ r_align = pci_resource_alignment(dev, r);
+ if (!r_align) {
+ dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n",
+ i, r);
+ continue;
+ }
+
+ tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
+ if (!tmp)
+ panic("pdev_sort_resources(): "
+ "kmalloc() failed!\n");
+ tmp->res = r;
+ tmp->dev = dev;
+
+ /* fallback is smallest one or list is empty*/
+ n = head;
+ list_for_each_entry(dev_res, head, list) {
+ resource_size_t align;
+
+ align = pci_resource_alignment(dev_res->dev,
+ dev_res->res);
+
+ if (r_align > align) {
+ n = &dev_res->list;
+ break;
+ }
+ }
+ /* Insert it just before n*/
+ list_add_tail(&tmp->list, n);
+ }
}
static void __dev_sort_resources(struct pci_dev *dev,
- struct resource_list *head)
+ struct list_head *head)
{
u16 class = dev->class >> 8;
@@ -136,49 +210,54 @@ static inline void reset_resource(struct resource *res)
* additional resources for the element, provided the element
* is in the head list.
*/
-static void reassign_resources_sorted(struct resource_list_x *realloc_head,
- struct resource_list *head)
+static void reassign_resources_sorted(struct list_head *realloc_head,
+ struct list_head *head)
{
struct resource *res;
- struct resource_list_x *list, *tmp, *prev;
- struct resource_list *hlist;
+ struct pci_dev_resource *add_res, *tmp;
+ struct pci_dev_resource *dev_res;
resource_size_t add_size;
int idx;
- prev = realloc_head;
- for (list = realloc_head->next; list;) {
- res = list->res;
+ list_for_each_entry_safe(add_res, tmp, realloc_head, list) {
+ bool found_match = false;
+
+ res = add_res->res;
/* skip resource that has been reset */
if (!res->flags)
goto out;
/* skip this resource if not found in head list */
- for (hlist = head->next; hlist && hlist->res != res;
- hlist = hlist->next);
- if (!hlist) { /* just skip */
- prev = list;
- list = list->next;
- continue;
+ list_for_each_entry(dev_res, head, list) {
+ if (dev_res->res == res) {
+ found_match = true;
+ break;
+ }
}
+ if (!found_match)/* just skip */
+ continue;
- idx = res - &list->dev->resource[0];
- add_size=list->add_size;
+ idx = res - &add_res->dev->resource[0];
+ add_size = add_res->add_size;
if (!resource_size(res)) {
- res->start = list->start;
+ res->start = add_res->start;
res->end = res->start + add_size - 1;
- if(pci_assign_resource(list->dev, idx))
+ if (pci_assign_resource(add_res->dev, idx))
reset_resource(res);
} else {
- resource_size_t align = list->min_align;
- res->flags |= list->flags & (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
- if (pci_reassign_resource(list->dev, idx, add_size, align))
- dev_printk(KERN_DEBUG, &list->dev->dev, "failed to add optional resources res=%pR\n",
- res);
+ resource_size_t align = add_res->min_align;
+ res->flags |= add_res->flags &
+ (IORESOURCE_STARTALIGN|IORESOURCE_SIZEALIGN);
+ if (pci_reassign_resource(add_res->dev, idx,
+ add_size, align))
+ dev_printk(KERN_DEBUG, &add_res->dev->dev,
+ "failed to add %llx res[%d]=%pR\n",
+ (unsigned long long)add_size,
+ idx, res);
}
out:
- tmp = list;
- prev->next = list = list->next;
- kfree(tmp);
+ list_del(&add_res->list);
+ kfree(add_res);
}
}
@@ -192,35 +271,99 @@ out:
* Satisfy resource requests of each element in the list. Add
* requests that could not satisfied to the failed_list.
*/
-static void assign_requested_resources_sorted(struct resource_list *head,
- struct resource_list_x *fail_head)
+static void assign_requested_resources_sorted(struct list_head *head,
+ struct list_head *fail_head)
{
struct resource *res;
- struct resource_list *list;
+ struct pci_dev_resource *dev_res;
int idx;
- for (list = head->next; list; list = list->next) {
- res = list->res;
- idx = res - &list->dev->resource[0];
- if (resource_size(res) && pci_assign_resource(list->dev, idx)) {
- if (fail_head && !pci_is_root_bus(list->dev->bus)) {
+ list_for_each_entry(dev_res, head, list) {
+ res = dev_res->res;
+ idx = res - &dev_res->dev->resource[0];
+ if (resource_size(res) &&
+ pci_assign_resource(dev_res->dev, idx)) {
+ if (fail_head && !pci_is_root_bus(dev_res->dev->bus)) {
/*
* if the failed res is for ROM BAR, and it will
* be enabled later, don't add it to the list
*/
if (!((idx == PCI_ROM_RESOURCE) &&
(!(res->flags & IORESOURCE_ROM_ENABLE))))
- add_to_failed_list(fail_head, list->dev, res);
+ add_to_list(fail_head,
+ dev_res->dev, res,
+ 0 /* dont care */,
+ 0 /* dont care */);
}
reset_resource(res);
}
}
}
-static void __assign_resources_sorted(struct resource_list *head,
- struct resource_list_x *realloc_head,
- struct resource_list_x *fail_head)
+static void __assign_resources_sorted(struct list_head *head,
+ struct list_head *realloc_head,
+ struct list_head *fail_head)
{
+ /*
+ * Should not assign requested resources at first.
+ * they could be adjacent, so later reassign can not reallocate
+ * them one by one in parent resource window.
+ * Try to assign requested + add_size at begining
+ * if could do that, could get out early.
+ * if could not do that, we still try to assign requested at first,
+ * then try to reassign add_size for some resources.
+ */
+ LIST_HEAD(save_head);
+ LIST_HEAD(local_fail_head);
+ struct pci_dev_resource *save_res;
+ struct pci_dev_resource *dev_res;
+
+ /* Check if optional add_size is there */
+ if (!realloc_head || list_empty(realloc_head))
+ goto requested_and_reassign;
+
+ /* Save original start, end, flags etc at first */
+ list_for_each_entry(dev_res, head, list) {
+ if (add_to_list(&save_head, dev_res->dev, dev_res->res, 0, 0)) {
+ free_list(&save_head);
+ goto requested_and_reassign;
+ }
+ }
+
+ /* Update res in head list with add_size in realloc_head list */
+ list_for_each_entry(dev_res, head, list)
+ dev_res->res->end += get_res_add_size(realloc_head,
+ dev_res->res);
+
+ /* Try updated head list with add_size added */
+ assign_requested_resources_sorted(head, &local_fail_head);
+
+ /* all assigned with add_size ? */
+ if (list_empty(&local_fail_head)) {
+ /* Remove head list from realloc_head list */
+ list_for_each_entry(dev_res, head, list)
+ remove_from_list(realloc_head, dev_res->res);
+ free_list(&save_head);
+ free_list(head);
+ return;
+ }
+
+ free_list(&local_fail_head);
+ /* Release assigned resource */
+ list_for_each_entry(dev_res, head, list)
+ if (dev_res->res->parent)
+ release_resource(dev_res->res);
+ /* Restore start/end/flags from saved list */
+ list_for_each_entry(save_res, &save_head, list) {
+ struct resource *res = save_res->res;
+
+ res->start = save_res->start;
+ res->end = save_res->end;
+ res->flags = save_res->flags;
+ }
+ free_list(&save_head);
+
+requested_and_reassign:
/* Satisfy the must-have resource requests */
assign_requested_resources_sorted(head, fail_head);
@@ -228,28 +371,27 @@ static void __assign_resources_sorted(struct resource_list *head,
requests */
if (realloc_head)
reassign_resources_sorted(realloc_head, head);
- free_list(resource_list, head);
+ free_list(head);
}
static void pdev_assign_resources_sorted(struct pci_dev *dev,
- struct resource_list_x *fail_head)
+ struct list_head *add_head,
+ struct list_head *fail_head)
{
- struct resource_list head;
+ LIST_HEAD(head);
- head.next = NULL;
__dev_sort_resources(dev, &head);
- __assign_resources_sorted(&head, NULL, fail_head);
+ __assign_resources_sorted(&head, add_head, fail_head);
}
static void pbus_assign_resources_sorted(const struct pci_bus *bus,
- struct resource_list_x *realloc_head,
- struct resource_list_x *fail_head)
+ struct list_head *realloc_head,
+ struct list_head *fail_head)
{
struct pci_dev *dev;
- struct resource_list head;
+ LIST_HEAD(head);
- head.next = NULL;
list_for_each_entry(dev, &bus->devices, bus_list)
__dev_sort_resources(dev, &head);
@@ -548,20 +690,6 @@ static resource_size_t calculate_memsize(resource_size_t size,
return size;
}
-static resource_size_t get_res_add_size(struct resource_list_x *realloc_head,
- struct resource *res)
-{
- struct resource_list_x *list;
-
- /* check if it is in realloc_head list */
- for (list = realloc_head->next; list && list->res != res;
- list = list->next);
- if (list)
- return list->add_size;
-
- return 0;
-}
-
/**
* pbus_size_io() - size the io window of a given bus
*
@@ -576,7 +704,7 @@ static resource_size_t get_res_add_size(struct resource_list_x *realloc_head,
* We must be careful with the ISA aliasing though.
*/
static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
- resource_size_t add_size, struct resource_list_x *realloc_head)
+ resource_size_t add_size, struct list_head *realloc_head)
{
struct pci_dev *dev;
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
@@ -612,7 +740,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
if (children_add_size > add_size)
add_size = children_add_size;
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
- calculate_iosize(size, min_size+add_size, size1,
+ calculate_iosize(size, min_size, add_size + size1,
resource_size(b_res), 4096);
if (!size0 && !size1) {
if (b_res->start || b_res->end)
@@ -626,8 +754,12 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
b_res->start = 4096;
b_res->end = b_res->start + size0 - 1;
b_res->flags |= IORESOURCE_STARTALIGN;
- if (size1 > size0 && realloc_head)
+ if (size1 > size0 && realloc_head) {
add_to_list(realloc_head, bus->self, b_res, size1-size0, 4096);
+ dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "
+ "%pR to [bus %02x-%02x] add_size %lx\n", b_res,
+ bus->secondary, bus->subordinate, size1-size0);
+ }
}
/**
@@ -644,7 +776,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
unsigned long type, resource_size_t min_size,
resource_size_t add_size,
- struct resource_list_x *realloc_head)
+ struct list_head *realloc_head)
{
struct pci_dev *dev;
resource_size_t min_align, align, size, size0, size1;
@@ -726,7 +858,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
if (children_add_size > add_size)
add_size = children_add_size;
size1 = (!realloc_head || (realloc_head && !add_size)) ? size0 :
- calculate_memsize(size, min_size+add_size, 0,
+ calculate_memsize(size, min_size, add_size,
resource_size(b_res), min_align);
if (!size0 && !size1) {
if (b_res->start || b_res->end)
@@ -739,8 +871,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
b_res->start = min_align;
b_res->end = size0 + min_align - 1;
b_res->flags |= IORESOURCE_STARTALIGN | mem64_mask;
- if (size1 > size0 && realloc_head)
+ if (size1 > size0 && realloc_head) {
add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align);
+ dev_printk(KERN_DEBUG, &bus->self->dev, "bridge window "
+ "%pR to [bus %02x-%02x] add_size %llx\n", b_res,
+ bus->secondary, bus->subordinate, (unsigned long long)size1-size0);
+ }
return 1;
}
@@ -754,25 +890,48 @@ unsigned long pci_cardbus_resource_alignment(struct resource *res)
}
static void pci_bus_size_cardbus(struct pci_bus *bus,
- struct resource_list_x *realloc_head)
+ struct list_head *realloc_head)
{
struct pci_dev *bridge = bus->self;
struct resource *b_res = &bridge->resource[PCI_BRIDGE_RESOURCES];
+ resource_size_t b_res_3_size = pci_cardbus_mem_size * 2;
u16 ctrl;
+ if (b_res[0].parent)
+ goto handle_b_res_1;
/*
* Reserve some resources for CardBus. We reserve
* a fixed amount of bus space for CardBus bridges.
*/
- b_res[0].start = 0;
- b_res[0].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
- if (realloc_head)
- add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size, 0 /* dont care */);
+ b_res[0].start = pci_cardbus_io_size;
+ b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1;
+ b_res[0].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[0].end -= pci_cardbus_io_size;
+ add_to_list(realloc_head, bridge, b_res, pci_cardbus_io_size,
+ pci_cardbus_io_size);
+ }
- b_res[1].start = 0;
- b_res[1].flags |= IORESOURCE_IO | IORESOURCE_SIZEALIGN;
- if (realloc_head)
- add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size, 0 /* dont care */);
+handle_b_res_1:
+ if (b_res[1].parent)
+ goto handle_b_res_2;
+ b_res[1].start = pci_cardbus_io_size;
+ b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1;
+ b_res[1].flags |= IORESOURCE_IO | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[1].end -= pci_cardbus_io_size;
+ add_to_list(realloc_head, bridge, b_res+1, pci_cardbus_io_size,
+ pci_cardbus_io_size);
+ }
+
+handle_b_res_2:
+ /* MEM1 must not be pref mmio */
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM1) {
+ ctrl &= ~PCI_CB_BRIDGE_CTL_PREFETCH_MEM1;
+ pci_write_config_word(bridge, PCI_CB_BRIDGE_CONTROL, ctrl);
+ pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
+ }
/*
* Check whether prefetchable memory is supported
@@ -785,38 +944,46 @@ static void pci_bus_size_cardbus(struct pci_bus *bus,
pci_read_config_word(bridge, PCI_CB_BRIDGE_CONTROL, &ctrl);
}
+ if (b_res[2].parent)
+ goto handle_b_res_3;
/*
* If we have prefetchable memory support, allocate
* two regions. Otherwise, allocate one region of
* twice the size.
*/
if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) {
- b_res[2].start = 0;
- b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH | IORESOURCE_SIZEALIGN;
- if (realloc_head)
- add_to_list(realloc_head, bridge, b_res+2, pci_cardbus_mem_size, 0 /* dont care */);
-
- b_res[3].start = 0;
- b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
- if (realloc_head)
- add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size, 0 /* dont care */);
- } else {
- b_res[3].start = 0;
- b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_SIZEALIGN;
- if (realloc_head)
- add_to_list(realloc_head, bridge, b_res+3, pci_cardbus_mem_size * 2, 0 /* dont care */);
+ b_res[2].start = pci_cardbus_mem_size;
+ b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1;
+ b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH |
+ IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[2].end -= pci_cardbus_mem_size;
+ add_to_list(realloc_head, bridge, b_res+2,
+ pci_cardbus_mem_size, pci_cardbus_mem_size);
+ }
+
+ /* reduce that to half */
+ b_res_3_size = pci_cardbus_mem_size;
+ }
+
+handle_b_res_3:
+ if (b_res[3].parent)
+ goto handle_done;
+ b_res[3].start = pci_cardbus_mem_size;
+ b_res[3].end = b_res[3].start + b_res_3_size - 1;
+ b_res[3].flags |= IORESOURCE_MEM | IORESOURCE_STARTALIGN;
+ if (realloc_head) {
+ b_res[3].end -= b_res_3_size;
+ add_to_list(realloc_head, bridge, b_res+3, b_res_3_size,
+ pci_cardbus_mem_size);
}
- /* set the size of the resource to zero, so that the resource does not
- * get assigned during required-resource allocation cycle but gets assigned
- * during the optional-resource allocation cycle.
- */
- b_res[0].start = b_res[1].start = b_res[2].start = b_res[3].start = 1;
- b_res[0].end = b_res[1].end = b_res[2].end = b_res[3].end = 0;
+handle_done:
+ ;
}
void __ref __pci_bus_size_bridges(struct pci_bus *bus,
- struct resource_list_x *realloc_head)
+ struct list_head *realloc_head)
{
struct pci_dev *dev;
unsigned long mask, prefmask;
@@ -858,7 +1025,8 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
* Follow thru
*/
default:
- pbus_size_io(bus, 0, additional_io_size, realloc_head);
+ pbus_size_io(bus, realloc_head ? 0 : additional_io_size,
+ additional_io_size, realloc_head);
/* If the bridge supports prefetchable range, size it
separately. If it doesn't, or its prefetchable window
has already been allocated by arch code, try
@@ -866,11 +1034,15 @@ void __ref __pci_bus_size_bridges(struct pci_bus *bus,
resources. */
mask = IORESOURCE_MEM;
prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH;
- if (pbus_size_mem(bus, prefmask, prefmask, 0, additional_mem_size, realloc_head))
+ if (pbus_size_mem(bus, prefmask, prefmask,
+ realloc_head ? 0 : additional_mem_size,
+ additional_mem_size, realloc_head))
mask = prefmask; /* Success, size non-prefetch only. */
else
additional_mem_size += additional_mem_size;
- pbus_size_mem(bus, mask, IORESOURCE_MEM, 0, additional_mem_size, realloc_head);
+ pbus_size_mem(bus, mask, IORESOURCE_MEM,
+ realloc_head ? 0 : additional_mem_size,
+ additional_mem_size, realloc_head);
break;
}
}
@@ -882,8 +1054,8 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus)
EXPORT_SYMBOL(pci_bus_size_bridges);
static void __ref __pci_bus_assign_resources(const struct pci_bus *bus,
- struct resource_list_x *realloc_head,
- struct resource_list_x *fail_head)
+ struct list_head *realloc_head,
+ struct list_head *fail_head)
{
struct pci_bus *b;
struct pci_dev *dev;
@@ -922,17 +1094,19 @@ void __ref pci_bus_assign_resources(const struct pci_bus *bus)
EXPORT_SYMBOL(pci_bus_assign_resources);
static void __ref __pci_bridge_assign_resources(const struct pci_dev *bridge,
- struct resource_list_x *fail_head)
+ struct list_head *add_head,
+ struct list_head *fail_head)
{
struct pci_bus *b;
- pdev_assign_resources_sorted((struct pci_dev *)bridge, fail_head);
+ pdev_assign_resources_sorted((struct pci_dev *)bridge,
+ add_head, fail_head);
b = bridge->subordinate;
if (!b)
return;
- __pci_bus_assign_resources(b, NULL, fail_head);
+ __pci_bus_assign_resources(b, add_head, fail_head);
switch (bridge->class >> 8) {
case PCI_CLASS_BRIDGE_PCI:
@@ -1095,6 +1269,58 @@ static int __init pci_get_max_depth(void)
return depth;
}
+/*
+ * -1: undefined, will auto detect later
+ * 0: disabled by user
+ * 1: disabled by auto detect
+ * 2: enabled by user
+ * 3: enabled by auto detect
+ */
+enum enable_type {
+ undefined = -1,
+ user_disabled,
+ auto_disabled,
+ user_enabled,
+ auto_enabled,
+};
+
+static enum enable_type pci_realloc_enable __initdata = undefined;
+void __init pci_realloc_get_opt(char *str)
+{
+ if (!strncmp(str, "off", 3))
+ pci_realloc_enable = user_disabled;
+ else if (!strncmp(str, "on", 2))
+ pci_realloc_enable = user_enabled;
+}
+static bool __init pci_realloc_enabled(void)
+{
+ return pci_realloc_enable >= user_enabled;
+}
+
+static void __init pci_realloc_detect(void)
+{
+#if defined(CONFIG_PCI_IOV) && defined(CONFIG_PCI_REALLOC_ENABLE_AUTO)
+ struct pci_dev *dev = NULL;
+
+ if (pci_realloc_enable != undefined)
+ return;
+
+ for_each_pci_dev(dev) {
+ int i;
+
+ for (i = PCI_IOV_RESOURCES; i <= PCI_IOV_RESOURCE_END; i++) {
+ struct resource *r = &dev->resource[i];
+
+ /* Not assigned, or rejected by kernel ? */
+ if (r->flags && !r->start) {
+ pci_realloc_enable = auto_enabled;
+
+ return;
+ }
+ }
+ }
+#endif
+}
/*
* first try will not touch pci bridge res
@@ -1105,59 +1331,57 @@ void __init
pci_assign_unassigned_resources(void)
{
struct pci_bus *bus;
- struct resource_list_x realloc_list; /* list of resources that
+ LIST_HEAD(realloc_head); /* list of resources that
want additional resources */
+ struct list_head *add_list = NULL;
int tried_times = 0;
enum release_type rel_type = leaf_only;
- struct resource_list_x head, *list;
+ LIST_HEAD(fail_head);
+ struct pci_dev_resource *fail_res;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
- unsigned long failed_type;
- int max_depth = pci_get_max_depth();
- int pci_try_num;
-
+ int pci_try_num = 1;
- head.next = NULL;
- realloc_list.next = NULL;
+ /* don't realloc if asked to do so */
+ pci_realloc_detect();
+ if (pci_realloc_enabled()) {
+ int max_depth = pci_get_max_depth();
- pci_try_num = max_depth + 1;
- printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
- max_depth, pci_try_num);
+ pci_try_num = max_depth + 1;
+ printk(KERN_DEBUG "PCI: max bus depth: %d pci_try_num: %d\n",
+ max_depth, pci_try_num);
+ }
again:
+ /*
+ * last try will use add_list, otherwise will try good to have as
+ * must have, so can realloc parent bridge resource
+ */
+ if (tried_times + 1 == pci_try_num)
+ add_list = &realloc_head;
/* Depth first, calculate sizes and alignments of all
subordinate buses. */
list_for_each_entry(bus, &pci_root_buses, node)
- __pci_bus_size_bridges(bus, &realloc_list);
+ __pci_bus_size_bridges(bus, add_list);
/* Depth last, allocate resources and update the hardware. */
list_for_each_entry(bus, &pci_root_buses, node)
- __pci_bus_assign_resources(bus, &realloc_list, &head);
- BUG_ON(realloc_list.next);
+ __pci_bus_assign_resources(bus, add_list, &fail_head);
+ if (add_list)
+ BUG_ON(!list_empty(add_list));
tried_times++;
/* any device complain? */
- if (!head.next)
+ if (list_empty(&fail_head))
goto enable_and_dump;
- /* don't realloc if asked to do so */
- if (!pci_realloc_enabled()) {
- free_list(resource_list_x, &head);
- goto enable_and_dump;
- }
+ if (tried_times >= pci_try_num) {
+ if (pci_realloc_enable == undefined)
+ printk(KERN_INFO "Some PCI device resources are unassigned, try booting with pci=realloc\n");
+ else if (pci_realloc_enable == auto_enabled)
+ printk(KERN_INFO "Automatically enabled pci realloc, if you have problem, try booting with pci=realloc=off\n");
- failed_type = 0;
- for (list = head.next; list;) {
- failed_type |= list->flags;
- list = list->next;
- }
- /*
- * io port are tight, don't try extra
- * or if reach the limit, don't want to try more
- */
- failed_type &= type_mask;
- if ((failed_type == IORESOURCE_IO) || (tried_times >= pci_try_num)) {
- free_list(resource_list_x, &head);
+ free_list(&fail_head);
goto enable_and_dump;
}
@@ -1172,25 +1396,23 @@ again:
* Try to release leaf bridge's resources that doesn't fit resource of
* child device under that bridge
*/
- for (list = head.next; list;) {
- bus = list->dev->bus;
- pci_bus_release_bridge_resources(bus, list->flags & type_mask,
- rel_type);
- list = list->next;
+ list_for_each_entry(fail_res, &fail_head, list) {
+ bus = fail_res->dev->bus;
+ pci_bus_release_bridge_resources(bus,
+ fail_res->flags & type_mask,
+ rel_type);
}
/* restore size and flags */
- for (list = head.next; list;) {
- struct resource *res = list->res;
+ list_for_each_entry(fail_res, &fail_head, list) {
+ struct resource *res = fail_res->res;
- res->start = list->start;
- res->end = list->end;
- res->flags = list->flags;
- if (list->dev->subordinate)
+ res->start = fail_res->start;
+ res->end = fail_res->end;
+ res->flags = fail_res->flags;
+ if (fail_res->dev->subordinate)
res->flags = 0;
-
- list = list->next;
}
- free_list(resource_list_x, &head);
+ free_list(&fail_head);
goto again;
@@ -1207,26 +1429,27 @@ enable_and_dump:
void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge)
{
struct pci_bus *parent = bridge->subordinate;
+ LIST_HEAD(add_list); /* list of resources that
+ want additional resources */
int tried_times = 0;
- struct resource_list_x head, *list;
+ LIST_HEAD(fail_head);
+ struct pci_dev_resource *fail_res;
int retval;
unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM |
IORESOURCE_PREFETCH;
- head.next = NULL;
-
again:
- pci_bus_size_bridges(parent);
- __pci_bridge_assign_resources(bridge, &head);
-
+ __pci_bus_size_bridges(parent, &add_list);
+ __pci_bridge_assign_resources(bridge, &add_list, &fail_head);
+ BUG_ON(!list_empty(&add_list));
tried_times++;
- if (!head.next)
+ if (list_empty(&fail_head))
goto enable_all;
if (tried_times >= 2) {
/* still fail, don't need to try more */
- free_list(resource_list_x, &head);
+ free_list(&fail_head);
goto enable_all;
}
@@ -1237,27 +1460,24 @@ again:
* Try to release leaf bridge's resources that doesn't fit resource of
* child device under that bridge
*/
- for (list = head.next; list;) {
- struct pci_bus *bus = list->dev->bus;
- unsigned long flags = list->flags;
+ list_for_each_entry(fail_res, &fail_head, list) {
+ struct pci_bus *bus = fail_res->dev->bus;
+ unsigned long flags = fail_res->flags;
pci_bus_release_bridge_resources(bus, flags & type_mask,
whole_subtree);
- list = list->next;
}
/* restore size and flags */
- for (list = head.next; list;) {
- struct resource *res = list->res;
+ list_for_each_entry(fail_res, &fail_head, list) {
+ struct resource *res = fail_res->res;
- res->start = list->start;
- res->end = list->end;
- res->flags = list->flags;
- if (list->dev->subordinate)
+ res->start = fail_res->start;
+ res->end = fail_res->end;
+ res->flags = fail_res->flags;
+ if (fail_res->dev->subordinate)
res->flags = 0;
-
- list = list->next;
}
- free_list(resource_list_x, &head);
+ free_list(&fail_head);
goto again;
@@ -1267,3 +1487,41 @@ enable_all:
pci_enable_bridges(parent);
}
EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources);
+
+#ifdef CONFIG_HOTPLUG
+/**
+ * pci_rescan_bus - scan a PCI bus for devices.
+ * @bus: PCI bus to scan
+ *
+ * Scan a PCI bus and child buses for new devices, adds them,
+ * and enables them.
+ *
+ * Returns the max number of subordinate bus discovered.
+ */
+unsigned int __ref pci_rescan_bus(struct pci_bus *bus)
+{
+ unsigned int max;
+ struct pci_dev *dev;
+ LIST_HEAD(add_list); /* list of resources that
+ want additional resources */
+
+ max = pci_scan_child_bus(bus);
+
+ down_read(&pci_bus_sem);
+ list_for_each_entry(dev, &bus->devices, bus_list)
+ if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
+ dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
+ if (dev->subordinate)
+ __pci_bus_size_bridges(dev->subordinate,
+ &add_list);
+ up_read(&pci_bus_sem);
+ __pci_bus_assign_resources(bus, &add_list, NULL);
+ BUG_ON(!list_empty(&add_list));
+
+ pci_enable_bridges(bus);
+ pci_bus_add_devices(bus);
+
+ return max;
+}
+EXPORT_SYMBOL_GPL(pci_rescan_bus);
+#endif
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index b66bfdbd21f7..eea85dafc763 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -114,7 +114,6 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
}
EXPORT_SYMBOL(pci_claim_resource);
-#ifdef CONFIG_PCI_QUIRKS
void pci_disable_bridge_window(struct pci_dev *dev)
{
dev_info(&dev->dev, "disabling bridge mem windows\n");
@@ -127,9 +126,6 @@ void pci_disable_bridge_window(struct pci_dev *dev)
pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0);
pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff);
}
-#endif /* CONFIG_PCI_QUIRKS */
-
-
static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
int resno, resource_size_t size, resource_size_t align)
@@ -158,22 +154,44 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev,
return ret;
}
+/*
+ * Generic function that returns a value indicating that the device's
+ * original BIOS BAR address was not saved and so is not available for
+ * reinstatement.
+ *
+ * Can be over-ridden by architecture specific code that implements
+ * reinstatement functionality rather than leaving it disabled when
+ * normal allocation attempts fail.
+ */
+resource_size_t __weak pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx)
+{
+ return 0;
+}
+
static int pci_revert_fw_address(struct resource *res, struct pci_dev *dev,
int resno, resource_size_t size)
{
struct resource *root, *conflict;
- resource_size_t start, end;
+ resource_size_t fw_addr, start, end;
int ret = 0;
- if (res->flags & IORESOURCE_IO)
- root = &ioport_resource;
- else
- root = &iomem_resource;
+ fw_addr = pcibios_retrieve_fw_addr(dev, resno);
+ if (!fw_addr)
+ return 1;
start = res->start;
end = res->end;
- res->start = dev->fw_addr[resno];
+ res->start = fw_addr;
res->end = res->start + size - 1;
+
+ root = pci_find_parent_resource(dev, res);
+ if (!root) {
+ if (res->flags & IORESOURCE_IO)
+ root = &ioport_resource;
+ else
+ root = &iomem_resource;
+ }
+
dev_info(&dev->dev, "BAR %d: trying firmware assignment %pR\n",
resno, res);
conflict = request_resource_conflict(root, res);
@@ -228,16 +246,17 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz
int ret;
if (!res->parent) {
- dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resouce %pR "
+ dev_info(&dev->dev, "BAR %d: can't reassign an unassigned resource %pR "
"\n", resno, res);
return -EINVAL;
}
- new_size = resource_size(res) + addsize + min_align;
+ /* already aligned with min_align */
+ new_size = resource_size(res) + addsize;
ret = _pci_assign_resource(dev, resno, new_size, min_align);
if (!ret) {
res->flags &= ~IORESOURCE_STARTALIGN;
- dev_info(&dev->dev, "BAR %d: assigned %pR\n", resno, res);
+ dev_info(&dev->dev, "BAR %d: reassigned %pR\n", resno, res);
if (resno < PCI_BRIDGE_RESOURCES)
pci_update_resource(dev, resno);
}
@@ -267,7 +286,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
* where firmware left it. That at least has a chance of
* working, which is better than just leaving it disabled.
*/
- if (ret < 0 && dev->fw_addr[resno])
+ if (ret < 0)
ret = pci_revert_fw_address(res, dev, resno, size);
if (!ret) {
@@ -279,53 +298,6 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
return ret;
}
-
-/* Sort resources by alignment */
-void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
-{
- int i;
-
- for (i = 0; i < PCI_NUM_RESOURCES; i++) {
- struct resource *r;
- struct resource_list *list, *tmp;
- resource_size_t r_align;
-
- r = &dev->resource[i];
-
- if (r->flags & IORESOURCE_PCI_FIXED)
- continue;
-
- if (!(r->flags) || r->parent)
- continue;
-
- r_align = pci_resource_alignment(dev, r);
- if (!r_align) {
- dev_warn(&dev->dev, "BAR %d: %pR has bogus alignment\n",
- i, r);
- continue;
- }
- for (list = head; ; list = list->next) {
- resource_size_t align = 0;
- struct resource_list *ln = list->next;
-
- if (ln)
- align = pci_resource_alignment(ln->dev, ln->res);
-
- if (r_align > align) {
- tmp = kmalloc(sizeof(*tmp), GFP_KERNEL);
- if (!tmp)
- panic("pdev_sort_resources(): "
- "kmalloc() failed!\n");
- tmp->next = ln;
- tmp->res = r;
- tmp->dev = dev;
- list->next = tmp;
- break;
- }
- }
- }
-}
-
int pci_enable_resources(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index 401090110922..fd00ff02ab4d 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -544,7 +544,7 @@ static void free_root_bus_devs(struct pci_bus *bus)
dev = container_of(bus->devices.next, struct pci_dev,
bus_list);
dev_dbg(&dev->dev, "removing device\n");
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
}
}
@@ -1044,7 +1044,7 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)
domain, bus, slot, func);
continue;
}
- pci_remove_bus_device(pci_dev);
+ pci_stop_and_remove_bus_device(pci_dev);
pci_dev_put(pci_dev);
dev_dbg(&pdev->xdev->dev,
diff --git a/drivers/pcmcia/Kconfig b/drivers/pcmcia/Kconfig
index f9e3fb3a285b..bba3ab2066ee 100644
--- a/drivers/pcmcia/Kconfig
+++ b/drivers/pcmcia/Kconfig
@@ -183,10 +183,14 @@ config PCMCIA_BCM63XX
config PCMCIA_SOC_COMMON
tristate
+config PCMCIA_SA11XX_BASE
+ tristate
+
config PCMCIA_SA1100
tristate "SA1100 support"
depends on ARM && ARCH_SA1100 && PCMCIA
select PCMCIA_SOC_COMMON
+ select PCMCIA_SA11XX_BASE
help
Say Y here to include support for SA11x0-based PCMCIA or CF
sockets, found on HP iPAQs, Yopy, and other StrongARM(R)/
@@ -196,8 +200,9 @@ config PCMCIA_SA1100
config PCMCIA_SA1111
tristate "SA1111 support"
- depends on ARM && ARCH_SA1100 && SA1111 && PCMCIA
+ depends on ARM && SA1111 && PCMCIA
select PCMCIA_SOC_COMMON
+ select PCMCIA_SA11XX_BASE if ARCH_SA1100
help
Say Y here to include support for SA1111-based PCMCIA or CF
sockets, found on the Jornada 720, Graphicsmaster and other
@@ -213,6 +218,7 @@ config PCMCIA_PXA2XX
|| ARCOM_PCMCIA || ARCH_PXA_ESERIES || MACH_STARGATE2 \
|| MACH_VPAC270 || MACH_BALLOON3 || MACH_COLIBRI \
|| MACH_COLIBRI320)
+ select PCMCIA_SA1111 if ARCH_LUBBOCK && SA1111
select PCMCIA_SOC_COMMON
help
Say Y here to include support for the PXA2xx PCMCIA controller
diff --git a/drivers/pcmcia/Makefile b/drivers/pcmcia/Makefile
index ec543a4ff2e4..47525de6a631 100644
--- a/drivers/pcmcia/Makefile
+++ b/drivers/pcmcia/Makefile
@@ -25,8 +25,9 @@ obj-$(CONFIG_I82092) += i82092.o
obj-$(CONFIG_TCIC) += tcic.o
obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o
obj-$(CONFIG_PCMCIA_SOC_COMMON) += soc_common.o
-obj-$(CONFIG_PCMCIA_SA1100) += sa11xx_base.o sa1100_cs.o
-obj-$(CONFIG_PCMCIA_SA1111) += sa11xx_base.o sa1111_cs.o
+obj-$(CONFIG_PCMCIA_SA11XX_BASE) += sa11xx_base.o
+obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o
+obj-$(CONFIG_PCMCIA_SA1111) += sa1111_cs.o
obj-$(CONFIG_M32R_PCC) += m32r_pcc.o
obj-$(CONFIG_M32R_CFC) += m32r_cfc.o
obj-$(CONFIG_PCMCIA_BCM63XX) += bcm63xx_pcmcia.o
@@ -39,9 +40,10 @@ obj-$(CONFIG_ELECTRA_CF) += electra_cf.o
obj-$(CONFIG_PCMCIA_ALCHEMY_DEVBOARD) += db1xxx_ss.o
sa1111_cs-y += sa1111_generic.o
-sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1100_neponset.o
-sa1111_cs-$(CONFIG_SA1100_BADGE4) += sa1100_badge4.o
-sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1100_jornada720.o
+sa1111_cs-$(CONFIG_ASSABET_NEPONSET) += sa1111_neponset.o
+sa1111_cs-$(CONFIG_SA1100_BADGE4) += sa1111_badge4.o
+sa1111_cs-$(CONFIG_SA1100_JORNADA720) += sa1111_jornada720.o
+sa1111_cs-$(CONFIG_ARCH_LUBBOCK) += sa1111_lubbock.o
sa1100_cs-y += sa1100_generic.o
sa1100_cs-$(CONFIG_SA1100_ASSABET) += sa1100_assabet.o
@@ -52,9 +54,7 @@ sa1100_cs-$(CONFIG_SA1100_NANOENGINE) += sa1100_nanoengine.o
sa1100_cs-$(CONFIG_SA1100_SHANNON) += sa1100_shannon.o
sa1100_cs-$(CONFIG_SA1100_SIMPAD) += sa1100_simpad.o
-pxa2xx_lubbock_cs-y += pxa2xx_lubbock.o sa1111_generic.o
pxa2xx_cm_x2xx_cs-y += pxa2xx_cm_x2xx.o pxa2xx_cm_x255.o pxa2xx_cm_x270.o
-pxa2xx-obj-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock_cs.o
pxa2xx-obj-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o
pxa2xx-obj-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o
pxa2xx-obj-$(CONFIG_MACH_ARMCORE) += pxa2xx_cm_x2xx_cs.o
diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c
index 4902206f53d9..1dd68f502634 100644
--- a/drivers/pcmcia/at91_cf.c
+++ b/drivers/pcmcia/at91_cf.c
@@ -26,6 +26,7 @@
#include <mach/board.h>
#include <mach/at91rm9200_mc.h>
+#include <mach/at91_ramc.h>
/*
@@ -156,7 +157,7 @@ static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
/*
* Use 16 bit accesses unless/until we need 8-bit i/o space.
*/
- csr = at91_sys_read(AT91_SMC_CSR(cf->board->chipselect)) & ~AT91_SMC_DBW;
+ csr = at91_ramc_read(0, AT91_SMC_CSR(cf->board->chipselect)) & ~AT91_SMC_DBW;
/*
* NOTE: this CF controller ignores IOIS16, so we can't really do
@@ -175,7 +176,7 @@ static int at91_cf_set_io_map(struct pcmcia_socket *s, struct pccard_io_map *io)
csr |= AT91_SMC_DBW_16;
pr_debug("%s: 16bit i/o bus\n", driver_name);
}
- at91_sys_write(AT91_SMC_CSR(cf->board->chipselect), csr);
+ at91_ramc_write(0, AT91_SMC_CSR(cf->board->chipselect), csr);
io->start = cf->socket.io_offset;
io->stop = io->start + SZ_2K - 1;
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index 9a58862f1401..6e75153c5b4f 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -108,5 +108,5 @@ void cb_free(struct pcmcia_socket *s)
struct pci_dev *bridge = s->cb_dev;
if (bridge)
- pci_remove_behind_bridge(bridge);
+ pci_stop_and_remove_behind_bridge(bridge);
}
diff --git a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
index d9ea192c4001..673c14ea11e3 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/i82092.c b/drivers/pcmcia/i82092.c
index 3e447d0387b7..0b66bfc0e148 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"
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index 72a033a2acdb..e6f3d17dd2b4 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 2adb0106a039..a26f38c6402a 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 1511ff71c87b..296514155cd5 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 271a590a5f3c..a317defd616d 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>
diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c
index 96c72e90b79c..0f8b70b27762 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"
diff --git a/drivers/pcmcia/pxa2xx_balloon3.c b/drivers/pcmcia/pxa2xx_balloon3.c
index 22a75e610f12..2ef576c5b69d 100644
--- a/drivers/pcmcia/pxa2xx_balloon3.c
+++ b/drivers/pcmcia/pxa2xx_balloon3.c
@@ -29,15 +29,6 @@
#include "soc_common.h"
-/*
- * These are a list of interrupt sources that provokes a polled
- * check of status
- */
-static struct pcmcia_irqs irqs[] = {
- { 0, BALLOON3_S0_CD_IRQ, "PCMCIA0 CD" },
- { 0, BALLOON3_BP_NSTSCHG_IRQ, "PCMCIA0 STSCHG" },
-};
-
static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
uint16_t ver;
@@ -49,12 +40,12 @@ static int balloon3_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
ver);
skt->socket.pci_irq = BALLOON3_BP_CF_NRDY_IRQ;
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
+ skt->stat[SOC_STAT_CD].gpio = BALLOON3_GPIO_S0_CD;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
+ skt->stat[SOC_STAT_BVD1].irq = BALLOON3_BP_NSTSCHG_IRQ;
+ skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG";
-static void balloon3_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ return 0;
}
static unsigned long balloon3_pcmcia_status[2] = {
@@ -85,13 +76,11 @@ static void balloon3_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
disable_irq(BALLOON3_BP_NSTSCHG_IRQ);
}
- state->detect = !gpio_get_value(BALLOON3_GPIO_S0_CD);
state->ready = !!(status & BALLOON3_CF_nIRQ);
state->bvd1 = !!(status & BALLOON3_CF_nSTSCHG_BVD1);
state->bvd2 = 0; /* not available */
state->vs_3v = 1; /* Always true its a CF card */
state->vs_Xv = 0; /* not available */
- state->wrprot = 0; /* not available */
}
static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
@@ -106,7 +95,6 @@ static int balloon3_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
static struct pcmcia_low_level balloon3_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = balloon3_pcmcia_hw_init,
- .hw_shutdown = balloon3_pcmcia_hw_shutdown,
.socket_state = balloon3_pcmcia_socket_state,
.configure_socket = balloon3_pcmcia_configure_socket,
.first = 0,
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 64d433ec4fc6..490bb82b5bdb 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>
@@ -318,10 +317,7 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
skt->nr = ops->first + i;
skt->clk = clk;
- skt->ops = ops;
- skt->socket.owner = ops->owner;
- skt->socket.dev.parent = &dev->dev;
- skt->socket.pci_irq = NO_IRQ;
+ soc_pcmcia_init_one(skt, ops, &dev->dev);
ret = pxa2xx_drv_pcmcia_add_one(skt);
if (ret)
diff --git a/drivers/pcmcia/pxa2xx_cm_x255.c b/drivers/pcmcia/pxa2xx_cm_x255.c
index 31ab6ddf52c9..da40908b29dd 100644
--- a/drivers/pcmcia/pxa2xx_cm_x255.c
+++ b/drivers/pcmcia/pxa2xx_cm_x255.c
@@ -25,17 +25,6 @@
#define GPIO_PCMCIA_S1_RDYINT (8)
#define GPIO_PCMCIA_RESET (9)
-#define PCMCIA_S0_CD_VALID gpio_to_irq(GPIO_PCMCIA_S0_CD_VALID)
-#define PCMCIA_S1_CD_VALID gpio_to_irq(GPIO_PCMCIA_S1_CD_VALID)
-#define PCMCIA_S0_RDYINT gpio_to_irq(GPIO_PCMCIA_S0_RDYINT)
-#define PCMCIA_S1_RDYINT gpio_to_irq(GPIO_PCMCIA_S1_RDYINT)
-
-
-static struct pcmcia_irqs irqs[] = {
- { .sock = 0, .str = "PCMCIA0 CD" },
- { .sock = 1, .str = "PCMCIA1 CD" },
-};
-
static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset");
@@ -43,19 +32,23 @@ static int cmx255_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return ret;
gpio_direction_output(GPIO_PCMCIA_RESET, 0);
- skt->socket.pci_irq = skt->nr == 0 ? PCMCIA_S0_RDYINT : PCMCIA_S1_RDYINT;
- irqs[0].irq = PCMCIA_S0_CD_VALID;
- irqs[1].irq = PCMCIA_S1_CD_VALID;
- ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
- if (!ret)
- gpio_free(GPIO_PCMCIA_RESET);
+ if (skt->nr == 0) {
+ skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S0_CD_VALID;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S0_RDYINT;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY";
+ } else {
+ skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S1_CD_VALID;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S1_RDYINT;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA1 RDY";
+ }
- return ret;
+ return 0;
}
static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
gpio_free(GPIO_PCMCIA_RESET);
}
@@ -63,16 +56,8 @@ static void cmx255_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
static void cmx255_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- int cd = skt->nr ? GPIO_PCMCIA_S1_CD_VALID : GPIO_PCMCIA_S0_CD_VALID;
- int rdy = skt->nr ? GPIO_PCMCIA_S1_RDYINT : GPIO_PCMCIA_S0_RDYINT;
-
- state->detect = !gpio_get_value(cd);
- state->ready = !!gpio_get_value(rdy);
- state->bvd1 = 1;
- state->bvd2 = 1;
state->vs_3v = 0;
state->vs_Xv = 0;
- state->wrprot = 0; /* not available */
}
diff --git a/drivers/pcmcia/pxa2xx_cm_x270.c b/drivers/pcmcia/pxa2xx_cm_x270.c
index 3dc7621a0767..f59223f2307d 100644
--- a/drivers/pcmcia/pxa2xx_cm_x270.c
+++ b/drivers/pcmcia/pxa2xx_cm_x270.c
@@ -22,14 +22,6 @@
#define GPIO_PCMCIA_S0_RDYINT (82)
#define GPIO_PCMCIA_RESET (53)
-#define PCMCIA_S0_CD_VALID gpio_to_irq(GPIO_PCMCIA_S0_CD_VALID)
-#define PCMCIA_S0_RDYINT gpio_to_irq(GPIO_PCMCIA_S0_RDYINT)
-
-
-static struct pcmcia_irqs irqs[] = {
- { .sock = 0, .str = "PCMCIA0 CD" },
-};
-
static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret = gpio_request(GPIO_PCMCIA_RESET, "PCCard reset");
@@ -37,18 +29,16 @@ static int cmx270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return ret;
gpio_direction_output(GPIO_PCMCIA_RESET, 0);
- skt->socket.pci_irq = PCMCIA_S0_RDYINT;
- irqs[0].irq = PCMCIA_S0_CD_VALID;
- ret = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
- if (!ret)
- gpio_free(GPIO_PCMCIA_RESET);
+ skt->stat[SOC_STAT_CD].gpio = GPIO_PCMCIA_S0_CD_VALID;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_PCMCIA_S0_RDYINT;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY";
return ret;
}
static void cmx270_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
gpio_free(GPIO_PCMCIA_RESET);
}
@@ -56,13 +46,8 @@ static void cmx270_pcmcia_shutdown(struct soc_pcmcia_socket *skt)
static void cmx270_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- state->detect = (gpio_get_value(GPIO_PCMCIA_S0_CD_VALID) == 0) ? 1 : 0;
- state->ready = (gpio_get_value(GPIO_PCMCIA_S0_RDYINT) == 0) ? 0 : 1;
- state->bvd1 = 1;
- state->bvd2 = 1;
state->vs_3v = 0;
state->vs_Xv = 0;
- state->wrprot = 0; /* not available */
}
diff --git a/drivers/pcmcia/pxa2xx_colibri.c b/drivers/pcmcia/pxa2xx_colibri.c
index c6dec572a05d..4dee7b2a8032 100644
--- a/drivers/pcmcia/pxa2xx_colibri.c
+++ b/drivers/pcmcia/pxa2xx_colibri.c
@@ -53,13 +53,6 @@ static struct gpio colibri_pcmcia_gpios[] = {
{ 0, GPIOF_INIT_HIGH,"PCMCIA Reset" },
};
-static struct pcmcia_irqs colibri_irqs[] = {
- {
- .sock = 0,
- .str = "PCMCIA CD"
- },
-};
-
static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
@@ -69,19 +62,10 @@ static int colibri_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
if (ret)
goto err1;
- colibri_irqs[0].irq = gpio_to_irq(colibri_pcmcia_gpios[DETECT].gpio);
skt->socket.pci_irq = gpio_to_irq(colibri_pcmcia_gpios[READY].gpio);
+ skt->stat[SOC_STAT_CD].irq = gpio_to_irq(colibri_pcmcia_gpios[DETECT].gpio);
+ skt->stat[SOC_STAT_CD].name = "PCMCIA CD";
- ret = soc_pcmcia_request_irqs(skt, colibri_irqs,
- ARRAY_SIZE(colibri_irqs));
- if (ret)
- goto err2;
-
- return ret;
-
-err2:
- gpio_free_array(colibri_pcmcia_gpios,
- ARRAY_SIZE(colibri_pcmcia_gpios));
err1:
return ret;
}
@@ -100,7 +84,6 @@ static void colibri_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
state->ready = !!gpio_get_value(colibri_pcmcia_gpios[READY].gpio);
state->bvd1 = !!gpio_get_value(colibri_pcmcia_gpios[BVD1].gpio);
state->bvd2 = !!gpio_get_value(colibri_pcmcia_gpios[BVD2].gpio);
- state->wrprot = 0;
state->vs_3v = 1;
state->vs_Xv = 0;
}
diff --git a/drivers/pcmcia/pxa2xx_e740.c b/drivers/pcmcia/pxa2xx_e740.c
index 17cd2ce7428f..8751a323b448 100644
--- a/drivers/pcmcia/pxa2xx_e740.c
+++ b/drivers/pcmcia/pxa2xx_e740.c
@@ -23,53 +23,27 @@
#include "soc_common.h"
-static struct pcmcia_irqs cd_irqs[] = {
- {
- .sock = 0,
- .str = "CF card detect"
- },
- {
- .sock = 1,
- .str = "Wifi switch"
- },
-};
-
static int e740_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- if (skt->nr == 0)
- skt->socket.pci_irq = gpio_to_irq(GPIO_E740_PCMCIA_RDY0);
- else
- skt->socket.pci_irq = gpio_to_irq(GPIO_E740_PCMCIA_RDY1);
-
- cd_irqs[0].irq = gpio_to_irq(GPIO_E740_PCMCIA_CD0);
- cd_irqs[1].irq = gpio_to_irq(GPIO_E740_PCMCIA_CD1);
-
- return soc_pcmcia_request_irqs(skt, &cd_irqs[skt->nr], 1);
-}
+ if (skt->nr == 0) {
+ skt->stat[SOC_STAT_CD].gpio = GPIO_E740_PCMCIA_CD0;
+ skt->stat[SOC_STAT_CD].name = "CF card detect";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_E740_PCMCIA_RDY0;
+ skt->stat[SOC_STAT_RDY].name = "CF ready";
+ } else {
+ skt->stat[SOC_STAT_CD].gpio = GPIO_E740_PCMCIA_CD1;
+ skt->stat[SOC_STAT_CD].name = "Wifi switch";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_E740_PCMCIA_RDY1;
+ skt->stat[SOC_STAT_RDY].name = "Wifi ready";
+ }
-/*
- * Release all resources.
- */
-static void e740_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, &cd_irqs[skt->nr], 1);
+ return 0;
}
static void e740_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- if (skt->nr == 0) {
- state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD0) ? 0 : 1;
- state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY0) ? 1 : 0;
- } else {
- state->detect = gpio_get_value(GPIO_E740_PCMCIA_CD1) ? 0 : 1;
- state->ready = gpio_get_value(GPIO_E740_PCMCIA_RDY1) ? 1 : 0;
- }
-
state->vs_3v = 1;
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->wrprot = 0;
state->vs_Xv = 0;
}
@@ -109,32 +83,11 @@ static int e740_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
return 0;
}
-/*
- * Enable card status IRQs on (re-)initialisation. This can
- * be called at initialisation, power management event, or
- * pcmcia event.
- */
-static void e740_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_enable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs));
-}
-
-/*
- * Disable card status IRQs on suspend.
- */
-static void e740_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_disable_irqs(skt, cd_irqs, ARRAY_SIZE(cd_irqs));
-}
-
static struct pcmcia_low_level e740_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = e740_pcmcia_hw_init,
- .hw_shutdown = e740_pcmcia_hw_shutdown,
.socket_state = e740_pcmcia_socket_state,
.configure_socket = e740_pcmcia_configure_socket,
- .socket_init = e740_pcmcia_socket_init,
- .socket_suspend = e740_pcmcia_socket_suspend,
.nr = 2,
};
diff --git a/drivers/pcmcia/pxa2xx_mainstone.c b/drivers/pcmcia/pxa2xx_mainstone.c
index aded706c0b9f..7e32e25cdcb2 100644
--- a/drivers/pcmcia/pxa2xx_mainstone.c
+++ b/drivers/pcmcia/pxa2xx_mainstone.c
@@ -30,27 +30,26 @@
#include "soc_common.h"
-static struct pcmcia_irqs irqs[] = {
- { 0, MAINSTONE_S0_CD_IRQ, "PCMCIA0 CD" },
- { 1, MAINSTONE_S1_CD_IRQ, "PCMCIA1 CD" },
- { 0, MAINSTONE_S0_STSCHG_IRQ, "PCMCIA0 STSCHG" },
- { 1, MAINSTONE_S1_STSCHG_IRQ, "PCMCIA1 STSCHG" },
-};
-
static int mst_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
/*
* Setup default state of GPIO outputs
* before we enable them as outputs.
*/
-
- skt->socket.pci_irq = (skt->nr == 0) ? MAINSTONE_S0_IRQ : MAINSTONE_S1_IRQ;
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void mst_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ if (skt->nr == 0) {
+ skt->socket.pci_irq = MAINSTONE_S0_IRQ;
+ skt->stat[SOC_STAT_CD].irq = MAINSTONE_S0_CD_IRQ;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
+ skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S0_STSCHG_IRQ;
+ skt->stat[SOC_STAT_BVD1].name = "PCMCIA0 STSCHG";
+ } else {
+ skt->socket.pci_irq = MAINSTONE_S1_IRQ;
+ skt->stat[SOC_STAT_CD].irq = MAINSTONE_S1_CD_IRQ;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA1 CD";
+ skt->stat[SOC_STAT_BVD1].irq = MAINSTONE_S1_STSCHG_IRQ;
+ skt->stat[SOC_STAT_BVD1].name = "PCMCIA1 STSCHG";
+ }
+ return 0;
}
static unsigned long mst_pcmcia_status[2];
@@ -84,7 +83,6 @@ static void mst_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
state->bvd2 = (status & MST_PCMCIA_nSPKR_BVD2) ? 1 : 0;
state->vs_3v = (status & MST_PCMCIA_nVS1) ? 0 : 1;
state->vs_Xv = (status & MST_PCMCIA_nVS2) ? 0 : 1;
- state->wrprot = 0; /* not available */
}
static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
@@ -131,7 +129,6 @@ static int mst_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
static struct pcmcia_low_level mst_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = mst_pcmcia_hw_init,
- .hw_shutdown = mst_pcmcia_hw_shutdown,
.socket_state = mst_pcmcia_socket_state,
.configure_socket = mst_pcmcia_configure_socket,
.nr = 2,
diff --git a/drivers/pcmcia/pxa2xx_palmld.c b/drivers/pcmcia/pxa2xx_palmld.c
index 6a8e011a8c13..ed7d4dbc39fa 100644
--- a/drivers/pcmcia/pxa2xx_palmld.c
+++ b/drivers/pcmcia/pxa2xx_palmld.c
@@ -23,7 +23,6 @@
static struct gpio palmld_pcmcia_gpios[] = {
{ GPIO_NR_PALMLD_PCMCIA_POWER, GPIOF_INIT_LOW, "PCMCIA Power" },
{ GPIO_NR_PALMLD_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" },
- { GPIO_NR_PALMLD_PCMCIA_READY, GPIOF_IN, "PCMCIA Ready" },
};
static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
@@ -33,7 +32,8 @@ static int palmld_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
ret = gpio_request_array(palmld_pcmcia_gpios,
ARRAY_SIZE(palmld_pcmcia_gpios));
- skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMLD_PCMCIA_READY);
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMLD_PCMCIA_READY;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
return ret;
}
@@ -47,10 +47,6 @@ static void palmld_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = 1; /* always inserted */
- state->ready = !!gpio_get_value(GPIO_NR_PALMLD_PCMCIA_READY);
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->wrprot = 0;
state->vs_3v = 1;
state->vs_Xv = 0;
}
diff --git a/drivers/pcmcia/pxa2xx_palmtc.c b/drivers/pcmcia/pxa2xx_palmtc.c
index 9e38de769ba3..81225a7a8cbb 100644
--- a/drivers/pcmcia/pxa2xx_palmtc.c
+++ b/drivers/pcmcia/pxa2xx_palmtc.c
@@ -26,7 +26,6 @@ static struct gpio palmtc_pcmcia_gpios[] = {
{ GPIO_NR_PALMTC_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" },
{ GPIO_NR_PALMTC_PCMCIA_POWER3, GPIOF_INIT_LOW, "PCMCIA Power 3" },
{ GPIO_NR_PALMTC_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" },
- { GPIO_NR_PALMTC_PCMCIA_READY, GPIOF_IN, "PCMCIA Ready" },
{ GPIO_NR_PALMTC_PCMCIA_PWRREADY, GPIOF_IN, "PCMCIA Power Ready" },
};
@@ -37,7 +36,8 @@ static int palmtc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
ret = gpio_request_array(palmtc_pcmcia_gpios,
ARRAY_SIZE(palmtc_pcmcia_gpios));
- skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMTC_PCMCIA_READY);
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTC_PCMCIA_READY;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
return ret;
}
@@ -51,10 +51,6 @@ static void palmtc_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = 1; /* always inserted */
- state->ready = !!gpio_get_value(GPIO_NR_PALMTC_PCMCIA_READY);
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->wrprot = 0;
state->vs_3v = 1;
state->vs_Xv = 0;
}
diff --git a/drivers/pcmcia/pxa2xx_palmtx.c b/drivers/pcmcia/pxa2xx_palmtx.c
index 80645a688ee3..069b6bbcf319 100644
--- a/drivers/pcmcia/pxa2xx_palmtx.c
+++ b/drivers/pcmcia/pxa2xx_palmtx.c
@@ -23,7 +23,6 @@ static struct gpio palmtx_pcmcia_gpios[] = {
{ GPIO_NR_PALMTX_PCMCIA_POWER1, GPIOF_INIT_LOW, "PCMCIA Power 1" },
{ GPIO_NR_PALMTX_PCMCIA_POWER2, GPIOF_INIT_LOW, "PCMCIA Power 2" },
{ GPIO_NR_PALMTX_PCMCIA_RESET, GPIOF_INIT_HIGH,"PCMCIA Reset" },
- { GPIO_NR_PALMTX_PCMCIA_READY, GPIOF_IN, "PCMCIA Ready" },
};
static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
@@ -33,7 +32,8 @@ static int palmtx_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
ret = gpio_request_array(palmtx_pcmcia_gpios,
ARRAY_SIZE(palmtx_pcmcia_gpios));
- skt->socket.pci_irq = gpio_to_irq(GPIO_NR_PALMTX_PCMCIA_READY);
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_NR_PALMTX_PCMCIA_READY;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
return ret;
}
@@ -47,10 +47,6 @@ static void palmtx_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
state->detect = 1; /* always inserted */
- state->ready = !!gpio_get_value(GPIO_NR_PALMTX_PCMCIA_READY);
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->wrprot = 0;
state->vs_3v = 1;
state->vs_Xv = 0;
}
diff --git a/drivers/pcmcia/pxa2xx_sharpsl.c b/drivers/pcmcia/pxa2xx_sharpsl.c
index 69ae2fd22400..b066273b6b4f 100644
--- a/drivers/pcmcia/pxa2xx_sharpsl.c
+++ b/drivers/pcmcia/pxa2xx_sharpsl.c
@@ -46,21 +46,9 @@ static void sharpsl_pcmcia_init_reset(struct soc_pcmcia_socket *skt)
static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- int ret;
-
- /* Register interrupts */
if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
- struct pcmcia_irqs cd_irq;
-
- cd_irq.sock = skt->nr;
- cd_irq.irq = SCOOP_DEV[skt->nr].cd_irq;
- cd_irq.str = SCOOP_DEV[skt->nr].cd_irq_str;
- ret = soc_pcmcia_request_irqs(skt, &cd_irq, 1);
-
- if (ret) {
- printk(KERN_ERR "Request for Compact Flash IRQ failed\n");
- return ret;
- }
+ skt->stat[SOC_STAT_CD].irq = SCOOP_DEV[skt->nr].cd_irq;
+ skt->stat[SOC_STAT_CD].name = SCOOP_DEV[skt->nr].cd_irq_str;
}
skt->socket.pci_irq = SCOOP_DEV[skt->nr].irq;
@@ -68,19 +56,6 @@ static int sharpsl_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
return 0;
}
-static void sharpsl_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- if (SCOOP_DEV[skt->nr].cd_irq >= 0) {
- struct pcmcia_irqs cd_irq;
-
- cd_irq.sock = skt->nr;
- cd_irq.irq = SCOOP_DEV[skt->nr].cd_irq;
- cd_irq.str = SCOOP_DEV[skt->nr].cd_irq_str;
- soc_pcmcia_free_irqs(skt, &cd_irq, 1);
- }
-}
-
-
static void sharpsl_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
@@ -222,7 +197,6 @@ static void sharpsl_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
static struct pcmcia_low_level sharpsl_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = sharpsl_pcmcia_hw_init,
- .hw_shutdown = sharpsl_pcmcia_hw_shutdown,
.socket_state = sharpsl_pcmcia_socket_state,
.configure_socket = sharpsl_pcmcia_configure_socket,
.socket_init = sharpsl_pcmcia_socket_init,
diff --git a/drivers/pcmcia/pxa2xx_stargate2.c b/drivers/pcmcia/pxa2xx_stargate2.c
index 6c2366b74a35..1d73c4401fdd 100644
--- a/drivers/pcmcia/pxa2xx_stargate2.c
+++ b/drivers/pcmcia/pxa2xx_stargate2.c
@@ -33,10 +33,6 @@
#define SG2_S0_GPIO_DETECT 53
#define SG2_S0_GPIO_READY 81
-static struct pcmcia_irqs irqs[] = {
- {.sock = 0, .str = "PCMCIA0 CD" },
-};
-
static struct gpio sg2_pcmcia_gpios[] = {
{ SG2_S0_GPIO_RESET, GPIOF_OUT_INIT_HIGH, "PCMCIA Reset" },
{ SG2_S0_POWER_CTL, GPIOF_OUT_INIT_HIGH, "PCMCIA Power Ctrl" },
@@ -44,27 +40,20 @@ static struct gpio sg2_pcmcia_gpios[] = {
static int sg2_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->socket.pci_irq = gpio_to_irq(SG2_S0_GPIO_READY);
- irqs[0].irq = gpio_to_irq(SG2_S0_GPIO_DETECT);
-
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void sg2_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ skt->stat[SOC_STAT_CD].gpio = SG2_S0_GPIO_DETECT;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA0 CD";
+ skt->stat[SOC_STAT_RDY].gpio = SG2_S0_GPIO_READY;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA0 RDY";
+ return 0;
}
static void sg2_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- state->detect = !gpio_get_value(SG2_S0_GPIO_DETECT);
- state->ready = !!gpio_get_value(SG2_S0_GPIO_READY);
state->bvd1 = 0; /* not available - battery detect on card */
state->bvd2 = 0; /* not available */
state->vs_3v = 1; /* not available - voltage detect for card */
state->vs_Xv = 0; /* not available */
- state->wrprot = 0; /* not available - write protect */
}
static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
@@ -94,24 +83,11 @@ static int sg2_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
return 0;
}
-static void sg2_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void sg2_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
static struct pcmcia_low_level sg2_pcmcia_ops __initdata = {
.owner = THIS_MODULE,
.hw_init = sg2_pcmcia_hw_init,
- .hw_shutdown = sg2_pcmcia_hw_shutdown,
.socket_state = sg2_pcmcia_socket_state,
.configure_socket = sg2_pcmcia_configure_socket,
- .socket_init = sg2_pcmcia_socket_init,
- .socket_suspend = sg2_pcmcia_socket_suspend,
.nr = 1,
};
diff --git a/drivers/pcmcia/pxa2xx_trizeps4.c b/drivers/pcmcia/pxa2xx_trizeps4.c
index 7c33f898135a..d326ba1fa1ce 100644
--- a/drivers/pcmcia/pxa2xx_trizeps4.c
+++ b/drivers/pcmcia/pxa2xx_trizeps4.c
@@ -29,32 +29,17 @@
extern void board_pcmcia_power(int power);
-static struct pcmcia_irqs irqs[] = {
- { .sock = 0, .str = "cs0_cd" }
- /* on other baseboards we can have more inputs */
-};
-
static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- int ret, i;
/* we dont have voltage/card/ready detection
* so we dont need interrupts for it
*/
switch (skt->nr) {
case 0:
- if (gpio_request(GPIO_PRDY, "cf_irq") < 0) {
- pr_err("%s: sock %d unable to request gpio %d\n", __func__,
- skt->nr, GPIO_PRDY);
- return -EBUSY;
- }
- if (gpio_direction_input(GPIO_PRDY) < 0) {
- pr_err("%s: sock %d unable to set input gpio %d\n", __func__,
- skt->nr, GPIO_PRDY);
- gpio_free(GPIO_PRDY);
- return -EINVAL;
- }
- skt->socket.pci_irq = gpio_to_irq(GPIO_PRDY);
- irqs[0].irq = gpio_to_irq(GPIO_PCD);
+ skt->stat[SOC_STAT_CD].gpio = GPIO_PCD;
+ skt->stat[SOC_STAT_CD].name = "cs0_cd";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_PRDY;
+ skt->stat[SOC_STAT_RDY].name = "cs0_rdy";
break;
default:
break;
@@ -62,39 +47,7 @@ static int trizeps_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
/* release the reset of this card */
pr_debug("%s: sock %d irq %d\n", __func__, skt->nr, skt->socket.pci_irq);
- /* supplementory irqs for the socket */
- for (i = 0; i < ARRAY_SIZE(irqs); i++) {
- if (irqs[i].sock != skt->nr)
- continue;
- if (gpio_request(irq_to_gpio(irqs[i].irq), irqs[i].str) < 0) {
- pr_err("%s: sock %d unable to request gpio %d\n",
- __func__, skt->nr, irq_to_gpio(irqs[i].irq));
- ret = -EBUSY;
- goto error;
- }
- if (gpio_direction_input(irq_to_gpio(irqs[i].irq)) < 0) {
- pr_err("%s: sock %d unable to set input gpio %d\n",
- __func__, skt->nr, irq_to_gpio(irqs[i].irq));
- ret = -EINVAL;
- goto error;
- }
- }
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-
-error:
- for (; i >= 0; i--) {
- gpio_free(irq_to_gpio(irqs[i].irq));
- }
- return (ret);
-}
-
-static void trizeps_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- int i;
- /* free allocated gpio's */
- gpio_free(GPIO_PRDY);
- for (i = 0; i < ARRAY_SIZE(irqs); i++)
- gpio_free(irq_to_gpio(irqs[i].irq));
+ return 0;
}
static unsigned long trizeps_pcmcia_status[2];
@@ -118,13 +71,10 @@ static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
switch (skt->nr) {
case 0:
/* just fill in fix states */
- state->detect = gpio_get_value(GPIO_PCD) ? 0 : 1;
- state->ready = gpio_get_value(GPIO_PRDY) ? 1 : 0;
state->bvd1 = (status & ConXS_CFSR_BVD1) ? 1 : 0;
state->bvd2 = (status & ConXS_CFSR_BVD2) ? 1 : 0;
state->vs_3v = (status & ConXS_CFSR_VS1) ? 0 : 1;
state->vs_Xv = (status & ConXS_CFSR_VS2) ? 0 : 1;
- state->wrprot = 0; /* not available */
break;
#ifndef CONFIG_MACH_TRIZEPS_CONXS
@@ -136,7 +86,6 @@ static void trizeps_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
state->bvd2 = 0;
state->vs_3v = 0;
state->vs_Xv = 0;
- state->wrprot = 0;
break;
#endif
@@ -204,7 +153,6 @@ static void trizeps_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
static struct pcmcia_low_level trizeps_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = trizeps_pcmcia_hw_init,
- .hw_shutdown = trizeps_pcmcia_hw_shutdown,
.socket_state = trizeps_pcmcia_socket_state,
.configure_socket = trizeps_pcmcia_configure_socket,
.socket_init = trizeps_pcmcia_socket_init,
diff --git a/drivers/pcmcia/pxa2xx_viper.c b/drivers/pcmcia/pxa2xx_viper.c
index 1064b1c2869d..adfae4987a42 100644
--- a/drivers/pcmcia/pxa2xx_viper.c
+++ b/drivers/pcmcia/pxa2xx_viper.c
@@ -32,13 +32,6 @@
static struct platform_device *arcom_pcmcia_dev;
-static struct pcmcia_irqs irqs[] = {
- {
- .sock = 0,
- .str = "PCMCIA_CD",
- },
-};
-
static inline struct arcom_pcmcia_pdata *viper_get_pdata(void)
{
return arcom_pcmcia_dev->dev.platform_data;
@@ -49,38 +42,28 @@ static int viper_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
unsigned long flags;
- skt->socket.pci_irq = gpio_to_irq(pdata->rdy_gpio);
- irqs[0].irq = gpio_to_irq(pdata->cd_gpio);
-
- if (gpio_request(pdata->cd_gpio, "CF detect"))
- goto err_request_cd;
-
- if (gpio_request(pdata->rdy_gpio, "CF ready"))
- goto err_request_rdy;
+ skt->stat[SOC_STAT_CD].gpio = pdata->cd_gpio;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA_CD";
+ skt->stat[SOC_STAT_RDY].gpio = pdata->rdy_gpio;
+ skt->stat[SOC_STAT_RDY].name = "CF ready";
if (gpio_request(pdata->pwr_gpio, "CF power"))
goto err_request_pwr;
local_irq_save(flags);
- if (gpio_direction_output(pdata->pwr_gpio, 0) ||
- gpio_direction_input(pdata->cd_gpio) ||
- gpio_direction_input(pdata->rdy_gpio)) {
+ if (gpio_direction_output(pdata->pwr_gpio, 0)) {
local_irq_restore(flags);
goto err_dir;
}
local_irq_restore(flags);
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ return 0;
err_dir:
gpio_free(pdata->pwr_gpio);
err_request_pwr:
- gpio_free(pdata->rdy_gpio);
-err_request_rdy:
- gpio_free(pdata->cd_gpio);
-err_request_cd:
dev_err(&arcom_pcmcia_dev->dev, "Failed to setup PCMCIA GPIOs\n");
return -1;
}
@@ -92,22 +75,12 @@ static void viper_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
gpio_free(pdata->pwr_gpio);
- gpio_free(pdata->rdy_gpio);
- gpio_free(pdata->cd_gpio);
}
static void viper_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- struct arcom_pcmcia_pdata *pdata = viper_get_pdata();
-
- state->detect = !gpio_get_value(pdata->cd_gpio);
- state->ready = !!gpio_get_value(pdata->rdy_gpio);
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->wrprot = 0;
state->vs_3v = 1; /* Can only apply 3.3V */
state->vs_Xv = 0;
}
diff --git a/drivers/pcmcia/pxa2xx_vpac270.c b/drivers/pcmcia/pxa2xx_vpac270.c
index 61b17d235dbe..a47dcd24a26a 100644
--- a/drivers/pcmcia/pxa2xx_vpac270.c
+++ b/drivers/pcmcia/pxa2xx_vpac270.c
@@ -23,29 +23,14 @@
#include "soc_common.h"
static struct gpio vpac270_pcmcia_gpios[] = {
- { GPIO84_VPAC270_PCMCIA_CD, GPIOF_IN, "PCMCIA Card Detect" },
- { GPIO35_VPAC270_PCMCIA_RDY, GPIOF_IN, "PCMCIA Ready" },
{ GPIO107_VPAC270_PCMCIA_PPEN, GPIOF_INIT_LOW, "PCMCIA PPEN" },
{ GPIO11_VPAC270_PCMCIA_RESET, GPIOF_INIT_LOW, "PCMCIA Reset" },
};
static struct gpio vpac270_cf_gpios[] = {
- { GPIO17_VPAC270_CF_CD, GPIOF_IN, "CF Card Detect" },
- { GPIO12_VPAC270_CF_RDY, GPIOF_IN, "CF Ready" },
{ GPIO16_VPAC270_CF_RESET, GPIOF_INIT_LOW, "CF Reset" },
};
-static struct pcmcia_irqs cd_irqs[] = {
- {
- .sock = 0,
- .str = "PCMCIA CD"
- },
- {
- .sock = 1,
- .str = "CF CD"
- },
-};
-
static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int ret;
@@ -54,20 +39,18 @@ static int vpac270_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
ret = gpio_request_array(vpac270_pcmcia_gpios,
ARRAY_SIZE(vpac270_pcmcia_gpios));
- skt->socket.pci_irq = gpio_to_irq(GPIO35_VPAC270_PCMCIA_RDY);
- cd_irqs[0].irq = gpio_to_irq(GPIO84_VPAC270_PCMCIA_CD);
-
- if (!ret)
- ret = soc_pcmcia_request_irqs(skt, &cd_irqs[0], 1);
+ skt->stat[SOC_STAT_CD].gpio = GPIO84_VPAC270_PCMCIA_CD;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA CD";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO35_VPAC270_PCMCIA_RDY;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA Ready";
} else {
ret = gpio_request_array(vpac270_cf_gpios,
ARRAY_SIZE(vpac270_cf_gpios));
- skt->socket.pci_irq = gpio_to_irq(GPIO12_VPAC270_CF_RDY);
- cd_irqs[1].irq = gpio_to_irq(GPIO17_VPAC270_CF_CD);
-
- if (!ret)
- ret = soc_pcmcia_request_irqs(skt, &cd_irqs[1], 1);
+ skt->stat[SOC_STAT_CD].gpio = GPIO17_VPAC270_CF_CD;
+ skt->stat[SOC_STAT_CD].name = "CF CD";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO12_VPAC270_CF_RDY;
+ skt->stat[SOC_STAT_RDY].name = "CF Ready";
}
return ret;
@@ -86,16 +69,6 @@ static void vpac270_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- if (skt->nr == 0) {
- state->detect = !gpio_get_value(GPIO84_VPAC270_PCMCIA_CD);
- state->ready = !!gpio_get_value(GPIO35_VPAC270_PCMCIA_RDY);
- } else {
- state->detect = !gpio_get_value(GPIO17_VPAC270_CF_CD);
- state->ready = !!gpio_get_value(GPIO12_VPAC270_CF_RDY);
- }
- state->bvd1 = 1;
- state->bvd2 = 1;
- state->wrprot = 0;
state->vs_3v = 1;
state->vs_Xv = 0;
}
@@ -117,14 +90,6 @@ vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
return 0;
}
-static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
-}
-
-static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
-}
-
static struct pcmcia_low_level vpac270_pcmcia_ops = {
.owner = THIS_MODULE,
@@ -136,9 +101,6 @@ static struct pcmcia_low_level vpac270_pcmcia_ops = {
.socket_state = vpac270_pcmcia_socket_state,
.configure_socket = vpac270_pcmcia_configure_socket,
-
- .socket_init = vpac270_pcmcia_socket_init,
- .socket_suspend = vpac270_pcmcia_socket_suspend,
};
static struct platform_device *vpac270_pcmcia_device;
diff --git a/drivers/pcmcia/sa1100_assabet.c b/drivers/pcmcia/sa1100_assabet.c
index f1e882272ab0..ba8557eea618 100644
--- a/drivers/pcmcia/sa1100_assabet.c
+++ b/drivers/pcmcia/sa1100_assabet.c
@@ -10,46 +10,30 @@
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/init.h>
+#include <linux/gpio.h>
-#include <mach/hardware.h>
#include <asm/mach-types.h>
-#include <asm/irq.h>
-#include <asm/signal.h>
#include <mach/assabet.h>
#include "sa1100_generic.h"
-static struct pcmcia_irqs irqs[] = {
- { 1, ASSABET_IRQ_GPIO_CF_CD, "CF CD" },
- { 1, ASSABET_IRQ_GPIO_CF_BVD2, "CF BVD2" },
- { 1, ASSABET_IRQ_GPIO_CF_BVD1, "CF BVD1" },
-};
-
static int assabet_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->socket.pci_irq = ASSABET_IRQ_GPIO_CF_IRQ;
-
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
+ skt->stat[SOC_STAT_CD].gpio = ASSABET_GPIO_CF_CD;
+ skt->stat[SOC_STAT_CD].name = "CF CD";
+ skt->stat[SOC_STAT_BVD1].gpio = ASSABET_GPIO_CF_BVD1;
+ skt->stat[SOC_STAT_BVD1].name = "CF BVD1";
+ skt->stat[SOC_STAT_BVD2].gpio = ASSABET_GPIO_CF_BVD2;
+ skt->stat[SOC_STAT_BVD2].name = "CF BVD2";
+ skt->stat[SOC_STAT_RDY].gpio = ASSABET_GPIO_CF_IRQ;
+ skt->stat[SOC_STAT_RDY].name = "CF RDY";
-/*
- * Release all resources.
- */
-static void assabet_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ return 0;
}
static void
assabet_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
- unsigned long levels = GPLR;
-
- state->detect = (levels & ASSABET_GPIO_CF_CD) ? 0 : 1;
- state->ready = (levels & ASSABET_GPIO_CF_IRQ) ? 1 : 0;
- state->bvd1 = (levels & ASSABET_GPIO_CF_BVD1) ? 1 : 0;
- state->bvd2 = (levels & ASSABET_GPIO_CF_BVD2) ? 1 : 0;
- state->wrprot = 0; /* Not available on Assabet. */
state->vs_3v = 1; /* Can only apply 3.3V on Assabet. */
state->vs_Xv = 0;
}
@@ -78,38 +62,24 @@ assabet_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_stat
return -1;
}
- /* Silently ignore Vpp, output enable, speaker enable. */
+ /* Silently ignore Vpp, speaker enable. */
if (state->flags & SS_RESET)
mask |= ASSABET_BCR_CF_RST;
+ if (!(state->flags & SS_OUTPUT_ENA))
+ mask |= ASSABET_BCR_CF_BUS_OFF;
- ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR, mask);
+ ASSABET_BCR_frob(ASSABET_BCR_CF_RST | ASSABET_BCR_CF_PWR |
+ ASSABET_BCR_CF_BUS_OFF, mask);
return 0;
}
/*
- * Enable card status IRQs on (re-)initialisation. This can
- * be called at initialisation, power management event, or
- * pcmcia event.
- */
-static void assabet_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- /*
- * Enable CF bus
- */
- ASSABET_BCR_clear(ASSABET_BCR_CF_BUS_OFF);
-
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-/*
* Disable card status IRQs on suspend.
*/
static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-
/*
* Tristate the CF bus signals. Also assert CF
* reset as per user guide page 4-11.
@@ -119,14 +89,9 @@ static void assabet_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
static struct pcmcia_low_level assabet_pcmcia_ops = {
.owner = THIS_MODULE,
-
.hw_init = assabet_pcmcia_hw_init,
- .hw_shutdown = assabet_pcmcia_hw_shutdown,
-
.socket_state = assabet_pcmcia_socket_state,
.configure_socket = assabet_pcmcia_configure_socket,
-
- .socket_init = assabet_pcmcia_socket_init,
.socket_suspend = assabet_pcmcia_socket_suspend,
};
diff --git a/drivers/pcmcia/sa1100_cerf.c b/drivers/pcmcia/sa1100_cerf.c
index 30560df8c76b..c59c44921a3a 100644
--- a/drivers/pcmcia/sa1100_cerf.c
+++ b/drivers/pcmcia/sa1100_cerf.c
@@ -10,6 +10,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
@@ -19,34 +20,34 @@
#define CERF_SOCKET 1
-static struct pcmcia_irqs irqs[] = {
- { CERF_SOCKET, CERF_IRQ_GPIO_CF_CD, "CF_CD" },
- { CERF_SOCKET, CERF_IRQ_GPIO_CF_BVD2, "CF_BVD2" },
- { CERF_SOCKET, CERF_IRQ_GPIO_CF_BVD1, "CF_BVD1" }
-};
-
static int cerf_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
- skt->socket.pci_irq = CERF_IRQ_GPIO_CF_IRQ;
+ int ret;
+
+ ret = gpio_request_one(CERF_GPIO_CF_RESET, GPIOF_OUT_INIT_LOW, "CF_RESET");
+ if (ret)
+ return ret;
+
+ skt->stat[SOC_STAT_CD].gpio = CERF_GPIO_CF_CD;
+ skt->stat[SOC_STAT_CD].name = "CF_CD";
+ skt->stat[SOC_STAT_BVD1].gpio = CERF_GPIO_CF_BVD1;
+ skt->stat[SOC_STAT_BVD1].name = "CF_BVD1";
+ skt->stat[SOC_STAT_BVD2].gpio = CERF_GPIO_CF_BVD2;
+ skt->stat[SOC_STAT_BVD2].name = "CF_BVD2";
+ skt->stat[SOC_STAT_RDY].gpio = CERF_GPIO_CF_IRQ;
+ skt->stat[SOC_STAT_RDY].name = "CF_IRQ";
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ return 0;
}
static void cerf_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ gpio_free(CERF_GPIO_CF_RESET);
}
static void
cerf_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
- unsigned long levels = GPLR;
-
- state->detect = (levels & CERF_GPIO_CF_CD) ?0:1;
- state->ready = (levels & CERF_GPIO_CF_IRQ) ?1:0;
- state->bvd1 = (levels & CERF_GPIO_CF_BVD1)?1:0;
- state->bvd2 = (levels & CERF_GPIO_CF_BVD2)?1:0;
- state->wrprot = 0;
state->vs_3v = 1;
state->vs_Xv = 0;
}
@@ -67,34 +68,17 @@ cerf_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
return -1;
}
- if (state->flags & SS_RESET) {
- GPSR = CERF_GPIO_CF_RESET;
- } else {
- GPCR = CERF_GPIO_CF_RESET;
- }
+ gpio_set_value(CERF_GPIO_CF_RESET, !!(state->flags & SS_RESET));
return 0;
}
-static void cerf_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void cerf_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
static struct pcmcia_low_level cerf_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = cerf_pcmcia_hw_init,
.hw_shutdown = cerf_pcmcia_hw_shutdown,
.socket_state = cerf_pcmcia_socket_state,
.configure_socket = cerf_pcmcia_configure_socket,
-
- .socket_init = cerf_pcmcia_socket_init,
- .socket_suspend = cerf_pcmcia_socket_suspend,
};
int __devinit pcmcia_cerf_init(struct device *dev)
diff --git a/drivers/pcmcia/sa1100_h3600.c b/drivers/pcmcia/sa1100_h3600.c
index edf8f0028898..d9c7337b909c 100644
--- a/drivers/pcmcia/sa1100_h3600.c
+++ b/drivers/pcmcia/sa1100_h3600.c
@@ -19,36 +19,20 @@
#include "sa1100_generic.h"
-static struct pcmcia_irqs irqs[] = {
- { .sock = 0, .str = "PCMCIA CD0" }, /* .irq will be filled later */
- { .sock = 1, .str = "PCMCIA CD1" }
-};
-
static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
int err;
switch (skt->nr) {
case 0:
- err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ0, "PCMCIA IRQ0");
- if (err)
- goto err00;
- err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ0);
- if (err)
- goto err01;
- skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ0);
-
- err = gpio_request(H3XXX_GPIO_PCMCIA_CD0, "PCMCIA CD0");
- if (err)
- goto err01;
- err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD0);
- if (err)
- goto err02;
- irqs[0].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD0);
+ skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD0;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA CD0";
+ skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ0;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ0";
err = gpio_request(H3XXX_EGPIO_OPT_NVRAM_ON, "OPT NVRAM ON");
if (err)
- goto err02;
+ goto err01;
err = gpio_direction_output(H3XXX_EGPIO_OPT_NVRAM_ON, 0);
if (err)
goto err03;
@@ -70,30 +54,12 @@ static int h3600_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
err = gpio_direction_output(H3XXX_EGPIO_CARD_RESET, 0);
if (err)
goto err06;
- err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
- if (err)
- goto err06;
break;
case 1:
- err = gpio_request(H3XXX_GPIO_PCMCIA_IRQ1, "PCMCIA IRQ1");
- if (err)
- goto err10;
- err = gpio_direction_input(H3XXX_GPIO_PCMCIA_IRQ1);
- if (err)
- goto err11;
- skt->socket.pci_irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_IRQ1);
-
- err = gpio_request(H3XXX_GPIO_PCMCIA_CD1, "PCMCIA CD1");
- if (err)
- goto err11;
- err = gpio_direction_input(H3XXX_GPIO_PCMCIA_CD1);
- if (err)
- goto err12;
- irqs[1].irq = gpio_to_irq(H3XXX_GPIO_PCMCIA_CD1);
-
- err = soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
- if (err)
- goto err12;
+ skt->stat[SOC_STAT_CD].gpio = H3XXX_GPIO_PCMCIA_CD1;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA CD1";
+ skt->stat[SOC_STAT_RDY].gpio = H3XXX_GPIO_PCMCIA_IRQ1;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA IRQ1";
break;
}
return 0;
@@ -102,19 +68,12 @@ err06: gpio_free(H3XXX_EGPIO_CARD_RESET);
err05: gpio_free(H3XXX_EGPIO_OPT_RESET);
err04: gpio_free(H3XXX_EGPIO_OPT_ON);
err03: gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON);
-err02: gpio_free(H3XXX_GPIO_PCMCIA_CD0);
err01: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
-err00: return err;
-
-err12: gpio_free(H3XXX_GPIO_PCMCIA_CD0);
-err11: gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
-err10: return err;
+ return err;
}
static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
-
switch (skt->nr) {
case 0:
/* Disable CF bus: */
@@ -126,12 +85,8 @@ static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
gpio_free(H3XXX_EGPIO_OPT_RESET);
gpio_free(H3XXX_EGPIO_OPT_ON);
gpio_free(H3XXX_EGPIO_OPT_NVRAM_ON);
- gpio_free(H3XXX_GPIO_PCMCIA_CD0);
- gpio_free(H3XXX_GPIO_PCMCIA_IRQ0);
break;
case 1:
- gpio_free(H3XXX_GPIO_PCMCIA_CD1);
- gpio_free(H3XXX_GPIO_PCMCIA_IRQ1);
break;
}
}
@@ -139,27 +94,10 @@ static void h3600_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
static void
h3600_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
- switch (skt->nr) {
- case 0:
- state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD0);
- state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ0);
- state->bvd1 = 0;
- state->bvd2 = 0;
- state->wrprot = 0; /* Not available on H3600. */
- state->vs_3v = 0;
- state->vs_Xv = 0;
- break;
-
- case 1:
- state->detect = !gpio_get_value(H3XXX_GPIO_PCMCIA_CD1);
- state->ready = !!gpio_get_value(H3XXX_GPIO_PCMCIA_IRQ1);
- state->bvd1 = 0;
- state->bvd2 = 0;
- state->wrprot = 0; /* Not available on H3600. */
- state->vs_3v = 0;
- state->vs_Xv = 0;
- break;
- }
+ state->bvd1 = 0;
+ state->bvd2 = 0;
+ state->vs_3v = 0;
+ state->vs_Xv = 0;
}
static int
@@ -186,14 +124,10 @@ static void h3600_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
gpio_set_value(H3XXX_EGPIO_OPT_RESET, 0);
msleep(10);
-
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
}
static void h3600_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-
/*
* FIXME: This doesn't fit well. We don't have the mechanism in
* the generic PCMCIA layer to deal with the idea of two sockets
diff --git a/drivers/pcmcia/sa1100_nanoengine.c b/drivers/pcmcia/sa1100_nanoengine.c
index 93b9c9ba57c3..35c30ff41e81 100644
--- a/drivers/pcmcia/sa1100_nanoengine.c
+++ b/drivers/pcmcia/sa1100_nanoengine.c
@@ -19,6 +19,7 @@
*/
#include <linux/device.h>
#include <linux/errno.h>
+#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/init.h>
@@ -34,43 +35,23 @@
#include "sa1100_generic.h"
-static struct pcmcia_irqs irqs_skt0[] = {
- /* socket, IRQ, name */
- { 0, NANOENGINE_IRQ_GPIO_PC_CD0, "PC CD0" },
-};
-
-static struct pcmcia_irqs irqs_skt1[] = {
- /* socket, IRQ, name */
- { 1, NANOENGINE_IRQ_GPIO_PC_CD1, "PC CD1" },
-};
-
struct nanoengine_pins {
- unsigned input_pins;
unsigned output_pins;
unsigned clear_outputs;
- unsigned transition_pins;
- unsigned pci_irq;
- struct pcmcia_irqs *pcmcia_irqs;
- unsigned pcmcia_irqs_size;
+ int gpio_rst;
+ int gpio_cd;
+ int gpio_rdy;
};
static struct nanoengine_pins nano_skts[] = {
{
- .input_pins = GPIO_PC_READY0 | GPIO_PC_CD0,
- .output_pins = GPIO_PC_RESET0,
- .clear_outputs = GPIO_PC_RESET0,
- .transition_pins = NANOENGINE_IRQ_GPIO_PC_CD0,
- .pci_irq = NANOENGINE_IRQ_GPIO_PC_READY0,
- .pcmcia_irqs = irqs_skt0,
- .pcmcia_irqs_size = ARRAY_SIZE(irqs_skt0)
+ .gpio_rst = GPIO_PC_RESET0,
+ .gpio_cd = GPIO_PC_CD0,
+ .gpio_rdy = GPIO_PC_READY0,
}, {
- .input_pins = GPIO_PC_READY1 | GPIO_PC_CD1,
- .output_pins = GPIO_PC_RESET1,
- .clear_outputs = GPIO_PC_RESET1,
- .transition_pins = NANOENGINE_IRQ_GPIO_PC_CD1,
- .pci_irq = NANOENGINE_IRQ_GPIO_PC_READY1,
- .pcmcia_irqs = irqs_skt1,
- .pcmcia_irqs_size = ARRAY_SIZE(irqs_skt1)
+ .gpio_rst = GPIO_PC_RESET1,
+ .gpio_cd = GPIO_PC_CD1,
+ .gpio_rdy = GPIO_PC_READY1,
}
};
@@ -79,58 +60,38 @@ unsigned num_nano_pcmcia_sockets = ARRAY_SIZE(nano_skts);
static int nanoengine_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
unsigned i = skt->nr;
+ int ret;
if (i >= num_nano_pcmcia_sockets)
return -ENXIO;
- GPDR &= ~nano_skts[i].input_pins;
- GPDR |= nano_skts[i].output_pins;
- GPCR = nano_skts[i].clear_outputs;
- irq_set_irq_type(nano_skts[i].transition_pins, IRQ_TYPE_EDGE_BOTH);
- skt->socket.pci_irq = nano_skts[i].pci_irq;
+ ret = gpio_request_one(nano_skts[i].gpio_rst, GPIOF_OUT_INIT_LOW,
+ i ? "PC RST1" : "PC RST0");
+ if (ret)
+ return ret;
+
+ skt->stat[SOC_STAT_CD].gpio = nano_skts[i].gpio_cd;
+ skt->stat[SOC_STAT_CD].name = i ? "PC CD1" : "PC CD0";
+ skt->stat[SOC_STAT_RDY].gpio = nano_skts[i].gpio_rdy;
+ skt->stat[SOC_STAT_RDY].name = i ? "PC RDY1" : "PC RDY0";
- return soc_pcmcia_request_irqs(skt,
- nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
+ return 0;
}
-/*
- * Release all resources.
- */
static void nanoengine_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
- unsigned i = skt->nr;
-
- if (i >= num_nano_pcmcia_sockets)
- return;
-
- soc_pcmcia_free_irqs(skt,
- nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
+ gpio_free(nano_skts[skt->nr].gpio_rst);
}
static int nanoengine_pcmcia_configure_socket(
struct soc_pcmcia_socket *skt, const socket_state_t *state)
{
- unsigned reset;
unsigned i = skt->nr;
if (i >= num_nano_pcmcia_sockets)
return -ENXIO;
- switch (i) {
- case 0:
- reset = GPIO_PC_RESET0;
- break;
- case 1:
- reset = GPIO_PC_RESET1;
- break;
- default:
- return -ENXIO;
- }
-
- if (state->flags & SS_RESET)
- GPSR = reset;
- else
- GPCR = reset;
+ gpio_set_value(nano_skts[skt->nr].gpio_rst, !!(state->flags & SS_RESET));
return 0;
}
@@ -138,62 +99,17 @@ static int nanoengine_pcmcia_configure_socket(
static void nanoengine_pcmcia_socket_state(
struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
- unsigned long levels = GPLR;
unsigned i = skt->nr;
if (i >= num_nano_pcmcia_sockets)
return;
- memset(state, 0, sizeof(struct pcmcia_state));
- switch (i) {
- case 0:
- state->ready = (levels & GPIO_PC_READY0) ? 1 : 0;
- state->detect = !(levels & GPIO_PC_CD0) ? 1 : 0;
- break;
- case 1:
- state->ready = (levels & GPIO_PC_READY1) ? 1 : 0;
- state->detect = !(levels & GPIO_PC_CD1) ? 1 : 0;
- break;
- default:
- return;
- }
state->bvd1 = 1;
state->bvd2 = 1;
- state->wrprot = 0; /* Not available */
state->vs_3v = 1; /* Can only apply 3.3V */
state->vs_Xv = 0;
}
-/*
- * Enable card status IRQs on (re-)initialisation. This can
- * be called at initialisation, power management event, or
- * pcmcia event.
- */
-static void nanoengine_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- unsigned i = skt->nr;
-
- if (i >= num_nano_pcmcia_sockets)
- return;
-
- soc_pcmcia_enable_irqs(skt,
- nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
-}
-
-/*
- * Disable card status IRQs on suspend.
- */
-static void nanoengine_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- unsigned i = skt->nr;
-
- if (i >= num_nano_pcmcia_sockets)
- return;
-
- soc_pcmcia_disable_irqs(skt,
- nano_skts[i].pcmcia_irqs, nano_skts[i].pcmcia_irqs_size);
-}
-
static struct pcmcia_low_level nanoengine_pcmcia_ops = {
.owner = THIS_MODULE,
@@ -202,8 +118,6 @@ static struct pcmcia_low_level nanoengine_pcmcia_ops = {
.configure_socket = nanoengine_pcmcia_configure_socket,
.socket_state = nanoengine_pcmcia_socket_state,
- .socket_init = nanoengine_pcmcia_socket_init,
- .socket_suspend = nanoengine_pcmcia_socket_suspend,
};
int pcmcia_nanoengine_init(struct device *dev)
diff --git a/drivers/pcmcia/sa1100_shannon.c b/drivers/pcmcia/sa1100_shannon.c
index 7ff1b43540b8..decb34730bcf 100644
--- a/drivers/pcmcia/sa1100_shannon.c
+++ b/drivers/pcmcia/sa1100_shannon.c
@@ -15,40 +15,35 @@
#include <asm/irq.h>
#include "sa1100_generic.h"
-static struct pcmcia_irqs irqs[] = {
- { 0, SHANNON_IRQ_GPIO_EJECT_0, "PCMCIA_CD_0" },
- { 1, SHANNON_IRQ_GPIO_EJECT_1, "PCMCIA_CD_1" },
-};
-
static int shannon_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
/* All those are inputs */
- GPDR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 |
- SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
- GAFR &= ~(SHANNON_GPIO_EJECT_0 | SHANNON_GPIO_EJECT_1 |
- SHANNON_GPIO_RDY_0 | SHANNON_GPIO_RDY_1);
-
- skt->socket.pci_irq = skt->nr ? SHANNON_IRQ_GPIO_RDY_1 : SHANNON_IRQ_GPIO_RDY_0;
-
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
+ GAFR &= ~(GPIO_GPIO(SHANNON_GPIO_EJECT_0) |
+ GPIO_GPIO(SHANNON_GPIO_EJECT_1) |
+ GPIO_GPIO(SHANNON_GPIO_RDY_0) |
+ GPIO_GPIO(SHANNON_GPIO_RDY_1));
+
+ if (skt->nr == 0) {
+ skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_0;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_0";
+ skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_0;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_0";
+ } else {
+ skt->stat[SOC_STAT_CD].gpio = SHANNON_GPIO_EJECT_1;
+ skt->stat[SOC_STAT_CD].name = "PCMCIA_CD_1";
+ skt->stat[SOC_STAT_RDY].gpio = SHANNON_GPIO_RDY_1;
+ skt->stat[SOC_STAT_RDY].name = "PCMCIA_RDY_1";
+ }
-static void shannon_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ return 0;
}
static void
shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- unsigned long levels = GPLR;
-
switch (skt->nr) {
case 0:
- state->detect = (levels & SHANNON_GPIO_EJECT_0) ? 0 : 1;
- state->ready = (levels & SHANNON_GPIO_RDY_0) ? 1 : 0;
- state->wrprot = 0; /* Not available on Shannon. */
state->bvd1 = 1;
state->bvd2 = 1;
state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
@@ -56,9 +51,6 @@ shannon_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
break;
case 1:
- state->detect = (levels & SHANNON_GPIO_EJECT_1) ? 0 : 1;
- state->ready = (levels & SHANNON_GPIO_RDY_1) ? 1 : 0;
- state->wrprot = 0; /* Not available on Shannon. */
state->bvd1 = 1;
state->bvd2 = 1;
state->vs_3v = 1; /* FIXME Can only apply 3.3V on Shannon. */
@@ -92,25 +84,11 @@ shannon_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
return 0;
}
-static void shannon_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void shannon_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
static struct pcmcia_low_level shannon_pcmcia_ops = {
.owner = THIS_MODULE,
.hw_init = shannon_pcmcia_hw_init,
- .hw_shutdown = shannon_pcmcia_hw_shutdown,
.socket_state = shannon_pcmcia_socket_state,
.configure_socket = shannon_pcmcia_configure_socket,
-
- .socket_init = shannon_pcmcia_socket_init,
- .socket_suspend = shannon_pcmcia_socket_suspend,
};
int __devinit pcmcia_shannon_init(struct device *dev)
diff --git a/drivers/pcmcia/sa1100_simpad.c b/drivers/pcmcia/sa1100_simpad.c
index 0fac9658b020..8647b17c449e 100644
--- a/drivers/pcmcia/sa1100_simpad.c
+++ b/drivers/pcmcia/sa1100_simpad.c
@@ -15,24 +15,21 @@
#include <mach/simpad.h>
#include "sa1100_generic.h"
-static struct pcmcia_irqs irqs[] = {
- { 1, IRQ_GPIO_CF_CD, "CF_CD" },
-};
-
static int simpad_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
{
simpad_clear_cs3_bit(VCC_3V_EN|VCC_5V_EN|EN0|EN1);
- skt->socket.pci_irq = IRQ_GPIO_CF_IRQ;
+ skt->stat[SOC_STAT_CD].gpio = GPIO_CF_CD;
+ skt->stat[SOC_STAT_CD].name = "CF_CD";
+ skt->stat[SOC_STAT_RDY].gpio = GPIO_CF_IRQ;
+ skt->stat[SOC_STAT_RDY].name = "CF_RDY";
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
+ return 0;
}
static void simpad_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
-
/* Disable CF bus: */
/*simpad_set_cs3_bit(PCMCIA_BUFF_DIS);*/
simpad_clear_cs3_bit(PCMCIA_RESET);
@@ -42,14 +39,13 @@ static void
simpad_pcmcia_socket_state(struct soc_pcmcia_socket *skt,
struct pcmcia_state *state)
{
- unsigned long levels = GPLR;
long cs3reg = simpad_get_cs3_ro();
- state->detect=((levels & GPIO_CF_CD)==0)?1:0;
- state->ready=(levels & GPIO_CF_IRQ)?1:0;
+ /* the detect signal is inverted - fix that up here */
+ state->detect = !state->detect;
+
state->bvd1 = 1; /* Might be cs3reg & PCMCIA_BVD1 */
state->bvd2 = 1; /* Might be cs3reg & PCMCIA_BVD2 */
- state->wrprot=0; /* Not available on Simpad. */
if ((cs3reg & (PCMCIA_VS1|PCMCIA_VS2)) ==
(PCMCIA_VS1|PCMCIA_VS2)) {
@@ -99,14 +95,8 @@ simpad_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
return 0;
}
-static void simpad_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
static void simpad_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
simpad_set_cs3_bit(PCMCIA_RESET);
}
@@ -116,7 +106,6 @@ static struct pcmcia_low_level simpad_pcmcia_ops = {
.hw_shutdown = simpad_pcmcia_hw_shutdown,
.socket_state = simpad_pcmcia_socket_state,
.configure_socket = simpad_pcmcia_configure_socket,
- .socket_init = simpad_pcmcia_socket_init,
.socket_suspend = simpad_pcmcia_socket_suspend,
};
diff --git a/drivers/pcmcia/sa1100_badge4.c b/drivers/pcmcia/sa1111_badge4.c
index 1ce53f493bef..4d206f4dd67b 100644
--- a/drivers/pcmcia/sa1100_badge4.c
+++ b/drivers/pcmcia/sa1111_badge4.c
@@ -122,13 +122,12 @@ badge4_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_state
local_irq_restore(flags);
}
- return 0;
+ return ret;
}
static struct pcmcia_low_level badge4_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = badge4_pcmcia_configure_socket,
- .socket_init = sa1111_pcmcia_socket_init,
.first = 0,
.nr = 2,
};
diff --git a/drivers/pcmcia/sa1111_generic.c b/drivers/pcmcia/sa1111_generic.c
index 27f2fe3b7fb4..70f728ce1856 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)
@@ -29,27 +63,10 @@
#define IDX_IRQ_S1_CD_VALID (4)
#define IDX_IRQ_S1_BVD1_STSCHG (5)
-static struct pcmcia_irqs irqs[] = {
- { 0, NO_IRQ, "SA1111 PCMCIA card detect" },
- { 0, NO_IRQ, "SA1111 PCMCIA BVD1" },
- { 1, NO_IRQ, "SA1111 CF card detect" },
- { 1, NO_IRQ, "SA1111 CF BVD1" },
-};
-
-static int sa1111_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
-{
- return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void sa1111_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *skt, struct pcmcia_state *state)
{
struct sa1111_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:
@@ -105,35 +122,22 @@ 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;
}
-void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
-static void sa1111_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt)
-{
- soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs));
-}
-
int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
int (*add)(struct soc_pcmcia_socket *))
{
struct sa1111_pcmcia_socket *s;
int i, ret = 0;
- ops->hw_init = sa1111_pcmcia_hw_init;
- ops->hw_shutdown = sa1111_pcmcia_hw_shutdown;
ops->socket_state = sa1111_pcmcia_socket_state;
- ops->socket_suspend = sa1111_pcmcia_socket_suspend;
for (i = 0; i < ops->nr; i++) {
s = kzalloc(sizeof(*s), GFP_KERNEL);
@@ -141,13 +145,21 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
return -ENOMEM;
s->soc.nr = ops->first + i;
- s->soc.ops = ops;
- s->soc.socket.owner = ops->owner;
- s->soc.socket.dev.parent = &dev->dev;
- s->soc.socket.pci_irq = s->soc.nr ?
- dev->irq[IDX_IRQ_S0_READY_NINT] :
- dev->irq[IDX_IRQ_S1_READY_NINT];
+ soc_pcmcia_init_one(&s->soc, ops, &dev->dev);
s->dev = dev;
+ if (s->soc.nr) {
+ s->soc.socket.pci_irq = dev->irq[IDX_IRQ_S1_READY_NINT];
+ s->soc.stat[SOC_STAT_CD].irq = dev->irq[IDX_IRQ_S1_CD_VALID];
+ s->soc.stat[SOC_STAT_CD].name = "SA1111 CF card detect";
+ s->soc.stat[SOC_STAT_BVD1].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG];
+ s->soc.stat[SOC_STAT_BVD1].name = "SA1111 CF BVD1";
+ } else {
+ s->soc.socket.pci_irq = dev->irq[IDX_IRQ_S0_READY_NINT];
+ s->soc.stat[SOC_STAT_CD].irq = dev->irq[IDX_IRQ_S0_CD_VALID];
+ s->soc.stat[SOC_STAT_CD].name = "SA1111 PCMCIA card detect";
+ s->soc.stat[SOC_STAT_BVD1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG];
+ s->soc.stat[SOC_STAT_BVD1].name = "SA1111 PCMCIA BVD1";
+ }
ret = add(&s->soc);
if (ret == 0) {
@@ -163,26 +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;
- /* Initialize PCMCIA IRQs */
- irqs[0].irq = dev->irq[IDX_IRQ_S0_CD_VALID];
- irqs[1].irq = dev->irq[IDX_IRQ_S0_BVD1_STSCHG];
- irqs[2].irq = dev->irq[IDX_IRQ_S1_CD_VALID];
- irqs[3].irq = dev->irq[IDX_IRQ_S1_BVD1_STSCHG];
-
/*
* 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);
@@ -212,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_generic.h b/drivers/pcmcia/sa1111_generic.h
index 02dc8577cdaf..f6376e34a7e4 100644
--- a/drivers/pcmcia/sa1111_generic.h
+++ b/drivers/pcmcia/sa1111_generic.h
@@ -17,7 +17,6 @@ int sa1111_pcmcia_add(struct sa1111_dev *dev, struct pcmcia_low_level *ops,
extern void sa1111_pcmcia_socket_state(struct soc_pcmcia_socket *, struct pcmcia_state *);
extern int sa1111_pcmcia_configure_socket(struct soc_pcmcia_socket *, const socket_state_t *);
-extern void sa1111_pcmcia_socket_init(struct soc_pcmcia_socket *);
extern int pcmcia_badge4_init(struct device *);
extern int pcmcia_jornada720_init(struct device *);
diff --git a/drivers/pcmcia/sa1100_jornada720.c b/drivers/pcmcia/sa1111_jornada720.c
index 6bcabee6bde4..69428d1f5ae1 100644
--- a/drivers/pcmcia/sa1100_jornada720.c
+++ b/drivers/pcmcia/sa1111_jornada720.c
@@ -78,13 +78,8 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s
}
ret = sa1111_pcmcia_configure_socket(skt, state);
- if (ret == 0) {
- unsigned long flags;
-
- local_irq_save(flags);
+ if (ret == 0)
sa1111_set_io(s->dev, pa_dwr_mask, pa_dwr_set);
- local_irq_restore(flags);
- }
return ret;
}
@@ -92,7 +87,6 @@ jornada720_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, const socket_s
static struct pcmcia_low_level jornada720_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = jornada720_pcmcia_configure_socket,
- .socket_init = sa1111_pcmcia_socket_init,
.first = 0,
.nr = 2,
};
diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/sa1111_lubbock.c
index c21888eebb58..c5caf5790451 100644
--- a/drivers/pcmcia/pxa2xx_lubbock.c
+++ b/drivers/pcmcia/sa1111_lubbock.c
@@ -202,7 +202,6 @@ lubbock_pcmcia_configure_socket(struct soc_pcmcia_socket *skt,
static struct pcmcia_low_level lubbock_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = lubbock_pcmcia_configure_socket,
- .socket_init = sa1111_pcmcia_socket_init,
.first = 0,
.nr = 2,
};
diff --git a/drivers/pcmcia/sa1100_neponset.c b/drivers/pcmcia/sa1111_neponset.c
index c95639b5f2a0..1d78739c4c07 100644
--- a/drivers/pcmcia/sa1100_neponset.c
+++ b/drivers/pcmcia/sa1111_neponset.c
@@ -94,30 +94,16 @@ 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);
}
- return 0;
-}
-
-static void neponset_pcmcia_socket_init(struct soc_pcmcia_socket *skt)
-{
- if (skt->nr == 0)
- NCR_0 &= ~(NCR_A0VPP | NCR_A1VPP);
-
- sa1111_pcmcia_socket_init(skt);
+ return ret;
}
static struct pcmcia_low_level neponset_pcmcia_ops = {
.owner = THIS_MODULE,
.configure_socket = neponset_pcmcia_configure_socket,
- .socket_init = neponset_pcmcia_socket_init,
.first = 0,
.nr = 2,
};
diff --git a/drivers/pcmcia/sa11xx_base.c b/drivers/pcmcia/sa11xx_base.c
index 0c62fe31a40e..6eecd7cddf57 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"
@@ -236,10 +235,7 @@ int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
skt = &sinfo->skt[i];
skt->nr = first + i;
- skt->ops = ops;
- skt->socket.owner = ops->owner;
- skt->socket.dev.parent = dev;
- skt->socket.pci_irq = NO_IRQ;
+ soc_pcmcia_init_one(skt, ops, dev);
ret = sa11xx_drv_pcmcia_add_one(skt);
if (ret)
diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c
index a0a9c2aa8d78..a2bc6ee1702e 100644
--- a/drivers/pcmcia/soc_common.c
+++ b/drivers/pcmcia/soc_common.c
@@ -32,6 +32,7 @@
#include <linux/cpufreq.h>
+#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/io.h>
@@ -45,10 +46,11 @@
#include <linux/timer.h>
#include <mach/hardware.h>
-#include <asm/system.h>
#include "soc_common.h"
+static irqreturn_t soc_common_pcmcia_interrupt(int irq, void *dev);
+
#ifdef CONFIG_PCMCIA_DEBUG
static int pc_debug;
@@ -104,6 +106,93 @@ void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *skt,
}
EXPORT_SYMBOL(soc_common_pcmcia_get_timing);
+static void __soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt,
+ unsigned int nr)
+{
+ unsigned int i;
+
+ for (i = 0; i < nr; i++) {
+ if (skt->stat[i].irq)
+ free_irq(skt->stat[i].irq, skt);
+ if (gpio_is_valid(skt->stat[i].gpio))
+ gpio_free(skt->stat[i].gpio);
+ }
+
+ if (skt->ops->hw_shutdown)
+ skt->ops->hw_shutdown(skt);
+}
+
+static void soc_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt)
+{
+ __soc_pcmcia_hw_shutdown(skt, ARRAY_SIZE(skt->stat));
+}
+
+static int soc_pcmcia_hw_init(struct soc_pcmcia_socket *skt)
+{
+ int ret = 0, i;
+
+ if (skt->ops->hw_init) {
+ ret = skt->ops->hw_init(skt);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(skt->stat); i++) {
+ if (gpio_is_valid(skt->stat[i].gpio)) {
+ int irq;
+
+ ret = gpio_request_one(skt->stat[i].gpio, GPIOF_IN,
+ skt->stat[i].name);
+ if (ret) {
+ __soc_pcmcia_hw_shutdown(skt, i);
+ return ret;
+ }
+
+ irq = gpio_to_irq(skt->stat[i].gpio);
+
+ if (i == SOC_STAT_RDY)
+ skt->socket.pci_irq = irq;
+ else
+ skt->stat[i].irq = irq;
+ }
+
+ if (skt->stat[i].irq) {
+ ret = request_irq(skt->stat[i].irq,
+ soc_common_pcmcia_interrupt,
+ IRQF_TRIGGER_NONE,
+ skt->stat[i].name, skt);
+ if (ret) {
+ if (gpio_is_valid(skt->stat[i].gpio))
+ gpio_free(skt->stat[i].gpio);
+ __soc_pcmcia_hw_shutdown(skt, i);
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void soc_pcmcia_hw_enable(struct soc_pcmcia_socket *skt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(skt->stat); i++)
+ if (skt->stat[i].irq) {
+ irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_RISING);
+ irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_EDGE_BOTH);
+ }
+}
+
+static void soc_pcmcia_hw_disable(struct soc_pcmcia_socket *skt)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(skt->stat); i++)
+ if (skt->stat[i].irq)
+ irq_set_irq_type(skt->stat[i].irq, IRQ_TYPE_NONE);
+}
+
static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
{
struct pcmcia_state state;
@@ -111,6 +200,22 @@ static unsigned int soc_common_pcmcia_skt_state(struct soc_pcmcia_socket *skt)
memset(&state, 0, sizeof(struct pcmcia_state));
+ /* Make battery voltage state report 'good' */
+ state.bvd1 = 1;
+ state.bvd2 = 1;
+
+ /* CD is active low by default */
+ if (gpio_is_valid(skt->stat[SOC_STAT_CD].gpio))
+ state.detect = !gpio_get_value(skt->stat[SOC_STAT_CD].gpio);
+
+ /* RDY and BVD are active high by default */
+ if (gpio_is_valid(skt->stat[SOC_STAT_RDY].gpio))
+ state.ready = !!gpio_get_value(skt->stat[SOC_STAT_RDY].gpio);
+ if (gpio_is_valid(skt->stat[SOC_STAT_BVD1].gpio))
+ state.bvd1 = !!gpio_get_value(skt->stat[SOC_STAT_BVD1].gpio);
+ if (gpio_is_valid(skt->stat[SOC_STAT_BVD2].gpio))
+ state.bvd2 = !!gpio_get_value(skt->stat[SOC_STAT_BVD2].gpio);
+
skt->ops->socket_state(skt, &state);
stat = state.detect ? SS_DETECT : 0;
@@ -188,6 +293,7 @@ static int soc_common_pcmcia_sock_init(struct pcmcia_socket *sock)
debug(skt, 2, "initializing socket\n");
if (skt->ops->socket_init)
skt->ops->socket_init(skt);
+ soc_pcmcia_hw_enable(skt);
return 0;
}
@@ -207,6 +313,7 @@ static int soc_common_pcmcia_suspend(struct pcmcia_socket *sock)
debug(skt, 2, "suspending socket\n");
+ soc_pcmcia_hw_disable(skt);
if (skt->ops->socket_suspend)
skt->ops->socket_suspend(skt);
@@ -526,69 +633,6 @@ static struct pccard_operations soc_common_pcmcia_operations = {
};
-int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt,
- struct pcmcia_irqs *irqs, int nr)
-{
- int i, res = 0;
-
- for (i = 0; i < nr; i++) {
- if (irqs[i].sock != skt->nr)
- continue;
- res = request_irq(irqs[i].irq, soc_common_pcmcia_interrupt,
- IRQF_DISABLED, irqs[i].str, skt);
- if (res)
- break;
- irq_set_irq_type(irqs[i].irq, IRQ_TYPE_NONE);
- }
-
- if (res) {
- printk(KERN_ERR "PCMCIA: request for IRQ%d failed (%d)\n",
- irqs[i].irq, res);
-
- while (i--)
- if (irqs[i].sock == skt->nr)
- free_irq(irqs[i].irq, skt);
- }
- return res;
-}
-EXPORT_SYMBOL(soc_pcmcia_request_irqs);
-
-void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt,
- struct pcmcia_irqs *irqs, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- if (irqs[i].sock == skt->nr)
- free_irq(irqs[i].irq, skt);
-}
-EXPORT_SYMBOL(soc_pcmcia_free_irqs);
-
-void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt,
- struct pcmcia_irqs *irqs, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- if (irqs[i].sock == skt->nr)
- irq_set_irq_type(irqs[i].irq, IRQ_TYPE_NONE);
-}
-EXPORT_SYMBOL(soc_pcmcia_disable_irqs);
-
-void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt,
- struct pcmcia_irqs *irqs, int nr)
-{
- int i;
-
- for (i = 0; i < nr; i++)
- if (irqs[i].sock == skt->nr) {
- irq_set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_RISING);
- irq_set_irq_type(irqs[i].irq, IRQ_TYPE_EDGE_BOTH);
- }
-}
-EXPORT_SYMBOL(soc_pcmcia_enable_irqs);
-
-
static LIST_HEAD(soc_pcmcia_sockets);
static DEFINE_MUTEX(soc_pcmcia_sockets_lock);
@@ -635,6 +679,21 @@ module_exit(soc_pcmcia_cpufreq_unregister);
#endif
+void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt,
+ struct pcmcia_low_level *ops, struct device *dev)
+{
+ int i;
+
+ skt->ops = ops;
+ skt->socket.owner = ops->owner;
+ skt->socket.dev.parent = dev;
+ skt->socket.pci_irq = NO_IRQ;
+
+ for (i = 0; i < ARRAY_SIZE(skt->stat); i++)
+ skt->stat[i].gpio = -EINVAL;
+}
+EXPORT_SYMBOL(soc_pcmcia_init_one);
+
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
{
mutex_lock(&soc_pcmcia_sockets_lock);
@@ -642,8 +701,9 @@ void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt)
pcmcia_unregister_socket(&skt->socket);
- skt->ops->hw_shutdown(skt);
+ soc_pcmcia_hw_shutdown(skt);
+ /* should not be required; violates some lowlevel drivers */
soc_common_pcmcia_config_skt(skt, &dead_socket);
list_del(&skt->node);
@@ -700,7 +760,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
*/
skt->ops->set_timing(skt);
- ret = skt->ops->hw_init(skt);
+ ret = soc_pcmcia_hw_init(skt);
if (ret)
goto out_err_6;
@@ -733,7 +793,7 @@ int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt)
pcmcia_unregister_socket(&skt->socket);
out_err_7:
- skt->ops->hw_shutdown(skt);
+ soc_pcmcia_hw_shutdown(skt);
out_err_6:
list_del(&skt->node);
mutex_unlock(&soc_pcmcia_sockets_lock);
diff --git a/drivers/pcmcia/soc_common.h b/drivers/pcmcia/soc_common.h
index 9daa73615c8b..e6fcbea5b682 100644
--- a/drivers/pcmcia/soc_common.h
+++ b/drivers/pcmcia/soc_common.h
@@ -50,6 +50,16 @@ struct soc_pcmcia_socket {
struct resource res_attr;
void __iomem *virt_io;
+ struct {
+ int gpio;
+ unsigned int irq;
+ const char *name;
+ } stat[4];
+#define SOC_STAT_CD 0 /* Card detect */
+#define SOC_STAT_BVD1 1 /* BATDEAD / IOSTSCHG */
+#define SOC_STAT_BVD2 2 /* BATWARN / IOSPKR */
+#define SOC_STAT_RDY 3 /* Ready / Interrupt */
+
unsigned int irq_state;
struct timer_list poll_timer;
@@ -115,25 +125,16 @@ struct pcmcia_low_level {
};
-struct pcmcia_irqs {
- int sock;
- int irq;
- const char *str;
-};
-
struct soc_pcmcia_timing {
unsigned short io;
unsigned short mem;
unsigned short attr;
};
-extern int soc_pcmcia_request_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
-extern void soc_pcmcia_free_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
-extern void soc_pcmcia_disable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
-extern void soc_pcmcia_enable_irqs(struct soc_pcmcia_socket *skt, struct pcmcia_irqs *irqs, int nr);
extern void soc_common_pcmcia_get_timing(struct soc_pcmcia_socket *, struct soc_pcmcia_timing *);
-
+void soc_pcmcia_init_one(struct soc_pcmcia_socket *skt,
+ struct pcmcia_low_level *ops, struct device *dev);
void soc_pcmcia_remove_one(struct soc_pcmcia_socket *skt);
int soc_pcmcia_add_one(struct soc_pcmcia_socket *skt);
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index 71aeed93037c..d6881514d38e 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 310160bffe38..cbe15fc37411 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/xxs1500_ss.c b/drivers/pcmcia/xxs1500_ss.c
index 379f4218857d..8f6698074f8e 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
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index afaf88558125..abfb96408779 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -17,21 +17,63 @@ config PINMUX
config PINCONF
bool "Support pin configuration controllers"
+config GENERIC_PINCONF
+ bool
+ select PINCONF
+
config DEBUG_PINCTRL
bool "Debug PINCTRL calls"
depends on DEBUG_KERNEL
help
Say Y here to add some extra checks and diagnostics to PINCTRL calls.
+config PINCTRL_PXA3xx
+ bool
+ select PINMUX
+
+config PINCTRL_MMP2
+ bool "MMP2 pin controller driver"
+ depends on ARCH_MMP
+ select PINCTRL_PXA3xx
+ select PINCONF
+
+config PINCTRL_PXA168
+ bool "PXA168 pin controller driver"
+ depends on ARCH_MMP
+ select PINCTRL_PXA3xx
+ select PINCONF
+
+config PINCTRL_PXA910
+ bool "PXA910 pin controller driver"
+ depends on ARCH_MMP
+ select PINCTRL_PXA3xx
+ select PINCONF
+
config PINCTRL_SIRF
bool "CSR SiRFprimaII pin controller driver"
depends on ARCH_PRIMA2
select PINMUX
+config PINCTRL_TEGRA
+ bool
+
+config PINCTRL_TEGRA20
+ bool
+ select PINMUX
+ select PINCONF
+ select PINCTRL_TEGRA
+
+config PINCTRL_TEGRA30
+ bool
+ select PINMUX
+ select PINCONF
+ select PINCTRL_TEGRA
+
config PINCTRL_U300
bool "U300 pin controller driver"
depends on ARCH_U300
select PINMUX
+ select GENERIC_PINCONF
config PINCTRL_COH901
bool "ST-Ericsson U300 COH 901 335/571 GPIO"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 827601cc68f6..6d4150b4eced 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -5,6 +5,14 @@ ccflags-$(CONFIG_DEBUG_PINCTRL) += -DDEBUG
obj-$(CONFIG_PINCTRL) += core.o
obj-$(CONFIG_PINMUX) += pinmux.o
obj-$(CONFIG_PINCONF) += pinconf.o
+obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o
+obj-$(CONFIG_PINCTRL_PXA3xx) += pinctrl-pxa3xx.o
+obj-$(CONFIG_PINCTRL_MMP2) += pinctrl-mmp2.o
+obj-$(CONFIG_PINCTRL_PXA168) += pinctrl-pxa168.o
+obj-$(CONFIG_PINCTRL_PXA910) += pinctrl-pxa910.o
obj-$(CONFIG_PINCTRL_SIRF) += pinctrl-sirf.o
+obj-$(CONFIG_PINCTRL_TEGRA) += pinctrl-tegra.o
+obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
+obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o
obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 894cd5e103da..ec3b8cc188af 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -1,12 +1,14 @@
/*
* Core driver for the pin control subsystem
*
- * Copyright (C) 2011 ST-Ericsson SA
+ * Copyright (C) 2011-2012 ST-Ericsson SA
* Written on behalf of Linaro for ST-Ericsson
* Based on bits of regulator core, gpio core and clk core
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) "pinctrl core: " fmt
@@ -16,11 +18,8 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
-#include <linux/radix-tree.h>
#include <linux/err.h>
#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
#include <linux/sysfs.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -30,10 +29,36 @@
#include "pinmux.h"
#include "pinconf.h"
-/* Global list of pin control devices */
-static DEFINE_MUTEX(pinctrldev_list_mutex);
+/**
+ * struct pinctrl_maps - a list item containing part of the mapping table
+ * @node: mapping table list node
+ * @maps: array of mapping table entries
+ * @num_maps: the number of entries in @maps
+ */
+struct pinctrl_maps {
+ struct list_head node;
+ struct pinctrl_map const *maps;
+ unsigned num_maps;
+};
+
+/* Mutex taken by all entry points */
+DEFINE_MUTEX(pinctrl_mutex);
+
+/* Global list of pin control devices (struct pinctrl_dev) */
static LIST_HEAD(pinctrldev_list);
+/* List of pin controller handles (struct pinctrl) */
+static LIST_HEAD(pinctrl_list);
+
+/* List of pinctrl maps (struct pinctrl_maps) */
+static LIST_HEAD(pinctrl_maps);
+
+#define for_each_maps(_maps_node_, _i_, _map_) \
+ list_for_each_entry(_maps_node_, &pinctrl_maps, node) \
+ for (_i_ = 0, _map_ = &_maps_node_->maps[_i_]; \
+ _i_ < _maps_node_->num_maps; \
+ i++, _map_ = &_maps_node_->maps[_i_])
+
const char *pinctrl_dev_get_name(struct pinctrl_dev *pctldev)
{
/* We're not allowed to register devices without name */
@@ -48,53 +73,31 @@ void *pinctrl_dev_get_drvdata(struct pinctrl_dev *pctldev)
EXPORT_SYMBOL_GPL(pinctrl_dev_get_drvdata);
/**
- * get_pinctrl_dev_from_dev() - look up pin controller device
- * @dev: a device pointer, this may be NULL but then devname needs to be
- * defined instead
- * @devname: the name of a device instance, as returned by dev_name(), this
- * may be NULL but then dev needs to be defined instead
+ * get_pinctrl_dev_from_devname() - look up pin controller device
+ * @devname: the name of a device instance, as returned by dev_name()
*
* Looks up a pin control device matching a certain device name or pure device
* pointer, the pure device pointer will take precedence.
*/
-struct pinctrl_dev *get_pinctrl_dev_from_dev(struct device *dev,
- const char *devname)
+struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname)
{
struct pinctrl_dev *pctldev = NULL;
bool found = false;
- mutex_lock(&pinctrldev_list_mutex);
- list_for_each_entry(pctldev, &pinctrldev_list, node) {
- if (dev && pctldev->dev == dev) {
- /* Matched on device pointer */
- found = true;
- break;
- }
+ if (!devname)
+ return NULL;
- if (devname &&
- !strcmp(dev_name(pctldev->dev), devname)) {
+ list_for_each_entry(pctldev, &pinctrldev_list, node) {
+ if (!strcmp(dev_name(pctldev->dev), devname)) {
/* Matched on device name */
found = true;
break;
}
}
- mutex_unlock(&pinctrldev_list_mutex);
return found ? pctldev : NULL;
}
-struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, unsigned int pin)
-{
- struct pin_desc *pindesc;
- unsigned long flags;
-
- spin_lock_irqsave(&pctldev->pin_desc_tree_lock, flags);
- pindesc = radix_tree_lookup(&pctldev->pin_desc_tree, pin);
- spin_unlock_irqrestore(&pctldev->pin_desc_tree_lock, flags);
-
- return pindesc;
-}
-
/**
* pin_get_from_name() - look up a pin number from a name
* @pctldev: the pin control device to lookup the pin on
@@ -135,11 +138,11 @@ bool pin_is_valid(struct pinctrl_dev *pctldev, int pin)
if (pin < 0)
return false;
+ mutex_lock(&pinctrl_mutex);
pindesc = pin_desc_get(pctldev, pin);
- if (pindesc == NULL)
- return false;
+ mutex_unlock(&pinctrl_mutex);
- return true;
+ return pindesc != NULL;
}
EXPORT_SYMBOL_GPL(pin_is_valid);
@@ -150,7 +153,6 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev,
{
int i;
- spin_lock(&pctldev->pin_desc_tree_lock);
for (i = 0; i < num_pins; i++) {
struct pin_desc *pindesc;
@@ -164,7 +166,6 @@ static void pinctrl_free_pindescs(struct pinctrl_dev *pctldev,
}
kfree(pindesc);
}
- spin_unlock(&pctldev->pin_desc_tree_lock);
}
static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,
@@ -180,10 +181,10 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,
}
pindesc = kzalloc(sizeof(*pindesc), GFP_KERNEL);
- if (pindesc == NULL)
+ if (pindesc == NULL) {
+ dev_err(pctldev->dev, "failed to alloc struct pin_desc\n");
return -ENOMEM;
-
- spin_lock_init(&pindesc->lock);
+ }
/* Set owner */
pindesc->pctldev = pctldev;
@@ -198,9 +199,7 @@ static int pinctrl_register_one_pin(struct pinctrl_dev *pctldev,
pindesc->dynamic_name = true;
}
- spin_lock(&pctldev->pin_desc_tree_lock);
radix_tree_insert(&pctldev->pin_desc_tree, number, pindesc);
- spin_unlock(&pctldev->pin_desc_tree_lock);
pr_debug("registered pin %d (%s) on %s\n",
number, pindesc->name, pctldev->desc->name);
return 0;
@@ -237,16 +236,13 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio)
struct pinctrl_gpio_range *range = NULL;
/* Loop over the ranges */
- mutex_lock(&pctldev->gpio_ranges_lock);
list_for_each_entry(range, &pctldev->gpio_ranges, node) {
/* Check if we're in the valid range */
if (gpio >= range->base &&
gpio < range->base + range->npins) {
- mutex_unlock(&pctldev->gpio_ranges_lock);
return range;
}
}
- mutex_unlock(&pctldev->gpio_ranges_lock);
return NULL;
}
@@ -261,14 +257,13 @@ pinctrl_match_gpio_range(struct pinctrl_dev *pctldev, unsigned gpio)
* the GPIO subsystem, return the device and the matching GPIO range. Returns
* negative if the GPIO range could not be found in any device.
*/
-int pinctrl_get_device_gpio_range(unsigned gpio,
- struct pinctrl_dev **outdev,
- struct pinctrl_gpio_range **outrange)
+static int pinctrl_get_device_gpio_range(unsigned gpio,
+ struct pinctrl_dev **outdev,
+ struct pinctrl_gpio_range **outrange)
{
struct pinctrl_dev *pctldev = NULL;
/* Loop over the pin controllers */
- mutex_lock(&pinctrldev_list_mutex);
list_for_each_entry(pctldev, &pinctrldev_list, node) {
struct pinctrl_gpio_range *range;
@@ -276,11 +271,9 @@ int pinctrl_get_device_gpio_range(unsigned gpio,
if (range != NULL) {
*outdev = pctldev;
*outrange = range;
- mutex_unlock(&pinctrldev_list_mutex);
return 0;
}
}
- mutex_unlock(&pinctrldev_list_mutex);
return -EINVAL;
}
@@ -296,10 +289,11 @@ int pinctrl_get_device_gpio_range(unsigned gpio,
void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range)
{
- mutex_lock(&pctldev->gpio_ranges_lock);
- list_add(&range->node, &pctldev->gpio_ranges);
- mutex_unlock(&pctldev->gpio_ranges_lock);
+ mutex_lock(&pinctrl_mutex);
+ list_add_tail(&range->node, &pctldev->gpio_ranges);
+ mutex_unlock(&pinctrl_mutex);
}
+EXPORT_SYMBOL_GPL(pinctrl_add_gpio_range);
/**
* pinctrl_remove_gpio_range() - remove a range of GPIOs fro a pin controller
@@ -309,10 +303,11 @@ void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev,
void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range)
{
- mutex_lock(&pctldev->gpio_ranges_lock);
+ mutex_lock(&pinctrl_mutex);
list_del(&range->node);
- mutex_unlock(&pctldev->gpio_ranges_lock);
+ mutex_unlock(&pinctrl_mutex);
}
+EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range);
/**
* pinctrl_get_group_selector() - returns the group selector for a group
@@ -345,6 +340,531 @@ int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
return -EINVAL;
}
+/**
+ * pinctrl_request_gpio() - request a single pin to be used in as GPIO
+ * @gpio: the GPIO pin number from the GPIO subsystem number space
+ *
+ * This function should *ONLY* be used from gpiolib-based GPIO drivers,
+ * as part of their gpio_request() semantics, platforms and individual drivers
+ * shall *NOT* request GPIO pins to be muxed in.
+ */
+int pinctrl_request_gpio(unsigned gpio)
+{
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range *range;
+ int ret;
+ int pin;
+
+ mutex_lock(&pinctrl_mutex);
+
+ ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
+ if (ret) {
+ mutex_unlock(&pinctrl_mutex);
+ return -EINVAL;
+ }
+
+ /* Convert to the pin controllers number space */
+ pin = gpio - range->base + range->pin_base;
+
+ ret = pinmux_request_gpio(pctldev, range, pin, gpio);
+
+ mutex_unlock(&pinctrl_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pinctrl_request_gpio);
+
+/**
+ * pinctrl_free_gpio() - free control on a single pin, currently used as GPIO
+ * @gpio: the GPIO pin number from the GPIO subsystem number space
+ *
+ * This function should *ONLY* be used from gpiolib-based GPIO drivers,
+ * as part of their gpio_free() semantics, platforms and individual drivers
+ * shall *NOT* request GPIO pins to be muxed out.
+ */
+void pinctrl_free_gpio(unsigned gpio)
+{
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range *range;
+ int ret;
+ int pin;
+
+ mutex_lock(&pinctrl_mutex);
+
+ ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
+ if (ret) {
+ mutex_unlock(&pinctrl_mutex);
+ return;
+ }
+
+ /* Convert to the pin controllers number space */
+ pin = gpio - range->base + range->pin_base;
+
+ pinmux_free_gpio(pctldev, pin, range);
+
+ mutex_unlock(&pinctrl_mutex);
+}
+EXPORT_SYMBOL_GPL(pinctrl_free_gpio);
+
+static int pinctrl_gpio_direction(unsigned gpio, bool input)
+{
+ struct pinctrl_dev *pctldev;
+ struct pinctrl_gpio_range *range;
+ int ret;
+ int pin;
+
+ ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
+ if (ret)
+ return ret;
+
+ /* Convert to the pin controllers number space */
+ pin = gpio - range->base + range->pin_base;
+
+ return pinmux_gpio_direction(pctldev, range, pin, input);
+}
+
+/**
+ * pinctrl_gpio_direction_input() - request a GPIO pin to go into input mode
+ * @gpio: the GPIO pin number from the GPIO subsystem number space
+ *
+ * This function should *ONLY* be used from gpiolib-based GPIO drivers,
+ * as part of their gpio_direction_input() semantics, platforms and individual
+ * drivers shall *NOT* touch pin control GPIO calls.
+ */
+int pinctrl_gpio_direction_input(unsigned gpio)
+{
+ int ret;
+ mutex_lock(&pinctrl_mutex);
+ ret = pinctrl_gpio_direction(gpio, true);
+ mutex_unlock(&pinctrl_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_input);
+
+/**
+ * pinctrl_gpio_direction_output() - request a GPIO pin to go into output mode
+ * @gpio: the GPIO pin number from the GPIO subsystem number space
+ *
+ * This function should *ONLY* be used from gpiolib-based GPIO drivers,
+ * as part of their gpio_direction_output() semantics, platforms and individual
+ * drivers shall *NOT* touch pin control GPIO calls.
+ */
+int pinctrl_gpio_direction_output(unsigned gpio)
+{
+ int ret;
+ mutex_lock(&pinctrl_mutex);
+ ret = pinctrl_gpio_direction(gpio, false);
+ mutex_unlock(&pinctrl_mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pinctrl_gpio_direction_output);
+
+static struct pinctrl_state *find_state(struct pinctrl *p,
+ const char *name)
+{
+ struct pinctrl_state *state;
+
+ list_for_each_entry(state, &p->states, node)
+ if (!strcmp(state->name, name))
+ return state;
+
+ return NULL;
+}
+
+static struct pinctrl_state *create_state(struct pinctrl *p,
+ const char *name)
+{
+ struct pinctrl_state *state;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (state == NULL) {
+ dev_err(p->dev,
+ "failed to alloc struct pinctrl_state\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ state->name = name;
+ INIT_LIST_HEAD(&state->settings);
+
+ list_add_tail(&state->node, &p->states);
+
+ return state;
+}
+
+static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
+{
+ struct pinctrl_state *state;
+ struct pinctrl_setting *setting;
+ int ret;
+
+ state = find_state(p, map->name);
+ if (!state)
+ state = create_state(p, map->name);
+ if (IS_ERR(state))
+ return PTR_ERR(state);
+
+ if (map->type == PIN_MAP_TYPE_DUMMY_STATE)
+ return 0;
+
+ setting = kzalloc(sizeof(*setting), GFP_KERNEL);
+ if (setting == NULL) {
+ dev_err(p->dev,
+ "failed to alloc struct pinctrl_setting\n");
+ return -ENOMEM;
+ }
+
+ setting->type = map->type;
+
+ setting->pctldev = get_pinctrl_dev_from_devname(map->ctrl_dev_name);
+ if (setting->pctldev == NULL) {
+ dev_err(p->dev, "unknown pinctrl device %s in map entry",
+ map->ctrl_dev_name);
+ kfree(setting);
+ /* Eventually, this should trigger deferred probe */
+ return -ENODEV;
+ }
+
+ switch (map->type) {
+ case PIN_MAP_TYPE_MUX_GROUP:
+ ret = pinmux_map_to_setting(map, setting);
+ break;
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ ret = pinconf_map_to_setting(map, setting);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret < 0) {
+ kfree(setting);
+ return ret;
+ }
+
+ list_add_tail(&setting->node, &state->settings);
+
+ return 0;
+}
+
+static struct pinctrl *find_pinctrl(struct device *dev)
+{
+ struct pinctrl *p;
+
+ list_for_each_entry(p, &pinctrl_list, node)
+ if (p->dev == dev)
+ return p;
+
+ return NULL;
+}
+
+static void pinctrl_put_locked(struct pinctrl *p, bool inlist);
+
+static struct pinctrl *create_pinctrl(struct device *dev)
+{
+ struct pinctrl *p;
+ const char *devname;
+ struct pinctrl_maps *maps_node;
+ int i;
+ struct pinctrl_map const *map;
+ int ret;
+
+ /*
+ * create the state cookie holder struct pinctrl for each
+ * mapping, this is what consumers will get when requesting
+ * a pin control handle with pinctrl_get()
+ */
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (p == NULL) {
+ dev_err(dev, "failed to alloc struct pinctrl\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ p->dev = dev;
+ INIT_LIST_HEAD(&p->states);
+
+ devname = dev_name(dev);
+
+ /* Iterate over the pin control maps to locate the right ones */
+ for_each_maps(maps_node, i, map) {
+ /* Map must be for this device */
+ if (strcmp(map->dev_name, devname))
+ continue;
+
+ ret = add_setting(p, map);
+ if (ret < 0) {
+ pinctrl_put_locked(p, false);
+ return ERR_PTR(ret);
+ }
+ }
+
+ /* Add the pinmux to the global list */
+ list_add_tail(&p->node, &pinctrl_list);
+
+ return p;
+}
+
+static struct pinctrl *pinctrl_get_locked(struct device *dev)
+{
+ struct pinctrl *p;
+
+ if (WARN_ON(!dev))
+ return ERR_PTR(-EINVAL);
+
+ p = find_pinctrl(dev);
+ if (p != NULL)
+ return ERR_PTR(-EBUSY);
+
+ p = create_pinctrl(dev);
+ if (IS_ERR(p))
+ return p;
+
+ return p;
+}
+
+/**
+ * pinctrl_get() - retrieves the pinctrl handle for a device
+ * @dev: the device to obtain the handle for
+ */
+struct pinctrl *pinctrl_get(struct device *dev)
+{
+ struct pinctrl *p;
+
+ mutex_lock(&pinctrl_mutex);
+ p = pinctrl_get_locked(dev);
+ mutex_unlock(&pinctrl_mutex);
+
+ return p;
+}
+EXPORT_SYMBOL_GPL(pinctrl_get);
+
+static void pinctrl_put_locked(struct pinctrl *p, bool inlist)
+{
+ struct pinctrl_state *state, *n1;
+ struct pinctrl_setting *setting, *n2;
+
+ list_for_each_entry_safe(state, n1, &p->states, node) {
+ list_for_each_entry_safe(setting, n2, &state->settings, node) {
+ switch (setting->type) {
+ case PIN_MAP_TYPE_MUX_GROUP:
+ if (state == p->state)
+ pinmux_disable_setting(setting);
+ pinmux_free_setting(setting);
+ break;
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ pinconf_free_setting(setting);
+ break;
+ default:
+ break;
+ }
+ list_del(&setting->node);
+ kfree(setting);
+ }
+ list_del(&state->node);
+ kfree(state);
+ }
+
+ if (inlist)
+ list_del(&p->node);
+ kfree(p);
+}
+
+/**
+ * pinctrl_put() - release a previously claimed pinctrl handle
+ * @p: the pinctrl handle to release
+ */
+void pinctrl_put(struct pinctrl *p)
+{
+ mutex_lock(&pinctrl_mutex);
+ pinctrl_put_locked(p, true);
+ mutex_unlock(&pinctrl_mutex);
+}
+EXPORT_SYMBOL_GPL(pinctrl_put);
+
+static struct pinctrl_state *pinctrl_lookup_state_locked(struct pinctrl *p,
+ const char *name)
+{
+ struct pinctrl_state *state;
+
+ state = find_state(p, name);
+ if (!state)
+ return ERR_PTR(-ENODEV);
+
+ return state;
+}
+
+/**
+ * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
+ * @p: the pinctrl handle to retrieve the state from
+ * @name: the state name to retrieve
+ */
+struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name)
+{
+ struct pinctrl_state *s;
+
+ mutex_lock(&pinctrl_mutex);
+ s = pinctrl_lookup_state_locked(p, name);
+ mutex_unlock(&pinctrl_mutex);
+
+ return s;
+}
+EXPORT_SYMBOL_GPL(pinctrl_lookup_state);
+
+static int pinctrl_select_state_locked(struct pinctrl *p,
+ struct pinctrl_state *state)
+{
+ struct pinctrl_setting *setting, *setting2;
+ int ret;
+
+ if (p->state == state)
+ return 0;
+
+ if (p->state) {
+ /*
+ * The set of groups with a mux configuration in the old state
+ * may not be identical to the set of groups with a mux setting
+ * in the new state. While this might be unusual, it's entirely
+ * possible for the "user"-supplied mapping table to be written
+ * that way. For each group that was configured in the old state
+ * but not in the new state, this code puts that group into a
+ * safe/disabled state.
+ */
+ list_for_each_entry(setting, &p->state->settings, node) {
+ bool found = false;
+ if (setting->type != PIN_MAP_TYPE_MUX_GROUP)
+ continue;
+ list_for_each_entry(setting2, &state->settings, node) {
+ if (setting2->type != PIN_MAP_TYPE_MUX_GROUP)
+ continue;
+ if (setting2->data.mux.group ==
+ setting->data.mux.group) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ pinmux_disable_setting(setting);
+ }
+ }
+
+ p->state = state;
+
+ /* Apply all the settings for the new state */
+ list_for_each_entry(setting, &state->settings, node) {
+ switch (setting->type) {
+ case PIN_MAP_TYPE_MUX_GROUP:
+ ret = pinmux_enable_setting(setting);
+ break;
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ ret = pinconf_apply_setting(setting);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret < 0) {
+ /* FIXME: Difficult to return to prev state */
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * pinctrl_select() - select/activate/program a pinctrl state to HW
+ * @p: the pinctrl handle for the device that requests configuratio
+ * @state: the state handle to select/activate/program
+ */
+int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *state)
+{
+ int ret;
+
+ mutex_lock(&pinctrl_mutex);
+ ret = pinctrl_select_state_locked(p, state);
+ mutex_unlock(&pinctrl_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pinctrl_select_state);
+
+/**
+ * pinctrl_register_mappings() - register a set of pin controller mappings
+ * @maps: the pincontrol mappings table to register. This should probably be
+ * marked with __initdata so it can be discarded after boot. This
+ * function will perform a shallow copy for the mapping entries.
+ * @num_maps: the number of maps in the mapping table
+ */
+int pinctrl_register_mappings(struct pinctrl_map const *maps,
+ unsigned num_maps)
+{
+ int i, ret;
+ struct pinctrl_maps *maps_node;
+
+ pr_debug("add %d pinmux maps\n", num_maps);
+
+ /* First sanity check the new mapping */
+ for (i = 0; i < num_maps; i++) {
+ if (!maps[i].dev_name) {
+ pr_err("failed to register map %s (%d): no device given\n",
+ maps[i].name, i);
+ return -EINVAL;
+ }
+
+ if (!maps[i].name) {
+ pr_err("failed to register map %d: no map name given\n",
+ i);
+ return -EINVAL;
+ }
+
+ if (maps[i].type != PIN_MAP_TYPE_DUMMY_STATE &&
+ !maps[i].ctrl_dev_name) {
+ pr_err("failed to register map %s (%d): no pin control device given\n",
+ maps[i].name, i);
+ return -EINVAL;
+ }
+
+ switch (maps[i].type) {
+ case PIN_MAP_TYPE_DUMMY_STATE:
+ break;
+ case PIN_MAP_TYPE_MUX_GROUP:
+ ret = pinmux_validate_map(&maps[i], i);
+ if (ret < 0)
+ return 0;
+ break;
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ ret = pinconf_validate_map(&maps[i], i);
+ if (ret < 0)
+ return 0;
+ break;
+ default:
+ pr_err("failed to register map %s (%d): invalid type given\n",
+ maps[i].name, i);
+ return -EINVAL;
+ }
+ }
+
+ maps_node = kzalloc(sizeof(*maps_node), GFP_KERNEL);
+ if (!maps_node) {
+ pr_err("failed to alloc struct pinctrl_maps\n");
+ return -ENOMEM;
+ }
+
+ maps_node->num_maps = num_maps;
+ maps_node->maps = kmemdup(maps, sizeof(*maps) * num_maps, GFP_KERNEL);
+ if (!maps_node->maps) {
+ pr_err("failed to duplicate mapping table\n");
+ kfree(maps_node);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&pinctrl_mutex);
+ list_add_tail(&maps_node->node, &pinctrl_maps);
+ mutex_unlock(&pinctrl_mutex);
+
+ return 0;
+}
+
#ifdef CONFIG_DEBUG_FS
static int pinctrl_pins_show(struct seq_file *s, void *what)
@@ -355,6 +875,8 @@ static int pinctrl_pins_show(struct seq_file *s, void *what)
seq_printf(s, "registered pins: %d\n", pctldev->desc->npins);
+ mutex_lock(&pinctrl_mutex);
+
/* The pin number can be retrived from the pin controller descriptor */
for (i = 0; i < pctldev->desc->npins; i++) {
struct pin_desc *desc;
@@ -375,6 +897,8 @@ static int pinctrl_pins_show(struct seq_file *s, void *what)
seq_puts(s, "\n");
}
+ mutex_unlock(&pinctrl_mutex);
+
return 0;
}
@@ -388,6 +912,8 @@ static int pinctrl_groups_show(struct seq_file *s, void *what)
if (!ops)
return 0;
+ mutex_lock(&pinctrl_mutex);
+
seq_puts(s, "registered pin groups:\n");
while (ops->list_groups(pctldev, selector) >= 0) {
const unsigned *pins;
@@ -410,6 +936,7 @@ static int pinctrl_groups_show(struct seq_file *s, void *what)
selector++;
}
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
@@ -421,8 +948,9 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
seq_puts(s, "GPIO ranges handled:\n");
+ mutex_lock(&pinctrl_mutex);
+
/* Loop over the ranges */
- mutex_lock(&pctldev->gpio_ranges_lock);
list_for_each_entry(range, &pctldev->gpio_ranges, node) {
seq_printf(s, "%u: %s GPIOS [%u - %u] PINS [%u - %u]\n",
range->id, range->name,
@@ -430,7 +958,8 @@ static int pinctrl_gpioranges_show(struct seq_file *s, void *what)
range->pin_base,
(range->pin_base + range->npins - 1));
}
- mutex_unlock(&pctldev->gpio_ranges_lock);
+
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
@@ -440,7 +969,9 @@ static int pinctrl_devices_show(struct seq_file *s, void *what)
struct pinctrl_dev *pctldev;
seq_puts(s, "name [pinmux] [pinconf]\n");
- mutex_lock(&pinctrldev_list_mutex);
+
+ mutex_lock(&pinctrl_mutex);
+
list_for_each_entry(pctldev, &pinctrldev_list, node) {
seq_printf(s, "%s ", pctldev->desc->name);
if (pctldev->desc->pmxops)
@@ -453,7 +984,108 @@ static int pinctrl_devices_show(struct seq_file *s, void *what)
seq_puts(s, "no");
seq_puts(s, "\n");
}
- mutex_unlock(&pinctrldev_list_mutex);
+
+ mutex_unlock(&pinctrl_mutex);
+
+ return 0;
+}
+
+static inline const char *map_type(enum pinctrl_map_type type)
+{
+ static const char * const names[] = {
+ "INVALID",
+ "DUMMY_STATE",
+ "MUX_GROUP",
+ "CONFIGS_PIN",
+ "CONFIGS_GROUP",
+ };
+
+ if (type >= ARRAY_SIZE(names))
+ return "UNKNOWN";
+
+ return names[type];
+}
+
+static int pinctrl_maps_show(struct seq_file *s, void *what)
+{
+ struct pinctrl_maps *maps_node;
+ int i;
+ struct pinctrl_map const *map;
+
+ seq_puts(s, "Pinctrl maps:\n");
+
+ mutex_lock(&pinctrl_mutex);
+
+ for_each_maps(maps_node, i, map) {
+ seq_printf(s, "device %s\nstate %s\ntype %s (%d)\n",
+ map->dev_name, map->name, map_type(map->type),
+ map->type);
+
+ if (map->type != PIN_MAP_TYPE_DUMMY_STATE)
+ seq_printf(s, "controlling device %s\n",
+ map->ctrl_dev_name);
+
+ switch (map->type) {
+ case PIN_MAP_TYPE_MUX_GROUP:
+ pinmux_show_map(s, map);
+ break;
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ pinconf_show_map(s, map);
+ break;
+ default:
+ break;
+ }
+
+ seq_printf(s, "\n");
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
+ return 0;
+}
+
+static int pinctrl_show(struct seq_file *s, void *what)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *state;
+ struct pinctrl_setting *setting;
+
+ seq_puts(s, "Requested pin control handlers their pinmux maps:\n");
+
+ mutex_lock(&pinctrl_mutex);
+
+ list_for_each_entry(p, &pinctrl_list, node) {
+ seq_printf(s, "device: %s current state: %s\n",
+ dev_name(p->dev),
+ p->state ? p->state->name : "none");
+
+ list_for_each_entry(state, &p->states, node) {
+ seq_printf(s, " state: %s\n", state->name);
+
+ list_for_each_entry(setting, &state->settings, node) {
+ struct pinctrl_dev *pctldev = setting->pctldev;
+
+ seq_printf(s, " type: %s controller %s ",
+ map_type(setting->type),
+ pinctrl_dev_get_name(pctldev));
+
+ switch (setting->type) {
+ case PIN_MAP_TYPE_MUX_GROUP:
+ pinmux_show_setting(s, setting);
+ break;
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ pinconf_show_setting(s, setting);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
@@ -478,6 +1110,16 @@ static int pinctrl_devices_open(struct inode *inode, struct file *file)
return single_open(file, pinctrl_devices_show, NULL);
}
+static int pinctrl_maps_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinctrl_maps_show, NULL);
+}
+
+static int pinctrl_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, pinctrl_show, NULL);
+}
+
static const struct file_operations pinctrl_pins_ops = {
.open = pinctrl_pins_open,
.read = seq_read,
@@ -506,6 +1148,20 @@ static const struct file_operations pinctrl_devices_ops = {
.release = single_release,
};
+static const struct file_operations pinctrl_maps_ops = {
+ .open = pinctrl_maps_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations pinctrl_ops = {
+ .open = pinctrl_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
static struct dentry *debugfs_root;
static void pinctrl_init_device_debugfs(struct pinctrl_dev *pctldev)
@@ -547,7 +1203,10 @@ static void pinctrl_init_debugfs(void)
debugfs_create_file("pinctrl-devices", S_IFREG | S_IRUGO,
debugfs_root, NULL, &pinctrl_devices_ops);
- pinmux_init_debugfs(debugfs_root);
+ debugfs_create_file("pinctrl-maps", S_IFREG | S_IRUGO,
+ debugfs_root, NULL, &pinctrl_maps_ops);
+ debugfs_create_file("pinctrl-handles", S_IFREG | S_IRUGO,
+ debugfs_root, NULL, &pinctrl_ops);
}
#else /* CONFIG_DEBUG_FS */
@@ -583,18 +1242,18 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
if (pctldesc->name == NULL)
return NULL;
- pctldev = kzalloc(sizeof(struct pinctrl_dev), GFP_KERNEL);
- if (pctldev == NULL)
+ pctldev = kzalloc(sizeof(*pctldev), GFP_KERNEL);
+ if (pctldev == NULL) {
+ dev_err(dev, "failed to alloc struct pinctrl_dev\n");
return NULL;
+ }
/* Initialize pin control device struct */
pctldev->owner = pctldesc->owner;
pctldev->desc = pctldesc;
pctldev->driver_data = driver_data;
INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
- spin_lock_init(&pctldev->pin_desc_tree_lock);
INIT_LIST_HEAD(&pctldev->gpio_ranges);
- mutex_init(&pctldev->gpio_ranges_lock);
pctldev->dev = dev;
/* If we're implementing pinmuxing, check the ops for sanity */
@@ -628,11 +1287,23 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
goto out_err;
}
+ mutex_lock(&pinctrl_mutex);
+
+ list_add_tail(&pctldev->node, &pinctrldev_list);
+
+ pctldev->p = pinctrl_get_locked(pctldev->dev);
+ if (!IS_ERR(pctldev->p)) {
+ struct pinctrl_state *s =
+ pinctrl_lookup_state_locked(pctldev->p,
+ PINCTRL_STATE_DEFAULT);
+ if (!IS_ERR(s))
+ pinctrl_select_state_locked(pctldev->p, s);
+ }
+
+ mutex_unlock(&pinctrl_mutex);
+
pinctrl_init_device_debugfs(pctldev);
- mutex_lock(&pinctrldev_list_mutex);
- list_add(&pctldev->node, &pinctrldev_list);
- mutex_unlock(&pinctrldev_list_mutex);
- pinmux_hog_maps(pctldev);
+
return pctldev;
out_err:
@@ -653,15 +1324,20 @@ void pinctrl_unregister(struct pinctrl_dev *pctldev)
return;
pinctrl_remove_device_debugfs(pctldev);
- pinmux_unhog_maps(pctldev);
+
+ mutex_lock(&pinctrl_mutex);
+
+ if (!IS_ERR(pctldev->p))
+ pinctrl_put_locked(pctldev->p, true);
+
/* TODO: check that no pinmuxes are still active? */
- mutex_lock(&pinctrldev_list_mutex);
list_del(&pctldev->node);
- mutex_unlock(&pinctrldev_list_mutex);
/* Destroy descriptor tree */
pinctrl_free_pindescs(pctldev, pctldev->desc->pins,
pctldev->desc->npins);
kfree(pctldev);
+
+ mutex_unlock(&pinctrl_mutex);
}
EXPORT_SYMBOL_GPL(pinctrl_unregister);
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index cfa86da6b4b1..17ecf651b123 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -9,7 +9,10 @@
* License terms: GNU General Public License (GPL) version 2
*/
+#include <linux/mutex.h>
+#include <linux/radix-tree.h>
#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/machine.h>
struct pinctrl_gpio_range;
@@ -20,34 +23,94 @@ struct pinctrl_gpio_range;
* controller
* @pin_desc_tree: each pin descriptor for this pin controller is stored in
* this radix tree
- * @pin_desc_tree_lock: lock for the descriptor tree
* @gpio_ranges: a list of GPIO ranges that is handled by this pin controller,
* ranges are added to this list at runtime
- * @gpio_ranges_lock: lock for the GPIO ranges list
* @dev: the device entry for this pin controller
* @owner: module providing the pin controller, used for refcounting
* @driver_data: driver data for drivers registering to the pin controller
* subsystem
- * @pinmux_hogs_lock: lock for the pinmux hog list
- * @pinmux_hogs: list of pinmux maps hogged by this device
+ * @p: result of pinctrl_get() for this device
+ * @device_root: debugfs root for this device
*/
struct pinctrl_dev {
struct list_head node;
struct pinctrl_desc *desc;
struct radix_tree_root pin_desc_tree;
- spinlock_t pin_desc_tree_lock;
struct list_head gpio_ranges;
- struct mutex gpio_ranges_lock;
struct device *dev;
struct module *owner;
void *driver_data;
+ struct pinctrl *p;
#ifdef CONFIG_DEBUG_FS
struct dentry *device_root;
#endif
-#ifdef CONFIG_PINMUX
- struct mutex pinmux_hogs_lock;
- struct list_head pinmux_hogs;
-#endif
+};
+
+/**
+ * struct pinctrl - per-device pin control state holder
+ * @node: global list node
+ * @dev: the device using this pin control handle
+ * @states: a list of states for this device
+ * @state: the current state
+ */
+struct pinctrl {
+ struct list_head node;
+ struct device *dev;
+ struct list_head states;
+ struct pinctrl_state *state;
+};
+
+/**
+ * struct pinctrl_state - a pinctrl state for a device
+ * @node: list not for struct pinctrl's @states field
+ * @name: the name of this state
+ * @settings: a list of settings for this state
+ */
+struct pinctrl_state {
+ struct list_head node;
+ const char *name;
+ struct list_head settings;
+};
+
+/**
+ * struct pinctrl_setting_mux - setting data for MAP_TYPE_MUX_GROUP
+ * @group: the group selector to program
+ * @func: the function selector to program
+ */
+struct pinctrl_setting_mux {
+ unsigned group;
+ unsigned func;
+};
+
+/**
+ * struct pinctrl_setting_configs - setting data for MAP_TYPE_CONFIGS_*
+ * @group_or_pin: the group selector or pin ID to program
+ * @configs: a pointer to an array of config parameters/values to program into
+ * hardware. Each individual pin controller defines the format and meaning
+ * of config parameters.
+ * @num_configs: the number of entries in array @configs
+ */
+struct pinctrl_setting_configs {
+ unsigned group_or_pin;
+ unsigned long *configs;
+ unsigned num_configs;
+};
+
+/**
+ * struct pinctrl_setting - an individual mux or config setting
+ * @node: list node for struct pinctrl_settings's @settings field
+ * @type: the type of setting
+ * @pctldev: pin control device handling to be programmed
+ * @data: Data specific to the setting type
+ */
+struct pinctrl_setting {
+ struct list_head node;
+ enum pinctrl_map_type type;
+ struct pinctrl_dev *pctldev;
+ union {
+ struct pinctrl_setting_mux mux;
+ struct pinctrl_setting_configs configs;
+ } data;
};
/**
@@ -56,28 +119,38 @@ struct pinctrl_dev {
* @name: a name for the pin, e.g. the name of the pin/pad/finger on a
* datasheet or such
* @dynamic_name: if the name of this pin was dynamically allocated
- * @lock: a lock to protect the descriptor structure
- * @mux_requested: whether the pin is already requested by pinmux or not
- * @mux_function: a named muxing function for the pin that will be passed to
- * subdrivers and shown in debugfs etc
+ * @mux_usecount: If zero, the pin is not claimed, and @owner should be NULL.
+ * If non-zero, this pin is claimed by @owner. This field is an integer
+ * rather than a boolean, since pinctrl_get() might process multiple
+ * mapping table entries that refer to, and hence claim, the same group
+ * or pin, and each of these will increment the @usecount.
+ * @mux_owner: The name of device that called pinctrl_get().
+ * @mux_setting: The most recent selected mux setting for this pin, if any.
+ * @gpio_owner: If pinctrl_request_gpio() was called for this pin, this is
+ * the name of the GPIO that "owns" this pin.
*/
struct pin_desc {
struct pinctrl_dev *pctldev;
const char *name;
bool dynamic_name;
- spinlock_t lock;
/* These fields only added when supporting pinmux drivers */
#ifdef CONFIG_PINMUX
- const char *mux_function;
+ unsigned mux_usecount;
+ const char *mux_owner;
+ const struct pinctrl_setting_mux *mux_setting;
+ const char *gpio_owner;
#endif
};
-struct pinctrl_dev *get_pinctrl_dev_from_dev(struct device *dev,
- const char *dev_name);
-struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev, unsigned int pin);
+struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name);
-int pinctrl_get_device_gpio_range(unsigned gpio,
- struct pinctrl_dev **outdev,
- struct pinctrl_gpio_range **outrange);
int pinctrl_get_group_selector(struct pinctrl_dev *pctldev,
const char *pin_group);
+
+static inline struct pin_desc *pin_desc_get(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ return radix_tree_lookup(&pctldev->pin_desc_tree, pin);
+}
+
+extern struct mutex pinctrl_mutex;
diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c
new file mode 100644
index 000000000000..33fbaeaa65dd
--- /dev/null
+++ b/drivers/pinctrl/pinconf-generic.c
@@ -0,0 +1,120 @@
+/*
+ * Core driver for the generic pin config portions of the pin control subsystem
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ * Written on behalf of Linaro for ST-Ericsson
+ *
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#define pr_fmt(fmt) "generic pinconfig core: " fmt
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include "core.h"
+#include "pinconf.h"
+
+#ifdef CONFIG_DEBUG_FS
+
+struct pin_config_item {
+ const enum pin_config_param param;
+ const char * const display;
+ const char * const format;
+};
+
+#define PCONFDUMP(a, b, c) { .param = a, .display = b, .format = c }
+
+struct pin_config_item conf_items[] = {
+ PCONFDUMP(PIN_CONFIG_BIAS_DISABLE, "input bias disabled", NULL),
+ PCONFDUMP(PIN_CONFIG_BIAS_HIGH_IMPEDANCE, "input bias high impedance", NULL),
+ PCONFDUMP(PIN_CONFIG_BIAS_PULL_UP, "input bias pull up", NULL),
+ PCONFDUMP(PIN_CONFIG_BIAS_PULL_DOWN, "input bias pull down", NULL),
+ PCONFDUMP(PIN_CONFIG_DRIVE_PUSH_PULL, "output drive push pull", NULL),
+ PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_DRAIN, "output drive open drain", NULL),
+ PCONFDUMP(PIN_CONFIG_DRIVE_OPEN_SOURCE, "output drive open source", NULL),
+ PCONFDUMP(PIN_CONFIG_INPUT_SCHMITT, "input schmitt trigger", NULL),
+ PCONFDUMP(PIN_CONFIG_INPUT_DEBOUNCE, "input debounce", "time units"),
+ PCONFDUMP(PIN_CONFIG_POWER_SOURCE, "pin power source", "selector"),
+ PCONFDUMP(PIN_CONFIG_LOW_POWER_MODE, "pin low power", "mode"),
+};
+
+void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin)
+{
+ const struct pinconf_ops *ops = pctldev->desc->confops;
+ int i;
+
+ if (!ops->is_generic)
+ return;
+
+ for(i = 0; i < ARRAY_SIZE(conf_items); i++) {
+ unsigned long config;
+ int ret;
+
+ /* We want to check out this parameter */
+ config = pinconf_to_config_packed(conf_items[i].param, 0);
+ ret = pin_config_get_for_pin(pctldev, pin, &config);
+ /* These are legal errors */
+ if (ret == -EINVAL || ret == -ENOTSUPP)
+ continue;
+ if (ret) {
+ seq_printf(s, "ERROR READING CONFIG SETTING %d ", i);
+ continue;
+ }
+ /* Space between multiple configs */
+ seq_puts(s, " ");
+ seq_puts(s, conf_items[i].display);
+ /* Print unit if available */
+ if (conf_items[i].format &&
+ pinconf_to_config_argument(config) != 0)
+ seq_printf(s, " (%u %s)",
+ pinconf_to_config_argument(config),
+ conf_items[i].format);
+ }
+}
+
+void pinconf_generic_dump_group(struct pinctrl_dev *pctldev,
+ struct seq_file *s, const char *gname)
+{
+ const struct pinconf_ops *ops = pctldev->desc->confops;
+ int i;
+
+ if (!ops->is_generic)
+ return;
+
+ for(i = 0; i < ARRAY_SIZE(conf_items); i++) {
+ unsigned long config;
+ int ret;
+
+ /* We want to check out this parameter */
+ config = pinconf_to_config_packed(conf_items[i].param, 0);
+ ret = pin_config_group_get(dev_name(pctldev->dev), gname,
+ &config);
+ /* These are legal errors */
+ if (ret == -EINVAL || ret == -ENOTSUPP)
+ continue;
+ if (ret) {
+ seq_printf(s, "ERROR READING CONFIG SETTING %d ", i);
+ continue;
+ }
+ /* Space between multiple configs */
+ seq_puts(s, " ");
+ seq_puts(s, conf_items[i].display);
+ /* Print unit if available */
+ if (conf_items[i].format && config != 0)
+ seq_printf(s, " (%u %s)",
+ pinconf_to_config_argument(config),
+ conf_items[i].format);
+ }
+}
+
+#endif
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index 9fb75456824c..7321e8601294 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -23,6 +23,37 @@
#include "core.h"
#include "pinconf.h"
+int pinconf_check_ops(struct pinctrl_dev *pctldev)
+{
+ const struct pinconf_ops *ops = pctldev->desc->confops;
+
+ /* We must be able to read out pin status */
+ if (!ops->pin_config_get && !ops->pin_config_group_get)
+ return -EINVAL;
+ /* We have to be able to config the pins in SOME way */
+ if (!ops->pin_config_set && !ops->pin_config_group_set)
+ return -EINVAL;
+ return 0;
+}
+
+int pinconf_validate_map(struct pinctrl_map const *map, int i)
+{
+ if (!map->data.configs.group_or_pin) {
+ pr_err("failed to register map %s (%d): no group/pin given\n",
+ map->name, i);
+ return -EINVAL;
+ }
+
+ if (map->data.configs.num_configs &&
+ !map->data.configs.configs) {
+ pr_err("failed to register map %s (%d): no configs ptr given\n",
+ map->name, i);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *config)
{
@@ -51,19 +82,27 @@ int pin_config_get(const char *dev_name, const char *name,
struct pinctrl_dev *pctldev;
int pin;
- pctldev = get_pinctrl_dev_from_dev(NULL, dev_name);
- if (!pctldev)
- return -EINVAL;
+ mutex_lock(&pinctrl_mutex);
+
+ pctldev = get_pinctrl_dev_from_devname(dev_name);
+ if (!pctldev) {
+ pin = -EINVAL;
+ goto unlock;
+ }
pin = pin_get_from_name(pctldev, name);
if (pin < 0)
- return pin;
+ goto unlock;
+
+ pin = pin_config_get_for_pin(pctldev, pin, config);
- return pin_config_get_for_pin(pctldev, pin, config);
+unlock:
+ mutex_unlock(&pinctrl_mutex);
+ return pin;
}
EXPORT_SYMBOL(pin_config_get);
-int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
+static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long config)
{
const struct pinconf_ops *ops = pctldev->desc->confops;
@@ -97,17 +136,27 @@ int pin_config_set(const char *dev_name, const char *name,
unsigned long config)
{
struct pinctrl_dev *pctldev;
- int pin;
+ int pin, ret;
- pctldev = get_pinctrl_dev_from_dev(NULL, dev_name);
- if (!pctldev)
- return -EINVAL;
+ mutex_lock(&pinctrl_mutex);
+
+ pctldev = get_pinctrl_dev_from_devname(dev_name);
+ if (!pctldev) {
+ ret = -EINVAL;
+ goto unlock;
+ }
pin = pin_get_from_name(pctldev, name);
- if (pin < 0)
- return pin;
+ if (pin < 0) {
+ ret = pin;
+ goto unlock;
+ }
+
+ ret = pin_config_set_for_pin(pctldev, pin, config);
- return pin_config_set_for_pin(pctldev, pin, config);
+unlock:
+ mutex_unlock(&pinctrl_mutex);
+ return ret;
}
EXPORT_SYMBOL(pin_config_set);
@@ -116,29 +165,39 @@ int pin_config_group_get(const char *dev_name, const char *pin_group,
{
struct pinctrl_dev *pctldev;
const struct pinconf_ops *ops;
- int selector;
+ int selector, ret;
- pctldev = get_pinctrl_dev_from_dev(NULL, dev_name);
- if (!pctldev)
- return -EINVAL;
+ mutex_lock(&pinctrl_mutex);
+
+ pctldev = get_pinctrl_dev_from_devname(dev_name);
+ if (!pctldev) {
+ ret = -EINVAL;
+ goto unlock;
+ }
ops = pctldev->desc->confops;
if (!ops || !ops->pin_config_group_get) {
dev_err(pctldev->dev, "cannot get configuration for pin "
"group, missing group config get function in "
"driver\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto unlock;
}
selector = pinctrl_get_group_selector(pctldev, pin_group);
- if (selector < 0)
- return selector;
+ if (selector < 0) {
+ ret = selector;
+ goto unlock;
+ }
+
+ ret = ops->pin_config_group_get(pctldev, selector, config);
- return ops->pin_config_group_get(pctldev, selector, config);
+unlock:
+ mutex_unlock(&pinctrl_mutex);
+ return ret;
}
EXPORT_SYMBOL(pin_config_group_get);
-
int pin_config_group_set(const char *dev_name, const char *pin_group,
unsigned long config)
{
@@ -151,27 +210,34 @@ int pin_config_group_set(const char *dev_name, const char *pin_group,
int ret;
int i;
- pctldev = get_pinctrl_dev_from_dev(NULL, dev_name);
- if (!pctldev)
- return -EINVAL;
+ mutex_lock(&pinctrl_mutex);
+
+ pctldev = get_pinctrl_dev_from_devname(dev_name);
+ if (!pctldev) {
+ ret = -EINVAL;
+ goto unlock;
+ }
ops = pctldev->desc->confops;
pctlops = pctldev->desc->pctlops;
if (!ops || (!ops->pin_config_group_set && !ops->pin_config_set)) {
dev_err(pctldev->dev, "cannot configure pin group, missing "
"config function in driver\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto unlock;
}
selector = pinctrl_get_group_selector(pctldev, pin_group);
- if (selector < 0)
- return selector;
+ if (selector < 0) {
+ ret = selector;
+ goto unlock;
+ }
ret = pctlops->get_group_pins(pctldev, selector, &pins, &num_pins);
if (ret) {
dev_err(pctldev->dev, "cannot configure pin group, error "
"getting pins\n");
- return ret;
+ goto unlock;
}
/*
@@ -185,46 +251,196 @@ int pin_config_group_set(const char *dev_name, const char *pin_group,
* pin-by-pin as well, it returns -EAGAIN.
*/
if (ret != -EAGAIN)
- return ret;
+ goto unlock;
}
/*
* If the controller cannot handle entire groups, we configure each pin
* individually.
*/
- if (!ops->pin_config_set)
- return 0;
+ if (!ops->pin_config_set) {
+ ret = 0;
+ goto unlock;
+ }
for (i = 0; i < num_pins; i++) {
ret = ops->pin_config_set(pctldev, pins[i], config);
if (ret < 0)
- return ret;
+ goto unlock;
}
- return 0;
+ ret = 0;
+
+unlock:
+ mutex_unlock(&pinctrl_mutex);
+
+ return ret;
}
EXPORT_SYMBOL(pin_config_group_set);
-int pinconf_check_ops(struct pinctrl_dev *pctldev)
+int pinconf_map_to_setting(struct pinctrl_map const *map,
+ struct pinctrl_setting *setting)
{
+ struct pinctrl_dev *pctldev = setting->pctldev;
+ int pin;
+
+ switch (setting->type) {
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ pin = pin_get_from_name(pctldev,
+ map->data.configs.group_or_pin);
+ if (pin < 0) {
+ dev_err(pctldev->dev, "could not map pin config for \"%s\"",
+ map->data.configs.group_or_pin);
+ return pin;
+ }
+ setting->data.configs.group_or_pin = pin;
+ break;
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ pin = pinctrl_get_group_selector(pctldev,
+ map->data.configs.group_or_pin);
+ if (pin < 0) {
+ dev_err(pctldev->dev, "could not map group config for \"%s\"",
+ map->data.configs.group_or_pin);
+ return pin;
+ }
+ setting->data.configs.group_or_pin = pin;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ setting->data.configs.num_configs = map->data.configs.num_configs;
+ setting->data.configs.configs = map->data.configs.configs;
+
+ return 0;
+}
+
+void pinconf_free_setting(struct pinctrl_setting const *setting)
+{
+}
+
+int pinconf_apply_setting(struct pinctrl_setting const *setting)
+{
+ struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinconf_ops *ops = pctldev->desc->confops;
+ int i, ret;
- /* We must be able to read out pin status */
- if (!ops->pin_config_get && !ops->pin_config_group_get)
+ if (!ops) {
+ dev_err(pctldev->dev, "missing confops\n");
return -EINVAL;
- /* We have to be able to config the pins in SOME way */
- if (!ops->pin_config_set && !ops->pin_config_group_set)
+ }
+
+ switch (setting->type) {
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ if (!ops->pin_config_set) {
+ dev_err(pctldev->dev, "missing pin_config_set op\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < setting->data.configs.num_configs; i++) {
+ ret = ops->pin_config_set(pctldev,
+ setting->data.configs.group_or_pin,
+ setting->data.configs.configs[i]);
+ if (ret < 0) {
+ dev_err(pctldev->dev,
+ "pin_config_set op failed for pin %d config %08lx\n",
+ setting->data.configs.group_or_pin,
+ setting->data.configs.configs[i]);
+ return ret;
+ }
+ }
+ break;
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ if (!ops->pin_config_group_set) {
+ dev_err(pctldev->dev,
+ "missing pin_config_group_set op\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < setting->data.configs.num_configs; i++) {
+ ret = ops->pin_config_group_set(pctldev,
+ setting->data.configs.group_or_pin,
+ setting->data.configs.configs[i]);
+ if (ret < 0) {
+ dev_err(pctldev->dev,
+ "pin_config_group_set op failed for group %d config %08lx\n",
+ setting->data.configs.group_or_pin,
+ setting->data.configs.configs[i]);
+ return ret;
+ }
+ }
+ break;
+ default:
return -EINVAL;
+ }
+
return 0;
}
#ifdef CONFIG_DEBUG_FS
+void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map)
+{
+ int i;
+
+ switch (map->type) {
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ seq_printf(s, "pin ");
+ break;
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ seq_printf(s, "group ");
+ break;
+ default:
+ break;
+ }
+
+ seq_printf(s, "%s\n", map->data.configs.group_or_pin);
+
+ for (i = 0; i < map->data.configs.num_configs; i++)
+ seq_printf(s, "config %08lx\n", map->data.configs.configs[i]);
+}
+
+void pinconf_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting)
+{
+ struct pinctrl_dev *pctldev = setting->pctldev;
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ struct pin_desc *desc;
+ int i;
+
+ switch (setting->type) {
+ case PIN_MAP_TYPE_CONFIGS_PIN:
+ desc = pin_desc_get(setting->pctldev,
+ setting->data.configs.group_or_pin);
+ seq_printf(s, "pin %s (%d)",
+ desc->name ? desc->name : "unnamed",
+ setting->data.configs.group_or_pin);
+ break;
+ case PIN_MAP_TYPE_CONFIGS_GROUP:
+ seq_printf(s, "group %s (%d)",
+ pctlops->get_group_name(pctldev,
+ setting->data.configs.group_or_pin),
+ setting->data.configs.group_or_pin);
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * FIXME: We should really get the pin controler to dump the config
+ * values, so they can be decoded to something meaningful.
+ */
+ for (i = 0; i < setting->data.configs.num_configs; i++)
+ seq_printf(s, " %08lx", setting->data.configs.configs[i]);
+
+ seq_printf(s, "\n");
+}
+
static void pinconf_dump_pin(struct pinctrl_dev *pctldev,
struct seq_file *s, int pin)
{
const struct pinconf_ops *ops = pctldev->desc->confops;
+ /* no-op when not using generic pin config */
+ pinconf_generic_dump_pin(pctldev, s, pin);
if (ops && ops->pin_config_dbg_show)
ops->pin_config_dbg_show(pctldev, s, pin);
}
@@ -237,6 +453,8 @@ static int pinconf_pins_show(struct seq_file *s, void *what)
seq_puts(s, "Pin config settings per pin\n");
seq_puts(s, "Format: pin (name): pinmux setting array\n");
+ mutex_lock(&pinctrl_mutex);
+
/* The pin number can be retrived from the pin controller descriptor */
for (i = 0; i < pctldev->desc->npins; i++) {
struct pin_desc *desc;
@@ -255,6 +473,8 @@ static int pinconf_pins_show(struct seq_file *s, void *what)
seq_printf(s, "\n");
}
+ mutex_unlock(&pinctrl_mutex);
+
return 0;
}
@@ -264,6 +484,8 @@ static void pinconf_dump_group(struct pinctrl_dev *pctldev,
{
const struct pinconf_ops *ops = pctldev->desc->confops;
+ /* no-op when not using generic pin config */
+ pinconf_generic_dump_group(pctldev, s, gname);
if (ops && ops->pin_config_group_dbg_show)
ops->pin_config_group_dbg_show(pctldev, s, selector);
}
@@ -281,14 +503,20 @@ static int pinconf_groups_show(struct seq_file *s, void *what)
seq_puts(s, "Pin config settings per pin group\n");
seq_puts(s, "Format: group (name): pinmux setting array\n");
+ mutex_lock(&pinctrl_mutex);
+
while (pctlops->list_groups(pctldev, selector) >= 0) {
const char *gname = pctlops->get_group_name(pctldev, selector);
seq_printf(s, "%u (%s):", selector, gname);
pinconf_dump_group(pctldev, s, selector, gname);
+ seq_printf(s, "\n");
+
selector++;
}
+ mutex_unlock(&pinctrl_mutex);
+
return 0;
}
diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h
index 006b77fa737e..54510de5e8c6 100644
--- a/drivers/pinctrl/pinconf.h
+++ b/drivers/pinctrl/pinconf.h
@@ -14,12 +14,25 @@
#ifdef CONFIG_PINCONF
int pinconf_check_ops(struct pinctrl_dev *pctldev);
+int pinconf_validate_map(struct pinctrl_map const *map, int i);
+int pinconf_map_to_setting(struct pinctrl_map const *map,
+ struct pinctrl_setting *setting);
+void pinconf_free_setting(struct pinctrl_setting const *setting);
+int pinconf_apply_setting(struct pinctrl_setting const *setting);
+void pinconf_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinconf_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev);
+
+/*
+ * You will only be interested in these if you're using PINCONF
+ * so don't supply any stubs for these.
+ */
int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
unsigned long *config);
-int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
- unsigned long config);
+int pin_config_group_get(const char *dev_name, const char *pin_group,
+ unsigned long *config);
#else
@@ -28,9 +41,70 @@ static inline int pinconf_check_ops(struct pinctrl_dev *pctldev)
return 0;
}
+static inline int pinconf_validate_map(struct pinctrl_map const *map, int i)
+{
+ return 0;
+}
+
+static inline int pinconf_map_to_setting(struct pinctrl_map const *map,
+ struct pinctrl_setting *setting)
+{
+ return 0;
+}
+
+static inline void pinconf_free_setting(struct pinctrl_setting const *setting)
+{
+}
+
+static inline int pinconf_apply_setting(struct pinctrl_setting const *setting)
+{
+ return 0;
+}
+
+static inline void pinconf_show_map(struct seq_file *s,
+ struct pinctrl_map const *map)
+{
+}
+
+static inline void pinconf_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting)
+{
+}
+
static inline void pinconf_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
}
#endif
+
+/*
+ * The following functions are available if the driver uses the generic
+ * pin config.
+ */
+
+#ifdef CONFIG_GENERIC_PINCONF
+
+void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned pin);
+
+void pinconf_generic_dump_group(struct pinctrl_dev *pctldev,
+ struct seq_file *s, const char *gname);
+
+#else
+
+static inline void pinconf_generic_dump_pin(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned pin)
+{
+ return;
+}
+
+static inline void pinconf_generic_dump_group(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ const char *gname)
+{
+ return;
+}
+
+#endif
diff --git a/drivers/pinctrl/pinctrl-coh901.c b/drivers/pinctrl/pinctrl-coh901.c
index 69fb7072a23e..0797eba3e33a 100644
--- a/drivers/pinctrl/pinctrl-coh901.c
+++ b/drivers/pinctrl/pinctrl-coh901.c
@@ -22,38 +22,10 @@
#include <linux/gpio.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <mach/gpio-u300.h>
-
-/*
- * Bias modes for U300 GPIOs
- *
- * GPIO_U300_CONFIG_BIAS_UNKNOWN: this bias mode is not known to us
- * GPIO_U300_CONFIG_BIAS_FLOAT: no specific bias, the GPIO will float or state
- * is not controlled by software
- * GPIO_U300_CONFIG_BIAS_PULL_UP: the GPIO will be pulled up (usually with high
- * impedance to VDD)
- */
-#define GPIO_U300_CONFIG_BIAS_UNKNOWN 0x1000
-#define GPIO_U300_CONFIG_BIAS_FLOAT 0x1001
-#define GPIO_U300_CONFIG_BIAS_PULL_UP 0x1002
-
-/*
- * Drive modes for U300 GPIOs (output)
- *
- * GPIO_U300_CONFIG_DRIVE_PUSH_PULL: the GPIO will be driven actively high and
- * low, this is the most typical case and is typically achieved with two
- * active transistors on the output
- * GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN: the GPIO will be driven with open drain
- * (open collector) which means it is usually wired with other output
- * ports which are then pulled up with an external resistor
- * GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE: the GPIO will be driven with open drain
- * (open emitter) which is the same as open drain mutatis mutandis but
- * pulled to ground
- */
-#define GPIO_U300_CONFIG_DRIVE_PUSH_PULL 0x2000
-#define GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN 0x2001
-#define GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE 0x2002
+#include "pinctrl-coh901.h"
/*
* Register definitions for COH 901 335 variant
@@ -181,12 +153,12 @@ struct u300_gpio_confdata {
#define BS365_GPIO_NUM_PORTS 5
#define U300_FLOATING_INPUT { \
- .bias_mode = GPIO_U300_CONFIG_BIAS_FLOAT, \
+ .bias_mode = PIN_CONFIG_BIAS_HIGH_IMPEDANCE, \
.output = false, \
}
#define U300_PULL_UP_INPUT { \
- .bias_mode = GPIO_U300_CONFIG_BIAS_PULL_UP, \
+ .bias_mode = PIN_CONFIG_BIAS_PULL_UP, \
.output = false, \
}
@@ -360,14 +332,14 @@ static int u300_gpio_request(struct gpio_chip *chip, unsigned offset)
*/
int gpio = chip->base + offset;
- return pinmux_request_gpio(gpio);
+ return pinctrl_request_gpio(gpio);
}
static void u300_gpio_free(struct gpio_chip *chip, unsigned offset)
{
int gpio = chip->base + offset;
- pinmux_free_gpio(gpio);
+ pinctrl_free_gpio(gpio);
}
static int u300_gpio_get(struct gpio_chip *chip, unsigned offset)
@@ -448,8 +420,68 @@ static int u300_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
return retirq;
}
-static int u300_gpio_config(struct gpio_chip *chip, unsigned offset,
- u16 param, unsigned long *data)
+/* Returning -EINVAL means "supported but not available" */
+int u300_gpio_config_get(struct gpio_chip *chip,
+ unsigned offset,
+ unsigned long *config)
+{
+ struct u300_gpio *gpio = to_u300_gpio(chip);
+ enum pin_config_param param = (enum pin_config_param) *config;
+ bool biasmode;
+ u32 drmode;
+
+ /* One bit per pin, clamp to bool range */
+ biasmode = !!(readl(U300_PIN_REG(offset, per)) & U300_PIN_BIT(offset));
+
+ /* Mask out the two bits for this pin and shift to bits 0,1 */
+ drmode = readl(U300_PIN_REG(offset, pcr));
+ drmode &= (U300_GPIO_PXPCR_PIN_MODE_MASK << ((offset & 0x07) << 1));
+ drmode >>= ((offset & 0x07) << 1);
+
+ switch(param) {
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+ *config = 0;
+ if (biasmode)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_BIAS_PULL_UP:
+ *config = 0;
+ if (!biasmode)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
+ *config = 0;
+ if (drmode == U300_GPIO_PXPCR_PIN_MODE_OUTPUT_PUSH_PULL)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
+ *config = 0;
+ if (drmode == U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_DRAIN)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
+ *config = 0;
+ if (drmode == U300_GPIO_PXPCR_PIN_MODE_OUTPUT_OPEN_SOURCE)
+ return 0;
+ else
+ return -EINVAL;
+ break;
+ default:
+ break;
+ }
+ return -ENOTSUPP;
+}
+
+int u300_gpio_config_set(struct gpio_chip *chip, unsigned offset,
+ enum pin_config_param param)
{
struct u300_gpio *gpio = to_u300_gpio(chip);
unsigned long flags;
@@ -457,16 +489,16 @@ static int u300_gpio_config(struct gpio_chip *chip, unsigned offset,
local_irq_save(flags);
switch (param) {
- case GPIO_U300_CONFIG_BIAS_UNKNOWN:
- case GPIO_U300_CONFIG_BIAS_FLOAT:
+ case PIN_CONFIG_BIAS_DISABLE:
+ case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
val = readl(U300_PIN_REG(offset, per));
writel(val | U300_PIN_BIT(offset), U300_PIN_REG(offset, per));
break;
- case GPIO_U300_CONFIG_BIAS_PULL_UP:
+ case PIN_CONFIG_BIAS_PULL_UP:
val = readl(U300_PIN_REG(offset, per));
writel(val & ~U300_PIN_BIT(offset), U300_PIN_REG(offset, per));
break;
- case GPIO_U300_CONFIG_DRIVE_PUSH_PULL:
+ case PIN_CONFIG_DRIVE_PUSH_PULL:
val = readl(U300_PIN_REG(offset, pcr));
val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK
<< ((offset & 0x07) << 1));
@@ -474,7 +506,7 @@ static int u300_gpio_config(struct gpio_chip *chip, unsigned offset,
<< ((offset & 0x07) << 1));
writel(val, U300_PIN_REG(offset, pcr));
break;
- case GPIO_U300_CONFIG_DRIVE_OPEN_DRAIN:
+ case PIN_CONFIG_DRIVE_OPEN_DRAIN:
val = readl(U300_PIN_REG(offset, pcr));
val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK
<< ((offset & 0x07) << 1));
@@ -482,7 +514,7 @@ static int u300_gpio_config(struct gpio_chip *chip, unsigned offset,
<< ((offset & 0x07) << 1));
writel(val, U300_PIN_REG(offset, pcr));
break;
- case GPIO_U300_CONFIG_DRIVE_OPEN_SOURCE:
+ case PIN_CONFIG_DRIVE_OPEN_SOURCE:
val = readl(U300_PIN_REG(offset, pcr));
val &= ~(U300_GPIO_PXPCR_PIN_MODE_MASK
<< ((offset & 0x07) << 1));
@@ -650,13 +682,12 @@ static void __init u300_gpio_init_pin(struct u300_gpio *gpio,
u300_gpio_direction_output(&gpio->chip, offset, conf->outval);
/* Deactivate bias mode for output */
- u300_gpio_config(&gpio->chip, offset,
- GPIO_U300_CONFIG_BIAS_FLOAT,
- NULL);
+ u300_gpio_config_set(&gpio->chip, offset,
+ PIN_CONFIG_BIAS_HIGH_IMPEDANCE);
/* Set drive mode for output */
- u300_gpio_config(&gpio->chip, offset,
- GPIO_U300_CONFIG_DRIVE_PUSH_PULL, NULL);
+ u300_gpio_config_set(&gpio->chip, offset,
+ PIN_CONFIG_DRIVE_PUSH_PULL);
dev_dbg(gpio->dev, "set up pin %d as output, value: %d\n",
offset, conf->outval);
@@ -667,7 +698,7 @@ static void __init u300_gpio_init_pin(struct u300_gpio *gpio,
u300_gpio_set(&gpio->chip, offset, 0);
/* Set bias mode for input */
- u300_gpio_config(&gpio->chip, offset, conf->bias_mode, NULL);
+ u300_gpio_config_set(&gpio->chip, offset, conf->bias_mode);
dev_dbg(gpio->dev, "set up pin %d as input, bias: %04x\n",
offset, conf->bias_mode);
@@ -705,7 +736,6 @@ static inline void u300_gpio_free_ports(struct u300_gpio *gpio)
list_for_each_safe(p, n, &gpio->port_list) {
port = list_entry(p, struct u300_gpio_port, node);
list_del(&port->node);
- free_irq(port->irq, port);
kfree(port);
}
}
@@ -861,10 +891,18 @@ static int __init u300_gpio_probe(struct platform_device *pdev)
goto err_no_chip;
}
+ /* Spawn pin controller device as child of the GPIO, pass gpio chip */
+ plat->pinctrl_device->dev.platform_data = &gpio->chip;
+ err = platform_device_register(plat->pinctrl_device);
+ if (err)
+ goto err_no_pinctrl;
+
platform_set_drvdata(pdev, gpio);
return 0;
+err_no_pinctrl:
+ err = gpiochip_remove(&gpio->chip);
err_no_chip:
err_no_port:
u300_gpio_free_ports(gpio);
@@ -919,7 +957,6 @@ static struct platform_driver u300_gpio_driver = {
.remove = __exit_p(u300_gpio_remove),
};
-
static int __init u300_gpio_init(void)
{
return platform_driver_probe(&u300_gpio_driver, u300_gpio_probe);
diff --git a/drivers/pinctrl/pinctrl-coh901.h b/drivers/pinctrl/pinctrl-coh901.h
new file mode 100644
index 000000000000..87294222583e
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-coh901.h
@@ -0,0 +1,5 @@
+int u300_gpio_config_get(struct gpio_chip *chip,
+ unsigned offset,
+ unsigned long *config);
+int u300_gpio_config_set(struct gpio_chip *chip, unsigned offset,
+ enum pin_config_param param);
diff --git a/drivers/pinctrl/pinctrl-mmp2.c b/drivers/pinctrl/pinctrl-mmp2.c
new file mode 100644
index 000000000000..2cfed552bbe4
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mmp2.c
@@ -0,0 +1,722 @@
+/*
+ * linux/drivers/pinctrl/pinmux-mmp2.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2011, Marvell Technology Group Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include "pinctrl-pxa3xx.h"
+
+#define MMP2_DS_MASK 0x1800
+#define MMP2_DS_SHIFT 11
+#define MMP2_SLEEP_MASK 0x38
+#define MMP2_SLEEP_SELECT (1 << 9)
+#define MMP2_SLEEP_DATA (1 << 8)
+#define MMP2_SLEEP_DIR (1 << 7)
+
+#define MFPR_MMP2(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \
+ { \
+ .name = #a, \
+ .pin = a, \
+ .mfpr = r, \
+ .func = { \
+ MMP2_MUX_##f0, \
+ MMP2_MUX_##f1, \
+ MMP2_MUX_##f2, \
+ MMP2_MUX_##f3, \
+ MMP2_MUX_##f4, \
+ MMP2_MUX_##f5, \
+ MMP2_MUX_##f6, \
+ MMP2_MUX_##f7, \
+ }, \
+ }
+
+#define GRP_MMP2(a, m, p) \
+ { .name = a, .mux = MMP2_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), }
+
+/* 174 pins */
+enum mmp2_pin_list {
+ /* 0~168: GPIO0~GPIO168 */
+ TWSI4_SCL = 169,
+ TWSI4_SDA, /* 170 */
+ G_CLKREQ,
+ VCXO_REQ,
+ VCXO_OUT,
+};
+
+enum mmp2_mux {
+ /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */
+ MMP2_MUX_GPIO = 0,
+ MMP2_MUX_G_CLKREQ,
+ MMP2_MUX_VCXO_REQ,
+ MMP2_MUX_VCXO_OUT,
+ MMP2_MUX_KP_MK,
+ MMP2_MUX_KP_DK,
+ MMP2_MUX_CCIC1,
+ MMP2_MUX_CCIC2,
+ MMP2_MUX_SPI,
+ MMP2_MUX_SSPA2,
+ MMP2_MUX_ROT,
+ MMP2_MUX_I2S,
+ MMP2_MUX_TB,
+ MMP2_MUX_CAM2,
+ MMP2_MUX_HDMI,
+ MMP2_MUX_TWSI2,
+ MMP2_MUX_TWSI3,
+ MMP2_MUX_TWSI4,
+ MMP2_MUX_TWSI5,
+ MMP2_MUX_TWSI6,
+ MMP2_MUX_UART1,
+ MMP2_MUX_UART2,
+ MMP2_MUX_UART3,
+ MMP2_MUX_UART4,
+ MMP2_MUX_SSP1_RX,
+ MMP2_MUX_SSP1_FRM,
+ MMP2_MUX_SSP1_TXRX,
+ MMP2_MUX_SSP2_RX,
+ MMP2_MUX_SSP2_FRM,
+ MMP2_MUX_SSP1,
+ MMP2_MUX_SSP2,
+ MMP2_MUX_SSP3,
+ MMP2_MUX_SSP4,
+ MMP2_MUX_MMC1,
+ MMP2_MUX_MMC2,
+ MMP2_MUX_MMC3,
+ MMP2_MUX_MMC4,
+ MMP2_MUX_ULPI,
+ MMP2_MUX_AC,
+ MMP2_MUX_CA,
+ MMP2_MUX_PWM,
+ MMP2_MUX_USIM,
+ MMP2_MUX_TIPU,
+ MMP2_MUX_PLL,
+ MMP2_MUX_NAND,
+ MMP2_MUX_FSIC,
+ MMP2_MUX_SLEEP_IND,
+ MMP2_MUX_EXT_DMA,
+ MMP2_MUX_ONE_WIRE,
+ MMP2_MUX_LCD,
+ MMP2_MUX_SMC,
+ MMP2_MUX_SMC_INT,
+ MMP2_MUX_MSP,
+ MMP2_MUX_G_CLKOUT,
+ MMP2_MUX_32K_CLKOUT,
+ MMP2_MUX_PRI_JTAG,
+ MMP2_MUX_AAS_JTAG,
+ MMP2_MUX_AAS_GPIO,
+ MMP2_MUX_AAS_SPI,
+ MMP2_MUX_AAS_TWSI,
+ MMP2_MUX_AAS_DEU_EX,
+ MMP2_MUX_NONE = 0xffff,
+};
+
+static struct pinctrl_pin_desc mmp2_pads[] = {
+ /*
+ * The name indicates function 0 of this pin.
+ * After reset, function 0 is the default function of pin.
+ */
+ PINCTRL_PIN(GPIO0, "GPIO0"),
+ PINCTRL_PIN(GPIO1, "GPIO1"),
+ PINCTRL_PIN(GPIO2, "GPIO2"),
+ PINCTRL_PIN(GPIO3, "GPIO3"),
+ PINCTRL_PIN(GPIO4, "GPIO4"),
+ PINCTRL_PIN(GPIO5, "GPIO5"),
+ PINCTRL_PIN(GPIO6, "GPIO6"),
+ PINCTRL_PIN(GPIO7, "GPIO7"),
+ PINCTRL_PIN(GPIO8, "GPIO8"),
+ PINCTRL_PIN(GPIO9, "GPIO9"),
+ PINCTRL_PIN(GPIO10, "GPIO10"),
+ PINCTRL_PIN(GPIO11, "GPIO11"),
+ PINCTRL_PIN(GPIO12, "GPIO12"),
+ PINCTRL_PIN(GPIO13, "GPIO13"),
+ PINCTRL_PIN(GPIO14, "GPIO14"),
+ PINCTRL_PIN(GPIO15, "GPIO15"),
+ PINCTRL_PIN(GPIO16, "GPIO16"),
+ PINCTRL_PIN(GPIO17, "GPIO17"),
+ PINCTRL_PIN(GPIO18, "GPIO18"),
+ PINCTRL_PIN(GPIO19, "GPIO19"),
+ PINCTRL_PIN(GPIO20, "GPIO20"),
+ PINCTRL_PIN(GPIO21, "GPIO21"),
+ PINCTRL_PIN(GPIO22, "GPIO22"),
+ PINCTRL_PIN(GPIO23, "GPIO23"),
+ PINCTRL_PIN(GPIO24, "GPIO24"),
+ PINCTRL_PIN(GPIO25, "GPIO25"),
+ PINCTRL_PIN(GPIO26, "GPIO26"),
+ PINCTRL_PIN(GPIO27, "GPIO27"),
+ PINCTRL_PIN(GPIO28, "GPIO28"),
+ PINCTRL_PIN(GPIO29, "GPIO29"),
+ PINCTRL_PIN(GPIO30, "GPIO30"),
+ PINCTRL_PIN(GPIO31, "GPIO31"),
+ PINCTRL_PIN(GPIO32, "GPIO32"),
+ PINCTRL_PIN(GPIO33, "GPIO33"),
+ PINCTRL_PIN(GPIO34, "GPIO34"),
+ PINCTRL_PIN(GPIO35, "GPIO35"),
+ PINCTRL_PIN(GPIO36, "GPIO36"),
+ PINCTRL_PIN(GPIO37, "GPIO37"),
+ PINCTRL_PIN(GPIO38, "GPIO38"),
+ PINCTRL_PIN(GPIO39, "GPIO39"),
+ PINCTRL_PIN(GPIO40, "GPIO40"),
+ PINCTRL_PIN(GPIO41, "GPIO41"),
+ PINCTRL_PIN(GPIO42, "GPIO42"),
+ PINCTRL_PIN(GPIO43, "GPIO43"),
+ PINCTRL_PIN(GPIO44, "GPIO44"),
+ PINCTRL_PIN(GPIO45, "GPIO45"),
+ PINCTRL_PIN(GPIO46, "GPIO46"),
+ PINCTRL_PIN(GPIO47, "GPIO47"),
+ PINCTRL_PIN(GPIO48, "GPIO48"),
+ PINCTRL_PIN(GPIO49, "GPIO49"),
+ PINCTRL_PIN(GPIO50, "GPIO50"),
+ PINCTRL_PIN(GPIO51, "GPIO51"),
+ PINCTRL_PIN(GPIO52, "GPIO52"),
+ PINCTRL_PIN(GPIO53, "GPIO53"),
+ PINCTRL_PIN(GPIO54, "GPIO54"),
+ PINCTRL_PIN(GPIO55, "GPIO55"),
+ PINCTRL_PIN(GPIO56, "GPIO56"),
+ PINCTRL_PIN(GPIO57, "GPIO57"),
+ PINCTRL_PIN(GPIO58, "GPIO58"),
+ PINCTRL_PIN(GPIO59, "GPIO59"),
+ PINCTRL_PIN(GPIO60, "GPIO60"),
+ PINCTRL_PIN(GPIO61, "GPIO61"),
+ PINCTRL_PIN(GPIO62, "GPIO62"),
+ PINCTRL_PIN(GPIO63, "GPIO63"),
+ PINCTRL_PIN(GPIO64, "GPIO64"),
+ PINCTRL_PIN(GPIO65, "GPIO65"),
+ PINCTRL_PIN(GPIO66, "GPIO66"),
+ PINCTRL_PIN(GPIO67, "GPIO67"),
+ PINCTRL_PIN(GPIO68, "GPIO68"),
+ PINCTRL_PIN(GPIO69, "GPIO69"),
+ PINCTRL_PIN(GPIO70, "GPIO70"),
+ PINCTRL_PIN(GPIO71, "GPIO71"),
+ PINCTRL_PIN(GPIO72, "GPIO72"),
+ PINCTRL_PIN(GPIO73, "GPIO73"),
+ PINCTRL_PIN(GPIO74, "GPIO74"),
+ PINCTRL_PIN(GPIO75, "GPIO75"),
+ PINCTRL_PIN(GPIO76, "GPIO76"),
+ PINCTRL_PIN(GPIO77, "GPIO77"),
+ PINCTRL_PIN(GPIO78, "GPIO78"),
+ PINCTRL_PIN(GPIO79, "GPIO79"),
+ PINCTRL_PIN(GPIO80, "GPIO80"),
+ PINCTRL_PIN(GPIO81, "GPIO81"),
+ PINCTRL_PIN(GPIO82, "GPIO82"),
+ PINCTRL_PIN(GPIO83, "GPIO83"),
+ PINCTRL_PIN(GPIO84, "GPIO84"),
+ PINCTRL_PIN(GPIO85, "GPIO85"),
+ PINCTRL_PIN(GPIO86, "GPIO86"),
+ PINCTRL_PIN(GPIO87, "GPIO87"),
+ PINCTRL_PIN(GPIO88, "GPIO88"),
+ PINCTRL_PIN(GPIO89, "GPIO89"),
+ PINCTRL_PIN(GPIO90, "GPIO90"),
+ PINCTRL_PIN(GPIO91, "GPIO91"),
+ PINCTRL_PIN(GPIO92, "GPIO92"),
+ PINCTRL_PIN(GPIO93, "GPIO93"),
+ PINCTRL_PIN(GPIO94, "GPIO94"),
+ PINCTRL_PIN(GPIO95, "GPIO95"),
+ PINCTRL_PIN(GPIO96, "GPIO96"),
+ PINCTRL_PIN(GPIO97, "GPIO97"),
+ PINCTRL_PIN(GPIO98, "GPIO98"),
+ PINCTRL_PIN(GPIO99, "GPIO99"),
+ PINCTRL_PIN(GPIO100, "GPIO100"),
+ PINCTRL_PIN(GPIO101, "GPIO101"),
+ PINCTRL_PIN(GPIO102, "GPIO102"),
+ PINCTRL_PIN(GPIO103, "GPIO103"),
+ PINCTRL_PIN(GPIO104, "GPIO104"),
+ PINCTRL_PIN(GPIO105, "GPIO105"),
+ PINCTRL_PIN(GPIO106, "GPIO106"),
+ PINCTRL_PIN(GPIO107, "GPIO107"),
+ PINCTRL_PIN(GPIO108, "GPIO108"),
+ PINCTRL_PIN(GPIO109, "GPIO109"),
+ PINCTRL_PIN(GPIO110, "GPIO110"),
+ PINCTRL_PIN(GPIO111, "GPIO111"),
+ PINCTRL_PIN(GPIO112, "GPIO112"),
+ PINCTRL_PIN(GPIO113, "GPIO113"),
+ PINCTRL_PIN(GPIO114, "GPIO114"),
+ PINCTRL_PIN(GPIO115, "GPIO115"),
+ PINCTRL_PIN(GPIO116, "GPIO116"),
+ PINCTRL_PIN(GPIO117, "GPIO117"),
+ PINCTRL_PIN(GPIO118, "GPIO118"),
+ PINCTRL_PIN(GPIO119, "GPIO119"),
+ PINCTRL_PIN(GPIO120, "GPIO120"),
+ PINCTRL_PIN(GPIO121, "GPIO121"),
+ PINCTRL_PIN(GPIO122, "GPIO122"),
+ PINCTRL_PIN(GPIO123, "GPIO123"),
+ PINCTRL_PIN(GPIO124, "GPIO124"),
+ PINCTRL_PIN(GPIO125, "GPIO125"),
+ PINCTRL_PIN(GPIO126, "GPIO126"),
+ PINCTRL_PIN(GPIO127, "GPIO127"),
+ PINCTRL_PIN(GPIO128, "GPIO128"),
+ PINCTRL_PIN(GPIO129, "GPIO129"),
+ PINCTRL_PIN(GPIO130, "GPIO130"),
+ PINCTRL_PIN(GPIO131, "GPIO131"),
+ PINCTRL_PIN(GPIO132, "GPIO132"),
+ PINCTRL_PIN(GPIO133, "GPIO133"),
+ PINCTRL_PIN(GPIO134, "GPIO134"),
+ PINCTRL_PIN(GPIO135, "GPIO135"),
+ PINCTRL_PIN(GPIO136, "GPIO136"),
+ PINCTRL_PIN(GPIO137, "GPIO137"),
+ PINCTRL_PIN(GPIO138, "GPIO138"),
+ PINCTRL_PIN(GPIO139, "GPIO139"),
+ PINCTRL_PIN(GPIO140, "GPIO140"),
+ PINCTRL_PIN(GPIO141, "GPIO141"),
+ PINCTRL_PIN(GPIO142, "GPIO142"),
+ PINCTRL_PIN(GPIO143, "GPIO143"),
+ PINCTRL_PIN(GPIO144, "GPIO144"),
+ PINCTRL_PIN(GPIO145, "GPIO145"),
+ PINCTRL_PIN(GPIO146, "GPIO146"),
+ PINCTRL_PIN(GPIO147, "GPIO147"),
+ PINCTRL_PIN(GPIO148, "GPIO148"),
+ PINCTRL_PIN(GPIO149, "GPIO149"),
+ PINCTRL_PIN(GPIO150, "GPIO150"),
+ PINCTRL_PIN(GPIO151, "GPIO151"),
+ PINCTRL_PIN(GPIO152, "GPIO152"),
+ PINCTRL_PIN(GPIO153, "GPIO153"),
+ PINCTRL_PIN(GPIO154, "GPIO154"),
+ PINCTRL_PIN(GPIO155, "GPIO155"),
+ PINCTRL_PIN(GPIO156, "GPIO156"),
+ PINCTRL_PIN(GPIO157, "GPIO157"),
+ PINCTRL_PIN(GPIO158, "GPIO158"),
+ PINCTRL_PIN(GPIO159, "GPIO159"),
+ PINCTRL_PIN(GPIO160, "GPIO160"),
+ PINCTRL_PIN(GPIO161, "GPIO161"),
+ PINCTRL_PIN(GPIO162, "GPIO162"),
+ PINCTRL_PIN(GPIO163, "GPIO163"),
+ PINCTRL_PIN(GPIO164, "GPIO164"),
+ PINCTRL_PIN(GPIO165, "GPIO165"),
+ PINCTRL_PIN(GPIO166, "GPIO166"),
+ PINCTRL_PIN(GPIO167, "GPIO167"),
+ PINCTRL_PIN(GPIO168, "GPIO168"),
+ PINCTRL_PIN(TWSI4_SCL, "TWSI4_SCL"),
+ PINCTRL_PIN(TWSI4_SDA, "TWSI4_SDA"),
+ PINCTRL_PIN(G_CLKREQ, "G_CLKREQ"),
+ PINCTRL_PIN(VCXO_REQ, "VCXO_REQ"),
+ PINCTRL_PIN(VCXO_OUT, "VCXO_OUT"),
+};
+
+struct pxa3xx_mfp_pin mmp2_mfp[] = {
+ /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */
+ MFPR_MMP2(GPIO0, 0x054, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO1, 0x058, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO2, 0x05C, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO3, 0x060, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO4, 0x064, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO5, 0x068, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO6, 0x06C, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO7, 0x070, GPIO, KP_MK, NONE, SPI, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO8, 0x074, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO9, 0x078, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO10, 0x07C, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO11, 0x080, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO12, 0x084, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO13, 0x088, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO14, 0x08C, GPIO, KP_MK, NONE, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO15, 0x090, GPIO, KP_MK, KP_DK, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO16, 0x094, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO17, 0x098, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO18, 0x09C, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO19, 0x0A0, GPIO, KP_DK, ROT, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO20, 0x0A4, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO21, 0x0A8, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO22, 0x0AC, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO23, 0x0B0, GPIO, KP_DK, TB, CCIC1, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO24, 0x0B4, GPIO, I2S, VCXO_OUT, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO25, 0x0B8, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO26, 0x0BC, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO27, 0x0C0, GPIO, I2S, HDMI, SSPA2, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO28, 0x0C4, GPIO, I2S, NONE, SSPA2, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO29, 0x0C8, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE),
+ MFPR_MMP2(GPIO30, 0x0CC, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE),
+ MFPR_MMP2(GPIO31, 0x0D0, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE),
+ MFPR_MMP2(GPIO32, 0x0D4, GPIO, UART1, KP_MK, NONE, NONE, NONE, AAS_SPI, NONE),
+ MFPR_MMP2(GPIO33, 0x0D8, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO34, 0x0DC, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO35, 0x0E0, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO36, 0x0E4, GPIO, SSPA2, I2S, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO37, 0x0E8, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI),
+ MFPR_MMP2(GPIO38, 0x0EC, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI),
+ MFPR_MMP2(GPIO39, 0x0F0, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI),
+ MFPR_MMP2(GPIO40, 0x0F4, GPIO, MMC2, SSP1, TWSI2, UART2, UART3, AAS_SPI, AAS_TWSI),
+ MFPR_MMP2(GPIO41, 0x0F8, GPIO, MMC2, TWSI5, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO42, 0x0FC, GPIO, MMC2, TWSI5, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO43, 0x100, GPIO, TWSI2, UART4, SSP1, UART2, UART3, NONE, AAS_TWSI),
+ MFPR_MMP2(GPIO44, 0x104, GPIO, TWSI2, UART4, SSP1, UART2, UART3, NONE, AAS_TWSI),
+ MFPR_MMP2(GPIO45, 0x108, GPIO, UART1, UART4, SSP1, UART2, UART3, NONE, NONE),
+ MFPR_MMP2(GPIO46, 0x10C, GPIO, UART1, UART4, SSP1, UART2, UART3, NONE, NONE),
+ MFPR_MMP2(GPIO47, 0x110, GPIO, UART2, SSP2, TWSI6, CAM2, AAS_SPI, AAS_GPIO, NONE),
+ MFPR_MMP2(GPIO48, 0x114, GPIO, UART2, SSP2, TWSI6, CAM2, AAS_SPI, AAS_GPIO, NONE),
+ MFPR_MMP2(GPIO49, 0x118, GPIO, UART2, SSP2, PWM, CCIC2, AAS_SPI, NONE, NONE),
+ MFPR_MMP2(GPIO50, 0x11C, GPIO, UART2, SSP2, PWM, CCIC2, AAS_SPI, NONE, NONE),
+ MFPR_MMP2(GPIO51, 0x120, GPIO, UART3, ROT, AAS_GPIO, PWM, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO52, 0x124, GPIO, UART3, ROT, AAS_GPIO, PWM, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO53, 0x128, GPIO, UART3, TWSI2, VCXO_REQ, NONE, PWM, NONE, AAS_TWSI),
+ MFPR_MMP2(GPIO54, 0x12C, GPIO, UART3, TWSI2, VCXO_OUT, HDMI, PWM, NONE, AAS_TWSI),
+ MFPR_MMP2(GPIO55, 0x130, GPIO, SSP2, SSP1, UART2, ROT, TWSI2, SSP3, AAS_TWSI),
+ MFPR_MMP2(GPIO56, 0x134, GPIO, SSP2, SSP1, UART2, ROT, TWSI2, KP_DK, AAS_TWSI),
+ MFPR_MMP2(GPIO57, 0x138, GPIO, SSP2_RX, SSP1_TXRX, SSP2_FRM, SSP1_RX, VCXO_REQ, KP_DK, NONE),
+ MFPR_MMP2(GPIO58, 0x13C, GPIO, SSP2, SSP1_RX, SSP1_FRM, SSP1_TXRX, VCXO_REQ, KP_DK, NONE),
+ MFPR_MMP2(GPIO59, 0x280, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, UART4, NONE),
+ MFPR_MMP2(GPIO60, 0x284, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, UART4, NONE),
+ MFPR_MMP2(GPIO61, 0x288, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, HDMI, NONE),
+ MFPR_MMP2(GPIO62, 0x28C, GPIO, CCIC1, ULPI, MMC3, CCIC2, UART3, NONE, NONE),
+ MFPR_MMP2(GPIO63, 0x290, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE),
+ MFPR_MMP2(GPIO64, 0x294, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE),
+ MFPR_MMP2(GPIO65, 0x298, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE),
+ MFPR_MMP2(GPIO66, 0x29C, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, UART4, NONE),
+ MFPR_MMP2(GPIO67, 0x2A0, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, NONE, NONE),
+ MFPR_MMP2(GPIO68, 0x2A4, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, LCD, NONE),
+ MFPR_MMP2(GPIO69, 0x2A8, GPIO, CCIC1, ULPI, MMC3, CCIC2, NONE, LCD, NONE),
+ MFPR_MMP2(GPIO70, 0x2AC, GPIO, CCIC1, ULPI, MMC3, CCIC2, MSP, LCD, NONE),
+ MFPR_MMP2(GPIO71, 0x2B0, GPIO, TWSI3, NONE, PWM, NONE, NONE, LCD, AAS_TWSI),
+ MFPR_MMP2(GPIO72, 0x2B4, GPIO, TWSI3, HDMI, PWM, NONE, NONE, LCD, AAS_TWSI),
+ MFPR_MMP2(GPIO73, 0x2B8, GPIO, VCXO_REQ, 32K_CLKOUT, PWM, VCXO_OUT, NONE, LCD, NONE),
+ MFPR_MMP2(GPIO74, 0x170, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU),
+ MFPR_MMP2(GPIO75, 0x174, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU),
+ MFPR_MMP2(GPIO76, 0x178, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU),
+ MFPR_MMP2(GPIO77, 0x17C, GPIO, LCD, SMC, MMC4, SSP3, UART2, UART4, TIPU),
+ MFPR_MMP2(GPIO78, 0x180, GPIO, LCD, HDMI, MMC4, NONE, SSP4, AAS_SPI, TIPU),
+ MFPR_MMP2(GPIO79, 0x184, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU),
+ MFPR_MMP2(GPIO80, 0x188, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU),
+ MFPR_MMP2(GPIO81, 0x18C, GPIO, LCD, AAS_GPIO, MMC4, NONE, SSP4, AAS_SPI, TIPU),
+ MFPR_MMP2(GPIO82, 0x190, GPIO, LCD, NONE, MMC4, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO83, 0x194, GPIO, LCD, NONE, MMC4, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO84, 0x198, GPIO, LCD, SMC, MMC2, NONE, TWSI5, AAS_TWSI, TIPU),
+ MFPR_MMP2(GPIO85, 0x19C, GPIO, LCD, SMC, MMC2, NONE, TWSI5, AAS_TWSI, TIPU),
+ MFPR_MMP2(GPIO86, 0x1A0, GPIO, LCD, SMC, MMC2, NONE, TWSI6, CCIC2, TIPU),
+ MFPR_MMP2(GPIO87, 0x1A4, GPIO, LCD, SMC, MMC2, NONE, TWSI6, CCIC2, TIPU),
+ MFPR_MMP2(GPIO88, 0x1A8, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO89, 0x1AC, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO90, 0x1B0, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO91, 0x1B4, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO92, 0x1B8, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO93, 0x1BC, GPIO, LCD, AAS_GPIO, MMC2, NONE, NONE, CCIC2, TIPU),
+ MFPR_MMP2(GPIO94, 0x1C0, GPIO, LCD, AAS_GPIO, SPI, NONE, AAS_SPI, CCIC2, TIPU),
+ MFPR_MMP2(GPIO95, 0x1C4, GPIO, LCD, TWSI3, SPI, AAS_DEU_EX, AAS_SPI, CCIC2, TIPU),
+ MFPR_MMP2(GPIO96, 0x1C8, GPIO, LCD, TWSI3, SPI, AAS_DEU_EX, AAS_SPI, NONE, TIPU),
+ MFPR_MMP2(GPIO97, 0x1CC, GPIO, LCD, TWSI6, SPI, AAS_DEU_EX, AAS_SPI, NONE, TIPU),
+ MFPR_MMP2(GPIO98, 0x1D0, GPIO, LCD, TWSI6, SPI, ONE_WIRE, NONE, NONE, TIPU),
+ MFPR_MMP2(GPIO99, 0x1D4, GPIO, LCD, SMC, SPI, TWSI5, NONE, NONE, TIPU),
+ MFPR_MMP2(GPIO100, 0x1D8, GPIO, LCD, SMC, SPI, TWSI5, NONE, NONE, TIPU),
+ MFPR_MMP2(GPIO101, 0x1DC, GPIO, LCD, SMC, SPI, NONE, NONE, NONE, TIPU),
+ MFPR_MMP2(GPIO102, 0x000, USIM, GPIO, FSIC, KP_DK, LCD, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO103, 0x004, USIM, GPIO, FSIC, KP_DK, LCD, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO104, 0x1FC, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO105, 0x1F8, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO106, 0x1F4, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO107, 0x1F0, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO108, 0x21C, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO109, 0x218, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO110, 0x214, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO111, 0x200, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO112, 0x244, NAND, GPIO, MMC3, SMC, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO113, 0x25C, SMC, GPIO, EXT_DMA, MMC3, SMC, HDMI, NONE, NONE),
+ MFPR_MMP2(GPIO114, 0x164, G_CLKOUT, 32K_CLKOUT, HDMI, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO115, 0x260, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE),
+ MFPR_MMP2(GPIO116, 0x264, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE),
+ MFPR_MMP2(GPIO117, 0x268, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE),
+ MFPR_MMP2(GPIO118, 0x26C, GPIO, NONE, AC, UART4, UART3, SSP1, NONE, NONE),
+ MFPR_MMP2(GPIO119, 0x270, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO120, 0x274, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO121, 0x278, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO122, 0x27C, GPIO, NONE, CA, SSP3, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO123, 0x148, GPIO, SLEEP_IND, ONE_WIRE, 32K_CLKOUT, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO124, 0x00C, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO125, 0x010, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO126, 0x014, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO127, 0x018, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO128, 0x01C, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO129, 0x020, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO130, 0x024, GPIO, MMC1, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO131, 0x028, GPIO, MMC1, NONE, MSP, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO132, 0x02C, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE),
+ MFPR_MMP2(GPIO133, 0x030, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE),
+ MFPR_MMP2(GPIO134, 0x034, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE),
+ MFPR_MMP2(GPIO135, 0x038, GPIO, NONE, LCD, MMC3, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO136, 0x03C, GPIO, MMC1, PRI_JTAG, MSP, SSP3, AAS_JTAG, NONE, NONE),
+ MFPR_MMP2(GPIO137, 0x040, GPIO, HDMI, LCD, MSP, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO138, 0x044, GPIO, NONE, LCD, MMC3, SMC, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO139, 0x048, GPIO, MMC1, PRI_JTAG, MSP, NONE, AAS_JTAG, NONE, NONE),
+ MFPR_MMP2(GPIO140, 0x04C, GPIO, MMC1, LCD, NONE, NONE, UART2, UART1, NONE),
+ MFPR_MMP2(GPIO141, 0x050, GPIO, MMC1, LCD, NONE, NONE, UART2, UART1, NONE),
+ MFPR_MMP2(GPIO142, 0x008, USIM, GPIO, FSIC, KP_DK, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO143, 0x220, NAND, GPIO, SMC, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO144, 0x224, NAND, GPIO, SMC_INT, SMC, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO145, 0x228, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO146, 0x22C, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO147, 0x230, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO148, 0x234, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO149, 0x238, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO150, 0x23C, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO151, 0x240, SMC, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO152, 0x248, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO153, 0x24C, SMC, GPIO, NONE, NONE, SMC, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO154, 0x254, SMC_INT, GPIO, SMC, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO155, 0x258, EXT_DMA, GPIO, SMC, NONE, EXT_DMA, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO156, 0x14C, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO157, 0x150, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO158, 0x154, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO159, 0x158, PRI_JTAG, GPIO, PWM, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO160, 0x250, NAND, GPIO, SMC, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO161, 0x210, NAND, GPIO, NONE, NONE, NAND, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO162, 0x20C, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO163, 0x208, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO164, 0x204, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO165, 0x1EC, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO166, 0x1E8, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO167, 0x1E4, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(GPIO168, 0x1E0, NAND, GPIO, MMC3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(TWSI4_SCL, 0x2BC, TWSI4, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(TWSI4_SDA, 0x2C0, TWSI4, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(G_CLKREQ, 0x160, G_CLKREQ, ONE_WIRE, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(VCXO_REQ, 0x168, VCXO_REQ, ONE_WIRE, PLL, NONE, NONE, NONE, NONE, NONE),
+ MFPR_MMP2(VCXO_OUT, 0x16C, VCXO_OUT, 32K_CLKOUT, NONE, NONE, NONE, NONE, NONE, NONE),
+};
+
+static const unsigned mmp2_uart1_pin1[] = {GPIO29, GPIO30, GPIO31, GPIO32};
+static const unsigned mmp2_uart1_pin2[] = {GPIO45, GPIO46};
+static const unsigned mmp2_uart1_pin3[] = {GPIO140, GPIO141};
+static const unsigned mmp2_uart2_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40};
+static const unsigned mmp2_uart2_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned mmp2_uart2_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50};
+static const unsigned mmp2_uart2_pin4[] = {GPIO74, GPIO75, GPIO76, GPIO77};
+static const unsigned mmp2_uart2_pin5[] = {GPIO55, GPIO56};
+static const unsigned mmp2_uart2_pin6[] = {GPIO140, GPIO141};
+static const unsigned mmp2_uart3_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40};
+static const unsigned mmp2_uart3_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned mmp2_uart3_pin3[] = {GPIO51, GPIO52, GPIO53, GPIO54};
+static const unsigned mmp2_uart3_pin4[] = {GPIO59, GPIO60, GPIO61, GPIO62};
+static const unsigned mmp2_uart3_pin5[] = {GPIO115, GPIO116, GPIO117, GPIO118};
+static const unsigned mmp2_uart3_pin6[] = {GPIO51, GPIO52};
+static const unsigned mmp2_uart4_pin1[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned mmp2_uart4_pin2[] = {GPIO63, GPIO64, GPIO65, GPIO66};
+static const unsigned mmp2_uart4_pin3[] = {GPIO74, GPIO75, GPIO76, GPIO77};
+static const unsigned mmp2_uart4_pin4[] = {GPIO115, GPIO116, GPIO117, GPIO118};
+static const unsigned mmp2_uart4_pin5[] = {GPIO59, GPIO60};
+static const unsigned mmp2_kpdk_pin1[] = {GPIO16, GPIO17, GPIO18, GPIO19};
+static const unsigned mmp2_kpdk_pin2[] = {GPIO16, GPIO17};
+static const unsigned mmp2_twsi2_pin1[] = {GPIO37, GPIO38};
+static const unsigned mmp2_twsi2_pin2[] = {GPIO39, GPIO40};
+static const unsigned mmp2_twsi2_pin3[] = {GPIO43, GPIO44};
+static const unsigned mmp2_twsi2_pin4[] = {GPIO53, GPIO54};
+static const unsigned mmp2_twsi2_pin5[] = {GPIO55, GPIO56};
+static const unsigned mmp2_twsi3_pin1[] = {GPIO71, GPIO72};
+static const unsigned mmp2_twsi3_pin2[] = {GPIO95, GPIO96};
+static const unsigned mmp2_twsi4_pin1[] = {TWSI4_SCL, TWSI4_SDA};
+static const unsigned mmp2_twsi5_pin1[] = {GPIO41, GPIO42};
+static const unsigned mmp2_twsi5_pin2[] = {GPIO84, GPIO85};
+static const unsigned mmp2_twsi5_pin3[] = {GPIO99, GPIO100};
+static const unsigned mmp2_twsi6_pin1[] = {GPIO47, GPIO48};
+static const unsigned mmp2_twsi6_pin2[] = {GPIO86, GPIO87};
+static const unsigned mmp2_twsi6_pin3[] = {GPIO97, GPIO98};
+static const unsigned mmp2_ccic1_pin1[] = {GPIO12, GPIO13, GPIO14, GPIO15,
+ GPIO16, GPIO17, GPIO18, GPIO19, GPIO20, GPIO21, GPIO22, GPIO23};
+static const unsigned mmp2_ccic1_pin2[] = {GPIO59, GPIO60, GPIO61, GPIO62,
+ GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70};
+static const unsigned mmp2_ccic2_pin1[] = {GPIO59, GPIO60, GPIO61, GPIO62,
+ GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70};
+static const unsigned mmp2_ccic2_pin2[] = {GPIO82, GPIO83, GPIO86, GPIO87,
+ GPIO88, GPIO89, GPIO90, GPIO91, GPIO92, GPIO93, GPIO94, GPIO95};
+static const unsigned mmp2_ulpi_pin1[] = {GPIO59, GPIO60, GPIO61, GPIO62,
+ GPIO63, GPIO64, GPIO65, GPIO66, GPIO67, GPIO68, GPIO69, GPIO70};
+static const unsigned mmp2_ro_pin1[] = {GPIO16, GPIO17};
+static const unsigned mmp2_ro_pin2[] = {GPIO18, GPIO19};
+static const unsigned mmp2_ro_pin3[] = {GPIO51, GPIO52};
+static const unsigned mmp2_ro_pin4[] = {GPIO55, GPIO56};
+static const unsigned mmp2_i2s_pin1[] = {GPIO24, GPIO25, GPIO26, GPIO27,
+ GPIO28};
+static const unsigned mmp2_i2s_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36};
+static const unsigned mmp2_ssp1_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40};
+static const unsigned mmp2_ssp1_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned mmp2_ssp1_pin3[] = {GPIO115, GPIO116, GPIO117, GPIO118};
+static const unsigned mmp2_ssp2_pin1[] = {GPIO47, GPIO48, GPIO49, GPIO50};
+static const unsigned mmp2_ssp3_pin1[] = {GPIO119, GPIO120, GPIO121, GPIO122};
+static const unsigned mmp2_ssp3_pin2[] = {GPIO132, GPIO133, GPIO133, GPIO136};
+static const unsigned mmp2_sspa2_pin1[] = {GPIO25, GPIO26, GPIO27, GPIO28};
+static const unsigned mmp2_sspa2_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36};
+static const unsigned mmp2_mmc1_pin1[] = {GPIO131, GPIO132, GPIO133, GPIO134,
+ GPIO136, GPIO139, GPIO140, GPIO141};
+static const unsigned mmp2_mmc2_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40,
+ GPIO41, GPIO42};
+static const unsigned mmp2_mmc3_pin1[] = {GPIO111, GPIO112, GPIO151, GPIO162,
+ GPIO163, GPIO164, GPIO165, GPIO166, GPIO167, GPIO168};
+
+static struct pxa3xx_pin_group mmp2_grps[] = {
+ GRP_MMP2("uart1 4p1", UART1, mmp2_uart1_pin1),
+ GRP_MMP2("uart1 2p2", UART1, mmp2_uart1_pin2),
+ GRP_MMP2("uart1 2p3", UART1, mmp2_uart1_pin3),
+ GRP_MMP2("uart2 4p1", UART2, mmp2_uart2_pin1),
+ GRP_MMP2("uart2 4p2", UART2, mmp2_uart2_pin2),
+ GRP_MMP2("uart2 4p3", UART2, mmp2_uart2_pin3),
+ GRP_MMP2("uart2 4p4", UART2, mmp2_uart2_pin4),
+ GRP_MMP2("uart2 2p5", UART2, mmp2_uart2_pin5),
+ GRP_MMP2("uart2 2p6", UART2, mmp2_uart2_pin6),
+ GRP_MMP2("uart3 4p1", UART3, mmp2_uart3_pin1),
+ GRP_MMP2("uart3 4p2", UART3, mmp2_uart3_pin2),
+ GRP_MMP2("uart3 4p3", UART3, mmp2_uart3_pin3),
+ GRP_MMP2("uart3 4p4", UART3, mmp2_uart3_pin4),
+ GRP_MMP2("uart3 4p5", UART3, mmp2_uart3_pin5),
+ GRP_MMP2("uart3 2p6", UART3, mmp2_uart3_pin6),
+ GRP_MMP2("uart4 4p1", UART4, mmp2_uart4_pin1),
+ GRP_MMP2("uart4 4p2", UART4, mmp2_uart4_pin2),
+ GRP_MMP2("uart4 4p3", UART4, mmp2_uart4_pin3),
+ GRP_MMP2("uart4 4p4", UART4, mmp2_uart4_pin4),
+ GRP_MMP2("uart4 2p5", UART4, mmp2_uart4_pin5),
+ GRP_MMP2("kpdk 4p1", KP_DK, mmp2_kpdk_pin1),
+ GRP_MMP2("kpdk 4p2", KP_DK, mmp2_kpdk_pin2),
+ GRP_MMP2("twsi2-1", TWSI2, mmp2_twsi2_pin1),
+ GRP_MMP2("twsi2-2", TWSI2, mmp2_twsi2_pin2),
+ GRP_MMP2("twsi2-3", TWSI2, mmp2_twsi2_pin3),
+ GRP_MMP2("twsi2-4", TWSI2, mmp2_twsi2_pin4),
+ GRP_MMP2("twsi2-5", TWSI2, mmp2_twsi2_pin5),
+ GRP_MMP2("twsi3-1", TWSI3, mmp2_twsi3_pin1),
+ GRP_MMP2("twsi3-2", TWSI3, mmp2_twsi3_pin2),
+ GRP_MMP2("twsi4", TWSI4, mmp2_twsi4_pin1),
+ GRP_MMP2("twsi5-1", TWSI5, mmp2_twsi5_pin1),
+ GRP_MMP2("twsi5-2", TWSI5, mmp2_twsi5_pin2),
+ GRP_MMP2("twsi5-3", TWSI5, mmp2_twsi5_pin3),
+ GRP_MMP2("twsi6-1", TWSI6, mmp2_twsi6_pin1),
+ GRP_MMP2("twsi6-2", TWSI6, mmp2_twsi6_pin2),
+ GRP_MMP2("twsi6-3", TWSI6, mmp2_twsi6_pin3),
+ GRP_MMP2("ccic1-1", CCIC1, mmp2_ccic1_pin1),
+ GRP_MMP2("ccic1-2", CCIC1, mmp2_ccic1_pin2),
+ GRP_MMP2("ccic2-1", CCIC2, mmp2_ccic2_pin1),
+ GRP_MMP2("ccic2-1", CCIC2, mmp2_ccic2_pin2),
+ GRP_MMP2("ulpi", ULPI, mmp2_ulpi_pin1),
+ GRP_MMP2("ro-1", ROT, mmp2_ro_pin1),
+ GRP_MMP2("ro-2", ROT, mmp2_ro_pin2),
+ GRP_MMP2("ro-3", ROT, mmp2_ro_pin3),
+ GRP_MMP2("ro-4", ROT, mmp2_ro_pin4),
+ GRP_MMP2("i2s 5p1", I2S, mmp2_i2s_pin1),
+ GRP_MMP2("i2s 4p2", I2S, mmp2_i2s_pin2),
+ GRP_MMP2("ssp1 4p1", SSP1, mmp2_ssp1_pin1),
+ GRP_MMP2("ssp1 4p2", SSP1, mmp2_ssp1_pin2),
+ GRP_MMP2("ssp1 4p3", SSP1, mmp2_ssp1_pin3),
+ GRP_MMP2("ssp2 4p1", SSP2, mmp2_ssp2_pin1),
+ GRP_MMP2("ssp3 4p1", SSP3, mmp2_ssp3_pin1),
+ GRP_MMP2("ssp3 4p2", SSP3, mmp2_ssp3_pin2),
+ GRP_MMP2("sspa2 4p1", SSPA2, mmp2_sspa2_pin1),
+ GRP_MMP2("sspa2 4p2", SSPA2, mmp2_sspa2_pin2),
+ GRP_MMP2("mmc1 8p1", MMC1, mmp2_mmc1_pin1),
+ GRP_MMP2("mmc2 6p1", MMC2, mmp2_mmc2_pin1),
+ GRP_MMP2("mmc3 10p1", MMC3, mmp2_mmc3_pin1),
+};
+
+static const char * const mmp2_uart1_grps[] = {"uart1 4p1", "uart1 2p2",
+ "uart1 2p3"};
+static const char * const mmp2_uart2_grps[] = {"uart2 4p1", "uart2 4p2",
+ "uart2 4p3", "uart2 4p4", "uart2 4p5", "uart2 4p6"};
+static const char * const mmp2_uart3_grps[] = {"uart3 4p1", "uart3 4p2",
+ "uart3 4p3", "uart3 4p4", "uart3 4p5", "uart3 2p6"};
+static const char * const mmp2_uart4_grps[] = {"uart4 4p1", "uart4 4p2",
+ "uart4 4p3", "uart4 4p4", "uart4 2p5"};
+static const char * const mmp2_kpdk_grps[] = {"kpdk 4p1", "kpdk 4p2"};
+static const char * const mmp2_twsi2_grps[] = {"twsi2-1", "twsi2-2",
+ "twsi2-3", "twsi2-4", "twsi2-5"};
+static const char * const mmp2_twsi3_grps[] = {"twsi3-1", "twsi3-2"};
+static const char * const mmp2_twsi4_grps[] = {"twsi4"};
+static const char * const mmp2_twsi5_grps[] = {"twsi5-1", "twsi5-2",
+ "twsi5-3"};
+static const char * const mmp2_twsi6_grps[] = {"twsi6-1", "twsi6-2",
+ "twsi6-3"};
+static const char * const mmp2_ccic1_grps[] = {"ccic1-1", "ccic1-2"};
+static const char * const mmp2_ccic2_grps[] = {"ccic2-1", "ccic2-2"};
+static const char * const mmp2_ulpi_grps[] = {"ulpi"};
+static const char * const mmp2_ro_grps[] = {"ro-1", "ro-2", "ro-3", "ro-4"};
+static const char * const mmp2_i2s_grps[] = {"i2s 5p1", "i2s 4p2"};
+static const char * const mmp2_ssp1_grps[] = {"ssp1 4p1", "ssp1 4p2",
+ "ssp1 4p3"};
+static const char * const mmp2_ssp2_grps[] = {"ssp2 4p1"};
+static const char * const mmp2_ssp3_grps[] = {"ssp3 4p1", "ssp3 4p2"};
+static const char * const mmp2_sspa2_grps[] = {"sspa2 4p1", "sspa2 4p2"};
+static const char * const mmp2_mmc1_grps[] = {"mmc1 8p1"};
+static const char * const mmp2_mmc2_grps[] = {"mmc2 6p1"};
+static const char * const mmp2_mmc3_grps[] = {"mmc3 10p1"};
+
+static struct pxa3xx_pmx_func mmp2_funcs[] = {
+ {"uart1", ARRAY_AND_SIZE(mmp2_uart1_grps)},
+ {"uart2", ARRAY_AND_SIZE(mmp2_uart2_grps)},
+ {"uart3", ARRAY_AND_SIZE(mmp2_uart3_grps)},
+ {"uart4", ARRAY_AND_SIZE(mmp2_uart4_grps)},
+ {"kpdk", ARRAY_AND_SIZE(mmp2_kpdk_grps)},
+ {"twsi2", ARRAY_AND_SIZE(mmp2_twsi2_grps)},
+ {"twsi3", ARRAY_AND_SIZE(mmp2_twsi3_grps)},
+ {"twsi4", ARRAY_AND_SIZE(mmp2_twsi4_grps)},
+ {"twsi5", ARRAY_AND_SIZE(mmp2_twsi5_grps)},
+ {"twsi6", ARRAY_AND_SIZE(mmp2_twsi6_grps)},
+ {"ccic1", ARRAY_AND_SIZE(mmp2_ccic1_grps)},
+ {"ccic2", ARRAY_AND_SIZE(mmp2_ccic2_grps)},
+ {"ulpi", ARRAY_AND_SIZE(mmp2_ulpi_grps)},
+ {"ro", ARRAY_AND_SIZE(mmp2_ro_grps)},
+ {"i2s", ARRAY_AND_SIZE(mmp2_i2s_grps)},
+ {"ssp1", ARRAY_AND_SIZE(mmp2_ssp1_grps)},
+ {"ssp2", ARRAY_AND_SIZE(mmp2_ssp2_grps)},
+ {"ssp3", ARRAY_AND_SIZE(mmp2_ssp3_grps)},
+ {"sspa2", ARRAY_AND_SIZE(mmp2_sspa2_grps)},
+ {"mmc1", ARRAY_AND_SIZE(mmp2_mmc1_grps)},
+ {"mmc2", ARRAY_AND_SIZE(mmp2_mmc2_grps)},
+ {"mmc3", ARRAY_AND_SIZE(mmp2_mmc3_grps)},
+};
+
+static struct pinctrl_desc mmp2_pctrl_desc = {
+ .name = "mmp2-pinctrl",
+ .owner = THIS_MODULE,
+};
+
+static struct pxa3xx_pinmux_info mmp2_info = {
+ .mfp = mmp2_mfp,
+ .num_mfp = ARRAY_SIZE(mmp2_mfp),
+ .grps = mmp2_grps,
+ .num_grps = ARRAY_SIZE(mmp2_grps),
+ .funcs = mmp2_funcs,
+ .num_funcs = ARRAY_SIZE(mmp2_funcs),
+ .num_gpio = 169,
+ .desc = &mmp2_pctrl_desc,
+ .pads = mmp2_pads,
+ .num_pads = ARRAY_SIZE(mmp2_pads),
+
+ .cputype = PINCTRL_MMP2,
+ .ds_mask = MMP2_DS_MASK,
+ .ds_shift = MMP2_DS_SHIFT,
+};
+
+static int __devinit mmp2_pinmux_probe(struct platform_device *pdev)
+{
+ return pxa3xx_pinctrl_register(pdev, &mmp2_info);
+}
+
+static int __devexit mmp2_pinmux_remove(struct platform_device *pdev)
+{
+ return pxa3xx_pinctrl_unregister(pdev);
+}
+
+static struct platform_driver mmp2_pinmux_driver = {
+ .driver = {
+ .name = "mmp2-pinmux",
+ .owner = THIS_MODULE,
+ },
+ .probe = mmp2_pinmux_probe,
+ .remove = __devexit_p(mmp2_pinmux_remove),
+};
+
+static int __init mmp2_pinmux_init(void)
+{
+ return platform_driver_register(&mmp2_pinmux_driver);
+}
+core_initcall_sync(mmp2_pinmux_init);
+
+static void __exit mmp2_pinmux_exit(void)
+{
+ platform_driver_unregister(&mmp2_pinmux_driver);
+}
+module_exit(mmp2_pinmux_exit);
+
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_DESCRIPTION("PXA3xx pin control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-pxa168.c b/drivers/pinctrl/pinctrl-pxa168.c
new file mode 100644
index 000000000000..c1997fa7f28c
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pxa168.c
@@ -0,0 +1,651 @@
+/*
+ * linux/drivers/pinctrl/pinmux-pxa168.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2011, Marvell Technology Group Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include "pinctrl-pxa3xx.h"
+
+#define PXA168_DS_MASK 0x1800
+#define PXA168_DS_SHIFT 11
+#define PXA168_SLEEP_MASK 0x38
+#define PXA168_SLEEP_SELECT (1 << 9)
+#define PXA168_SLEEP_DATA (1 << 8)
+#define PXA168_SLEEP_DIR (1 << 7)
+
+#define MFPR_168(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \
+ { \
+ .name = #a, \
+ .pin = a, \
+ .mfpr = r, \
+ .func = { \
+ PXA168_MUX_##f0, \
+ PXA168_MUX_##f1, \
+ PXA168_MUX_##f2, \
+ PXA168_MUX_##f3, \
+ PXA168_MUX_##f4, \
+ PXA168_MUX_##f5, \
+ PXA168_MUX_##f6, \
+ PXA168_MUX_##f7, \
+ }, \
+ }
+
+#define GRP_168(a, m, p) \
+ { .name = a, .mux = PXA168_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), }
+
+/* 131 pins */
+enum pxa168_pin_list {
+ /* 0~122: GPIO0~GPIO122 */
+ PWR_SCL = 123,
+ PWR_SDA,
+ TDI,
+ TMS,
+ TCK,
+ TDO,
+ TRST,
+ WAKEUP = 130,
+};
+
+enum pxa168_mux {
+ /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */
+ PXA168_MUX_GPIO = 0,
+ PXA168_MUX_DFIO,
+ PXA168_MUX_NAND,
+ PXA168_MUX_SMC,
+ PXA168_MUX_SMC_CS0,
+ PXA168_MUX_SMC_CS1,
+ PXA168_MUX_SMC_INT,
+ PXA168_MUX_SMC_RDY,
+ PXA168_MUX_MMC1,
+ PXA168_MUX_MMC2,
+ PXA168_MUX_MMC2_CMD,
+ PXA168_MUX_MMC2_CLK,
+ PXA168_MUX_MMC3,
+ PXA168_MUX_MMC3_CMD,
+ PXA168_MUX_MMC3_CLK,
+ PXA168_MUX_MMC4,
+ PXA168_MUX_MSP,
+ PXA168_MUX_MSP_DAT3,
+ PXA168_MUX_MSP_INS,
+ PXA168_MUX_I2C,
+ PXA168_MUX_PWRI2C,
+ PXA168_MUX_AC97,
+ PXA168_MUX_AC97_SYSCLK,
+ PXA168_MUX_PWM,
+ PXA168_MUX_PWM1,
+ PXA168_MUX_XD,
+ PXA168_MUX_XP,
+ PXA168_MUX_LCD,
+ PXA168_MUX_CCIC,
+ PXA168_MUX_CF,
+ PXA168_MUX_CF_RDY,
+ PXA168_MUX_CF_nINPACK,
+ PXA168_MUX_CF_nWAIT,
+ PXA168_MUX_KP_MKOUT,
+ PXA168_MUX_KP_MKIN,
+ PXA168_MUX_KP_DK,
+ PXA168_MUX_ETH,
+ PXA168_MUX_ETH_TX,
+ PXA168_MUX_ETH_RX,
+ PXA168_MUX_ONE_WIRE,
+ PXA168_MUX_UART1,
+ PXA168_MUX_UART1_TX,
+ PXA168_MUX_UART1_CTS,
+ PXA168_MUX_UART1_nRI,
+ PXA168_MUX_UART1_DTR,
+ PXA168_MUX_UART2,
+ PXA168_MUX_UART2_TX,
+ PXA168_MUX_UART3,
+ PXA168_MUX_UART3_TX,
+ PXA168_MUX_UART3_CTS,
+ PXA168_MUX_SSP1,
+ PXA168_MUX_SSP1_TX,
+ PXA168_MUX_SSP2,
+ PXA168_MUX_SSP2_TX,
+ PXA168_MUX_SSP3,
+ PXA168_MUX_SSP3_TX,
+ PXA168_MUX_SSP4,
+ PXA168_MUX_SSP4_TX,
+ PXA168_MUX_SSP5,
+ PXA168_MUX_SSP5_TX,
+ PXA168_MUX_USB,
+ PXA168_MUX_JTAG,
+ PXA168_MUX_RESET,
+ PXA168_MUX_WAKEUP,
+ PXA168_MUX_EXT_32K_IN,
+ PXA168_MUX_NONE = 0xffff,
+};
+
+static struct pinctrl_pin_desc pxa168_pads[] = {
+ PINCTRL_PIN(GPIO0, "GPIO0"),
+ PINCTRL_PIN(GPIO1, "GPIO1"),
+ PINCTRL_PIN(GPIO2, "GPIO2"),
+ PINCTRL_PIN(GPIO3, "GPIO3"),
+ PINCTRL_PIN(GPIO4, "GPIO4"),
+ PINCTRL_PIN(GPIO5, "GPIO5"),
+ PINCTRL_PIN(GPIO6, "GPIO6"),
+ PINCTRL_PIN(GPIO7, "GPIO7"),
+ PINCTRL_PIN(GPIO8, "GPIO8"),
+ PINCTRL_PIN(GPIO9, "GPIO9"),
+ PINCTRL_PIN(GPIO10, "GPIO10"),
+ PINCTRL_PIN(GPIO11, "GPIO11"),
+ PINCTRL_PIN(GPIO12, "GPIO12"),
+ PINCTRL_PIN(GPIO13, "GPIO13"),
+ PINCTRL_PIN(GPIO14, "GPIO14"),
+ PINCTRL_PIN(GPIO15, "GPIO15"),
+ PINCTRL_PIN(GPIO16, "GPIO16"),
+ PINCTRL_PIN(GPIO17, "GPIO17"),
+ PINCTRL_PIN(GPIO18, "GPIO18"),
+ PINCTRL_PIN(GPIO19, "GPIO19"),
+ PINCTRL_PIN(GPIO20, "GPIO20"),
+ PINCTRL_PIN(GPIO21, "GPIO21"),
+ PINCTRL_PIN(GPIO22, "GPIO22"),
+ PINCTRL_PIN(GPIO23, "GPIO23"),
+ PINCTRL_PIN(GPIO24, "GPIO24"),
+ PINCTRL_PIN(GPIO25, "GPIO25"),
+ PINCTRL_PIN(GPIO26, "GPIO26"),
+ PINCTRL_PIN(GPIO27, "GPIO27"),
+ PINCTRL_PIN(GPIO28, "GPIO28"),
+ PINCTRL_PIN(GPIO29, "GPIO29"),
+ PINCTRL_PIN(GPIO30, "GPIO30"),
+ PINCTRL_PIN(GPIO31, "GPIO31"),
+ PINCTRL_PIN(GPIO32, "GPIO32"),
+ PINCTRL_PIN(GPIO33, "GPIO33"),
+ PINCTRL_PIN(GPIO34, "GPIO34"),
+ PINCTRL_PIN(GPIO35, "GPIO35"),
+ PINCTRL_PIN(GPIO36, "GPIO36"),
+ PINCTRL_PIN(GPIO37, "GPIO37"),
+ PINCTRL_PIN(GPIO38, "GPIO38"),
+ PINCTRL_PIN(GPIO39, "GPIO39"),
+ PINCTRL_PIN(GPIO40, "GPIO40"),
+ PINCTRL_PIN(GPIO41, "GPIO41"),
+ PINCTRL_PIN(GPIO42, "GPIO42"),
+ PINCTRL_PIN(GPIO43, "GPIO43"),
+ PINCTRL_PIN(GPIO44, "GPIO44"),
+ PINCTRL_PIN(GPIO45, "GPIO45"),
+ PINCTRL_PIN(GPIO46, "GPIO46"),
+ PINCTRL_PIN(GPIO47, "GPIO47"),
+ PINCTRL_PIN(GPIO48, "GPIO48"),
+ PINCTRL_PIN(GPIO49, "GPIO49"),
+ PINCTRL_PIN(GPIO50, "GPIO50"),
+ PINCTRL_PIN(GPIO51, "GPIO51"),
+ PINCTRL_PIN(GPIO52, "GPIO52"),
+ PINCTRL_PIN(GPIO53, "GPIO53"),
+ PINCTRL_PIN(GPIO54, "GPIO54"),
+ PINCTRL_PIN(GPIO55, "GPIO55"),
+ PINCTRL_PIN(GPIO56, "GPIO56"),
+ PINCTRL_PIN(GPIO57, "GPIO57"),
+ PINCTRL_PIN(GPIO58, "GPIO58"),
+ PINCTRL_PIN(GPIO59, "GPIO59"),
+ PINCTRL_PIN(GPIO60, "GPIO60"),
+ PINCTRL_PIN(GPIO61, "GPIO61"),
+ PINCTRL_PIN(GPIO62, "GPIO62"),
+ PINCTRL_PIN(GPIO63, "GPIO63"),
+ PINCTRL_PIN(GPIO64, "GPIO64"),
+ PINCTRL_PIN(GPIO65, "GPIO65"),
+ PINCTRL_PIN(GPIO66, "GPIO66"),
+ PINCTRL_PIN(GPIO67, "GPIO67"),
+ PINCTRL_PIN(GPIO68, "GPIO68"),
+ PINCTRL_PIN(GPIO69, "GPIO69"),
+ PINCTRL_PIN(GPIO70, "GPIO70"),
+ PINCTRL_PIN(GPIO71, "GPIO71"),
+ PINCTRL_PIN(GPIO72, "GPIO72"),
+ PINCTRL_PIN(GPIO73, "GPIO73"),
+ PINCTRL_PIN(GPIO74, "GPIO74"),
+ PINCTRL_PIN(GPIO75, "GPIO75"),
+ PINCTRL_PIN(GPIO76, "GPIO76"),
+ PINCTRL_PIN(GPIO77, "GPIO77"),
+ PINCTRL_PIN(GPIO78, "GPIO78"),
+ PINCTRL_PIN(GPIO79, "GPIO79"),
+ PINCTRL_PIN(GPIO80, "GPIO80"),
+ PINCTRL_PIN(GPIO81, "GPIO81"),
+ PINCTRL_PIN(GPIO82, "GPIO82"),
+ PINCTRL_PIN(GPIO83, "GPIO83"),
+ PINCTRL_PIN(GPIO84, "GPIO84"),
+ PINCTRL_PIN(GPIO85, "GPIO85"),
+ PINCTRL_PIN(GPIO86, "GPIO86"),
+ PINCTRL_PIN(GPIO87, "GPIO87"),
+ PINCTRL_PIN(GPIO88, "GPIO88"),
+ PINCTRL_PIN(GPIO89, "GPIO89"),
+ PINCTRL_PIN(GPIO90, "GPIO90"),
+ PINCTRL_PIN(GPIO91, "GPIO91"),
+ PINCTRL_PIN(GPIO92, "GPIO92"),
+ PINCTRL_PIN(GPIO93, "GPIO93"),
+ PINCTRL_PIN(GPIO94, "GPIO94"),
+ PINCTRL_PIN(GPIO95, "GPIO95"),
+ PINCTRL_PIN(GPIO96, "GPIO96"),
+ PINCTRL_PIN(GPIO97, "GPIO97"),
+ PINCTRL_PIN(GPIO98, "GPIO98"),
+ PINCTRL_PIN(GPIO99, "GPIO99"),
+ PINCTRL_PIN(GPIO100, "GPIO100"),
+ PINCTRL_PIN(GPIO101, "GPIO101"),
+ PINCTRL_PIN(GPIO102, "GPIO102"),
+ PINCTRL_PIN(GPIO103, "GPIO103"),
+ PINCTRL_PIN(GPIO104, "GPIO104"),
+ PINCTRL_PIN(GPIO105, "GPIO105"),
+ PINCTRL_PIN(GPIO106, "GPIO106"),
+ PINCTRL_PIN(GPIO107, "GPIO107"),
+ PINCTRL_PIN(GPIO108, "GPIO108"),
+ PINCTRL_PIN(GPIO109, "GPIO109"),
+ PINCTRL_PIN(GPIO110, "GPIO110"),
+ PINCTRL_PIN(GPIO111, "GPIO111"),
+ PINCTRL_PIN(GPIO112, "GPIO112"),
+ PINCTRL_PIN(GPIO113, "GPIO113"),
+ PINCTRL_PIN(GPIO114, "GPIO114"),
+ PINCTRL_PIN(GPIO115, "GPIO115"),
+ PINCTRL_PIN(GPIO116, "GPIO116"),
+ PINCTRL_PIN(GPIO117, "GPIO117"),
+ PINCTRL_PIN(GPIO118, "GPIO118"),
+ PINCTRL_PIN(GPIO119, "GPIO119"),
+ PINCTRL_PIN(GPIO120, "GPIO120"),
+ PINCTRL_PIN(GPIO121, "GPIO121"),
+ PINCTRL_PIN(GPIO122, "GPIO122"),
+ PINCTRL_PIN(PWR_SCL, "PWR_SCL"),
+ PINCTRL_PIN(PWR_SDA, "PWR_SDA"),
+ PINCTRL_PIN(TDI, "TDI"),
+ PINCTRL_PIN(TMS, "TMS"),
+ PINCTRL_PIN(TCK, "TCK"),
+ PINCTRL_PIN(TDO, "TDO"),
+ PINCTRL_PIN(TRST, "TRST"),
+ PINCTRL_PIN(WAKEUP, "WAKEUP"),
+};
+
+struct pxa3xx_mfp_pin pxa168_mfp[] = {
+ /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */
+ MFPR_168(GPIO0, 0x04C, DFIO, NONE, NONE, MSP, MMC3_CMD, GPIO, MMC3, NONE),
+ MFPR_168(GPIO1, 0x050, DFIO, NONE, NONE, MSP, MMC3_CLK, GPIO, MMC3, NONE),
+ MFPR_168(GPIO2, 0x054, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE),
+ MFPR_168(GPIO3, 0x058, DFIO, NONE, NONE, NONE, NONE, GPIO, MMC3, NONE),
+ MFPR_168(GPIO4, 0x05C, DFIO, NONE, NONE, MSP_DAT3, NONE, GPIO, MMC3, NONE),
+ MFPR_168(GPIO5, 0x060, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE),
+ MFPR_168(GPIO6, 0x064, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE),
+ MFPR_168(GPIO7, 0x068, DFIO, NONE, NONE, MSP, NONE, GPIO, MMC3, NONE),
+ MFPR_168(GPIO8, 0x06C, DFIO, MMC2, UART3_TX, NONE, MMC2_CMD, GPIO, MMC3_CLK, NONE),
+ MFPR_168(GPIO9, 0x070, DFIO, MMC2, UART3, NONE, MMC2_CLK, GPIO, MMC3_CMD, NONE),
+ MFPR_168(GPIO10, 0x074, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP_DAT3, NONE),
+ MFPR_168(GPIO11, 0x078, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE),
+ MFPR_168(GPIO12, 0x07C, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE),
+ MFPR_168(GPIO13, 0x080, DFIO, MMC2, UART3, NONE, NONE, GPIO, MSP, NONE),
+ MFPR_168(GPIO14, 0x084, DFIO, MMC2, NONE, NONE, NONE, GPIO, MSP, NONE),
+ MFPR_168(GPIO15, 0x088, DFIO, MMC2, NONE, NONE, NONE, GPIO, MSP, NONE),
+ MFPR_168(GPIO16, 0x08C, GPIO, NAND, SMC_CS0, SMC_CS1, NONE, NONE, MMC3, NONE),
+ MFPR_168(GPIO17, 0x090, NAND, NONE, NONE, NONE, NONE, GPIO, MSP, NONE),
+ MFPR_168(GPIO18, 0x094, GPIO, NAND, SMC_CS1, SMC_CS0, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO19, 0x098, SMC_CS0, NONE, NONE, CF, NONE, GPIO, NONE, NONE),
+ MFPR_168(GPIO20, 0x09C, GPIO, NONE, SMC_CS1, CF, CF_RDY, NONE, NONE, NONE),
+ MFPR_168(GPIO21, 0x0A0, NAND, MMC2_CLK, NONE, NONE, NONE, GPIO, NONE, NONE),
+ MFPR_168(GPIO22, 0x0A4, NAND, MMC2_CMD, NONE, NONE, NONE, GPIO, NONE, NONE),
+ MFPR_168(GPIO23, 0x0A8, SMC, NAND, NONE, CF, NONE, GPIO, NONE, NONE),
+ MFPR_168(GPIO24, 0x0AC, NAND, NONE, NONE, NONE, NONE, GPIO, NONE, NONE),
+ MFPR_168(GPIO25, 0x0B0, SMC, NAND, NONE, CF, NONE, GPIO, NONE, NONE),
+ MFPR_168(GPIO26, 0x0B4, GPIO, NAND, NONE, NONE, CF, NONE, NONE, NONE),
+ MFPR_168(GPIO27, 0x0B8, SMC_INT, NAND, SMC, NONE, SMC_RDY, GPIO, NONE, NONE),
+ MFPR_168(GPIO28, 0x0BC, SMC_RDY, MMC4, SMC, CF_RDY, NONE, GPIO, MMC2_CMD, NONE),
+ MFPR_168(GPIO29, 0x0C0, SMC, MMC4, NONE, CF, NONE, GPIO, MMC2_CLK, KP_DK),
+ MFPR_168(GPIO30, 0x0C4, SMC, MMC4, UART3_TX, CF, NONE, GPIO, MMC2, KP_DK),
+ MFPR_168(GPIO31, 0x0C8, SMC, MMC4, UART3, CF, NONE, GPIO, MMC2, KP_DK),
+ MFPR_168(GPIO32, 0x0CC, SMC, MMC4, UART3, CF, NONE, GPIO, MMC2, KP_DK),
+ MFPR_168(GPIO33, 0x0D0, SMC, MMC4, UART3, CF, CF_nINPACK, GPIO, MMC2, KP_DK),
+ MFPR_168(GPIO34, 0x0D4, GPIO, NONE, SMC_CS1, CF, CF_nWAIT, NONE, MMC3, KP_DK),
+ MFPR_168(GPIO35, 0x0D8, GPIO, NONE, SMC, CF_nINPACK, NONE, NONE, MMC3_CMD, KP_DK),
+ MFPR_168(GPIO36, 0x0DC, GPIO, NONE, SMC, CF_nWAIT, NONE, NONE, MMC3_CLK, KP_DK),
+ MFPR_168(GPIO37, 0x000, GPIO, MMC1, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO38, 0x004, GPIO, MMC1, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO39, 0x008, GPIO, NONE, NONE, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO40, 0x00C, GPIO, MMC1, MSP, KP_MKOUT, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO41, 0x010, GPIO, MMC1, MSP, NONE, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO42, 0x014, GPIO, I2C, NONE, MSP, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO43, 0x018, GPIO, MMC1, MSP, MSP_INS, NONE, NONE, KP_MKIN, KP_DK),
+ MFPR_168(GPIO44, 0x01C, GPIO, MMC1, MSP_DAT3, MSP, CCIC, XP, KP_MKIN, KP_DK),
+ MFPR_168(GPIO45, 0x020, GPIO, NONE, NONE, MSP, CCIC, XP, NONE, KP_DK),
+ MFPR_168(GPIO46, 0x024, GPIO, MMC1, MSP_INS, MSP, CCIC, NONE, KP_MKOUT, KP_DK),
+ MFPR_168(GPIO47, 0x028, GPIO, NONE, NONE, MSP_INS, NONE, XP, NONE, KP_DK),
+ MFPR_168(GPIO48, 0x02C, GPIO, MMC1, NONE, MSP_DAT3, CCIC, NONE, NONE, KP_DK),
+ MFPR_168(GPIO49, 0x030, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO50, 0x034, GPIO, I2C, NONE, MSP, CCIC, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO51, 0x038, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO52, 0x03C, GPIO, MMC1, NONE, MSP, NONE, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO53, 0x040, GPIO, MMC1, NONE, NONE, NONE, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO54, 0x044, GPIO, MMC1, NONE, NONE, CCIC, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO55, 0x048, GPIO, NONE, NONE, MSP, CCIC, XD, KP_MKOUT, NONE),
+ MFPR_168(GPIO56, 0x0E0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO57, 0x0E4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO58, 0x0E8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO59, 0x0EC, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO60, 0x0F0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO61, 0x0F4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO62, 0x0F8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO63, 0x0FC, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO64, 0x100, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO65, 0x104, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO66, 0x108, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO67, 0x10C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO68, 0x110, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO69, 0x114, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO70, 0x118, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO71, 0x11C, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO72, 0x120, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO73, 0x124, GPIO, LCD, NONE, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO74, 0x128, GPIO, LCD, PWM, XD, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO75, 0x12C, GPIO, LCD, PWM, XD, ONE_WIRE, NONE, NONE, NONE),
+ MFPR_168(GPIO76, 0x130, GPIO, LCD, PWM, I2C, NONE, NONE, MSP_INS, NONE),
+ MFPR_168(GPIO77, 0x134, GPIO, LCD, PWM1, I2C, ONE_WIRE, NONE, XD, NONE),
+ MFPR_168(GPIO78, 0x138, GPIO, LCD, NONE, NONE, NONE, MMC4, NONE, NONE),
+ MFPR_168(GPIO79, 0x13C, GPIO, LCD, NONE, NONE, ONE_WIRE, MMC4, NONE, NONE),
+ MFPR_168(GPIO80, 0x140, GPIO, LCD, NONE, I2C, NONE, MMC4, NONE, NONE),
+ MFPR_168(GPIO81, 0x144, GPIO, LCD, NONE, I2C, ONE_WIRE, MMC4, NONE, NONE),
+ MFPR_168(GPIO82, 0x148, GPIO, LCD, PWM, NONE, NONE, MMC4, NONE, NONE),
+ MFPR_168(GPIO83, 0x14C, GPIO, LCD, PWM, NONE, RESET, MMC4, NONE, NONE),
+ MFPR_168(GPIO84, 0x150, GPIO, NONE, PWM, ONE_WIRE, PWM1, NONE, NONE, EXT_32K_IN),
+ MFPR_168(GPIO85, 0x154, GPIO, NONE, PWM1, NONE, NONE, NONE, NONE, USB),
+ MFPR_168(GPIO86, 0x158, GPIO, MMC2, UART2, NONE, JTAG, ETH_TX, SSP5_TX, SSP5),
+ MFPR_168(GPIO87, 0x15C, GPIO, MMC2, UART2, NONE, JTAG, ETH_TX, SSP5, SSP5_TX),
+ MFPR_168(GPIO88, 0x160, GPIO, MMC2, UART2, UART2_TX, JTAG, ETH_TX, ETH_RX, SSP5),
+ MFPR_168(GPIO89, 0x164, GPIO, MMC2, UART2_TX, UART2, JTAG, ETH_TX, ETH_RX, SSP5),
+ MFPR_168(GPIO90, 0x168, GPIO, MMC2, NONE, SSP3, JTAG, ETH_TX, ETH_RX, NONE),
+ MFPR_168(GPIO91, 0x16C, GPIO, MMC2, NONE, SSP3, SSP4, ETH_TX, ETH_RX, NONE),
+ MFPR_168(GPIO92, 0x170, GPIO, MMC2, NONE, SSP3, SSP3_TX, ETH, NONE, NONE),
+ MFPR_168(GPIO93, 0x174, GPIO, MMC2, NONE, SSP3_TX, SSP3, ETH, NONE, NONE),
+ MFPR_168(GPIO94, 0x178, GPIO, MMC2_CMD, SSP3, AC97_SYSCLK, AC97, ETH, NONE, NONE),
+ MFPR_168(GPIO95, 0x17C, GPIO, MMC2_CLK, NONE, NONE, AC97, ETH, NONE, NONE),
+ MFPR_168(GPIO96, 0x180, GPIO, PWM, NONE, MMC2, NONE, ETH_RX, ETH_TX, NONE),
+ MFPR_168(GPIO97, 0x184, GPIO, PWM, ONE_WIRE, NONE, NONE, ETH_RX, ETH_TX, NONE),
+ MFPR_168(GPIO98, 0x188, GPIO, PWM1, UART3_TX, UART3, NONE, ETH_RX, ETH_TX, NONE),
+ MFPR_168(GPIO99, 0x18C, GPIO, ONE_WIRE, UART3, UART3_TX, NONE, ETH_RX, ETH_TX, NONE),
+ MFPR_168(GPIO100, 0x190, GPIO, NONE, UART3_CTS, UART3, NONE, ETH, NONE, NONE),
+ MFPR_168(GPIO101, 0x194, GPIO, NONE, UART3, UART3_CTS, NONE, ETH, NONE, NONE),
+ MFPR_168(GPIO102, 0x198, GPIO, I2C, UART3, SSP4, NONE, NONE, NONE, NONE),
+ MFPR_168(GPIO103, 0x19C, GPIO, I2C, UART3, SSP4, SSP2, ETH, NONE, NONE),
+ MFPR_168(GPIO104, 0x1A0, GPIO, PWM, UART1, SSP4, SSP4_TX, AC97, KP_MKOUT, NONE),
+ MFPR_168(GPIO105, 0x1A4, GPIO, I2C, UART1, SSP4_TX, SSP4, AC97, KP_MKOUT, NONE),
+ MFPR_168(GPIO106, 0x1A8, GPIO, I2C, PWM1, AC97_SYSCLK, MMC2, NONE, KP_MKOUT, NONE),
+ MFPR_168(GPIO107, 0x1AC, GPIO, UART1_TX, UART1, NONE, SSP2, MSP_DAT3, NONE, KP_MKIN),
+ MFPR_168(GPIO108, 0x1B0, GPIO, UART1, UART1_TX, NONE, SSP2_TX, MSP, NONE, KP_MKIN),
+ MFPR_168(GPIO109, 0x1B4, GPIO, UART1_CTS, UART1, NONE, AC97_SYSCLK, MSP, NONE, KP_MKIN),
+ MFPR_168(GPIO110, 0x1B8, GPIO, UART1, UART1_CTS, NONE, SMC_RDY, MSP, NONE, KP_MKIN),
+ MFPR_168(GPIO111, 0x1BC, GPIO, UART1_nRI, UART1, SSP3, SSP2, MSP, XD, KP_MKOUT),
+ MFPR_168(GPIO112, 0x1C0, GPIO, UART1_DTR, UART1, ONE_WIRE, SSP2, MSP, XD, KP_MKOUT),
+ MFPR_168(GPIO113, 0x1C4, GPIO, NONE, NONE, NONE, NONE, NONE, AC97_SYSCLK, NONE),
+ MFPR_168(GPIO114, 0x1C8, GPIO, SSP1, NONE, NONE, NONE, NONE, AC97, NONE),
+ MFPR_168(GPIO115, 0x1CC, GPIO, SSP1, NONE, NONE, NONE, NONE, AC97, NONE),
+ MFPR_168(GPIO116, 0x1D0, GPIO, SSP1_TX, SSP1, NONE, NONE, NONE, AC97, NONE),
+ MFPR_168(GPIO117, 0x1D4, GPIO, SSP1, SSP1_TX, NONE, MMC2_CMD, NONE, AC97, NONE),
+ MFPR_168(GPIO118, 0x1D8, GPIO, SSP2, NONE, NONE, MMC2_CLK, NONE, AC97, KP_MKIN),
+ MFPR_168(GPIO119, 0x1DC, GPIO, SSP2, NONE, NONE, MMC2, NONE, AC97, KP_MKIN),
+ MFPR_168(GPIO120, 0x1E0, GPIO, SSP2, SSP2_TX, NONE, MMC2, NONE, NONE, KP_MKIN),
+ MFPR_168(GPIO121, 0x1E4, GPIO, SSP2_TX, SSP2, NONE, MMC2, NONE, NONE, KP_MKIN),
+ MFPR_168(GPIO122, 0x1E8, GPIO, AC97_SYSCLK, SSP2, PWM, MMC2, NONE, NONE, NONE),
+ MFPR_168(PWR_SCL, 0x1EC, PWRI2C, NONE, NONE, NONE, NONE, NONE, GPIO, MMC4),
+ MFPR_168(PWR_SDA, 0x1F0, PWRI2C, NONE, NONE, NONE, NONE, NONE, GPIO, NONE),
+ MFPR_168(TDI, 0x1F4, JTAG, PWM1, UART2, MMC4, SSP5, NONE, XD, MMC4),
+ MFPR_168(TMS, 0x1F8, JTAG, PWM, UART2, NONE, SSP5, NONE, XD, MMC4),
+ MFPR_168(TCK, 0x1FC, JTAG, PWM, UART2, UART2_TX, SSP5, NONE, XD, MMC4),
+ MFPR_168(TDO, 0x200, JTAG, PWM, UART2_TX, UART2, SSP5_TX, NONE, XD, MMC4),
+ MFPR_168(TRST, 0x204, JTAG, ONE_WIRE, SSP2, SSP3, AC97_SYSCLK, NONE, XD, MMC4),
+ MFPR_168(WAKEUP, 0x208, WAKEUP, ONE_WIRE, PWM1, PWM, SSP2, NONE, GPIO, MMC4),
+};
+
+static const unsigned p168_jtag_pin1[] = {TDI, TMS, TCK, TDO, TRST};
+static const unsigned p168_wakeup_pin1[] = {WAKEUP};
+static const unsigned p168_ssp1rx_pin1[] = {GPIO114, GPIO115, GPIO116};
+static const unsigned p168_ssp1tx_pin1[] = {GPIO117};
+static const unsigned p168_ssp4rx_pin1[] = {GPIO102, GPIO103, GPIO104};
+static const unsigned p168_ssp4tx_pin1[] = {GPIO105};
+static const unsigned p168_ssp5rx_pin1[] = {GPIO86, GPIO88, GPIO89};
+static const unsigned p168_ssp5tx_pin1[] = {GPIO87};
+static const unsigned p168_i2c_pin1[] = {GPIO105, GPIO106};
+static const unsigned p168_pwri2c_pin1[] = {PWR_SCL, PWR_SDA};
+static const unsigned p168_mmc1_pin1[] = {GPIO40, GPIO41, GPIO43, GPIO46,
+ GPIO49, GPIO51, GPIO52, GPIO53};
+static const unsigned p168_mmc2_data_pin1[] = {GPIO90, GPIO91, GPIO92, GPIO93};
+static const unsigned p168_mmc2_cmd_pin1[] = {GPIO94};
+static const unsigned p168_mmc2_clk_pin1[] = {GPIO95};
+static const unsigned p168_mmc3_data_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3,
+ GPIO4, GPIO5, GPIO6, GPIO7};
+static const unsigned p168_mmc3_cmd_pin1[] = {GPIO9};
+static const unsigned p168_mmc3_clk_pin1[] = {GPIO8};
+static const unsigned p168_eth_pin1[] = {GPIO92, GPIO93, GPIO100, GPIO101,
+ GPIO103};
+static const unsigned p168_ethtx_pin1[] = {GPIO86, GPIO87, GPIO88, GPIO89,
+ GPIO90, GPIO91};
+static const unsigned p168_ethrx_pin1[] = {GPIO94, GPIO95, GPIO96, GPIO97,
+ GPIO98, GPIO99};
+static const unsigned p168_uart1rx_pin1[] = {GPIO107};
+static const unsigned p168_uart1tx_pin1[] = {GPIO108};
+static const unsigned p168_uart3rx_pin1[] = {GPIO98, GPIO100, GPIO101};
+static const unsigned p168_uart3tx_pin1[] = {GPIO99};
+static const unsigned p168_msp_pin1[] = {GPIO40, GPIO41, GPIO42, GPIO43,
+ GPIO44, GPIO50};
+static const unsigned p168_ccic_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40,
+ GPIO41, GPIO42, GPIO44, GPIO45, GPIO46, GPIO48, GPIO54, GPIO55};
+static const unsigned p168_xd_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40,
+ GPIO41, GPIO42, GPIO44, GPIO45, GPIO47, GPIO48, GPIO49, GPIO50,
+ GPIO51, GPIO52};
+static const unsigned p168_lcd_pin1[] = {GPIO56, GPIO57, GPIO58, GPIO59,
+ GPIO60, GPIO61, GPIO62, GPIO63, GPIO64, GPIO65, GPIO66, GPIO67,
+ GPIO68, GPIO69, GPIO70, GPIO71, GPIO72, GPIO73, GPIO74, GPIO75,
+ GPIO76, GPIO77, GPIO78, GPIO79, GPIO80, GPIO81, GPIO82, GPIO83};
+static const unsigned p168_dfio_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3,
+ GPIO4, GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12,
+ GPIO13, GPIO14, GPIO15};
+static const unsigned p168_nand_pin1[] = {GPIO16, GPIO17, GPIO21, GPIO22,
+ GPIO24, GPIO26};
+static const unsigned p168_smc_pin1[] = {GPIO23, GPIO25, GPIO29, GPIO35,
+ GPIO36};
+static const unsigned p168_smccs0_pin1[] = {GPIO18};
+static const unsigned p168_smccs1_pin1[] = {GPIO34};
+static const unsigned p168_smcrdy_pin1[] = {GPIO28};
+static const unsigned p168_ac97sysclk_pin1[] = {GPIO113};
+static const unsigned p168_ac97_pin1[] = {GPIO114, GPIO115, GPIO117, GPIO118,
+ GPIO119};
+static const unsigned p168_cf_pin1[] = {GPIO19, GPIO20, GPIO23, GPIO25,
+ GPIO28, GPIO29, GPIO30, GPIO31, GPIO32, GPIO33, GPIO34, GPIO35,
+ GPIO36};
+static const unsigned p168_kpmkin_pin1[] = {GPIO109, GPIO110, GPIO121};
+static const unsigned p168_kpmkout_pin1[] = {GPIO111, GPIO112};
+static const unsigned p168_gpio86_pin1[] = {WAKEUP};
+static const unsigned p168_gpio86_pin2[] = {GPIO86};
+static const unsigned p168_gpio87_pin1[] = {GPIO87};
+static const unsigned p168_gpio87_pin2[] = {PWR_SDA};
+static const unsigned p168_gpio88_pin1[] = {GPIO88};
+static const unsigned p168_gpio88_pin2[] = {PWR_SCL};
+
+static struct pxa3xx_pin_group pxa168_grps[] = {
+ GRP_168("uart1rx-1", UART1, p168_uart1rx_pin1),
+ GRP_168("uart1tx-1", UART1_TX, p168_uart1tx_pin1),
+ GRP_168("uart3rx-1", UART3, p168_uart3rx_pin1),
+ GRP_168("uart3tx-1", UART3_TX, p168_uart3tx_pin1),
+ GRP_168("ssp1rx-1", SSP1, p168_ssp1rx_pin1),
+ GRP_168("ssp1tx-1", SSP1_TX, p168_ssp1tx_pin1),
+ GRP_168("ssp4rx-1", SSP4, p168_ssp4rx_pin1),
+ GRP_168("ssp4tx-1", SSP4_TX, p168_ssp4tx_pin1),
+ GRP_168("ssp5rx-1", SSP5, p168_ssp5rx_pin1),
+ GRP_168("ssp5tx-1", SSP5_TX, p168_ssp5tx_pin1),
+ GRP_168("jtag", JTAG, p168_jtag_pin1),
+ GRP_168("wakeup", WAKEUP, p168_wakeup_pin1),
+ GRP_168("i2c", I2C, p168_i2c_pin1),
+ GRP_168("pwri2c", PWRI2C, p168_pwri2c_pin1),
+ GRP_168("mmc1 8p1", MMC1, p168_mmc1_pin1),
+ GRP_168("mmc2 4p1", MMC2, p168_mmc2_data_pin1),
+ GRP_168("mmc2 cmd1", MMC2_CMD, p168_mmc2_cmd_pin1),
+ GRP_168("mmc2 clk1", MMC2_CLK, p168_mmc2_clk_pin1),
+ GRP_168("mmc3 8p1", MMC3, p168_mmc3_data_pin1),
+ GRP_168("mmc3 cmd1", MMC3_CMD, p168_mmc3_cmd_pin1),
+ GRP_168("mmc3 clk1", MMC3_CLK, p168_mmc3_clk_pin1),
+ GRP_168("eth", ETH, p168_eth_pin1),
+ GRP_168("eth rx", ETH_RX, p168_ethrx_pin1),
+ GRP_168("eth tx", ETH_TX, p168_ethtx_pin1),
+ GRP_168("msp", MSP, p168_msp_pin1),
+ GRP_168("ccic", CCIC, p168_ccic_pin1),
+ GRP_168("xd", XD, p168_xd_pin1),
+ GRP_168("lcd", LCD, p168_lcd_pin1),
+ GRP_168("dfio", DFIO, p168_dfio_pin1),
+ GRP_168("nand", NAND, p168_nand_pin1),
+ GRP_168("smc", SMC, p168_smc_pin1),
+ GRP_168("smc cs0", SMC_CS0, p168_smccs0_pin1),
+ GRP_168("smc cs1", SMC_CS1, p168_smccs1_pin1),
+ GRP_168("smc rdy", SMC_RDY, p168_smcrdy_pin1),
+ GRP_168("ac97 sysclk", AC97_SYSCLK, p168_ac97sysclk_pin1),
+ GRP_168("ac97", AC97, p168_ac97_pin1),
+ GRP_168("cf", CF, p168_cf_pin1),
+ GRP_168("kp mkin 3p1", KP_MKIN, p168_kpmkin_pin1),
+ GRP_168("kp mkout 2p1", KP_MKOUT, p168_kpmkout_pin1),
+ GRP_168("gpio86-1", GPIO, p168_gpio86_pin1),
+ GRP_168("gpio86-2", GPIO, p168_gpio86_pin2),
+ GRP_168("gpio87-1", GPIO, p168_gpio87_pin1),
+ GRP_168("gpio87-2", GPIO, p168_gpio87_pin2),
+ GRP_168("gpio88-1", GPIO, p168_gpio88_pin1),
+ GRP_168("gpio88-2", GPIO, p168_gpio88_pin2),
+};
+
+static const char * const p168_uart1rx_grps[] = {"uart1rx-1"};
+static const char * const p168_uart1tx_grps[] = {"uart1tx-1"};
+static const char * const p168_uart3rx_grps[] = {"uart3rx-1"};
+static const char * const p168_uart3tx_grps[] = {"uart3tx-1"};
+static const char * const p168_ssp1rx_grps[] = {"ssp1rx-1"};
+static const char * const p168_ssp1tx_grps[] = {"ssp1tx-1"};
+static const char * const p168_ssp4rx_grps[] = {"ssp4rx-1"};
+static const char * const p168_ssp4tx_grps[] = {"ssp4tx-1"};
+static const char * const p168_ssp5rx_grps[] = {"ssp5rx-1"};
+static const char * const p168_ssp5tx_grps[] = {"ssp5tx-1"};
+static const char * const p168_i2c_grps[] = {"i2c"};
+static const char * const p168_pwri2c_grps[] = {"pwri2c"};
+static const char * const p168_mmc1_grps[] = {"mmc1 8p1"};
+static const char * const p168_mmc2_data_grps[] = {"mmc2 4p1"};
+static const char * const p168_mmc2_cmd_grps[] = {"mmc2 cmd1"};
+static const char * const p168_mmc2_clk_grps[] = {"mmc2 clk1"};
+static const char * const p168_mmc3_data_grps[] = {"mmc3 8p1"};
+static const char * const p168_mmc3_cmd_grps[] = {"mmc3 cmd1"};
+static const char * const p168_mmc3_clk_grps[] = {"mmc3 clk1"};
+static const char * const p168_eth_grps[] = {"eth"};
+static const char * const p168_ethrx_grps[] = {"eth rx"};
+static const char * const p168_ethtx_grps[] = {"eth tx"};
+static const char * const p168_msp_grps[] = {"msp"};
+static const char * const p168_ccic_grps[] = {"ccic"};
+static const char * const p168_xd_grps[] = {"xd"};
+static const char * const p168_lcd_grps[] = {"lcd"};
+static const char * const p168_dfio_grps[] = {"dfio"};
+static const char * const p168_nand_grps[] = {"nand"};
+static const char * const p168_smc_grps[] = {"smc"};
+static const char * const p168_smccs0_grps[] = {"smc cs0"};
+static const char * const p168_smccs1_grps[] = {"smc cs1"};
+static const char * const p168_smcrdy_grps[] = {"smc rdy"};
+static const char * const p168_ac97sysclk_grps[] = {"ac97 sysclk"};
+static const char * const p168_ac97_grps[] = {"ac97"};
+static const char * const p168_cf_grps[] = {"cf"};
+static const char * const p168_kpmkin_grps[] = {"kp mkin 3p1"};
+static const char * const p168_kpmkout_grps[] = {"kp mkout 2p1"};
+static const char * const p168_gpio86_grps[] = {"gpio86-1", "gpio86-2"};
+static const char * const p168_gpio87_grps[] = {"gpio87-1", "gpio87-2"};
+static const char * const p168_gpio88_grps[] = {"gpio88-1", "gpio88-2"};
+
+static struct pxa3xx_pmx_func pxa168_funcs[] = {
+ {"uart1 rx", ARRAY_AND_SIZE(p168_uart1rx_grps)},
+ {"uart1 tx", ARRAY_AND_SIZE(p168_uart1tx_grps)},
+ {"uart3 rx", ARRAY_AND_SIZE(p168_uart3rx_grps)},
+ {"uart3 tx", ARRAY_AND_SIZE(p168_uart3tx_grps)},
+ {"ssp1 rx", ARRAY_AND_SIZE(p168_ssp1rx_grps)},
+ {"ssp1 tx", ARRAY_AND_SIZE(p168_ssp1tx_grps)},
+ {"ssp4 rx", ARRAY_AND_SIZE(p168_ssp4rx_grps)},
+ {"ssp4 tx", ARRAY_AND_SIZE(p168_ssp4tx_grps)},
+ {"ssp5 rx", ARRAY_AND_SIZE(p168_ssp5rx_grps)},
+ {"ssp5 tx", ARRAY_AND_SIZE(p168_ssp5tx_grps)},
+ {"i2c", ARRAY_AND_SIZE(p168_i2c_grps)},
+ {"pwri2c", ARRAY_AND_SIZE(p168_pwri2c_grps)},
+ {"mmc1", ARRAY_AND_SIZE(p168_mmc1_grps)},
+ {"mmc2", ARRAY_AND_SIZE(p168_mmc2_data_grps)},
+ {"mmc2 cmd", ARRAY_AND_SIZE(p168_mmc2_cmd_grps)},
+ {"mmc2 clk", ARRAY_AND_SIZE(p168_mmc2_clk_grps)},
+ {"mmc3", ARRAY_AND_SIZE(p168_mmc3_data_grps)},
+ {"mmc3 cmd", ARRAY_AND_SIZE(p168_mmc3_cmd_grps)},
+ {"mmc3 clk", ARRAY_AND_SIZE(p168_mmc3_clk_grps)},
+ {"eth", ARRAY_AND_SIZE(p168_eth_grps)},
+ {"eth rx", ARRAY_AND_SIZE(p168_ethrx_grps)},
+ {"eth tx", ARRAY_AND_SIZE(p168_ethtx_grps)},
+ {"msp", ARRAY_AND_SIZE(p168_msp_grps)},
+ {"ccic", ARRAY_AND_SIZE(p168_ccic_grps)},
+ {"xd", ARRAY_AND_SIZE(p168_xd_grps)},
+ {"lcd", ARRAY_AND_SIZE(p168_lcd_grps)},
+ {"dfio", ARRAY_AND_SIZE(p168_dfio_grps)},
+ {"nand", ARRAY_AND_SIZE(p168_nand_grps)},
+ {"smc", ARRAY_AND_SIZE(p168_smc_grps)},
+ {"smc cs0", ARRAY_AND_SIZE(p168_smccs0_grps)},
+ {"smc cs1", ARRAY_AND_SIZE(p168_smccs1_grps)},
+ {"smc rdy", ARRAY_AND_SIZE(p168_smcrdy_grps)},
+ {"ac97", ARRAY_AND_SIZE(p168_ac97_grps)},
+ {"ac97 sysclk", ARRAY_AND_SIZE(p168_ac97sysclk_grps)},
+ {"cf", ARRAY_AND_SIZE(p168_cf_grps)},
+ {"kpmkin", ARRAY_AND_SIZE(p168_kpmkin_grps)},
+ {"kpmkout", ARRAY_AND_SIZE(p168_kpmkout_grps)},
+ {"gpio86", ARRAY_AND_SIZE(p168_gpio86_grps)},
+ {"gpio87", ARRAY_AND_SIZE(p168_gpio87_grps)},
+ {"gpio88", ARRAY_AND_SIZE(p168_gpio88_grps)},
+};
+
+static struct pinctrl_desc pxa168_pctrl_desc = {
+ .name = "pxa168-pinctrl",
+ .owner = THIS_MODULE,
+};
+
+static struct pxa3xx_pinmux_info pxa168_info = {
+ .mfp = pxa168_mfp,
+ .num_mfp = ARRAY_SIZE(pxa168_mfp),
+ .grps = pxa168_grps,
+ .num_grps = ARRAY_SIZE(pxa168_grps),
+ .funcs = pxa168_funcs,
+ .num_funcs = ARRAY_SIZE(pxa168_funcs),
+ .num_gpio = 128,
+ .desc = &pxa168_pctrl_desc,
+ .pads = pxa168_pads,
+ .num_pads = ARRAY_SIZE(pxa168_pads),
+
+ .cputype = PINCTRL_PXA168,
+ .ds_mask = PXA168_DS_MASK,
+ .ds_shift = PXA168_DS_SHIFT,
+};
+
+static int __devinit pxa168_pinmux_probe(struct platform_device *pdev)
+{
+ return pxa3xx_pinctrl_register(pdev, &pxa168_info);
+}
+
+static int __devexit pxa168_pinmux_remove(struct platform_device *pdev)
+{
+ return pxa3xx_pinctrl_unregister(pdev);
+}
+
+static struct platform_driver pxa168_pinmux_driver = {
+ .driver = {
+ .name = "pxa168-pinmux",
+ .owner = THIS_MODULE,
+ },
+ .probe = pxa168_pinmux_probe,
+ .remove = __devexit_p(pxa168_pinmux_remove),
+};
+
+static int __init pxa168_pinmux_init(void)
+{
+ return platform_driver_register(&pxa168_pinmux_driver);
+}
+core_initcall_sync(pxa168_pinmux_init);
+
+static void __exit pxa168_pinmux_exit(void)
+{
+ platform_driver_unregister(&pxa168_pinmux_driver);
+}
+module_exit(pxa168_pinmux_exit);
+
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_DESCRIPTION("PXA3xx pin control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-pxa3xx.c b/drivers/pinctrl/pinctrl-pxa3xx.c
new file mode 100644
index 000000000000..079dce0e93e9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pxa3xx.c
@@ -0,0 +1,244 @@
+/*
+ * linux/drivers/pinctrl/pinctrl-pxa3xx.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2011, Marvell Technology Group Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "pinctrl-pxa3xx.h"
+
+static struct pinctrl_gpio_range pxa3xx_pinctrl_gpio_range = {
+ .name = "PXA3xx GPIO",
+ .id = 0,
+ .base = 0,
+ .pin_base = 0,
+};
+
+static int pxa3xx_list_groups(struct pinctrl_dev *pctrldev, unsigned selector)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ if (selector >= info->num_grps)
+ return -EINVAL;
+ return 0;
+}
+
+static const char *pxa3xx_get_group_name(struct pinctrl_dev *pctrldev,
+ unsigned selector)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ if (selector >= info->num_grps)
+ return NULL;
+ return info->grps[selector].name;
+}
+
+static int pxa3xx_get_group_pins(struct pinctrl_dev *pctrldev,
+ unsigned selector,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ if (selector >= info->num_grps)
+ return -EINVAL;
+ *pins = info->grps[selector].pins;
+ *num_pins = info->grps[selector].npins;
+ return 0;
+}
+
+static struct pinctrl_ops pxa3xx_pctrl_ops = {
+ .list_groups = pxa3xx_list_groups,
+ .get_group_name = pxa3xx_get_group_name,
+ .get_group_pins = pxa3xx_get_group_pins,
+};
+
+static int pxa3xx_pmx_list_func(struct pinctrl_dev *pctrldev, unsigned func)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ if (func >= info->num_funcs)
+ return -EINVAL;
+ return 0;
+}
+
+static const char *pxa3xx_pmx_get_func_name(struct pinctrl_dev *pctrldev,
+ unsigned func)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ return info->funcs[func].name;
+}
+
+static int pxa3xx_pmx_get_groups(struct pinctrl_dev *pctrldev, unsigned func,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ *groups = info->funcs[func].groups;
+ *num_groups = info->funcs[func].num_groups;
+ return 0;
+}
+
+/* Return function number. If failure, return negative value. */
+static int match_mux(struct pxa3xx_mfp_pin *mfp, unsigned mux)
+{
+ int i;
+ for (i = 0; i < PXA3xx_MAX_MUX; i++) {
+ if (mfp->func[i] == mux)
+ break;
+ }
+ if (i >= PXA3xx_MAX_MUX)
+ return -EINVAL;
+ return i;
+}
+
+/* check whether current pin configuration is valid. Negative for failure */
+static int match_group_mux(struct pxa3xx_pin_group *grp,
+ struct pxa3xx_pinmux_info *info,
+ unsigned mux)
+{
+ int i, pin, ret = 0;
+ for (i = 0; i < grp->npins; i++) {
+ pin = grp->pins[i];
+ ret = match_mux(&info->mfp[pin], mux);
+ if (ret < 0) {
+ dev_err(info->dev, "Can't find mux %d on pin%d\n",
+ mux, pin);
+ break;
+ }
+ }
+ return ret;
+}
+
+static int pxa3xx_pmx_enable(struct pinctrl_dev *pctrldev, unsigned func,
+ unsigned group)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ struct pxa3xx_pin_group *pin_grp = &info->grps[group];
+ unsigned int data;
+ int i, mfpr, pin, pin_func;
+
+ if (!pin_grp->npins ||
+ (match_group_mux(pin_grp, info, pin_grp->mux) < 0)) {
+ dev_err(info->dev, "Failed to set the pin group: %d\n", group);
+ return -EINVAL;
+ }
+ for (i = 0; i < pin_grp->npins; i++) {
+ pin = pin_grp->pins[i];
+ pin_func = match_mux(&info->mfp[pin], pin_grp->mux);
+ mfpr = info->mfp[pin].mfpr;
+ data = readl_relaxed(info->virt_base + mfpr);
+ data &= ~MFPR_FUNC_MASK;
+ data |= pin_func;
+ writel_relaxed(data, info->virt_base + mfpr);
+ }
+ return 0;
+}
+
+static void pxa3xx_pmx_disable(struct pinctrl_dev *pctrldev, unsigned func,
+ unsigned group)
+{
+}
+
+static int pxa3xx_pmx_request_gpio(struct pinctrl_dev *pctrldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin)
+{
+ struct pxa3xx_pinmux_info *info = pinctrl_dev_get_drvdata(pctrldev);
+ unsigned int data;
+ int pin_func, mfpr;
+
+ pin_func = match_mux(&info->mfp[pin], PXA3xx_MUX_GPIO);
+ if (pin_func < 0) {
+ dev_err(info->dev, "No GPIO function on pin%d (%s)\n",
+ pin, info->pads[pin].name);
+ return -EINVAL;
+ }
+ mfpr = info->mfp[pin].mfpr;
+ /* write gpio function into mfpr register */
+ data = readl_relaxed(info->virt_base + mfpr) & ~MFPR_FUNC_MASK;
+ data |= pin_func;
+ writel_relaxed(data, info->virt_base + mfpr);
+ return 0;
+}
+
+static struct pinmux_ops pxa3xx_pmx_ops = {
+ .list_functions = pxa3xx_pmx_list_func,
+ .get_function_name = pxa3xx_pmx_get_func_name,
+ .get_function_groups = pxa3xx_pmx_get_groups,
+ .enable = pxa3xx_pmx_enable,
+ .disable = pxa3xx_pmx_disable,
+ .gpio_request_enable = pxa3xx_pmx_request_gpio,
+};
+
+int pxa3xx_pinctrl_register(struct platform_device *pdev,
+ struct pxa3xx_pinmux_info *info)
+{
+ struct pinctrl_desc *desc;
+ struct resource *res;
+ int ret = 0;
+
+ if (!info || !info->cputype)
+ return -EINVAL;
+ desc = info->desc;
+ desc->pins = info->pads;
+ desc->npins = info->num_pads;
+ desc->pctlops = &pxa3xx_pctrl_ops;
+ desc->pmxops = &pxa3xx_pmx_ops;
+ info->dev = &pdev->dev;
+ pxa3xx_pinctrl_gpio_range.npins = info->num_gpio;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENOENT;
+ info->phy_base = res->start;
+ info->phy_size = resource_size(res);
+ info->virt_base = ioremap(info->phy_base, info->phy_size);
+ if (!info->virt_base)
+ return -ENOMEM;
+ info->pctrl = pinctrl_register(desc, &pdev->dev, info);
+ if (!info->pctrl) {
+ dev_err(&pdev->dev, "failed to register PXA pinmux driver\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ pinctrl_add_gpio_range(info->pctrl, &pxa3xx_pinctrl_gpio_range);
+ platform_set_drvdata(pdev, info);
+ return 0;
+err:
+ iounmap(info->virt_base);
+ return ret;
+}
+
+int pxa3xx_pinctrl_unregister(struct platform_device *pdev)
+{
+ struct pxa3xx_pinmux_info *info = platform_get_drvdata(pdev);
+
+ pinctrl_unregister(info->pctrl);
+ iounmap(info->virt_base);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static int __init pxa3xx_pinctrl_init(void)
+{
+ pr_info("pxa3xx-pinctrl: PXA3xx pinctrl driver initializing\n");
+ return 0;
+}
+core_initcall_sync(pxa3xx_pinctrl_init);
+
+static void __exit pxa3xx_pinctrl_exit(void)
+{
+}
+module_exit(pxa3xx_pinctrl_exit);
+
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_DESCRIPTION("PXA3xx pin control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-pxa3xx.h b/drivers/pinctrl/pinctrl-pxa3xx.h
new file mode 100644
index 000000000000..8135744d6599
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pxa3xx.h
@@ -0,0 +1,264 @@
+/*
+ * linux/drivers/pinctrl/pinctrl-pxa3xx.h
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2011, Marvell Technology Group Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ */
+
+#ifndef __PINCTRL_PXA3XX_H
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
+
+#define PXA3xx_MUX_GPIO 0
+
+#define PXA3xx_MAX_MUX 8
+#define MFPR_FUNC_MASK 0x7
+
+enum pxa_cpu_type {
+ PINCTRL_INVALID = 0,
+ PINCTRL_PXA300,
+ PINCTRL_PXA310,
+ PINCTRL_PXA320,
+ PINCTRL_PXA168,
+ PINCTRL_PXA910,
+ PINCTRL_PXA930,
+ PINCTRL_PXA955,
+ PINCTRL_MMP2,
+ PINCTRL_MAX,
+};
+
+struct pxa3xx_mfp_pin {
+ const char *name;
+ const unsigned int pin;
+ const unsigned int mfpr; /* register offset */
+ const unsigned short func[8];
+};
+
+struct pxa3xx_pin_group {
+ const char *name;
+ const unsigned mux;
+ const unsigned *pins;
+ const unsigned npins;
+};
+
+struct pxa3xx_pmx_func {
+ const char *name;
+ const char * const * groups;
+ const unsigned num_groups;
+};
+
+struct pxa3xx_pinmux_info {
+ struct device *dev;
+ struct pinctrl_dev *pctrl;
+ enum pxa_cpu_type cputype;
+ unsigned int phy_base;
+ unsigned int phy_size;
+ void __iomem *virt_base;
+
+ struct pxa3xx_mfp_pin *mfp;
+ unsigned int num_mfp;
+ struct pxa3xx_pin_group *grps;
+ unsigned int num_grps;
+ struct pxa3xx_pmx_func *funcs;
+ unsigned int num_funcs;
+ unsigned int num_gpio;
+ struct pinctrl_desc *desc;
+ struct pinctrl_pin_desc *pads;
+ unsigned int num_pads;
+
+ unsigned ds_mask; /* drive strength mask */
+ unsigned ds_shift; /* drive strength shift */
+ unsigned slp_mask; /* sleep mask */
+ unsigned slp_input_low;
+ unsigned slp_input_high;
+ unsigned slp_output_low;
+ unsigned slp_output_high;
+ unsigned slp_float;
+};
+
+enum pxa3xx_pin_list {
+ GPIO0 = 0,
+ GPIO1,
+ GPIO2,
+ GPIO3,
+ GPIO4,
+ GPIO5,
+ GPIO6,
+ GPIO7,
+ GPIO8,
+ GPIO9,
+ GPIO10, /* 10 */
+ GPIO11,
+ GPIO12,
+ GPIO13,
+ GPIO14,
+ GPIO15,
+ GPIO16,
+ GPIO17,
+ GPIO18,
+ GPIO19,
+ GPIO20, /* 20 */
+ GPIO21,
+ GPIO22,
+ GPIO23,
+ GPIO24,
+ GPIO25,
+ GPIO26,
+ GPIO27,
+ GPIO28,
+ GPIO29,
+ GPIO30, /* 30 */
+ GPIO31,
+ GPIO32,
+ GPIO33,
+ GPIO34,
+ GPIO35,
+ GPIO36,
+ GPIO37,
+ GPIO38,
+ GPIO39,
+ GPIO40, /* 40 */
+ GPIO41,
+ GPIO42,
+ GPIO43,
+ GPIO44,
+ GPIO45,
+ GPIO46,
+ GPIO47,
+ GPIO48,
+ GPIO49,
+ GPIO50, /* 50 */
+ GPIO51,
+ GPIO52,
+ GPIO53,
+ GPIO54,
+ GPIO55,
+ GPIO56,
+ GPIO57,
+ GPIO58,
+ GPIO59,
+ GPIO60, /* 60 */
+ GPIO61,
+ GPIO62,
+ GPIO63,
+ GPIO64,
+ GPIO65,
+ GPIO66,
+ GPIO67,
+ GPIO68,
+ GPIO69,
+ GPIO70, /* 70 */
+ GPIO71,
+ GPIO72,
+ GPIO73,
+ GPIO74,
+ GPIO75,
+ GPIO76,
+ GPIO77,
+ GPIO78,
+ GPIO79,
+ GPIO80, /* 80 */
+ GPIO81,
+ GPIO82,
+ GPIO83,
+ GPIO84,
+ GPIO85,
+ GPIO86,
+ GPIO87,
+ GPIO88,
+ GPIO89,
+ GPIO90, /* 90 */
+ GPIO91,
+ GPIO92,
+ GPIO93,
+ GPIO94,
+ GPIO95,
+ GPIO96,
+ GPIO97,
+ GPIO98,
+ GPIO99,
+ GPIO100, /* 100 */
+ GPIO101,
+ GPIO102,
+ GPIO103,
+ GPIO104,
+ GPIO105,
+ GPIO106,
+ GPIO107,
+ GPIO108,
+ GPIO109,
+ GPIO110, /* 110 */
+ GPIO111,
+ GPIO112,
+ GPIO113,
+ GPIO114,
+ GPIO115,
+ GPIO116,
+ GPIO117,
+ GPIO118,
+ GPIO119,
+ GPIO120, /* 120 */
+ GPIO121,
+ GPIO122,
+ GPIO123,
+ GPIO124,
+ GPIO125,
+ GPIO126,
+ GPIO127,
+ GPIO128,
+ GPIO129,
+ GPIO130, /* 130 */
+ GPIO131,
+ GPIO132,
+ GPIO133,
+ GPIO134,
+ GPIO135,
+ GPIO136,
+ GPIO137,
+ GPIO138,
+ GPIO139,
+ GPIO140, /* 140 */
+ GPIO141,
+ GPIO142,
+ GPIO143,
+ GPIO144,
+ GPIO145,
+ GPIO146,
+ GPIO147,
+ GPIO148,
+ GPIO149,
+ GPIO150, /* 150 */
+ GPIO151,
+ GPIO152,
+ GPIO153,
+ GPIO154,
+ GPIO155,
+ GPIO156,
+ GPIO157,
+ GPIO158,
+ GPIO159,
+ GPIO160, /* 160 */
+ GPIO161,
+ GPIO162,
+ GPIO163,
+ GPIO164,
+ GPIO165,
+ GPIO166,
+ GPIO167,
+ GPIO168,
+ GPIO169,
+};
+
+extern int pxa3xx_pinctrl_register(struct platform_device *pdev,
+ struct pxa3xx_pinmux_info *info);
+extern int pxa3xx_pinctrl_unregister(struct platform_device *pdev);
+#endif /* __PINCTRL_PXA3XX_H */
diff --git a/drivers/pinctrl/pinctrl-pxa910.c b/drivers/pinctrl/pinctrl-pxa910.c
new file mode 100644
index 000000000000..c72ab4b9cc8c
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-pxa910.c
@@ -0,0 +1,1007 @@
+/*
+ * linux/drivers/pinctrl/pinmux-pxa910.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * publishhed by the Free Software Foundation.
+ *
+ * Copyright (C) 2011, Marvell Technology Group Ltd.
+ *
+ * Author: Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include "pinctrl-pxa3xx.h"
+
+#define PXA910_DS_MASK 0x1800
+#define PXA910_DS_SHIFT 11
+#define PXA910_SLEEP_MASK 0x38
+#define PXA910_SLEEP_SELECT (1 << 9)
+#define PXA910_SLEEP_DATA (1 << 8)
+#define PXA910_SLEEP_DIR (1 << 7)
+
+#define MFPR_910(a, r, f0, f1, f2, f3, f4, f5, f6, f7) \
+ { \
+ .name = #a, \
+ .pin = a, \
+ .mfpr = r, \
+ .func = { \
+ PXA910_MUX_##f0, \
+ PXA910_MUX_##f1, \
+ PXA910_MUX_##f2, \
+ PXA910_MUX_##f3, \
+ PXA910_MUX_##f4, \
+ PXA910_MUX_##f5, \
+ PXA910_MUX_##f6, \
+ PXA910_MUX_##f7, \
+ }, \
+ }
+
+#define GRP_910(a, m, p) \
+ { .name = a, .mux = PXA910_MUX_##m, .pins = p, .npins = ARRAY_SIZE(p), }
+
+/* 170 pins */
+enum pxa910_pin_list {
+ /* 0~127: GPIO0~GPIO127 */
+ ND_IO15 = 128,
+ ND_IO14,
+ ND_IO13, /* 130 */
+ ND_IO12,
+ ND_IO11,
+ ND_IO10,
+ ND_IO9,
+ ND_IO8,
+ ND_IO7,
+ ND_IO6,
+ ND_IO5,
+ ND_IO4,
+ ND_IO3, /* 140 */
+ ND_IO2,
+ ND_IO1,
+ ND_IO0,
+ ND_NCS0,
+ ND_NCS1,
+ SM_NCS0,
+ SM_NCS1,
+ ND_NWE,
+ ND_NRE,
+ ND_CLE, /* 150 */
+ ND_ALE,
+ SM_SCLK,
+ ND_RDY0,
+ SM_ADV,
+ ND_RDY1,
+ SM_ADVMUX,
+ SM_RDY,
+ MMC1_DAT7,
+ MMC1_DAT6,
+ MMC1_DAT5, /* 160 */
+ MMC1_DAT4,
+ MMC1_DAT3,
+ MMC1_DAT2,
+ MMC1_DAT1,
+ MMC1_DAT0,
+ MMC1_CMD,
+ MMC1_CLK,
+ MMC1_CD,
+ VCXO_OUT,
+};
+
+enum pxa910_mux {
+ /* PXA3xx_MUX_GPIO = 0 (predefined in pinctrl-pxa3xx.h) */
+ PXA910_MUX_GPIO = 0,
+ PXA910_MUX_NAND,
+ PXA910_MUX_USIM2,
+ PXA910_MUX_EXT_DMA,
+ PXA910_MUX_EXT_INT,
+ PXA910_MUX_MMC1,
+ PXA910_MUX_MMC2,
+ PXA910_MUX_MMC3,
+ PXA910_MUX_SM_INT,
+ PXA910_MUX_PRI_JTAG,
+ PXA910_MUX_SEC1_JTAG,
+ PXA910_MUX_SEC2_JTAG,
+ PXA910_MUX_RESET, /* SLAVE RESET OUT */
+ PXA910_MUX_CLK_REQ,
+ PXA910_MUX_VCXO_REQ,
+ PXA910_MUX_VCXO_OUT,
+ PXA910_MUX_VCXO_REQ2,
+ PXA910_MUX_VCXO_OUT2,
+ PXA910_MUX_SPI,
+ PXA910_MUX_SPI2,
+ PXA910_MUX_GSSP,
+ PXA910_MUX_SSP0,
+ PXA910_MUX_SSP1,
+ PXA910_MUX_SSP2,
+ PXA910_MUX_DSSP2,
+ PXA910_MUX_DSSP3,
+ PXA910_MUX_UART0,
+ PXA910_MUX_UART1,
+ PXA910_MUX_UART2,
+ PXA910_MUX_TWSI,
+ PXA910_MUX_CCIC,
+ PXA910_MUX_PWM0,
+ PXA910_MUX_PWM1,
+ PXA910_MUX_PWM2,
+ PXA910_MUX_PWM3,
+ PXA910_MUX_HSL,
+ PXA910_MUX_ONE_WIRE,
+ PXA910_MUX_LCD,
+ PXA910_MUX_DAC_ST23,
+ PXA910_MUX_ULPI,
+ PXA910_MUX_TB,
+ PXA910_MUX_KP_MK,
+ PXA910_MUX_KP_DK,
+ PXA910_MUX_TCU_GPOA,
+ PXA910_MUX_TCU_GPOB,
+ PXA910_MUX_ROT,
+ PXA910_MUX_TDS,
+ PXA910_MUX_32K_CLK, /* 32KHz CLK OUT */
+ PXA910_MUX_MN_CLK, /* MN CLK OUT */
+ PXA910_MUX_SMC,
+ PXA910_MUX_SM_ADDR18,
+ PXA910_MUX_SM_ADDR19,
+ PXA910_MUX_SM_ADDR20,
+ PXA910_MUX_NONE = 0xffff,
+};
+
+
+static struct pinctrl_pin_desc pxa910_pads[] = {
+ PINCTRL_PIN(GPIO0, "GPIO0"),
+ PINCTRL_PIN(GPIO1, "GPIO1"),
+ PINCTRL_PIN(GPIO2, "GPIO2"),
+ PINCTRL_PIN(GPIO3, "GPIO3"),
+ PINCTRL_PIN(GPIO4, "GPIO4"),
+ PINCTRL_PIN(GPIO5, "GPIO5"),
+ PINCTRL_PIN(GPIO6, "GPIO6"),
+ PINCTRL_PIN(GPIO7, "GPIO7"),
+ PINCTRL_PIN(GPIO8, "GPIO8"),
+ PINCTRL_PIN(GPIO9, "GPIO9"),
+ PINCTRL_PIN(GPIO10, "GPIO10"),
+ PINCTRL_PIN(GPIO11, "GPIO11"),
+ PINCTRL_PIN(GPIO12, "GPIO12"),
+ PINCTRL_PIN(GPIO13, "GPIO13"),
+ PINCTRL_PIN(GPIO14, "GPIO14"),
+ PINCTRL_PIN(GPIO15, "GPIO15"),
+ PINCTRL_PIN(GPIO16, "GPIO16"),
+ PINCTRL_PIN(GPIO17, "GPIO17"),
+ PINCTRL_PIN(GPIO18, "GPIO18"),
+ PINCTRL_PIN(GPIO19, "GPIO19"),
+ PINCTRL_PIN(GPIO20, "GPIO20"),
+ PINCTRL_PIN(GPIO21, "GPIO21"),
+ PINCTRL_PIN(GPIO22, "GPIO22"),
+ PINCTRL_PIN(GPIO23, "GPIO23"),
+ PINCTRL_PIN(GPIO24, "GPIO24"),
+ PINCTRL_PIN(GPIO25, "GPIO25"),
+ PINCTRL_PIN(GPIO26, "GPIO26"),
+ PINCTRL_PIN(GPIO27, "GPIO27"),
+ PINCTRL_PIN(GPIO28, "GPIO28"),
+ PINCTRL_PIN(GPIO29, "GPIO29"),
+ PINCTRL_PIN(GPIO30, "GPIO30"),
+ PINCTRL_PIN(GPIO31, "GPIO31"),
+ PINCTRL_PIN(GPIO32, "GPIO32"),
+ PINCTRL_PIN(GPIO33, "GPIO33"),
+ PINCTRL_PIN(GPIO34, "GPIO34"),
+ PINCTRL_PIN(GPIO35, "GPIO35"),
+ PINCTRL_PIN(GPIO36, "GPIO36"),
+ PINCTRL_PIN(GPIO37, "GPIO37"),
+ PINCTRL_PIN(GPIO38, "GPIO38"),
+ PINCTRL_PIN(GPIO39, "GPIO39"),
+ PINCTRL_PIN(GPIO40, "GPIO40"),
+ PINCTRL_PIN(GPIO41, "GPIO41"),
+ PINCTRL_PIN(GPIO42, "GPIO42"),
+ PINCTRL_PIN(GPIO43, "GPIO43"),
+ PINCTRL_PIN(GPIO44, "GPIO44"),
+ PINCTRL_PIN(GPIO45, "GPIO45"),
+ PINCTRL_PIN(GPIO46, "GPIO46"),
+ PINCTRL_PIN(GPIO47, "GPIO47"),
+ PINCTRL_PIN(GPIO48, "GPIO48"),
+ PINCTRL_PIN(GPIO49, "GPIO49"),
+ PINCTRL_PIN(GPIO50, "GPIO50"),
+ PINCTRL_PIN(GPIO51, "GPIO51"),
+ PINCTRL_PIN(GPIO52, "GPIO52"),
+ PINCTRL_PIN(GPIO53, "GPIO53"),
+ PINCTRL_PIN(GPIO54, "GPIO54"),
+ PINCTRL_PIN(GPIO55, "GPIO55"),
+ PINCTRL_PIN(GPIO56, "GPIO56"),
+ PINCTRL_PIN(GPIO57, "GPIO57"),
+ PINCTRL_PIN(GPIO58, "GPIO58"),
+ PINCTRL_PIN(GPIO59, "GPIO59"),
+ PINCTRL_PIN(GPIO60, "GPIO60"),
+ PINCTRL_PIN(GPIO61, "GPIO61"),
+ PINCTRL_PIN(GPIO62, "GPIO62"),
+ PINCTRL_PIN(GPIO63, "GPIO63"),
+ PINCTRL_PIN(GPIO64, "GPIO64"),
+ PINCTRL_PIN(GPIO65, "GPIO65"),
+ PINCTRL_PIN(GPIO66, "GPIO66"),
+ PINCTRL_PIN(GPIO67, "GPIO67"),
+ PINCTRL_PIN(GPIO68, "GPIO68"),
+ PINCTRL_PIN(GPIO69, "GPIO69"),
+ PINCTRL_PIN(GPIO70, "GPIO70"),
+ PINCTRL_PIN(GPIO71, "GPIO71"),
+ PINCTRL_PIN(GPIO72, "GPIO72"),
+ PINCTRL_PIN(GPIO73, "GPIO73"),
+ PINCTRL_PIN(GPIO74, "GPIO74"),
+ PINCTRL_PIN(GPIO75, "GPIO75"),
+ PINCTRL_PIN(GPIO76, "GPIO76"),
+ PINCTRL_PIN(GPIO77, "GPIO77"),
+ PINCTRL_PIN(GPIO78, "GPIO78"),
+ PINCTRL_PIN(GPIO79, "GPIO79"),
+ PINCTRL_PIN(GPIO80, "GPIO80"),
+ PINCTRL_PIN(GPIO81, "GPIO81"),
+ PINCTRL_PIN(GPIO82, "GPIO82"),
+ PINCTRL_PIN(GPIO83, "GPIO83"),
+ PINCTRL_PIN(GPIO84, "GPIO84"),
+ PINCTRL_PIN(GPIO85, "GPIO85"),
+ PINCTRL_PIN(GPIO86, "GPIO86"),
+ PINCTRL_PIN(GPIO87, "GPIO87"),
+ PINCTRL_PIN(GPIO88, "GPIO88"),
+ PINCTRL_PIN(GPIO89, "GPIO89"),
+ PINCTRL_PIN(GPIO90, "GPIO90"),
+ PINCTRL_PIN(GPIO91, "GPIO91"),
+ PINCTRL_PIN(GPIO92, "GPIO92"),
+ PINCTRL_PIN(GPIO93, "GPIO93"),
+ PINCTRL_PIN(GPIO94, "GPIO94"),
+ PINCTRL_PIN(GPIO95, "GPIO95"),
+ PINCTRL_PIN(GPIO96, "GPIO96"),
+ PINCTRL_PIN(GPIO97, "GPIO97"),
+ PINCTRL_PIN(GPIO98, "GPIO98"),
+ PINCTRL_PIN(GPIO99, "GPIO99"),
+ PINCTRL_PIN(GPIO100, "GPIO100"),
+ PINCTRL_PIN(GPIO101, "GPIO101"),
+ PINCTRL_PIN(GPIO102, "GPIO102"),
+ PINCTRL_PIN(GPIO103, "GPIO103"),
+ PINCTRL_PIN(GPIO104, "GPIO104"),
+ PINCTRL_PIN(GPIO105, "GPIO105"),
+ PINCTRL_PIN(GPIO106, "GPIO106"),
+ PINCTRL_PIN(GPIO107, "GPIO107"),
+ PINCTRL_PIN(GPIO108, "GPIO108"),
+ PINCTRL_PIN(GPIO109, "GPIO109"),
+ PINCTRL_PIN(GPIO110, "GPIO110"),
+ PINCTRL_PIN(GPIO111, "GPIO111"),
+ PINCTRL_PIN(GPIO112, "GPIO112"),
+ PINCTRL_PIN(GPIO113, "GPIO113"),
+ PINCTRL_PIN(GPIO114, "GPIO114"),
+ PINCTRL_PIN(GPIO115, "GPIO115"),
+ PINCTRL_PIN(GPIO116, "GPIO116"),
+ PINCTRL_PIN(GPIO117, "GPIO117"),
+ PINCTRL_PIN(GPIO118, "GPIO118"),
+ PINCTRL_PIN(GPIO119, "GPIO119"),
+ PINCTRL_PIN(GPIO120, "GPIO120"),
+ PINCTRL_PIN(GPIO121, "GPIO121"),
+ PINCTRL_PIN(GPIO122, "GPIO122"),
+ PINCTRL_PIN(GPIO123, "GPIO123"),
+ PINCTRL_PIN(GPIO124, "GPIO124"),
+ PINCTRL_PIN(GPIO125, "GPIO125"),
+ PINCTRL_PIN(GPIO126, "GPIO126"),
+ PINCTRL_PIN(GPIO127, "GPIO127"),
+ PINCTRL_PIN(ND_IO15, "ND_IO15"),
+ PINCTRL_PIN(ND_IO14, "ND_IO14"),
+ PINCTRL_PIN(ND_IO13, "ND_IO13"),
+ PINCTRL_PIN(ND_IO12, "ND_IO12"),
+ PINCTRL_PIN(ND_IO11, "ND_IO11"),
+ PINCTRL_PIN(ND_IO10, "ND_IO10"),
+ PINCTRL_PIN(ND_IO9, "ND_IO9"),
+ PINCTRL_PIN(ND_IO8, "ND_IO8"),
+ PINCTRL_PIN(ND_IO7, "ND_IO7"),
+ PINCTRL_PIN(ND_IO6, "ND_IO6"),
+ PINCTRL_PIN(ND_IO5, "ND_IO5"),
+ PINCTRL_PIN(ND_IO4, "ND_IO4"),
+ PINCTRL_PIN(ND_IO3, "ND_IO3"),
+ PINCTRL_PIN(ND_IO2, "ND_IO2"),
+ PINCTRL_PIN(ND_IO1, "ND_IO1"),
+ PINCTRL_PIN(ND_IO0, "ND_IO0"),
+ PINCTRL_PIN(ND_NCS0, "ND_NCS0_SM_NCS2"),
+ PINCTRL_PIN(ND_NCS1, "ND_NCS1_SM_NCS3"),
+ PINCTRL_PIN(SM_NCS0, "SM_NCS0"),
+ PINCTRL_PIN(SM_NCS1, "SM_NCS1"),
+ PINCTRL_PIN(ND_NWE, "ND_NWE"),
+ PINCTRL_PIN(ND_NRE, "ND_NRE"),
+ PINCTRL_PIN(ND_CLE, "ND_CLE_SM_NOE"),
+ PINCTRL_PIN(ND_ALE, "ND_ALE_SM_NWE"),
+ PINCTRL_PIN(SM_SCLK, "SM_SCLK"),
+ PINCTRL_PIN(ND_RDY0, "ND_RDY0"),
+ PINCTRL_PIN(SM_ADV, "SM_ADV"),
+ PINCTRL_PIN(ND_RDY1, "ND_RDY1"),
+ PINCTRL_PIN(SM_RDY, "SM_RDY"),
+ PINCTRL_PIN(MMC1_DAT7, "MMC1_DAT7"),
+ PINCTRL_PIN(MMC1_DAT6, "MMC1_DAT6"),
+ PINCTRL_PIN(MMC1_DAT5, "MMC1_DAT5"),
+ PINCTRL_PIN(MMC1_DAT4, "MMC1_DAT4"),
+ PINCTRL_PIN(MMC1_DAT3, "MMC1_DAT3"),
+ PINCTRL_PIN(MMC1_DAT2, "MMC1_DAT2"),
+ PINCTRL_PIN(MMC1_DAT1, "MMC1_DAT1"),
+ PINCTRL_PIN(MMC1_DAT0, "MMC1_DAT0"),
+ PINCTRL_PIN(MMC1_CMD, "MMC1 CMD"),
+ PINCTRL_PIN(MMC1_CLK, "MMC1 CLK"),
+ PINCTRL_PIN(MMC1_CD, "MMC1 CD"),
+ PINCTRL_PIN(VCXO_OUT, "VCXO_OUT"),
+};
+
+struct pxa3xx_mfp_pin pxa910_mfp[] = {
+ /* pin offs f0 f1 f2 f3 f4 f5 f6 f7 */
+ MFPR_910(GPIO0, 0x0DC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO1, 0x0E0, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO2, 0x0E4, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO3, 0x0E8, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO4, 0x0EC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO5, 0x0F0, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO6, 0x0F4, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO7, 0x0F8, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO8, 0x0FC, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO9, 0x100, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO10, 0x104, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO11, 0x108, GPIO, KP_MK, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO12, 0x10C, GPIO, KP_MK, NONE, NONE, KP_DK, NONE, NONE, NONE),
+ MFPR_910(GPIO13, 0x110, GPIO, KP_MK, NONE, NONE, KP_DK, NONE, NONE, NONE),
+ MFPR_910(GPIO14, 0x114, GPIO, KP_MK, NONE, NONE, KP_DK, TB, NONE, NONE),
+ MFPR_910(GPIO15, 0x118, GPIO, KP_MK, NONE, NONE, KP_DK, TB, NONE, NONE),
+ MFPR_910(GPIO16, 0x11C, GPIO, KP_DK, NONE, NONE, NONE, TB, NONE, NONE),
+ MFPR_910(GPIO17, 0x120, GPIO, KP_DK, NONE, NONE, NONE, TB, NONE, NONE),
+ MFPR_910(GPIO18, 0x124, GPIO, KP_DK, NONE, NONE, ROT, NONE, NONE, NONE),
+ MFPR_910(GPIO19, 0x128, GPIO, KP_DK, NONE, NONE, ROT, NONE, NONE, NONE),
+ MFPR_910(GPIO20, 0x12C, GPIO, SSP1, NONE, NONE, VCXO_OUT, NONE, NONE, NONE),
+ MFPR_910(GPIO21, 0x130, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO22, 0x134, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO23, 0x138, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO24, 0x13C, GPIO, SSP1, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO25, 0x140, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO26, 0x144, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO27, 0x148, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO28, 0x14C, GPIO, GSSP, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO29, 0x150, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE),
+ MFPR_910(GPIO30, 0x154, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE),
+ MFPR_910(GPIO31, 0x158, GPIO, UART0, NONE, NONE, UART1, NONE, NONE, NONE),
+ MFPR_910(GPIO32, 0x15C, GPIO, UART0, DAC_ST23, NONE, UART1, NONE, NONE, NONE),
+ MFPR_910(GPIO33, 0x160, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3),
+ MFPR_910(GPIO34, 0x164, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3),
+ MFPR_910(GPIO35, 0x168, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3),
+ MFPR_910(GPIO36, 0x16C, GPIO, MMC2, SSP0, SSP2, NONE, SPI, NONE, MMC3),
+ MFPR_910(GPIO37, 0x170, GPIO, MMC2, NONE, NONE, NONE, SPI, HSL, NONE),
+ MFPR_910(GPIO38, 0x174, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE),
+ MFPR_910(GPIO39, 0x178, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE),
+ MFPR_910(GPIO40, 0x17C, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE),
+ MFPR_910(GPIO41, 0x180, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE),
+ MFPR_910(GPIO42, 0x184, GPIO, MMC2, NONE, NONE, NONE, NONE, HSL, NONE),
+ MFPR_910(GPIO43, 0x188, GPIO, UART1, NONE, DAC_ST23, NONE, DSSP2, SPI, UART2),
+ MFPR_910(GPIO44, 0x18C, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2),
+ MFPR_910(GPIO45, 0x190, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2),
+ MFPR_910(GPIO46, 0x194, GPIO, UART1, NONE, EXT_INT, NONE, DSSP2, SPI, UART2),
+ MFPR_910(GPIO47, 0x198, GPIO, SSP0, NONE, NONE, NONE, SSP2, UART1, NONE),
+ MFPR_910(GPIO48, 0x19C, GPIO, SSP0, NONE, NONE, NONE, SSP2, UART1, NONE),
+ MFPR_910(GPIO49, 0x1A0, GPIO, SSP0, UART0, VCXO_REQ, NONE, SSP2, NONE, MMC3),
+ MFPR_910(GPIO50, 0x1A4, GPIO, SSP0, UART0, VCXO_OUT, NONE, SSP2, NONE, MMC3),
+ MFPR_910(GPIO51, 0x1A8, GPIO, UART2, PWM1, TWSI, SSP0, NONE, DSSP3, NONE),
+ MFPR_910(GPIO52, 0x1AC, GPIO, UART2, DAC_ST23, TWSI, SSP0, NONE, DSSP3, NONE),
+ MFPR_910(GPIO53, 0x1B0, GPIO, UART2, TWSI, NONE, SSP0, NONE, DSSP3, NONE),
+ MFPR_910(GPIO54, 0x1B4, GPIO, UART2, TWSI, SSP0, NONE, NONE, DSSP3, NONE),
+ MFPR_910(GPIO55, 0x2F0, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO56, 0x2F4, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO57, 0x2F8, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO58, 0x2FC, TDS, GPIO, TB, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO59, 0x300, TDS, GPIO, TCU_GPOA, TCU_GPOB, ONE_WIRE, NONE, NONE, NONE),
+ MFPR_910(GPIO60, 0x304, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO61, 0x308, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL),
+ MFPR_910(GPIO62, 0x30C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL),
+ MFPR_910(GPIO63, 0x310, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL),
+ MFPR_910(GPIO64, 0x314, GPIO, SPI2, NONE, NONE, NONE, NONE, NONE, HSL),
+ MFPR_910(GPIO65, 0x318, GPIO, SPI2, NONE, NONE, NONE, NONE, ONE_WIRE, HSL),
+ MFPR_910(GPIO66, 0x31C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, HSL),
+ MFPR_910(GPIO67, 0x1B8, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2),
+ MFPR_910(GPIO68, 0x1BC, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2),
+ MFPR_910(GPIO69, 0x1C0, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, USIM2),
+ MFPR_910(GPIO70, 0x1C4, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO71, 0x1C8, GPIO, CCIC, SPI, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO72, 0x1CC, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO73, 0x1D0, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO74, 0x1D4, GPIO, CCIC, EXT_DMA, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO75, 0x1D8, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO76, 0x1DC, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO77, 0x1E0, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO78, 0x1E4, GPIO, CCIC, NONE, NONE, NONE, ULPI, NONE, NONE),
+ MFPR_910(GPIO79, 0x1E8, GPIO, TWSI, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO80, 0x1EC, GPIO, TWSI, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO81, 0x1F0, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO82, 0x1F4, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO83, 0x1F8, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO84, 0x1FC, GPIO, LCD, VCXO_REQ2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO85, 0x200, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO86, 0x204, GPIO, LCD, VCXO_OUT2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO87, 0x208, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO88, 0x20C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO89, 0x210, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO90, 0x214, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO91, 0x218, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO92, 0x21C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO93, 0x220, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO94, 0x224, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO95, 0x228, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO96, 0x22C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO97, 0x230, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO98, 0x234, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO99, 0x0B0, MMC1, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO100, 0x238, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO101, 0x23C, GPIO, LCD, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO102, 0x240, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, SPI2),
+ MFPR_910(GPIO103, 0x244, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, SPI2),
+ MFPR_910(GPIO104, 0x248, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO105, 0x24C, GPIO, LCD, DSSP2, SPI, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO106, 0x250, GPIO, LCD, DSSP3, ONE_WIRE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO107, 0x254, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO108, 0x258, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO109, 0x25C, GPIO, LCD, DSSP3, SPI, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO110, 0x298, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO111, 0x29C, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO112, 0x2A0, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO113, 0x2A4, GPIO, NONE, DSSP2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO114, 0x2A8, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO115, 0x2AC, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO116, 0x2B0, GPIO, NONE, DSSP3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO117, 0x0B4, PRI_JTAG, GPIO, PWM0, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO118, 0x0B8, PRI_JTAG, GPIO, PWM1, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO119, 0x0BC, PRI_JTAG, GPIO, PWM2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO120, 0x0C0, PRI_JTAG, GPIO, PWM3, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO121, 0x32C, GPIO, NONE, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO122, 0x0C8, RESET, GPIO, 32K_CLK, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO123, 0x0CC, CLK_REQ, GPIO, ONE_WIRE, EXT_DMA, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO124, 0x0D0, GPIO, MN_CLK, DAC_ST23, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO125, 0x0D4, VCXO_REQ, GPIO, NONE, EXT_INT, NONE, NONE, NONE, NONE),
+ MFPR_910(GPIO126, 0x06C, GPIO, SMC, NONE, SM_ADDR18, NONE, EXT_DMA, NONE, NONE),
+ MFPR_910(GPIO127, 0x070, GPIO, SMC, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO15, 0x004, NAND, GPIO, USIM2, EXT_DMA, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO14, 0x008, NAND, GPIO, USIM2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO13, 0x00C, NAND, GPIO, USIM2, EXT_INT, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO12, 0x010, NAND, GPIO, SSP2, EXT_INT, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO11, 0x014, NAND, GPIO, SSP2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO10, 0x018, NAND, GPIO, SSP2, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO9, 0x01C, NAND, GPIO, SSP2, NONE, VCXO_OUT2, NONE, NONE, NONE),
+ MFPR_910(ND_IO8, 0x020, NAND, GPIO, NONE, NONE, PWM3, NONE, NONE, NONE),
+ MFPR_910(ND_IO7, 0x024, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO6, 0x028, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO5, 0x02C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO4, 0x030, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO3, 0x034, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO2, 0x038, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO1, 0x03C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_IO0, 0x040, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_NCS0, 0x044, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_NCS1, 0x048, NAND, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(SM_NCS0, 0x04C, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(SM_NCS1, 0x050, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_NWE, 0x054, GPIO, NAND, NONE, SM_ADDR20, NONE, SMC, NONE, NONE),
+ MFPR_910(ND_NRE, 0x058, GPIO, NAND, NONE, SMC, NONE, EXT_DMA, NONE, NONE),
+ MFPR_910(ND_CLE, 0x05C, NAND, MMC3, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_ALE, 0x060, GPIO, NAND, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(SM_SCLK, 0x064, MMC3, NONE, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_RDY0, 0x068, NAND, GPIO, NONE, SMC, NONE, NONE, NONE, NONE),
+ MFPR_910(SM_ADV, 0x074, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(ND_RDY1, 0x078, NAND, GPIO, NONE, SMC, NONE, NONE, NONE, NONE),
+ MFPR_910(SM_ADVMUX, 0x07C, SMC, GPIO, NONE, SM_ADDR19, NONE, NONE, NONE, NONE),
+ MFPR_910(SM_RDY, 0x080, SMC, GPIO, NONE, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT7, 0x084, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT6, 0x088, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT5, 0x08C, MMC1, GPIO, SEC1_JTAG, TB, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT4, 0x090, MMC1, GPIO, NONE, TB, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT3, 0x094, MMC1, HSL, SEC2_JTAG, SSP0, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT2, 0x098, MMC1, HSL, SEC2_JTAG, SSP2, SSP0, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT1, 0x09C, MMC1, HSL, SEC2_JTAG, SSP2, SSP0, NONE, NONE, NONE),
+ MFPR_910(MMC1_DAT0, 0x0A0, MMC1, HSL, SEC2_JTAG, SSP2, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_CMD, 0x0A4, MMC1, HSL, SEC1_JTAG, SSP2, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_CLK, 0x0A8, MMC1, HSL, SEC2_JTAG, SSP0, NONE, NONE, NONE, NONE),
+ MFPR_910(MMC1_CD, 0x0AC, MMC1, GPIO, SEC1_JTAG, NONE, NONE, NONE, NONE, NONE),
+ MFPR_910(VCXO_OUT, 0x0D8, VCXO_OUT, PWM3, NONE, NONE, NONE, NONE, NONE, NONE),
+};
+
+
+static const unsigned p910_usim2_pin1[] = {GPIO67, GPIO68, GPIO69};
+static const unsigned p910_usim2_pin2[] = {ND_IO15, ND_IO14, ND_IO13};
+static const unsigned p910_mmc1_pin1[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5,
+ MMC1_DAT4, MMC1_DAT3, MMC1_DAT2, MMC1_DAT1, MMC1_DAT0, MMC1_CMD,
+ MMC1_CLK, MMC1_CD, GPIO99};
+static const unsigned p910_mmc2_pin1[] = {GPIO33, GPIO34, GPIO35, GPIO36,
+ GPIO37, GPIO38, GPIO39, GPIO40, GPIO41, GPIO42};
+static const unsigned p910_mmc3_pin1[] = {GPIO33, GPIO34, GPIO35, GPIO36,
+ GPIO49, GPIO50};
+static const unsigned p910_mmc3_pin2[] = {ND_IO7, ND_IO6, ND_IO5, ND_IO4,
+ ND_IO3, ND_IO2, ND_IO1, ND_IO0, ND_CLE, SM_SCLK};
+static const unsigned p910_uart0_pin1[] = {GPIO29, GPIO30, GPIO31, GPIO32};
+static const unsigned p910_uart1_pin1[] = {GPIO47, GPIO48};
+static const unsigned p910_uart1_pin2[] = {GPIO31, GPIO32};
+static const unsigned p910_uart1_pin3[] = {GPIO45, GPIO46};
+static const unsigned p910_uart1_pin4[] = {GPIO29, GPIO30, GPIO31, GPIO32};
+static const unsigned p910_uart1_pin5[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned p910_uart2_pin1[] = {GPIO43, GPIO44};
+static const unsigned p910_uart2_pin2[] = {GPIO51, GPIO52};
+static const unsigned p910_uart2_pin3[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned p910_uart2_pin4[] = {GPIO51, GPIO52, GPIO53, GPIO54};
+static const unsigned p910_twsi_pin1[] = {GPIO51, GPIO52};
+static const unsigned p910_twsi_pin2[] = {GPIO53, GPIO54};
+static const unsigned p910_twsi_pin3[] = {GPIO79, GPIO80};
+static const unsigned p910_ccic_pin1[] = {GPIO67, GPIO68, GPIO69, GPIO70,
+ GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, GPIO76, GPIO77, GPIO78};
+static const unsigned p910_lcd_pin1[] = {GPIO81, GPIO82, GPIO83, GPIO84,
+ GPIO85, GPIO86, GPIO87, GPIO88, GPIO89, GPIO90, GPIO91, GPIO92,
+ GPIO93, GPIO94, GPIO95, GPIO96, GPIO97, GPIO98, GPIO100, GPIO101,
+ GPIO102, GPIO103};
+static const unsigned p910_spi_pin1[] = {GPIO104, GPIO105, GPIO107, GPIO108};
+static const unsigned p910_spi_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned p910_spi_pin3[] = {GPIO33, GPIO34, GPIO35, GPIO36,
+ GPIO37};
+static const unsigned p910_spi_pin4[] = {GPIO67, GPIO68, GPIO69, GPIO70,
+ GPIO71};
+static const unsigned p910_spi2_pin1[] = {GPIO64, GPIO65};
+static const unsigned p910_spi2_pin2[] = {GPIO102, GPIO103};
+static const unsigned p910_dssp2_pin1[] = {GPIO102, GPIO103, GPIO104, GPIO105};
+static const unsigned p910_dssp2_pin2[] = {GPIO43, GPIO44, GPIO45, GPIO46};
+static const unsigned p910_dssp2_pin3[] = {GPIO111, GPIO112, GPIO113};
+static const unsigned p910_dssp3_pin1[] = {GPIO106, GPIO107, GPIO108, GPIO109};
+static const unsigned p910_dssp3_pin2[] = {GPIO51, GPIO52, GPIO53, GPIO54};
+static const unsigned p910_dssp3_pin3[] = {GPIO114, GPIO115, GPIO116};
+static const unsigned p910_ssp0_pin1[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1,
+ MMC1_CLK};
+static const unsigned p910_ssp0_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36};
+static const unsigned p910_ssp0_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50};
+static const unsigned p910_ssp0_pin4[] = {GPIO51, GPIO52, GPIO53, GPIO54};
+static const unsigned p910_ssp1_pin1[] = {GPIO21, GPIO22, GPIO23, GPIO24};
+static const unsigned p910_ssp1_pin2[] = {GPIO20, GPIO21, GPIO22, GPIO23,
+ GPIO24};
+static const unsigned p910_ssp2_pin1[] = {MMC1_DAT2, MMC1_DAT1, MMC1_DAT0,
+ MMC1_CMD};
+static const unsigned p910_ssp2_pin2[] = {GPIO33, GPIO34, GPIO35, GPIO36};
+static const unsigned p910_ssp2_pin3[] = {GPIO47, GPIO48, GPIO49, GPIO50};
+static const unsigned p910_ssp2_pin4[] = {ND_IO12, ND_IO11, ND_IO10, ND_IO9};
+static const unsigned p910_gssp_pin1[] = {GPIO25, GPIO26, GPIO27, GPIO28};
+static const unsigned p910_pwm0_pin1[] = {GPIO117};
+static const unsigned p910_pwm1_pin1[] = {GPIO118};
+static const unsigned p910_pwm1_pin2[] = {GPIO51};
+static const unsigned p910_pwm2_pin1[] = {GPIO119};
+static const unsigned p910_pwm3_pin1[] = {GPIO120};
+static const unsigned p910_pwm3_pin2[] = {ND_IO8};
+static const unsigned p910_pwm3_pin3[] = {VCXO_OUT};
+static const unsigned p910_pri_jtag_pin1[] = {GPIO117, GPIO118, GPIO119,
+ GPIO120};
+static const unsigned p910_sec1_jtag_pin1[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5,
+ MMC1_CMD, MMC1_CD};
+static const unsigned p910_sec2_jtag_pin1[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1,
+ MMC1_DAT0, MMC1_CLK};
+static const unsigned p910_hsl_pin1[] = {GPIO37, GPIO38, GPIO39, GPIO40,
+ GPIO41, GPIO42};
+static const unsigned p910_hsl_pin2[] = {GPIO61, GPIO62, GPIO63, GPIO64,
+ GPIO65, GPIO66};
+static const unsigned p910_hsl_pin3[] = {MMC1_DAT3, MMC1_DAT2, MMC1_DAT1,
+ MMC1_DAT0, MMC1_CMD, MMC1_CLK};
+static const unsigned p910_w1_pin1[] = {GPIO59};
+static const unsigned p910_w1_pin2[] = {GPIO65};
+static const unsigned p910_w1_pin3[] = {GPIO106};
+static const unsigned p910_w1_pin4[] = {GPIO123};
+static const unsigned p910_kpmk_pin1[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4,
+ GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO10, GPIO11, GPIO12, GPIO13,
+ GPIO14, GPIO15};
+static const unsigned p910_kpmk_pin2[] = {GPIO0, GPIO1, GPIO2, GPIO3, GPIO4,
+ GPIO5, GPIO6, GPIO7, GPIO8, GPIO9, GPIO12};
+static const unsigned p910_kpdk_pin1[] = {GPIO12, GPIO13, GPIO14, GPIO15,
+ GPIO16, GPIO17, GPIO18, GPIO19};
+static const unsigned p910_tds_pin1[] = {GPIO55, GPIO56, GPIO57, GPIO58,
+ GPIO59};
+static const unsigned p910_tds_pin2[] = {GPIO55, GPIO57, GPIO58, GPIO59};
+static const unsigned p910_tb_pin1[] = {GPIO14, GPIO15, GPIO16, GPIO17};
+static const unsigned p910_tb_pin2[] = {GPIO55, GPIO56, GPIO57, GPIO58};
+static const unsigned p910_tb_pin3[] = {MMC1_DAT7, MMC1_DAT6, MMC1_DAT5,
+ MMC1_DAT4};
+static const unsigned p910_ext_dma0_pin1[] = {GPIO72};
+static const unsigned p910_ext_dma0_pin2[] = {ND_IO15};
+static const unsigned p910_ext_dma0_pin3[] = {ND_NRE};
+static const unsigned p910_ext_dma1_pin1[] = {GPIO73};
+static const unsigned p910_ext_dma1_pin2[] = {GPIO123};
+static const unsigned p910_ext_dma1_pin3[] = {GPIO126};
+static const unsigned p910_ext_dma2_pin1[] = {GPIO74};
+static const unsigned p910_ext0_int_pin1[] = {GPIO44};
+static const unsigned p910_ext0_int_pin2[] = {ND_IO13};
+static const unsigned p910_ext1_int_pin1[] = {GPIO45};
+static const unsigned p910_ext1_int_pin2[] = {ND_IO12};
+static const unsigned p910_ext2_int_pin1[] = {GPIO46};
+static const unsigned p910_ext2_int_pin2[] = {GPIO125};
+static const unsigned p910_dac_st23_pin1[] = {GPIO32};
+static const unsigned p910_dac_st23_pin2[] = {GPIO43};
+static const unsigned p910_dac_st23_pin3[] = {GPIO52};
+static const unsigned p910_dac_st23_pin4[] = {GPIO124};
+static const unsigned p910_vcxo_out_pin1[] = {GPIO50};
+static const unsigned p910_vcxo_out_pin2[] = {VCXO_OUT};
+static const unsigned p910_vcxo_out_pin3[] = {GPIO20};
+static const unsigned p910_vcxo_req_pin1[] = {GPIO49};
+static const unsigned p910_vcxo_req_pin2[] = {GPIO125};
+static const unsigned p910_vcxo_out2_pin1[] = {GPIO86};
+static const unsigned p910_vcxo_out2_pin2[] = {ND_IO9};
+static const unsigned p910_vcxo_req2_pin1[] = {GPIO84};
+static const unsigned p910_ulpi_pin1[] = {GPIO67, GPIO68, GPIO69, GPIO70,
+ GPIO71, GPIO72, GPIO73, GPIO74, GPIO75, GPIO76, GPIO77, GPIO78};
+static const unsigned p910_nand_pin1[] = {ND_IO15, ND_IO14, ND_IO13, ND_IO12,
+ ND_IO11, ND_IO10, ND_IO9, ND_IO8, ND_IO7, ND_IO6, ND_IO5, ND_IO4,
+ ND_IO3, ND_IO2, ND_IO1, ND_IO0, ND_NCS0, ND_NWE, ND_NRE, ND_CLE,
+ ND_ALE, ND_RDY0};
+static const unsigned p910_gpio0_pin1[] = {GPIO0};
+static const unsigned p910_gpio0_pin2[] = {SM_ADV};
+static const unsigned p910_gpio1_pin1[] = {GPIO1};
+static const unsigned p910_gpio1_pin2[] = {ND_RDY1};
+static const unsigned p910_gpio2_pin1[] = {GPIO2};
+static const unsigned p910_gpio2_pin2[] = {SM_ADVMUX};
+static const unsigned p910_gpio3_pin1[] = {GPIO3};
+static const unsigned p910_gpio3_pin2[] = {SM_RDY};
+static const unsigned p910_gpio20_pin1[] = {GPIO20};
+static const unsigned p910_gpio20_pin2[] = {ND_IO15};
+static const unsigned p910_gpio20_pin3[] = {MMC1_DAT6};
+static const unsigned p910_gpio21_pin1[] = {GPIO21};
+static const unsigned p910_gpio21_pin2[] = {ND_IO14};
+static const unsigned p910_gpio21_pin3[] = {MMC1_DAT5};
+static const unsigned p910_gpio22_pin1[] = {GPIO22};
+static const unsigned p910_gpio22_pin2[] = {ND_IO13};
+static const unsigned p910_gpio22_pin3[] = {MMC1_DAT4};
+static const unsigned p910_gpio23_pin1[] = {GPIO23};
+static const unsigned p910_gpio23_pin2[] = {ND_IO12};
+static const unsigned p910_gpio23_pin3[] = {MMC1_CD};
+static const unsigned p910_gpio24_pin1[] = {GPIO24};
+static const unsigned p910_gpio24_pin2[] = {ND_IO11};
+static const unsigned p910_gpio24_pin3[] = {MMC1_DAT7};
+static const unsigned p910_gpio25_pin1[] = {GPIO25};
+static const unsigned p910_gpio25_pin2[] = {ND_IO10};
+static const unsigned p910_gpio26_pin1[] = {GPIO26};
+static const unsigned p910_gpio26_pin2[] = {ND_IO9};
+static const unsigned p910_gpio27_pin1[] = {GPIO27};
+static const unsigned p910_gpio27_pin2[] = {ND_IO8};
+static const unsigned p910_gpio85_pin1[] = {GPIO85};
+static const unsigned p910_gpio85_pin2[] = {ND_NCS0};
+static const unsigned p910_gpio86_pin1[] = {GPIO86};
+static const unsigned p910_gpio86_pin2[] = {ND_NCS1};
+static const unsigned p910_gpio87_pin1[] = {GPIO87};
+static const unsigned p910_gpio87_pin2[] = {SM_NCS0};
+static const unsigned p910_gpio88_pin1[] = {GPIO88};
+static const unsigned p910_gpio88_pin2[] = {SM_NCS1};
+static const unsigned p910_gpio89_pin1[] = {GPIO89};
+static const unsigned p910_gpio89_pin2[] = {ND_NWE};
+static const unsigned p910_gpio90_pin1[] = {GPIO90};
+static const unsigned p910_gpio90_pin2[] = {ND_NRE};
+static const unsigned p910_gpio91_pin1[] = {GPIO91};
+static const unsigned p910_gpio91_pin2[] = {ND_ALE};
+static const unsigned p910_gpio92_pin1[] = {GPIO92};
+static const unsigned p910_gpio92_pin2[] = {ND_RDY0};
+
+static struct pxa3xx_pin_group pxa910_grps[] = {
+ GRP_910("usim2 3p1", USIM2, p910_usim2_pin1),
+ GRP_910("usim2 3p2", USIM2, p910_usim2_pin2),
+ GRP_910("mmc1 12p", MMC1, p910_mmc1_pin1),
+ GRP_910("mmc2 10p", MMC2, p910_mmc2_pin1),
+ GRP_910("mmc3 6p", MMC3, p910_mmc3_pin1),
+ GRP_910("mmc3 10p", MMC3, p910_mmc3_pin2),
+ GRP_910("uart0 4p", UART0, p910_uart0_pin1),
+ GRP_910("uart1 2p1", UART1, p910_uart1_pin1),
+ GRP_910("uart1 2p2", UART1, p910_uart1_pin2),
+ GRP_910("uart1 2p3", UART1, p910_uart1_pin3),
+ GRP_910("uart1 4p4", UART1, p910_uart1_pin4),
+ GRP_910("uart1 4p5", UART1, p910_uart1_pin5),
+ GRP_910("uart2 2p1", UART2, p910_uart2_pin1),
+ GRP_910("uart2 2p2", UART2, p910_uart2_pin2),
+ GRP_910("uart2 4p3", UART2, p910_uart2_pin3),
+ GRP_910("uart2 4p4", UART2, p910_uart2_pin4),
+ GRP_910("twsi 2p1", TWSI, p910_twsi_pin1),
+ GRP_910("twsi 2p2", TWSI, p910_twsi_pin2),
+ GRP_910("twsi 2p3", TWSI, p910_twsi_pin3),
+ GRP_910("ccic", CCIC, p910_ccic_pin1),
+ GRP_910("lcd", LCD, p910_lcd_pin1),
+ GRP_910("spi 4p1", SPI, p910_spi_pin1),
+ GRP_910("spi 4p2", SPI, p910_spi_pin2),
+ GRP_910("spi 5p3", SPI, p910_spi_pin3),
+ GRP_910("spi 5p4", SPI, p910_spi_pin4),
+ GRP_910("dssp2 4p1", DSSP2, p910_dssp2_pin1),
+ GRP_910("dssp2 4p2", DSSP2, p910_dssp2_pin2),
+ GRP_910("dssp2 3p3", DSSP2, p910_dssp2_pin3),
+ GRP_910("dssp3 4p1", DSSP3, p910_dssp3_pin1),
+ GRP_910("dssp3 4p2", DSSP3, p910_dssp3_pin2),
+ GRP_910("dssp3 3p3", DSSP3, p910_dssp3_pin3),
+ GRP_910("ssp0 4p1", SSP0, p910_ssp0_pin1),
+ GRP_910("ssp0 4p2", SSP0, p910_ssp0_pin2),
+ GRP_910("ssp0 4p3", SSP0, p910_ssp0_pin3),
+ GRP_910("ssp0 4p4", SSP0, p910_ssp0_pin4),
+ GRP_910("ssp1 4p1", SSP1, p910_ssp1_pin1),
+ GRP_910("ssp1 5p2", SSP1, p910_ssp1_pin2),
+ GRP_910("ssp2 4p1", SSP2, p910_ssp2_pin1),
+ GRP_910("ssp2 4p2", SSP2, p910_ssp2_pin2),
+ GRP_910("ssp2 4p3", SSP2, p910_ssp2_pin3),
+ GRP_910("ssp2 4p4", SSP2, p910_ssp2_pin4),
+ GRP_910("gssp", GSSP, p910_gssp_pin1),
+ GRP_910("pwm0", PWM0, p910_pwm0_pin1),
+ GRP_910("pwm1-1", PWM1, p910_pwm1_pin1),
+ GRP_910("pwm1-2", PWM1, p910_pwm1_pin2),
+ GRP_910("pwm2", PWM2, p910_pwm2_pin1),
+ GRP_910("pwm3-1", PWM3, p910_pwm3_pin1),
+ GRP_910("pwm3-2", PWM3, p910_pwm3_pin2),
+ GRP_910("pwm3-3", PWM3, p910_pwm3_pin3),
+ GRP_910("pri jtag", PRI_JTAG, p910_pri_jtag_pin1),
+ GRP_910("sec1 jtag", SEC1_JTAG, p910_sec1_jtag_pin1),
+ GRP_910("sec2 jtag", SEC2_JTAG, p910_sec2_jtag_pin1),
+ GRP_910("hsl 6p1", HSL, p910_hsl_pin1),
+ GRP_910("hsl 6p2", HSL, p910_hsl_pin2),
+ GRP_910("hsl 6p3", HSL, p910_hsl_pin3),
+ GRP_910("w1-1", ONE_WIRE, p910_w1_pin1),
+ GRP_910("w1-2", ONE_WIRE, p910_w1_pin2),
+ GRP_910("w1-3", ONE_WIRE, p910_w1_pin3),
+ GRP_910("w1-4", ONE_WIRE, p910_w1_pin4),
+ GRP_910("kpmk 16p1", KP_MK, p910_kpmk_pin1),
+ GRP_910("kpmk 11p2", KP_MK, p910_kpmk_pin2),
+ GRP_910("kpdk 8p1", KP_DK, p910_kpdk_pin1),
+ GRP_910("tds 5p1", TDS, p910_tds_pin1),
+ GRP_910("tds 4p2", TDS, p910_tds_pin2),
+ GRP_910("tb 4p1", TB, p910_tb_pin1),
+ GRP_910("tb 4p2", TB, p910_tb_pin2),
+ GRP_910("tb 4p3", TB, p910_tb_pin3),
+ GRP_910("ext dma0-1", EXT_DMA, p910_ext_dma0_pin1),
+ GRP_910("ext dma0-2", EXT_DMA, p910_ext_dma0_pin2),
+ GRP_910("ext dma0-3", EXT_DMA, p910_ext_dma0_pin3),
+ GRP_910("ext dma1-1", EXT_DMA, p910_ext_dma1_pin1),
+ GRP_910("ext dma1-2", EXT_DMA, p910_ext_dma1_pin2),
+ GRP_910("ext dma1-3", EXT_DMA, p910_ext_dma1_pin3),
+ GRP_910("ext dma2", EXT_DMA, p910_ext_dma2_pin1),
+ GRP_910("ext0 int-1", EXT_INT, p910_ext0_int_pin1),
+ GRP_910("ext0 int-2", EXT_INT, p910_ext0_int_pin2),
+ GRP_910("ext1 int-1", EXT_INT, p910_ext1_int_pin1),
+ GRP_910("ext1 int-2", EXT_INT, p910_ext1_int_pin2),
+ GRP_910("ext2 int-1", EXT_INT, p910_ext2_int_pin1),
+ GRP_910("ext2 int-2", EXT_INT, p910_ext2_int_pin2),
+ GRP_910("dac st23-1", DAC_ST23, p910_dac_st23_pin1),
+ GRP_910("dac st23-2", DAC_ST23, p910_dac_st23_pin2),
+ GRP_910("dac st23-3", DAC_ST23, p910_dac_st23_pin3),
+ GRP_910("dac st23-4", DAC_ST23, p910_dac_st23_pin4),
+ GRP_910("vcxo out-1", VCXO_OUT, p910_vcxo_out_pin1),
+ GRP_910("vcxo out-2", VCXO_OUT, p910_vcxo_out_pin2),
+ GRP_910("vcxo out-3", VCXO_OUT, p910_vcxo_out_pin3),
+ GRP_910("vcxo req-1", VCXO_REQ, p910_vcxo_req_pin1),
+ GRP_910("vcxo req-2", VCXO_REQ, p910_vcxo_req_pin2),
+ GRP_910("vcxo out2-1", VCXO_OUT2, p910_vcxo_out2_pin1),
+ GRP_910("vcxo out2-2", VCXO_OUT2, p910_vcxo_out2_pin2),
+ GRP_910("vcxo req2", VCXO_REQ2, p910_vcxo_req2_pin1),
+ GRP_910("ulpi", ULPI, p910_ulpi_pin1),
+ GRP_910("nand", NAND, p910_nand_pin1),
+ GRP_910("gpio0-1", GPIO, p910_gpio0_pin1),
+ GRP_910("gpio0-2", GPIO, p910_gpio0_pin2),
+ GRP_910("gpio1-1", GPIO, p910_gpio1_pin1),
+ GRP_910("gpio1-2", GPIO, p910_gpio1_pin2),
+ GRP_910("gpio2-1", GPIO, p910_gpio2_pin1),
+ GRP_910("gpio2-2", GPIO, p910_gpio2_pin2),
+ GRP_910("gpio3-1", GPIO, p910_gpio3_pin1),
+ GRP_910("gpio3-2", GPIO, p910_gpio3_pin2),
+ GRP_910("gpio20-1", GPIO, p910_gpio20_pin1),
+ GRP_910("gpio20-2", GPIO, p910_gpio20_pin2),
+ GRP_910("gpio21-1", GPIO, p910_gpio21_pin1),
+ GRP_910("gpio21-2", GPIO, p910_gpio21_pin2),
+ GRP_910("gpio22-1", GPIO, p910_gpio22_pin1),
+ GRP_910("gpio22-2", GPIO, p910_gpio22_pin2),
+ GRP_910("gpio23-1", GPIO, p910_gpio23_pin1),
+ GRP_910("gpio23-2", GPIO, p910_gpio23_pin2),
+ GRP_910("gpio24-1", GPIO, p910_gpio24_pin1),
+ GRP_910("gpio24-2", GPIO, p910_gpio24_pin2),
+ GRP_910("gpio25-1", GPIO, p910_gpio25_pin1),
+ GRP_910("gpio25-2", GPIO, p910_gpio25_pin2),
+ GRP_910("gpio26-1", GPIO, p910_gpio26_pin1),
+ GRP_910("gpio26-2", GPIO, p910_gpio26_pin2),
+ GRP_910("gpio27-1", GPIO, p910_gpio27_pin1),
+ GRP_910("gpio27-2", GPIO, p910_gpio27_pin2),
+ GRP_910("gpio85-1", GPIO, p910_gpio85_pin1),
+ GRP_910("gpio85-2", GPIO, p910_gpio85_pin2),
+ GRP_910("gpio86-1", GPIO, p910_gpio86_pin1),
+ GRP_910("gpio86-2", GPIO, p910_gpio86_pin2),
+ GRP_910("gpio87-1", GPIO, p910_gpio87_pin1),
+ GRP_910("gpio87-2", GPIO, p910_gpio87_pin2),
+ GRP_910("gpio88-1", GPIO, p910_gpio88_pin1),
+ GRP_910("gpio88-2", GPIO, p910_gpio88_pin2),
+ GRP_910("gpio89-1", GPIO, p910_gpio89_pin1),
+ GRP_910("gpio89-2", GPIO, p910_gpio89_pin2),
+ GRP_910("gpio90-1", GPIO, p910_gpio90_pin1),
+ GRP_910("gpio90-2", GPIO, p910_gpio90_pin2),
+ GRP_910("gpio91-1", GPIO, p910_gpio91_pin1),
+ GRP_910("gpio91-2", GPIO, p910_gpio91_pin2),
+ GRP_910("gpio92-1", GPIO, p910_gpio92_pin1),
+ GRP_910("gpio92-2", GPIO, p910_gpio92_pin2),
+};
+
+static const char * const p910_usim2_grps[] = {"usim2 3p1", "usim2 3p2"};
+static const char * const p910_mmc1_grps[] = {"mmc1 12p"};
+static const char * const p910_mmc2_grps[] = {"mmc2 10p"};
+static const char * const p910_mmc3_grps[] = {"mmc3 6p", "mmc3 10p"};
+static const char * const p910_uart0_grps[] = {"uart0 4p"};
+static const char * const p910_uart1_grps[] = {"uart1 2p1", "uart1 2p2",
+ "uart1 2p3", "uart1 4p4", "uart1 4p5"};
+static const char * const p910_uart2_grps[] = {"uart2 2p1", "uart2 2p2",
+ "uart2 4p3", "uart2 4p4"};
+static const char * const p910_twsi_grps[] = {"twsi 2p1", "twsi 2p2",
+ "twsi 2p3"};
+static const char * const p910_ccic_grps[] = {"ccic"};
+static const char * const p910_lcd_grps[] = {"lcd"};
+static const char * const p910_spi_grps[] = {"spi 4p1", "spi 4p2", "spi 5p3",
+ "spi 5p4"};
+static const char * const p910_dssp2_grps[] = {"dssp2 4p1", "dssp2 4p2",
+ "dssp2 3p3"};
+static const char * const p910_dssp3_grps[] = {"dssp3 4p1", "dssp3 4p2",
+ "dssp3 3p3"};
+static const char * const p910_ssp0_grps[] = {"ssp0 4p1", "ssp0 4p2",
+ "ssp0 4p3", "ssp0 4p4"};
+static const char * const p910_ssp1_grps[] = {"ssp1 4p1", "ssp1 5p2"};
+static const char * const p910_ssp2_grps[] = {"ssp2 4p1", "ssp2 4p2",
+ "ssp2 4p3", "ssp2 4p4"};
+static const char * const p910_gssp_grps[] = {"gssp"};
+static const char * const p910_pwm0_grps[] = {"pwm0"};
+static const char * const p910_pwm1_grps[] = {"pwm1-1", "pwm1-2"};
+static const char * const p910_pwm2_grps[] = {"pwm2"};
+static const char * const p910_pwm3_grps[] = {"pwm3-1", "pwm3-2", "pwm3-3"};
+static const char * const p910_pri_jtag_grps[] = {"pri jtag"};
+static const char * const p910_sec1_jtag_grps[] = {"sec1 jtag"};
+static const char * const p910_sec2_jtag_grps[] = {"sec2 jtag"};
+static const char * const p910_hsl_grps[] = {"hsl 6p1", "hsl 6p2", "hsl 6p3"};
+static const char * const p910_w1_grps[] = {"w1-1", "w1-2", "w1-3", "w1-4"};
+static const char * const p910_kpmk_grps[] = {"kpmk 16p1", "kpmk 11p2"};
+static const char * const p910_kpdk_grps[] = {"kpdk 8p1"};
+static const char * const p910_tds_grps[] = {"tds 5p1", "tds 4p2"};
+static const char * const p910_tb_grps[] = {"tb 4p1", "tb 4p2", "tb 4p3"};
+static const char * const p910_dma0_grps[] = {"ext dma0-1", "ext dma0-2",
+ "ext dma0-3"};
+static const char * const p910_dma1_grps[] = {"ext dma1-1", "ext dma1-2",
+ "ext dma1-3"};
+static const char * const p910_dma2_grps[] = {"ext dma2"};
+static const char * const p910_int0_grps[] = {"ext0 int-1", "ext0 int-2"};
+static const char * const p910_int1_grps[] = {"ext1 int-1", "ext1 int-2"};
+static const char * const p910_int2_grps[] = {"ext2 int-1", "ext2 int-2"};
+static const char * const p910_dac_st23_grps[] = {"dac st23-1", "dac st23-2",
+ "dac st23-3", "dac st23-4"};
+static const char * const p910_vcxo_out_grps[] = {"vcxo out-1", "vcxo out-2",
+ "vcxo out-3"};
+static const char * const p910_vcxo_req_grps[] = {"vcxo req-1", "vcxo req-2"};
+static const char * const p910_vcxo_out2_grps[] = {"vcxo out2-1",
+ "vcxo out2-2"};
+static const char * const p910_vcxo_req2_grps[] = {"vcxo req2"};
+static const char * const p910_ulpi_grps[] = {"ulpi"};
+static const char * const p910_nand_grps[] = {"nand"};
+static const char * const p910_gpio0_grps[] = {"gpio0-1", "gpio0-2"};
+static const char * const p910_gpio1_grps[] = {"gpio1-1", "gpio1-2"};
+static const char * const p910_gpio2_grps[] = {"gpio2-1", "gpio2-2"};
+static const char * const p910_gpio3_grps[] = {"gpio3-1", "gpio3-2"};
+static const char * const p910_gpio20_grps[] = {"gpio20-1", "gpio20-2"};
+static const char * const p910_gpio21_grps[] = {"gpio21-1", "gpio21-2"};
+static const char * const p910_gpio22_grps[] = {"gpio22-1", "gpio22-2"};
+static const char * const p910_gpio23_grps[] = {"gpio23-1", "gpio23-2"};
+static const char * const p910_gpio24_grps[] = {"gpio24-1", "gpio24-2"};
+static const char * const p910_gpio25_grps[] = {"gpio25-1", "gpio25-2"};
+static const char * const p910_gpio26_grps[] = {"gpio26-1", "gpio26-2"};
+static const char * const p910_gpio27_grps[] = {"gpio27-1", "gpio27-2"};
+static const char * const p910_gpio85_grps[] = {"gpio85-1", "gpio85-2"};
+static const char * const p910_gpio86_grps[] = {"gpio86-1", "gpio86-2"};
+static const char * const p910_gpio87_grps[] = {"gpio87-1", "gpio87-2"};
+static const char * const p910_gpio88_grps[] = {"gpio88-1", "gpio88-2"};
+static const char * const p910_gpio89_grps[] = {"gpio89-1", "gpio89-2"};
+static const char * const p910_gpio90_grps[] = {"gpio90-1", "gpio90-2"};
+static const char * const p910_gpio91_grps[] = {"gpio91-1", "gpio91-2"};
+static const char * const p910_gpio92_grps[] = {"gpio92-1", "gpio92-2"};
+
+static struct pxa3xx_pmx_func pxa910_funcs[] = {
+ {"usim2", ARRAY_AND_SIZE(p910_usim2_grps)},
+ {"mmc1", ARRAY_AND_SIZE(p910_mmc1_grps)},
+ {"mmc2", ARRAY_AND_SIZE(p910_mmc2_grps)},
+ {"mmc3", ARRAY_AND_SIZE(p910_mmc3_grps)},
+ {"uart0", ARRAY_AND_SIZE(p910_uart0_grps)},
+ {"uart1", ARRAY_AND_SIZE(p910_uart1_grps)},
+ {"uart2", ARRAY_AND_SIZE(p910_uart2_grps)},
+ {"twsi", ARRAY_AND_SIZE(p910_twsi_grps)},
+ {"ccic", ARRAY_AND_SIZE(p910_ccic_grps)},
+ {"lcd", ARRAY_AND_SIZE(p910_lcd_grps)},
+ {"spi", ARRAY_AND_SIZE(p910_spi_grps)},
+ {"dssp2", ARRAY_AND_SIZE(p910_dssp2_grps)},
+ {"dssp3", ARRAY_AND_SIZE(p910_dssp3_grps)},
+ {"ssp0", ARRAY_AND_SIZE(p910_ssp0_grps)},
+ {"ssp1", ARRAY_AND_SIZE(p910_ssp1_grps)},
+ {"ssp2", ARRAY_AND_SIZE(p910_ssp2_grps)},
+ {"gssp", ARRAY_AND_SIZE(p910_gssp_grps)},
+ {"pwm0", ARRAY_AND_SIZE(p910_pwm0_grps)},
+ {"pwm1", ARRAY_AND_SIZE(p910_pwm1_grps)},
+ {"pwm2", ARRAY_AND_SIZE(p910_pwm2_grps)},
+ {"pwm3", ARRAY_AND_SIZE(p910_pwm3_grps)},
+ {"pri_jtag", ARRAY_AND_SIZE(p910_pri_jtag_grps)},
+ {"sec1_jtag", ARRAY_AND_SIZE(p910_sec1_jtag_grps)},
+ {"sec2_jtag", ARRAY_AND_SIZE(p910_sec2_jtag_grps)},
+ {"hsl", ARRAY_AND_SIZE(p910_hsl_grps)},
+ {"w1", ARRAY_AND_SIZE(p910_w1_grps)},
+ {"kpmk", ARRAY_AND_SIZE(p910_kpmk_grps)},
+ {"kpdk", ARRAY_AND_SIZE(p910_kpdk_grps)},
+ {"tds", ARRAY_AND_SIZE(p910_tds_grps)},
+ {"tb", ARRAY_AND_SIZE(p910_tb_grps)},
+ {"dma0", ARRAY_AND_SIZE(p910_dma0_grps)},
+ {"dma1", ARRAY_AND_SIZE(p910_dma1_grps)},
+ {"dma2", ARRAY_AND_SIZE(p910_dma2_grps)},
+ {"int0", ARRAY_AND_SIZE(p910_int0_grps)},
+ {"int1", ARRAY_AND_SIZE(p910_int1_grps)},
+ {"int2", ARRAY_AND_SIZE(p910_int2_grps)},
+ {"dac_st23", ARRAY_AND_SIZE(p910_dac_st23_grps)},
+ {"vcxo_out", ARRAY_AND_SIZE(p910_vcxo_out_grps)},
+ {"vcxo_req", ARRAY_AND_SIZE(p910_vcxo_req_grps)},
+ {"vcxo_out2", ARRAY_AND_SIZE(p910_vcxo_out2_grps)},
+ {"vcxo_req2", ARRAY_AND_SIZE(p910_vcxo_req2_grps)},
+ {"ulpi", ARRAY_AND_SIZE(p910_ulpi_grps)},
+ {"nand", ARRAY_AND_SIZE(p910_nand_grps)},
+ {"gpio0", ARRAY_AND_SIZE(p910_gpio0_grps)},
+ {"gpio1", ARRAY_AND_SIZE(p910_gpio1_grps)},
+ {"gpio2", ARRAY_AND_SIZE(p910_gpio2_grps)},
+ {"gpio3", ARRAY_AND_SIZE(p910_gpio3_grps)},
+ {"gpio20", ARRAY_AND_SIZE(p910_gpio20_grps)},
+ {"gpio21", ARRAY_AND_SIZE(p910_gpio21_grps)},
+ {"gpio22", ARRAY_AND_SIZE(p910_gpio22_grps)},
+ {"gpio23", ARRAY_AND_SIZE(p910_gpio23_grps)},
+ {"gpio24", ARRAY_AND_SIZE(p910_gpio24_grps)},
+ {"gpio25", ARRAY_AND_SIZE(p910_gpio25_grps)},
+ {"gpio26", ARRAY_AND_SIZE(p910_gpio26_grps)},
+ {"gpio27", ARRAY_AND_SIZE(p910_gpio27_grps)},
+ {"gpio85", ARRAY_AND_SIZE(p910_gpio85_grps)},
+ {"gpio86", ARRAY_AND_SIZE(p910_gpio86_grps)},
+ {"gpio87", ARRAY_AND_SIZE(p910_gpio87_grps)},
+ {"gpio88", ARRAY_AND_SIZE(p910_gpio88_grps)},
+ {"gpio89", ARRAY_AND_SIZE(p910_gpio89_grps)},
+ {"gpio90", ARRAY_AND_SIZE(p910_gpio90_grps)},
+ {"gpio91", ARRAY_AND_SIZE(p910_gpio91_grps)},
+ {"gpio92", ARRAY_AND_SIZE(p910_gpio92_grps)},
+};
+
+static struct pinctrl_desc pxa910_pctrl_desc = {
+ .name = "pxa910-pinctrl",
+ .owner = THIS_MODULE,
+};
+
+static struct pxa3xx_pinmux_info pxa910_info = {
+ .mfp = pxa910_mfp,
+ .num_mfp = ARRAY_SIZE(pxa910_mfp),
+ .grps = pxa910_grps,
+ .num_grps = ARRAY_SIZE(pxa910_grps),
+ .funcs = pxa910_funcs,
+ .num_funcs = ARRAY_SIZE(pxa910_funcs),
+ .num_gpio = 128,
+ .desc = &pxa910_pctrl_desc,
+ .pads = pxa910_pads,
+ .num_pads = ARRAY_SIZE(pxa910_pads),
+
+ .cputype = PINCTRL_PXA910,
+ .ds_mask = PXA910_DS_MASK,
+ .ds_shift = PXA910_DS_SHIFT,
+};
+
+static int __devinit pxa910_pinmux_probe(struct platform_device *pdev)
+{
+ return pxa3xx_pinctrl_register(pdev, &pxa910_info);
+}
+
+static int __devexit pxa910_pinmux_remove(struct platform_device *pdev)
+{
+ return pxa3xx_pinctrl_unregister(pdev);
+}
+
+static struct platform_driver pxa910_pinmux_driver = {
+ .driver = {
+ .name = "pxa910-pinmux",
+ .owner = THIS_MODULE,
+ },
+ .probe = pxa910_pinmux_probe,
+ .remove = __devexit_p(pxa910_pinmux_remove),
+};
+
+static int __init pxa910_pinmux_init(void)
+{
+ return platform_driver_register(&pxa910_pinmux_driver);
+}
+core_initcall_sync(pxa910_pinmux_init);
+
+static void __exit pxa910_pinmux_exit(void)
+{
+ platform_driver_unregister(&pxa910_pinmux_driver);
+}
+module_exit(pxa910_pinmux_exit);
+
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_DESCRIPTION("PXA3xx pin control driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
new file mode 100644
index 000000000000..9b329688120c
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -0,0 +1,559 @@
+/*
+ * Driver for the NVIDIA Tegra pinmux
+ *
+ * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Derived from code:
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 NVIDIA Corporation
+ * Copyright (C) 2009-2011 ST-Ericsson AB
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+
+#include <mach/pinconf-tegra.h>
+
+#include "pinctrl-tegra.h"
+
+#define DRIVER_NAME "tegra-pinmux-disabled"
+
+struct tegra_pmx {
+ struct device *dev;
+ struct pinctrl_dev *pctl;
+
+ const struct tegra_pinctrl_soc_data *soc;
+
+ int nbanks;
+ void __iomem **regs;
+};
+
+static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg)
+{
+ return readl(pmx->regs[bank] + reg);
+}
+
+static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg)
+{
+ writel(val, pmx->regs[bank] + reg);
+}
+
+static int tegra_pinctrl_list_groups(struct pinctrl_dev *pctldev,
+ unsigned group)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (group >= pmx->soc->ngroups)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned group)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (group >= pmx->soc->ngroups)
+ return NULL;
+
+ return pmx->soc->groups[group].name;
+}
+
+static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned group,
+ const unsigned **pins,
+ unsigned *num_pins)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (group >= pmx->soc->ngroups)
+ return -EINVAL;
+
+ *pins = pmx->soc->groups[group].pins;
+ *num_pins = pmx->soc->groups[group].npins;
+
+ return 0;
+}
+
+static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s,
+ unsigned offset)
+{
+ seq_printf(s, " " DRIVER_NAME);
+}
+
+static struct pinctrl_ops tegra_pinctrl_ops = {
+ .list_groups = tegra_pinctrl_list_groups,
+ .get_group_name = tegra_pinctrl_get_group_name,
+ .get_group_pins = tegra_pinctrl_get_group_pins,
+ .pin_dbg_show = tegra_pinctrl_pin_dbg_show,
+};
+
+static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (function >= pmx->soc->nfunctions)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned function)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (function >= pmx->soc->nfunctions)
+ return NULL;
+
+ return pmx->soc->functions[function].name;
+}
+
+static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+
+ if (function >= pmx->soc->nfunctions)
+ return -EINVAL;
+
+ *groups = pmx->soc->functions[function].groups;
+ *num_groups = pmx->soc->functions[function].ngroups;
+
+ return 0;
+}
+
+static int tegra_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function,
+ unsigned group)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct tegra_pingroup *g;
+ int i;
+ u32 val;
+
+ if (group >= pmx->soc->ngroups)
+ return -EINVAL;
+ g = &pmx->soc->groups[group];
+
+ if (g->mux_reg < 0)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(g->funcs); i++) {
+ if (g->funcs[i] == function)
+ break;
+ }
+ if (i == ARRAY_SIZE(g->funcs))
+ return -EINVAL;
+
+ val = pmx_readl(pmx, g->mux_bank, g->mux_reg);
+ val &= ~(0x3 << g->mux_bit);
+ val |= i << g->mux_bit;
+ pmx_writel(pmx, val, g->mux_bank, g->mux_reg);
+
+ return 0;
+}
+
+static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev,
+ unsigned function, unsigned group)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ const struct tegra_pingroup *g;
+ u32 val;
+
+ if (group >= pmx->soc->ngroups)
+ return;
+ g = &pmx->soc->groups[group];
+
+ if (g->mux_reg < 0)
+ return;
+
+ val = pmx_readl(pmx, g->mux_bank, g->mux_reg);
+ val &= ~(0x3 << g->mux_bit);
+ val |= g->func_safe << g->mux_bit;
+ pmx_writel(pmx, val, g->mux_bank, g->mux_reg);
+}
+
+static struct pinmux_ops tegra_pinmux_ops = {
+ .list_functions = tegra_pinctrl_list_funcs,
+ .get_function_name = tegra_pinctrl_get_func_name,
+ .get_function_groups = tegra_pinctrl_get_func_groups,
+ .enable = tegra_pinctrl_enable,
+ .disable = tegra_pinctrl_disable,
+};
+
+static int tegra_pinconf_reg(struct tegra_pmx *pmx,
+ const struct tegra_pingroup *g,
+ enum tegra_pinconf_param param,
+ s8 *bank, s16 *reg, s8 *bit, s8 *width)
+{
+ switch (param) {
+ case TEGRA_PINCONF_PARAM_PULL:
+ *bank = g->pupd_bank;
+ *reg = g->pupd_reg;
+ *bit = g->pupd_bit;
+ *width = 2;
+ break;
+ case TEGRA_PINCONF_PARAM_TRISTATE:
+ *bank = g->tri_bank;
+ *reg = g->tri_reg;
+ *bit = g->tri_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_ENABLE_INPUT:
+ *bank = g->einput_bank;
+ *reg = g->einput_reg;
+ *bit = g->einput_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_OPEN_DRAIN:
+ *bank = g->odrain_bank;
+ *reg = g->odrain_reg;
+ *bit = g->odrain_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_LOCK:
+ *bank = g->lock_bank;
+ *reg = g->lock_reg;
+ *bit = g->lock_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_IORESET:
+ *bank = g->ioreset_bank;
+ *reg = g->ioreset_reg;
+ *bit = g->ioreset_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->hsm_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_SCHMITT:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->schmitt_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_LOW_POWER_MODE:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->lpmd_bit;
+ *width = 1;
+ break;
+ case TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->drvdn_bit;
+ *width = g->drvdn_width;
+ break;
+ case TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->drvup_bit;
+ *width = g->drvup_width;
+ break;
+ case TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->slwf_bit;
+ *width = g->slwf_width;
+ break;
+ case TEGRA_PINCONF_PARAM_SLEW_RATE_RISING:
+ *bank = g->drv_bank;
+ *reg = g->drv_reg;
+ *bit = g->slwr_bit;
+ *width = g->slwr_width;
+ break;
+ default:
+ dev_err(pmx->dev, "Invalid config param %04x\n", param);
+ return -ENOTSUPP;
+ }
+
+ if (*reg < 0) {
+ dev_err(pmx->dev,
+ "Config param %04x not supported on group %s\n",
+ param, g->name);
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int tegra_pinconf_get(struct pinctrl_dev *pctldev,
+ unsigned pin, unsigned long *config)
+{
+ return -ENOTSUPP;
+}
+
+static int tegra_pinconf_set(struct pinctrl_dev *pctldev,
+ unsigned pin, unsigned long config)
+{
+ return -ENOTSUPP;
+}
+
+static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev,
+ unsigned group, unsigned long *config)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(*config);
+ u16 arg;
+ const struct tegra_pingroup *g;
+ int ret;
+ s8 bank, bit, width;
+ s16 reg;
+ u32 val, mask;
+
+ if (group >= pmx->soc->ngroups)
+ return -EINVAL;
+ g = &pmx->soc->groups[group];
+
+ ret = tegra_pinconf_reg(pmx, g, param, &bank, &reg, &bit, &width);
+ if (ret < 0)
+ return ret;
+
+ val = pmx_readl(pmx, bank, reg);
+ mask = (1 << width) - 1;
+ arg = (val >> bit) & mask;
+
+ *config = TEGRA_PINCONF_PACK(param, arg);
+
+ return 0;
+}
+
+static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned group, unsigned long config)
+{
+ struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+ enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config);
+ u16 arg = TEGRA_PINCONF_UNPACK_ARG(config);
+ const struct tegra_pingroup *g;
+ int ret;
+ s8 bank, bit, width;
+ s16 reg;
+ u32 val, mask;
+
+ if (group >= pmx->soc->ngroups)
+ return -EINVAL;
+ g = &pmx->soc->groups[group];
+
+ ret = tegra_pinconf_reg(pmx, g, param, &bank, &reg, &bit, &width);
+ if (ret < 0)
+ return ret;
+
+ val = pmx_readl(pmx, bank, reg);
+
+ /* LOCK can't be cleared */
+ if (param == TEGRA_PINCONF_PARAM_LOCK) {
+ if ((val & BIT(bit)) && !arg)
+ return -EINVAL;
+ }
+
+ /* Special-case Boolean values; allow any non-zero as true */
+ if (width == 1)
+ arg = !!arg;
+
+ /* Range-check user-supplied value */
+ mask = (1 << width) - 1;
+ if (arg & ~mask)
+ return -EINVAL;
+
+ /* Update register */
+ val &= ~(mask << bit);
+ val |= arg << bit;
+ pmx_writel(pmx, val, bank, reg);
+
+ return 0;
+}
+
+static void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned offset)
+{
+}
+
+static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
+ struct seq_file *s, unsigned selector)
+{
+}
+
+struct pinconf_ops tegra_pinconf_ops = {
+ .pin_config_get = tegra_pinconf_get,
+ .pin_config_set = tegra_pinconf_set,
+ .pin_config_group_get = tegra_pinconf_group_get,
+ .pin_config_group_set = tegra_pinconf_group_set,
+ .pin_config_dbg_show = tegra_pinconf_dbg_show,
+ .pin_config_group_dbg_show = tegra_pinconf_group_dbg_show,
+};
+
+static struct pinctrl_gpio_range tegra_pinctrl_gpio_range = {
+ .name = "Tegra GPIOs",
+ .id = 0,
+ .base = 0,
+};
+
+static struct pinctrl_desc tegra_pinctrl_desc = {
+ .name = DRIVER_NAME,
+ .pctlops = &tegra_pinctrl_ops,
+ .pmxops = &tegra_pinmux_ops,
+ .confops = &tegra_pinconf_ops,
+ .owner = THIS_MODULE,
+};
+
+static struct of_device_id tegra_pinctrl_of_match[] __devinitdata = {
+#ifdef CONFIG_PINCTRL_TEGRA20
+ {
+ .compatible = "nvidia,tegra20-pinmux-disabled",
+ .data = tegra20_pinctrl_init,
+ },
+#endif
+#ifdef CONFIG_PINCTRL_TEGRA30
+ {
+ .compatible = "nvidia,tegra30-pinmux-disabled",
+ .data = tegra30_pinctrl_init,
+ },
+#endif
+ {},
+};
+
+static int __devinit tegra_pinctrl_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ tegra_pinctrl_soc_initf initf = NULL;
+ struct tegra_pmx *pmx;
+ struct resource *res;
+ int i;
+
+ match = of_match_device(tegra_pinctrl_of_match, &pdev->dev);
+ if (match)
+ initf = (tegra_pinctrl_soc_initf)match->data;
+#ifdef CONFIG_PINCTRL_TEGRA20
+ if (!initf)
+ initf = tegra20_pinctrl_init;
+#endif
+ if (!initf) {
+ dev_err(&pdev->dev,
+ "Could not determine SoC-specific init func\n");
+ return -EINVAL;
+ }
+
+ pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL);
+ if (!pmx) {
+ dev_err(&pdev->dev, "Can't alloc tegra_pmx\n");
+ return -ENOMEM;
+ }
+ pmx->dev = &pdev->dev;
+
+ (*initf)(&pmx->soc);
+
+ tegra_pinctrl_gpio_range.npins = pmx->soc->ngpios;
+ tegra_pinctrl_desc.pins = pmx->soc->pins;
+ tegra_pinctrl_desc.npins = pmx->soc->npins;
+
+ for (i = 0; ; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res)
+ break;
+ }
+ pmx->nbanks = i;
+
+ pmx->regs = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(*pmx->regs),
+ GFP_KERNEL);
+ if (!pmx->regs) {
+ dev_err(&pdev->dev, "Can't alloc regs pointer\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < pmx->nbanks; i++) {
+ res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+ if (!res) {
+ dev_err(&pdev->dev, "Missing MEM resource\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res),
+ dev_name(&pdev->dev))) {
+ dev_err(&pdev->dev,
+ "Couldn't request MEM resource %d\n", i);
+ return -ENODEV;
+ }
+
+ pmx->regs[i] = devm_ioremap(&pdev->dev, res->start,
+ resource_size(res));
+ if (!pmx->regs[i]) {
+ dev_err(&pdev->dev, "Couldn't ioremap regs %d\n", i);
+ return -ENODEV;
+ }
+ }
+
+ pmx->pctl = pinctrl_register(&tegra_pinctrl_desc, &pdev->dev, pmx);
+ if (IS_ERR(pmx->pctl)) {
+ dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
+ return PTR_ERR(pmx->pctl);
+ }
+
+ pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
+
+ platform_set_drvdata(pdev, pmx);
+
+ dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n");
+
+ return 0;
+}
+
+static int __devexit tegra_pinctrl_remove(struct platform_device *pdev)
+{
+ struct tegra_pmx *pmx = platform_get_drvdata(pdev);
+
+ pinctrl_remove_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range);
+ pinctrl_unregister(pmx->pctl);
+
+ return 0;
+}
+
+static struct platform_driver tegra_pinctrl_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra_pinctrl_of_match,
+ },
+ .probe = tegra_pinctrl_probe,
+ .remove = __devexit_p(tegra_pinctrl_remove),
+};
+
+static int __init tegra_pinctrl_init(void)
+{
+ return platform_driver_register(&tegra_pinctrl_driver);
+}
+arch_initcall(tegra_pinctrl_init);
+
+static void __exit tegra_pinctrl_exit(void)
+{
+ platform_driver_unregister(&tegra_pinctrl_driver);
+}
+module_exit(tegra_pinctrl_exit);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("NVIDIA Tegra pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, tegra_pinctrl_of_match);
diff --git a/drivers/pinctrl/pinctrl-tegra.h b/drivers/pinctrl/pinctrl-tegra.h
new file mode 100644
index 000000000000..782c795326ef
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tegra.h
@@ -0,0 +1,163 @@
+/*
+ * Driver for the NVIDIA Tegra pinmux
+ *
+ * Copyright (c) 2011, 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.
+ */
+
+#ifndef __PINMUX_TEGRA_H__
+#define __PINMUX_TEGRA_H__
+
+/**
+ * struct tegra_function - Tegra pinctrl mux function
+ * @name: The name of the function, exported to pinctrl core.
+ * @groups: An array of pin groups that may select this function.
+ * @ngroups: The number of entries in @groups.
+ */
+struct tegra_function {
+ const char *name;
+ const char * const *groups;
+ unsigned ngroups;
+};
+
+/**
+ * struct tegra_pingroup - Tegra pin group
+ * @mux_reg: Mux register offset. -1 if unsupported.
+ * @mux_bank: Mux register bank. 0 if unsupported.
+ * @mux_bit: Mux register bit. 0 if unsupported.
+ * @pupd_reg: Pull-up/down register offset. -1 if unsupported.
+ * @pupd_bank: Pull-up/down register bank. 0 if unsupported.
+ * @pupd_bit: Pull-up/down register bit. 0 if unsupported.
+ * @tri_reg: Tri-state register offset. -1 if unsupported.
+ * @tri_bank: Tri-state register bank. 0 if unsupported.
+ * @tri_bit: Tri-state register bit. 0 if unsupported.
+ * @einput_reg: Enable-input register offset. -1 if unsupported.
+ * @einput_bank: Enable-input register bank. 0 if unsupported.
+ * @einput_bit: Enable-input register bit. 0 if unsupported.
+ * @odrain_reg: Open-drain register offset. -1 if unsupported.
+ * @odrain_bank: Open-drain register bank. 0 if unsupported.
+ * @odrain_bit: Open-drain register bit. 0 if unsupported.
+ * @lock_reg: Lock register offset. -1 if unsupported.
+ * @lock_bank: Lock register bank. 0 if unsupported.
+ * @lock_bit: Lock register bit. 0 if unsupported.
+ * @ioreset_reg: IO reset register offset. -1 if unsupported.
+ * @ioreset_bank: IO reset register bank. 0 if unsupported.
+ * @ioreset_bit: IO reset register bit. 0 if unsupported.
+ * @drv_reg: Drive fields register offset. -1 if unsupported.
+ * This register contains the hsm, schmitt, lpmd, drvdn,
+ * drvup, slwr, and slwf parameters.
+ * @drv_bank: Drive fields register bank. 0 if unsupported.
+ * @hsm_bit: High Speed Mode register bit. 0 if unsupported.
+ * @schmitt_bit: Scmitt register bit. 0 if unsupported.
+ * @lpmd_bit: Low Power Mode register bit. 0 if unsupported.
+ * @drvdn_bit: Drive Down register bit. 0 if unsupported.
+ * @drvdn_width: Drive Down field width. 0 if unsupported.
+ * @drvup_bit: Drive Up register bit. 0 if unsupported.
+ * @drvup_width: Drive Up field width. 0 if unsupported.
+ * @slwr_bit: Slew Rising register bit. 0 if unsupported.
+ * @slwr_width: Slew Rising field width. 0 if unsupported.
+ * @slwf_bit: Slew Falling register bit. 0 if unsupported.
+ * @slwf_width: Slew Falling field width. 0 if unsupported.
+ *
+ * A representation of a group of pins (possibly just one pin) in the Tegra
+ * pin controller. Each group allows some parameter or parameters to be
+ * configured. The most common is mux function selection. Many others exist
+ * such as pull-up/down, tri-state, etc. Tegra's pin controller is complex;
+ * certain groups may only support configuring certain parameters, hence
+ * each parameter is optional, represented by a -1 "reg" value.
+ */
+struct tegra_pingroup {
+ const char *name;
+ const unsigned *pins;
+ unsigned npins;
+ unsigned funcs[4];
+ unsigned func_safe;
+ s16 mux_reg;
+ s16 pupd_reg;
+ s16 tri_reg;
+ s16 einput_reg;
+ s16 odrain_reg;
+ s16 lock_reg;
+ s16 ioreset_reg;
+ s16 drv_reg;
+ u32 mux_bank:2;
+ u32 pupd_bank:2;
+ u32 tri_bank:2;
+ u32 einput_bank:2;
+ u32 odrain_bank:2;
+ u32 ioreset_bank:2;
+ u32 lock_bank:2;
+ u32 drv_bank:2;
+ u32 mux_bit:5;
+ u32 pupd_bit:5;
+ u32 tri_bit:5;
+ u32 einput_bit:5;
+ u32 odrain_bit:5;
+ u32 lock_bit:5;
+ u32 ioreset_bit:5;
+ u32 hsm_bit:5;
+ u32 schmitt_bit:5;
+ u32 lpmd_bit:5;
+ u32 drvdn_bit:5;
+ u32 drvup_bit:5;
+ u32 slwr_bit:5;
+ u32 slwf_bit:5;
+ u32 drvdn_width:6;
+ u32 drvup_width:6;
+ u32 slwr_width:6;
+ u32 slwf_width:6;
+};
+
+/**
+ * struct tegra_pinctrl_soc_data - Tegra pin controller driver configuration
+ * @ngpios: The number of GPIO pins the pin controller HW affects.
+ * @pins: An array describing all pins the pin controller affects.
+ * All pins which are also GPIOs must be listed first within the
+ * array, and be numbered identically to the GPIO controller's
+ * numbering.
+ * @npins: The numbmer of entries in @pins.
+ * @functions: An array describing all mux functions the SoC supports.
+ * @nfunctions: The numbmer of entries in @functions.
+ * @groups: An array describing all pin groups the pin SoC supports.
+ * @ngroups: The numbmer of entries in @groups.
+ */
+struct tegra_pinctrl_soc_data {
+ unsigned ngpios;
+ const struct pinctrl_pin_desc *pins;
+ unsigned npins;
+ const struct tegra_function *functions;
+ unsigned nfunctions;
+ const struct tegra_pingroup *groups;
+ unsigned ngroups;
+};
+
+/**
+ * tegra_pinctrl_soc_initf() - Retrieve pin controller details for a SoC.
+ * @soc_data: This pointer must be updated to point at a struct containing
+ * details of the SoC.
+ */
+typedef void (*tegra_pinctrl_soc_initf)(
+ const struct tegra_pinctrl_soc_data **soc_data);
+
+/**
+ * tegra20_pinctrl_init() - Retrieve pin controller details for Tegra20
+ * @soc_data: This pointer will be updated to point at a struct containing
+ * details of Tegra20's pin controller.
+ */
+void tegra20_pinctrl_init(const struct tegra_pinctrl_soc_data **soc_data);
+/**
+ * tegra30_pinctrl_init() - Retrieve pin controller details for Tegra20
+ * @soc_data: This pointer will be updated to point at a struct containing
+ * details of Tegra30's pin controller.
+ */
+void tegra30_pinctrl_init(const struct tegra_pinctrl_soc_data **soc_data);
+
+#endif
diff --git a/drivers/pinctrl/pinctrl-tegra20.c b/drivers/pinctrl/pinctrl-tegra20.c
new file mode 100644
index 000000000000..f69ff96aa292
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tegra20.c
@@ -0,0 +1,2860 @@
+/*
+ * Pinctrl data for the NVIDIA Tegra20 pinmux
+ *
+ * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Derived from code:
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "pinctrl-tegra.h"
+
+/*
+ * Most pins affected by the pinmux can also be GPIOs. Define these first.
+ * These must match how the GPIO driver names/numbers its pins.
+ */
+#define _GPIO(offset) (offset)
+
+#define TEGRA_PIN_VI_GP6_PA0 _GPIO(0)
+#define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1)
+#define TEGRA_PIN_DAP2_FS_PA2 _GPIO(2)
+#define TEGRA_PIN_DAP2_SCLK_PA3 _GPIO(3)
+#define TEGRA_PIN_DAP2_DIN_PA4 _GPIO(4)
+#define TEGRA_PIN_DAP2_DOUT_PA5 _GPIO(5)
+#define TEGRA_PIN_SDIO3_CLK_PA6 _GPIO(6)
+#define TEGRA_PIN_SDIO3_CMD_PA7 _GPIO(7)
+#define TEGRA_PIN_GMI_AD17_PB0 _GPIO(8)
+#define TEGRA_PIN_GMI_AD18_PB1 _GPIO(9)
+#define TEGRA_PIN_LCD_PWR0_PB2 _GPIO(10)
+#define TEGRA_PIN_LCD_PCLK_PB3 _GPIO(11)
+#define TEGRA_PIN_SDIO3_DAT3_PB4 _GPIO(12)
+#define TEGRA_PIN_SDIO3_DAT2_PB5 _GPIO(13)
+#define TEGRA_PIN_SDIO3_DAT1_PB6 _GPIO(14)
+#define TEGRA_PIN_SDIO3_DAT0_PB7 _GPIO(15)
+#define TEGRA_PIN_UART3_RTS_N_PC0 _GPIO(16)
+#define TEGRA_PIN_LCD_PWR1_PC1 _GPIO(17)
+#define TEGRA_PIN_UART2_TXD_PC2 _GPIO(18)
+#define TEGRA_PIN_UART2_RXD_PC3 _GPIO(19)
+#define TEGRA_PIN_GEN1_I2C_SCL_PC4 _GPIO(20)
+#define TEGRA_PIN_GEN1_I2C_SDA_PC5 _GPIO(21)
+#define TEGRA_PIN_LCD_PWR2_PC6 _GPIO(22)
+#define TEGRA_PIN_GMI_WP_N_PC7 _GPIO(23)
+#define TEGRA_PIN_SDIO3_DAT5_PD0 _GPIO(24)
+#define TEGRA_PIN_SDIO3_DAT4_PD1 _GPIO(25)
+#define TEGRA_PIN_VI_GP5_PD2 _GPIO(26)
+#define TEGRA_PIN_SDIO3_DAT6_PD3 _GPIO(27)
+#define TEGRA_PIN_SDIO3_DAT7_PD4 _GPIO(28)
+#define TEGRA_PIN_VI_D1_PD5 _GPIO(29)
+#define TEGRA_PIN_VI_VSYNC_PD6 _GPIO(30)
+#define TEGRA_PIN_VI_HSYNC_PD7 _GPIO(31)
+#define TEGRA_PIN_LCD_D0_PE0 _GPIO(32)
+#define TEGRA_PIN_LCD_D1_PE1 _GPIO(33)
+#define TEGRA_PIN_LCD_D2_PE2 _GPIO(34)
+#define TEGRA_PIN_LCD_D3_PE3 _GPIO(35)
+#define TEGRA_PIN_LCD_D4_PE4 _GPIO(36)
+#define TEGRA_PIN_LCD_D5_PE5 _GPIO(37)
+#define TEGRA_PIN_LCD_D6_PE6 _GPIO(38)
+#define TEGRA_PIN_LCD_D7_PE7 _GPIO(39)
+#define TEGRA_PIN_LCD_D8_PF0 _GPIO(40)
+#define TEGRA_PIN_LCD_D9_PF1 _GPIO(41)
+#define TEGRA_PIN_LCD_D10_PF2 _GPIO(42)
+#define TEGRA_PIN_LCD_D11_PF3 _GPIO(43)
+#define TEGRA_PIN_LCD_D12_PF4 _GPIO(44)
+#define TEGRA_PIN_LCD_D13_PF5 _GPIO(45)
+#define TEGRA_PIN_LCD_D14_PF6 _GPIO(46)
+#define TEGRA_PIN_LCD_D15_PF7 _GPIO(47)
+#define TEGRA_PIN_GMI_AD0_PG0 _GPIO(48)
+#define TEGRA_PIN_GMI_AD1_PG1 _GPIO(49)
+#define TEGRA_PIN_GMI_AD2_PG2 _GPIO(50)
+#define TEGRA_PIN_GMI_AD3_PG3 _GPIO(51)
+#define TEGRA_PIN_GMI_AD4_PG4 _GPIO(52)
+#define TEGRA_PIN_GMI_AD5_PG5 _GPIO(53)
+#define TEGRA_PIN_GMI_AD6_PG6 _GPIO(54)
+#define TEGRA_PIN_GMI_AD7_PG7 _GPIO(55)
+#define TEGRA_PIN_GMI_AD8_PH0 _GPIO(56)
+#define TEGRA_PIN_GMI_AD9_PH1 _GPIO(57)
+#define TEGRA_PIN_GMI_AD10_PH2 _GPIO(58)
+#define TEGRA_PIN_GMI_AD11_PH3 _GPIO(59)
+#define TEGRA_PIN_GMI_AD12_PH4 _GPIO(60)
+#define TEGRA_PIN_GMI_AD13_PH5 _GPIO(61)
+#define TEGRA_PIN_GMI_AD14_PH6 _GPIO(62)
+#define TEGRA_PIN_GMI_AD15_PH7 _GPIO(63)
+#define TEGRA_PIN_GMI_HIOW_N_PI0 _GPIO(64)
+#define TEGRA_PIN_GMI_HIOR_N_PI1 _GPIO(65)
+#define TEGRA_PIN_GMI_CS5_N_PI2 _GPIO(66)
+#define TEGRA_PIN_GMI_CS6_N_PI3 _GPIO(67)
+#define TEGRA_PIN_GMI_RST_N_PI4 _GPIO(68)
+#define TEGRA_PIN_GMI_IORDY_PI5 _GPIO(69)
+#define TEGRA_PIN_GMI_CS7_N_PI6 _GPIO(70)
+#define TEGRA_PIN_GMI_WAIT_PI7 _GPIO(71)
+#define TEGRA_PIN_GMI_CS0_N_PJ0 _GPIO(72)
+#define TEGRA_PIN_LCD_DE_PJ1 _GPIO(73)
+#define TEGRA_PIN_GMI_CS1_N_PJ2 _GPIO(74)
+#define TEGRA_PIN_LCD_HSYNC_PJ3 _GPIO(75)
+#define TEGRA_PIN_LCD_VSYNC_PJ4 _GPIO(76)
+#define TEGRA_PIN_UART2_CTS_N_PJ5 _GPIO(77)
+#define TEGRA_PIN_UART2_RTS_N_PJ6 _GPIO(78)
+#define TEGRA_PIN_GMI_AD16_PJ7 _GPIO(79)
+#define TEGRA_PIN_GMI_ADV_N_PK0 _GPIO(80)
+#define TEGRA_PIN_GMI_CLK_PK1 _GPIO(81)
+#define TEGRA_PIN_GMI_CS4_N_PK2 _GPIO(82)
+#define TEGRA_PIN_GMI_CS2_N_PK3 _GPIO(83)
+#define TEGRA_PIN_GMI_CS3_N_PK4 _GPIO(84)
+#define TEGRA_PIN_SPDIF_OUT_PK5 _GPIO(85)
+#define TEGRA_PIN_SPDIF_IN_PK6 _GPIO(86)
+#define TEGRA_PIN_GMI_AD19_PK7 _GPIO(87)
+#define TEGRA_PIN_VI_D2_PL0 _GPIO(88)
+#define TEGRA_PIN_VI_D3_PL1 _GPIO(89)
+#define TEGRA_PIN_VI_D4_PL2 _GPIO(90)
+#define TEGRA_PIN_VI_D5_PL3 _GPIO(91)
+#define TEGRA_PIN_VI_D6_PL4 _GPIO(92)
+#define TEGRA_PIN_VI_D7_PL5 _GPIO(93)
+#define TEGRA_PIN_VI_D8_PL6 _GPIO(94)
+#define TEGRA_PIN_VI_D9_PL7 _GPIO(95)
+#define TEGRA_PIN_LCD_D16_PM0 _GPIO(96)
+#define TEGRA_PIN_LCD_D17_PM1 _GPIO(97)
+#define TEGRA_PIN_LCD_D18_PM2 _GPIO(98)
+#define TEGRA_PIN_LCD_D19_PM3 _GPIO(99)
+#define TEGRA_PIN_LCD_D20_PM4 _GPIO(100)
+#define TEGRA_PIN_LCD_D21_PM5 _GPIO(101)
+#define TEGRA_PIN_LCD_D22_PM6 _GPIO(102)
+#define TEGRA_PIN_LCD_D23_PM7 _GPIO(103)
+#define TEGRA_PIN_DAP1_FS_PN0 _GPIO(104)
+#define TEGRA_PIN_DAP1_DIN_PN1 _GPIO(105)
+#define TEGRA_PIN_DAP1_DOUT_PN2 _GPIO(106)
+#define TEGRA_PIN_DAP1_SCLK_PN3 _GPIO(107)
+#define TEGRA_PIN_LCD_CS0_N_PN4 _GPIO(108)
+#define TEGRA_PIN_LCD_SDOUT_PN5 _GPIO(109)
+#define TEGRA_PIN_LCD_DC0_PN6 _GPIO(110)
+#define TEGRA_PIN_HDMI_INT_N_PN7 _GPIO(111)
+#define TEGRA_PIN_ULPI_DATA7_PO0 _GPIO(112)
+#define TEGRA_PIN_ULPI_DATA0_PO1 _GPIO(113)
+#define TEGRA_PIN_ULPI_DATA1_PO2 _GPIO(114)
+#define TEGRA_PIN_ULPI_DATA2_PO3 _GPIO(115)
+#define TEGRA_PIN_ULPI_DATA3_PO4 _GPIO(116)
+#define TEGRA_PIN_ULPI_DATA4_PO5 _GPIO(117)
+#define TEGRA_PIN_ULPI_DATA5_PO6 _GPIO(118)
+#define TEGRA_PIN_ULPI_DATA6_PO7 _GPIO(119)
+#define TEGRA_PIN_DAP3_FS_PP0 _GPIO(120)
+#define TEGRA_PIN_DAP3_DIN_PP1 _GPIO(121)
+#define TEGRA_PIN_DAP3_DOUT_PP2 _GPIO(122)
+#define TEGRA_PIN_DAP3_SCLK_PP3 _GPIO(123)
+#define TEGRA_PIN_DAP4_FS_PP4 _GPIO(124)
+#define TEGRA_PIN_DAP4_DIN_PP5 _GPIO(125)
+#define TEGRA_PIN_DAP4_DOUT_PP6 _GPIO(126)
+#define TEGRA_PIN_DAP4_SCLK_PP7 _GPIO(127)
+#define TEGRA_PIN_KB_COL0_PQ0 _GPIO(128)
+#define TEGRA_PIN_KB_COL1_PQ1 _GPIO(129)
+#define TEGRA_PIN_KB_COL2_PQ2 _GPIO(130)
+#define TEGRA_PIN_KB_COL3_PQ3 _GPIO(131)
+#define TEGRA_PIN_KB_COL4_PQ4 _GPIO(132)
+#define TEGRA_PIN_KB_COL5_PQ5 _GPIO(133)
+#define TEGRA_PIN_KB_COL6_PQ6 _GPIO(134)
+#define TEGRA_PIN_KB_COL7_PQ7 _GPIO(135)
+#define TEGRA_PIN_KB_ROW0_PR0 _GPIO(136)
+#define TEGRA_PIN_KB_ROW1_PR1 _GPIO(137)
+#define TEGRA_PIN_KB_ROW2_PR2 _GPIO(138)
+#define TEGRA_PIN_KB_ROW3_PR3 _GPIO(139)
+#define TEGRA_PIN_KB_ROW4_PR4 _GPIO(140)
+#define TEGRA_PIN_KB_ROW5_PR5 _GPIO(141)
+#define TEGRA_PIN_KB_ROW6_PR6 _GPIO(142)
+#define TEGRA_PIN_KB_ROW7_PR7 _GPIO(143)
+#define TEGRA_PIN_KB_ROW8_PS0 _GPIO(144)
+#define TEGRA_PIN_KB_ROW9_PS1 _GPIO(145)
+#define TEGRA_PIN_KB_ROW10_PS2 _GPIO(146)
+#define TEGRA_PIN_KB_ROW11_PS3 _GPIO(147)
+#define TEGRA_PIN_KB_ROW12_PS4 _GPIO(148)
+#define TEGRA_PIN_KB_ROW13_PS5 _GPIO(149)
+#define TEGRA_PIN_KB_ROW14_PS6 _GPIO(150)
+#define TEGRA_PIN_KB_ROW15_PS7 _GPIO(151)
+#define TEGRA_PIN_VI_PCLK_PT0 _GPIO(152)
+#define TEGRA_PIN_VI_MCLK_PT1 _GPIO(153)
+#define TEGRA_PIN_VI_D10_PT2 _GPIO(154)
+#define TEGRA_PIN_VI_D11_PT3 _GPIO(155)
+#define TEGRA_PIN_VI_D0_PT4 _GPIO(156)
+#define TEGRA_PIN_GEN2_I2C_SCL_PT5 _GPIO(157)
+#define TEGRA_PIN_GEN2_I2C_SDA_PT6 _GPIO(158)
+#define TEGRA_PIN_GMI_DPD_PT7 _GPIO(159)
+#define TEGRA_PIN_PU0 _GPIO(160)
+#define TEGRA_PIN_PU1 _GPIO(161)
+#define TEGRA_PIN_PU2 _GPIO(162)
+#define TEGRA_PIN_PU3 _GPIO(163)
+#define TEGRA_PIN_PU4 _GPIO(164)
+#define TEGRA_PIN_PU5 _GPIO(165)
+#define TEGRA_PIN_PU6 _GPIO(166)
+#define TEGRA_PIN_JTAG_RTCK_PU7 _GPIO(167)
+#define TEGRA_PIN_PV0 _GPIO(168)
+#define TEGRA_PIN_PV1 _GPIO(169)
+#define TEGRA_PIN_PV2 _GPIO(170)
+#define TEGRA_PIN_PV3 _GPIO(171)
+#define TEGRA_PIN_PV4 _GPIO(172)
+#define TEGRA_PIN_PV5 _GPIO(173)
+#define TEGRA_PIN_PV6 _GPIO(174)
+#define TEGRA_PIN_LCD_DC1_PV7 _GPIO(175)
+#define TEGRA_PIN_LCD_CS1_N_PW0 _GPIO(176)
+#define TEGRA_PIN_LCD_M1_PW1 _GPIO(177)
+#define TEGRA_PIN_SPI2_CS1_N_PW2 _GPIO(178)
+#define TEGRA_PIN_SPI2_CS2_N_PW3 _GPIO(179)
+#define TEGRA_PIN_DAP_MCLK1_PW4 _GPIO(180)
+#define TEGRA_PIN_DAP_MCLK2_PW5 _GPIO(181)
+#define TEGRA_PIN_UART3_TXD_PW6 _GPIO(182)
+#define TEGRA_PIN_UART3_RXD_PW7 _GPIO(183)
+#define TEGRA_PIN_SPI2_MOSI_PX0 _GPIO(184)
+#define TEGRA_PIN_SPI2_MISO_PX1 _GPIO(185)
+#define TEGRA_PIN_SPI2_SCK_PX2 _GPIO(186)
+#define TEGRA_PIN_SPI2_CS0_N_PX3 _GPIO(187)
+#define TEGRA_PIN_SPI1_MOSI_PX4 _GPIO(188)
+#define TEGRA_PIN_SPI1_SCK_PX5 _GPIO(189)
+#define TEGRA_PIN_SPI1_CS0_N_PX6 _GPIO(190)
+#define TEGRA_PIN_SPI1_MISO_PX7 _GPIO(191)
+#define TEGRA_PIN_ULPI_CLK_PY0 _GPIO(192)
+#define TEGRA_PIN_ULPI_DIR_PY1 _GPIO(193)
+#define TEGRA_PIN_ULPI_NXT_PY2 _GPIO(194)
+#define TEGRA_PIN_ULPI_STP_PY3 _GPIO(195)
+#define TEGRA_PIN_SDIO1_DAT3_PY4 _GPIO(196)
+#define TEGRA_PIN_SDIO1_DAT2_PY5 _GPIO(197)
+#define TEGRA_PIN_SDIO1_DAT1_PY6 _GPIO(198)
+#define TEGRA_PIN_SDIO1_DAT0_PY7 _GPIO(199)
+#define TEGRA_PIN_SDIO1_CLK_PZ0 _GPIO(200)
+#define TEGRA_PIN_SDIO1_CMD_PZ1 _GPIO(201)
+#define TEGRA_PIN_LCD_SDIN_PZ2 _GPIO(202)
+#define TEGRA_PIN_LCD_WR_N_PZ3 _GPIO(203)
+#define TEGRA_PIN_LCD_SCK_PZ4 _GPIO(204)
+#define TEGRA_PIN_SYS_CLK_REQ_PZ5 _GPIO(205)
+#define TEGRA_PIN_PWR_I2C_SCL_PZ6 _GPIO(206)
+#define TEGRA_PIN_PWR_I2C_SDA_PZ7 _GPIO(207)
+#define TEGRA_PIN_GMI_AD20_PAA0 _GPIO(208)
+#define TEGRA_PIN_GMI_AD21_PAA1 _GPIO(209)
+#define TEGRA_PIN_GMI_AD22_PAA2 _GPIO(210)
+#define TEGRA_PIN_GMI_AD23_PAA3 _GPIO(211)
+#define TEGRA_PIN_GMI_AD24_PAA4 _GPIO(212)
+#define TEGRA_PIN_GMI_AD25_PAA5 _GPIO(213)
+#define TEGRA_PIN_GMI_AD26_PAA6 _GPIO(214)
+#define TEGRA_PIN_GMI_AD27_PAA7 _GPIO(215)
+#define TEGRA_PIN_LED_BLINK_PBB0 _GPIO(216)
+#define TEGRA_PIN_VI_GP0_PBB1 _GPIO(217)
+#define TEGRA_PIN_CAM_I2C_SCL_PBB2 _GPIO(218)
+#define TEGRA_PIN_CAM_I2C_SDA_PBB3 _GPIO(219)
+#define TEGRA_PIN_VI_GP3_PBB4 _GPIO(220)
+#define TEGRA_PIN_VI_GP4_PBB5 _GPIO(221)
+#define TEGRA_PIN_PBB6 _GPIO(222)
+#define TEGRA_PIN_PBB7 _GPIO(223)
+
+/* All non-GPIO pins follow */
+#define NUM_GPIOS (TEGRA_PIN_PBB7 + 1)
+#define _PIN(offset) (NUM_GPIOS + (offset))
+
+#define TEGRA_PIN_CRT_HSYNC _PIN(30)
+#define TEGRA_PIN_CRT_VSYNC _PIN(31)
+#define TEGRA_PIN_DDC_SCL _PIN(32)
+#define TEGRA_PIN_DDC_SDA _PIN(33)
+#define TEGRA_PIN_OWC _PIN(34)
+#define TEGRA_PIN_CORE_PWR_REQ _PIN(35)
+#define TEGRA_PIN_CPU_PWR_REQ _PIN(36)
+#define TEGRA_PIN_PWR_INT_N _PIN(37)
+#define TEGRA_PIN_CLK_32_K_IN _PIN(38)
+#define TEGRA_PIN_DDR_COMP_PD _PIN(39)
+#define TEGRA_PIN_DDR_COMP_PU _PIN(40)
+#define TEGRA_PIN_DDR_A0 _PIN(41)
+#define TEGRA_PIN_DDR_A1 _PIN(42)
+#define TEGRA_PIN_DDR_A2 _PIN(43)
+#define TEGRA_PIN_DDR_A3 _PIN(44)
+#define TEGRA_PIN_DDR_A4 _PIN(45)
+#define TEGRA_PIN_DDR_A5 _PIN(46)
+#define TEGRA_PIN_DDR_A6 _PIN(47)
+#define TEGRA_PIN_DDR_A7 _PIN(48)
+#define TEGRA_PIN_DDR_A8 _PIN(49)
+#define TEGRA_PIN_DDR_A9 _PIN(50)
+#define TEGRA_PIN_DDR_A10 _PIN(51)
+#define TEGRA_PIN_DDR_A11 _PIN(52)
+#define TEGRA_PIN_DDR_A12 _PIN(53)
+#define TEGRA_PIN_DDR_A13 _PIN(54)
+#define TEGRA_PIN_DDR_A14 _PIN(55)
+#define TEGRA_PIN_DDR_CAS_N _PIN(56)
+#define TEGRA_PIN_DDR_BA0 _PIN(57)
+#define TEGRA_PIN_DDR_BA1 _PIN(58)
+#define TEGRA_PIN_DDR_BA2 _PIN(59)
+#define TEGRA_PIN_DDR_DQS0P _PIN(60)
+#define TEGRA_PIN_DDR_DQS0N _PIN(61)
+#define TEGRA_PIN_DDR_DQS1P _PIN(62)
+#define TEGRA_PIN_DDR_DQS1N _PIN(63)
+#define TEGRA_PIN_DDR_DQS2P _PIN(64)
+#define TEGRA_PIN_DDR_DQS2N _PIN(65)
+#define TEGRA_PIN_DDR_DQS3P _PIN(66)
+#define TEGRA_PIN_DDR_DQS3N _PIN(67)
+#define TEGRA_PIN_DDR_CKE0 _PIN(68)
+#define TEGRA_PIN_DDR_CKE1 _PIN(69)
+#define TEGRA_PIN_DDR_CLK _PIN(70)
+#define TEGRA_PIN_DDR_CLK_N _PIN(71)
+#define TEGRA_PIN_DDR_DM0 _PIN(72)
+#define TEGRA_PIN_DDR_DM1 _PIN(73)
+#define TEGRA_PIN_DDR_DM2 _PIN(74)
+#define TEGRA_PIN_DDR_DM3 _PIN(75)
+#define TEGRA_PIN_DDR_ODT _PIN(76)
+#define TEGRA_PIN_DDR_QUSE0 _PIN(77)
+#define TEGRA_PIN_DDR_QUSE1 _PIN(78)
+#define TEGRA_PIN_DDR_QUSE2 _PIN(79)
+#define TEGRA_PIN_DDR_QUSE3 _PIN(80)
+#define TEGRA_PIN_DDR_RAS_N _PIN(81)
+#define TEGRA_PIN_DDR_WE_N _PIN(82)
+#define TEGRA_PIN_DDR_DQ0 _PIN(83)
+#define TEGRA_PIN_DDR_DQ1 _PIN(84)
+#define TEGRA_PIN_DDR_DQ2 _PIN(85)
+#define TEGRA_PIN_DDR_DQ3 _PIN(86)
+#define TEGRA_PIN_DDR_DQ4 _PIN(87)
+#define TEGRA_PIN_DDR_DQ5 _PIN(88)
+#define TEGRA_PIN_DDR_DQ6 _PIN(89)
+#define TEGRA_PIN_DDR_DQ7 _PIN(90)
+#define TEGRA_PIN_DDR_DQ8 _PIN(91)
+#define TEGRA_PIN_DDR_DQ9 _PIN(92)
+#define TEGRA_PIN_DDR_DQ10 _PIN(93)
+#define TEGRA_PIN_DDR_DQ11 _PIN(94)
+#define TEGRA_PIN_DDR_DQ12 _PIN(95)
+#define TEGRA_PIN_DDR_DQ13 _PIN(96)
+#define TEGRA_PIN_DDR_DQ14 _PIN(97)
+#define TEGRA_PIN_DDR_DQ15 _PIN(98)
+#define TEGRA_PIN_DDR_DQ16 _PIN(99)
+#define TEGRA_PIN_DDR_DQ17 _PIN(100)
+#define TEGRA_PIN_DDR_DQ18 _PIN(101)
+#define TEGRA_PIN_DDR_DQ19 _PIN(102)
+#define TEGRA_PIN_DDR_DQ20 _PIN(103)
+#define TEGRA_PIN_DDR_DQ21 _PIN(104)
+#define TEGRA_PIN_DDR_DQ22 _PIN(105)
+#define TEGRA_PIN_DDR_DQ23 _PIN(106)
+#define TEGRA_PIN_DDR_DQ24 _PIN(107)
+#define TEGRA_PIN_DDR_DQ25 _PIN(108)
+#define TEGRA_PIN_DDR_DQ26 _PIN(109)
+#define TEGRA_PIN_DDR_DQ27 _PIN(110)
+#define TEGRA_PIN_DDR_DQ28 _PIN(111)
+#define TEGRA_PIN_DDR_DQ29 _PIN(112)
+#define TEGRA_PIN_DDR_DQ30 _PIN(113)
+#define TEGRA_PIN_DDR_DQ31 _PIN(114)
+#define TEGRA_PIN_DDR_CS0_N _PIN(115)
+#define TEGRA_PIN_DDR_CS1_N _PIN(116)
+#define TEGRA_PIN_SYS_RESET _PIN(117)
+#define TEGRA_PIN_JTAG_TRST_N _PIN(118)
+#define TEGRA_PIN_JTAG_TDO _PIN(119)
+#define TEGRA_PIN_JTAG_TMS _PIN(120)
+#define TEGRA_PIN_JTAG_TCK _PIN(121)
+#define TEGRA_PIN_JTAG_TDI _PIN(122)
+#define TEGRA_PIN_TEST_MODE_EN _PIN(123)
+
+static const struct pinctrl_pin_desc tegra20_pins[] = {
+ PINCTRL_PIN(TEGRA_PIN_VI_GP6_PA0, "VI_GP6 PA0"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PA3, "DAP2_SCLK PA3"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PA4, "DAP2_DIN PA4"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PA5, "DAP2_DOUT PA5"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_CLK_PA6, "SDIO3_CLK PA6"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_CMD_PA7, "SDIO3_CMD PA7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD17_PB0, "GMI_AD17 PB0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD18_PB1, "GMI_AD18 PB1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PWR0_PB2, "LCD_PWR0 PB2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PCLK_PB3, "LCD_PCLK PB3"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT3_PB4, "SDIO3_DAT3 PB4"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT2_PB5, "SDIO3_DAT2 PB5"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT1_PB6, "SDIO3_DAT1 PB6"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT0_PB7, "SDIO3_DAT0 PB7"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_RTS_N_PC0, "UART3_RTS_N PC0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PWR1_PC1, "LCD_PWR1 PC1"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_TXD_PC2, "UART2_TXD PC2"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_RXD_PC3, "UART2_RXD PC3"),
+ PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PC4, "GEN1_I2C_SCL PC4"),
+ PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PC5, "GEN1_I2C_SDA PC5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PWR2_PC6, "LCD_PWR2 PC6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_WP_N_PC7, "GMI_WP_N PC7"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT5_PD0, "SDIO3_DAT5 PD0"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT4_PD1, "SDIO3_DAT4 PD1"),
+ PINCTRL_PIN(TEGRA_PIN_VI_GP5_PD2, "VI_GP5 PD2"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT6_PD3, "SDIO3_DAT6 PD3"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO3_DAT7_PD4, "SDIO3_DAT7 PD4"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D1_PD5, "VI_D1 PD5"),
+ PINCTRL_PIN(TEGRA_PIN_VI_VSYNC_PD6, "VI_VSYNC PD6"),
+ PINCTRL_PIN(TEGRA_PIN_VI_HSYNC_PD7, "VI_HSYNC PD7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D0_PE0, "LCD_D0 PE0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D1_PE1, "LCD_D1 PE1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D2_PE2, "LCD_D2 PE2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D3_PE3, "LCD_D3 PE3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D4_PE4, "LCD_D4 PE4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D5_PE5, "LCD_D5 PE5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D6_PE6, "LCD_D6 PE6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D7_PE7, "LCD_D7 PE7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D8_PF0, "LCD_D8 PF0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D9_PF1, "LCD_D9 PF1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D10_PF2, "LCD_D10 PF2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D11_PF3, "LCD_D11 PF3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D12_PF4, "LCD_D12 PF4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D13_PF5, "LCD_D13 PF5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D14_PF6, "LCD_D14 PF6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D15_PF7, "LCD_D15 PF7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD0_PG0, "GMI_AD0 PG0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD1_PG1, "GMI_AD1 PG1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD2_PG2, "GMI_AD2 PG2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD3_PG3, "GMI_AD3 PG3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD4_PG4, "GMI_AD4 PG4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD5_PG5, "GMI_AD5 PG5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD6_PG6, "GMI_AD6 PG6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD7_PG7, "GMI_AD7 PG7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD8_PH0, "GMI_AD8 PH0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD9_PH1, "GMI_AD9 PH1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD10_PH2, "GMI_AD10 PH2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD11_PH3, "GMI_AD11 PH3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD12_PH4, "GMI_AD12 PH4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD13_PH5, "GMI_AD13 PH5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD14_PH6, "GMI_AD14 PH6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD15_PH7, "GMI_AD15 PH7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_HIOW_N_PI0, "GMI_HIOW_N PI0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_HIOR_N_PI1, "GMI_HIOR_N PI1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS5_N_PI2, "GMI_CS5_N PI2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS6_N_PI3, "GMI_CS6_N PI3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_RST_N_PI4, "GMI_RST_N PI4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_IORDY_PI5, "GMI_IORDY PI5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS7_N_PI6, "GMI_CS7_N PI6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_WAIT_PI7, "GMI_WAIT PI7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS0_N_PJ0, "GMI_CS0_N PJ0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_DE_PJ1, "LCD_DE PJ1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS1_N_PJ2, "GMI_CS1_N PJ2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_HSYNC_PJ3, "LCD_HSYNC PJ3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_VSYNC_PJ4, "LCD_VSYNC PJ4"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_CTS_N_PJ5, "UART2_CTS_N PJ5"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_RTS_N_PJ6, "UART2_RTS_N PJ6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD16_PJ7, "GMI_AD16 PJ7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_ADV_N_PK0, "GMI_ADV_N PK0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CLK_PK1, "GMI_CLK PK1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS4_N_PK2, "GMI_CS4_N PK2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS2_N_PK3, "GMI_CS2_N PK3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS3_N_PK4, "GMI_CS3_N PK4"),
+ PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PK5, "SPDIF_OUT PK5"),
+ PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PK6, "SPDIF_IN PK6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD19_PK7, "GMI_AD19 PK7"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D2_PL0, "VI_D2 PL0"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D3_PL1, "VI_D3 PL1"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D4_PL2, "VI_D4 PL2"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D5_PL3, "VI_D5 PL3"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D6_PL4, "VI_D6 PL4"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D7_PL5, "VI_D7 PL5"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D8_PL6, "VI_D8 PL6"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D9_PL7, "VI_D9 PL7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D16_PM0, "LCD_D16 PM0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D17_PM1, "LCD_D17 PM1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D18_PM2, "LCD_D18 PM2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D19_PM3, "LCD_D19 PM3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D20_PM4, "LCD_D20 PM4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D21_PM5, "LCD_D21 PM5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D22_PM6, "LCD_D22 PM6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D23_PM7, "LCD_D23 PM7"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PN0, "DAP1_FS PN0"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PN1, "DAP1_DIN PN1"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PN2, "DAP1_DOUT PN2"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PN3, "DAP1_SCLK PN3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_CS0_N_PN4, "LCD_CS0_N PN4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_SDOUT_PN5, "LCD_SDOUT PN5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_DC0_PN6, "LCD_DC0 PN6"),
+ PINCTRL_PIN(TEGRA_PIN_HDMI_INT_N_PN7, "HDMI_INT_N PN7"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA7_PO0, "ULPI_DATA7 PO0"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA0_PO1, "ULPI_DATA0 PO1"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA1_PO2, "ULPI_DATA1 PO2"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA2_PO3, "ULPI_DATA2 PO3"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA3_PO4, "ULPI_DATA3 PO4"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA4_PO5, "ULPI_DATA4 PO5"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA5_PO6, "ULPI_DATA5 PO6"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA6_PO7, "ULPI_DATA6 PO7"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PP0, "DAP3_FS PP0"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PP1, "DAP3_DIN PP1"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PP2, "DAP3_DOUT PP2"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_SCLK_PP3, "DAP3_SCLK PP3"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP4, "DAP4_FS PP4"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP5, "DAP4_DIN PP5"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP6, "DAP4_DOUT PP6"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP7, "DAP4_SCLK PP7"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL0_PQ0, "KB_COL0 PQ0"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL1_PQ1, "KB_COL1 PQ1"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL2_PQ2, "KB_COL2 PQ2"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL3_PQ3, "KB_COL3 PQ3"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL4_PQ4, "KB_COL4 PQ4"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL5_PQ5, "KB_COL5 PQ5"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL6_PQ6, "KB_COL6 PQ6"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL7_PQ7, "KB_COL7 PQ7"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW0_PR0, "KB_ROW0 PR0"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW1_PR1, "KB_ROW1 PR1"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW2_PR2, "KB_ROW2 PR2"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW3_PR3, "KB_ROW3 PR3"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW4_PR4, "KB_ROW4 PR4"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW5_PR5, "KB_ROW5 PR5"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW6_PR6, "KB_ROW6 PR6"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW7_PR7, "KB_ROW7 PR7"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW11_PS3, "KB_ROW11 PS3"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW12_PS4, "KB_ROW12 PS4"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW13_PS5, "KB_ROW13 PS5"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW14_PS6, "KB_ROW14 PS6"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW15_PS7, "KB_ROW15 PS7"),
+ PINCTRL_PIN(TEGRA_PIN_VI_PCLK_PT0, "VI_PCLK PT0"),
+ PINCTRL_PIN(TEGRA_PIN_VI_MCLK_PT1, "VI_MCLK PT1"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D10_PT2, "VD_D10 PT2"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D11_PT3, "VI_D11 PT3"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D0_PT4, "VI_D0 PT4"),
+ PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"),
+ PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_DPD_PT7, "GMI_DPD PT7"),
+ /* PU0..6: GPIO only */
+ PINCTRL_PIN(TEGRA_PIN_PU0, "PU0"),
+ PINCTRL_PIN(TEGRA_PIN_PU1, "PU1"),
+ PINCTRL_PIN(TEGRA_PIN_PU2, "PU2"),
+ PINCTRL_PIN(TEGRA_PIN_PU3, "PU3"),
+ PINCTRL_PIN(TEGRA_PIN_PU4, "PU4"),
+ PINCTRL_PIN(TEGRA_PIN_PU5, "PU5"),
+ PINCTRL_PIN(TEGRA_PIN_PU6, "PU6"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK_PU7, "JTAG_RTCK PU7"),
+ /* PV0..1: GPIO only */
+ PINCTRL_PIN(TEGRA_PIN_PV0, "PV0"),
+ PINCTRL_PIN(TEGRA_PIN_PV1, "PV1"),
+ /* PV2..3: Balls are named after GPIO not function */
+ PINCTRL_PIN(TEGRA_PIN_PV2, "PV2"),
+ PINCTRL_PIN(TEGRA_PIN_PV3, "PV3"),
+ /* PV4..6: GPIO only */
+ PINCTRL_PIN(TEGRA_PIN_PV4, "PV4"),
+ PINCTRL_PIN(TEGRA_PIN_PV5, "PV5"),
+ PINCTRL_PIN(TEGRA_PIN_PV6, "PV6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_DC1_PV7, "LCD_DC1 PV7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_CS1_N_PW0, "LCD_CS1_N PW0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_M1_PW1, "LCD_M1 PW1"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_CS1_N_PW2, "SPI2_CS1_N PW2"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_CS2_N_PW3, "SPI2_CS2_N PW3"),
+ PINCTRL_PIN(TEGRA_PIN_DAP_MCLK1_PW4, "DAP_MCLK1 PW4"),
+ PINCTRL_PIN(TEGRA_PIN_DAP_MCLK2_PW5, "DAP_MCLK2 PW5"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_TXD_PW6, "UART3_TXD PW6"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_RXD_PW7, "UART3_RXD PW7"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PX0, "SPI2_MOSI PX0"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PX1, "SPI2_MISO PX1"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PX2, "SPI2_SCK PX2"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_N_PX3, "SPI2_CS0_N PX3"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PX4, "SPI1_MOSI PX4"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PX5, "SPI1_SCK PX5"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_N_PX6, "SPI1_CS0_N PX6"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PX7, "SPI1_MISO PX7"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_CLK_PY0, "ULPI_CLK PY0"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DIR_PY1, "ULPI_DIR PY1"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_NXT_PY2, "ULPI_NXT PY2"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_STP_PY3, "ULPI_STP PY3"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT3_PY4, "SDIO1_DAT3 PY4"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT2_PY5, "SDIO1_DAT2 PY5"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT1_PY6, "SDIO1_DAT1 PY6"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO1_DAT0_PY7, "SDIO1_DAT0 PY7"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO1_CLK_PZ0, "SDIO1_CLK PZ0"),
+ PINCTRL_PIN(TEGRA_PIN_SDIO1_CMD_PZ1, "SDIO1_CMD PZ1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_SDIN_PZ2, "LCD_SDIN PZ2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_WR_N_PZ3, "LCD_WR_N PZ3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_SCK_PZ4, "LCD_SCK PZ4"),
+ PINCTRL_PIN(TEGRA_PIN_SYS_CLK_REQ_PZ5, "SYS_CLK_REQ PZ5"),
+ PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PZ6, "PWR_I2C_SCL PZ6"),
+ PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PZ7, "PWR_I2C_SDA PZ7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD20_PAA0, "GMI_AD20 PAA0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD21_PAA1, "GMI_AD21 PAA1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD22_PAA2, "GMI_AD22 PAA2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD23_PAA3, "GMI_AD23 PAA3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD24_PAA4, "GMI_AD24 PAA4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD25_PAA5, "GMI_AD25 PAA5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD26_PAA6, "GMI_AD26 PAA6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD27_PAA7, "GMI_AD27 PAA7"),
+ PINCTRL_PIN(TEGRA_PIN_LED_BLINK_PBB0, "LED_BLINK PBB0"),
+ PINCTRL_PIN(TEGRA_PIN_VI_GP0_PBB1, "VI_GP0 PBB1"),
+ PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PBB2, "CAM_I2C_SCL PBB2"),
+ PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PBB3, "CAM_I2C_SDA PBB3"),
+ PINCTRL_PIN(TEGRA_PIN_VI_GP3_PBB4, "VI_GP3 PBB4"),
+ PINCTRL_PIN(TEGRA_PIN_VI_GP4_PBB5, "VI_GP4 PBB5"),
+ PINCTRL_PIN(TEGRA_PIN_PBB6, "PBB6"),
+ PINCTRL_PIN(TEGRA_PIN_PBB7, "PBB7"),
+ PINCTRL_PIN(TEGRA_PIN_CRT_HSYNC, "CRT_HSYNC"),
+ PINCTRL_PIN(TEGRA_PIN_CRT_VSYNC, "CRT_VSYNC"),
+ PINCTRL_PIN(TEGRA_PIN_DDC_SCL, "DDC_SCL"),
+ PINCTRL_PIN(TEGRA_PIN_DDC_SDA, "DDC_SDA"),
+ PINCTRL_PIN(TEGRA_PIN_OWC, "OWC"),
+ PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"),
+ PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"),
+ PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"),
+ PINCTRL_PIN(TEGRA_PIN_CLK_32_K_IN, "CLK_32_K_IN"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_COMP_PD, "DDR_COMP_PD"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_COMP_PU, "DDR_COMP_PU"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A0, "DDR_A0"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A1, "DDR_A1"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A2, "DDR_A2"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A3, "DDR_A3"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A4, "DDR_A4"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A5, "DDR_A5"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A6, "DDR_A6"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A7, "DDR_A7"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A8, "DDR_A8"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A9, "DDR_A9"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A10, "DDR_A10"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A11, "DDR_A11"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A12, "DDR_A12"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A13, "DDR_A13"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_A14, "DDR_A14"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CAS_N, "DDR_CAS_N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_BA0, "DDR_BA0"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_BA1, "DDR_BA1"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_BA2, "DDR_BA2"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS0P, "DDR_DQS0P"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS0N, "DDR_DQS0N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS1P, "DDR_DQS1P"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS1N, "DDR_DQS1N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS2P, "DDR_DQS2P"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS2N, "DDR_DQS2N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS3P, "DDR_DQS3P"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQS3N, "DDR_DQS3N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CKE0, "DDR_CKE0"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CKE1, "DDR_CKE1"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CLK, "DDR_CLK"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CLK_N, "DDR_CLK_N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DM0, "DDR_DM0"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DM1, "DDR_DM1"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DM2, "DDR_DM2"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DM3, "DDR_DM3"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_ODT, "DDR_ODT"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_QUSE0, "DDR_QUSE0"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_QUSE1, "DDR_QUSE1"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_QUSE2, "DDR_QUSE2"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_QUSE3, "DDR_QUSE3"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_RAS_N, "DDR_RAS_N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_WE_N, "DDR_WE_N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ0, "DDR_DQ0"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ1, "DDR_DQ1"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ2, "DDR_DQ2"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ3, "DDR_DQ3"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ4, "DDR_DQ4"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ5, "DDR_DQ5"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ6, "DDR_DQ6"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ7, "DDR_DQ7"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ8, "DDR_DQ8"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ9, "DDR_DQ9"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ10, "DDR_DQ10"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ11, "DDR_DQ11"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ12, "DDR_DQ12"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ13, "DDR_DQ13"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ14, "DDR_DQ14"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ15, "DDR_DQ15"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ16, "DDR_DQ16"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ17, "DDR_DQ17"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ18, "DDR_DQ18"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ19, "DDR_DQ19"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ20, "DDR_DQ20"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ21, "DDR_DQ21"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ22, "DDR_DQ22"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ23, "DDR_DQ23"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ24, "DDR_DQ24"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ25, "DDR_DQ25"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ26, "DDR_DQ26"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ27, "DDR_DQ27"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ28, "DDR_DQ28"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ29, "DDR_DQ29"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ30, "DDR_DQ30"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_DQ31, "DDR_DQ31"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CS0_N, "DDR_CS0_N"),
+ PINCTRL_PIN(TEGRA_PIN_DDR_CS1_N, "DDR_CS1_N"),
+ PINCTRL_PIN(TEGRA_PIN_SYS_RESET, "SYS_RESET"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TRST_N, "JTAG_TRST_N"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TDO, "JTAG_TDO"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TMS, "JTAG_TMS"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TCK, "JTAG_TCK"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TDI, "JTAG_TDI"),
+ PINCTRL_PIN(TEGRA_PIN_TEST_MODE_EN, "TEST_MODE_EN"),
+};
+
+static const unsigned ata_pins[] = {
+ TEGRA_PIN_GMI_CS6_N_PI3,
+ TEGRA_PIN_GMI_CS7_N_PI6,
+ TEGRA_PIN_GMI_RST_N_PI4,
+};
+
+static const unsigned atb_pins[] = {
+ TEGRA_PIN_GMI_CS5_N_PI2,
+ TEGRA_PIN_GMI_DPD_PT7,
+};
+
+static const unsigned atc_pins[] = {
+ TEGRA_PIN_GMI_IORDY_PI5,
+ TEGRA_PIN_GMI_WAIT_PI7,
+ TEGRA_PIN_GMI_ADV_N_PK0,
+ TEGRA_PIN_GMI_CLK_PK1,
+ TEGRA_PIN_GMI_CS2_N_PK3,
+ TEGRA_PIN_GMI_CS3_N_PK4,
+ TEGRA_PIN_GMI_CS4_N_PK2,
+ TEGRA_PIN_GMI_AD0_PG0,
+ TEGRA_PIN_GMI_AD1_PG1,
+ TEGRA_PIN_GMI_AD2_PG2,
+ TEGRA_PIN_GMI_AD3_PG3,
+ TEGRA_PIN_GMI_AD4_PG4,
+ TEGRA_PIN_GMI_AD5_PG5,
+ TEGRA_PIN_GMI_AD6_PG6,
+ TEGRA_PIN_GMI_AD7_PG7,
+ TEGRA_PIN_GMI_HIOW_N_PI0,
+ TEGRA_PIN_GMI_HIOR_N_PI1,
+};
+
+static const unsigned atd_pins[] = {
+ TEGRA_PIN_GMI_AD8_PH0,
+ TEGRA_PIN_GMI_AD9_PH1,
+ TEGRA_PIN_GMI_AD10_PH2,
+ TEGRA_PIN_GMI_AD11_PH3,
+};
+
+static const unsigned ate_pins[] = {
+ TEGRA_PIN_GMI_AD12_PH4,
+ TEGRA_PIN_GMI_AD13_PH5,
+ TEGRA_PIN_GMI_AD14_PH6,
+ TEGRA_PIN_GMI_AD15_PH7,
+};
+
+static const unsigned cdev1_pins[] = {
+ TEGRA_PIN_DAP_MCLK1_PW4,
+};
+
+static const unsigned cdev2_pins[] = {
+ TEGRA_PIN_DAP_MCLK2_PW5,
+};
+
+static const unsigned crtp_pins[] = {
+ TEGRA_PIN_CRT_HSYNC,
+ TEGRA_PIN_CRT_VSYNC,
+};
+
+static const unsigned csus_pins[] = {
+ TEGRA_PIN_VI_MCLK_PT1,
+};
+
+static const unsigned dap1_pins[] = {
+ TEGRA_PIN_DAP1_FS_PN0,
+ TEGRA_PIN_DAP1_DIN_PN1,
+ TEGRA_PIN_DAP1_DOUT_PN2,
+ TEGRA_PIN_DAP1_SCLK_PN3,
+};
+
+static const unsigned dap2_pins[] = {
+ TEGRA_PIN_DAP2_FS_PA2,
+ TEGRA_PIN_DAP2_SCLK_PA3,
+ TEGRA_PIN_DAP2_DIN_PA4,
+ TEGRA_PIN_DAP2_DOUT_PA5,
+};
+
+static const unsigned dap3_pins[] = {
+ TEGRA_PIN_DAP3_FS_PP0,
+ TEGRA_PIN_DAP3_DIN_PP1,
+ TEGRA_PIN_DAP3_DOUT_PP2,
+ TEGRA_PIN_DAP3_SCLK_PP3,
+};
+
+static const unsigned dap4_pins[] = {
+ TEGRA_PIN_DAP4_FS_PP4,
+ TEGRA_PIN_DAP4_DIN_PP5,
+ TEGRA_PIN_DAP4_DOUT_PP6,
+ TEGRA_PIN_DAP4_SCLK_PP7,
+};
+
+static const unsigned ddc_pins[] = {
+ TEGRA_PIN_DDC_SCL,
+ TEGRA_PIN_DDC_SDA,
+};
+
+static const unsigned dta_pins[] = {
+ TEGRA_PIN_VI_D0_PT4,
+ TEGRA_PIN_VI_D1_PD5,
+};
+
+static const unsigned dtb_pins[] = {
+ TEGRA_PIN_VI_D10_PT2,
+ TEGRA_PIN_VI_D11_PT3,
+};
+
+static const unsigned dtc_pins[] = {
+ TEGRA_PIN_VI_HSYNC_PD7,
+ TEGRA_PIN_VI_VSYNC_PD6,
+};
+
+static const unsigned dtd_pins[] = {
+ TEGRA_PIN_VI_PCLK_PT0,
+ TEGRA_PIN_VI_D2_PL0,
+ TEGRA_PIN_VI_D3_PL1,
+ TEGRA_PIN_VI_D4_PL2,
+ TEGRA_PIN_VI_D5_PL3,
+ TEGRA_PIN_VI_D6_PL4,
+ TEGRA_PIN_VI_D7_PL5,
+ TEGRA_PIN_VI_D8_PL6,
+ TEGRA_PIN_VI_D9_PL7,
+};
+
+static const unsigned dte_pins[] = {
+ TEGRA_PIN_VI_GP0_PBB1,
+ TEGRA_PIN_VI_GP3_PBB4,
+ TEGRA_PIN_VI_GP4_PBB5,
+ TEGRA_PIN_VI_GP5_PD2,
+ TEGRA_PIN_VI_GP6_PA0,
+};
+
+static const unsigned dtf_pins[] = {
+ TEGRA_PIN_CAM_I2C_SCL_PBB2,
+ TEGRA_PIN_CAM_I2C_SDA_PBB3,
+};
+
+static const unsigned gma_pins[] = {
+ TEGRA_PIN_GMI_AD20_PAA0,
+ TEGRA_PIN_GMI_AD21_PAA1,
+ TEGRA_PIN_GMI_AD22_PAA2,
+ TEGRA_PIN_GMI_AD23_PAA3,
+};
+
+static const unsigned gmb_pins[] = {
+ TEGRA_PIN_GMI_WP_N_PC7,
+};
+
+static const unsigned gmc_pins[] = {
+ TEGRA_PIN_GMI_AD16_PJ7,
+ TEGRA_PIN_GMI_AD17_PB0,
+ TEGRA_PIN_GMI_AD18_PB1,
+ TEGRA_PIN_GMI_AD19_PK7,
+};
+
+static const unsigned gmd_pins[] = {
+ TEGRA_PIN_GMI_CS0_N_PJ0,
+ TEGRA_PIN_GMI_CS1_N_PJ2,
+};
+
+static const unsigned gme_pins[] = {
+ TEGRA_PIN_GMI_AD24_PAA4,
+ TEGRA_PIN_GMI_AD25_PAA5,
+ TEGRA_PIN_GMI_AD26_PAA6,
+ TEGRA_PIN_GMI_AD27_PAA7,
+};
+
+static const unsigned gpu_pins[] = {
+ TEGRA_PIN_PU0,
+ TEGRA_PIN_PU1,
+ TEGRA_PIN_PU2,
+ TEGRA_PIN_PU3,
+ TEGRA_PIN_PU4,
+ TEGRA_PIN_PU5,
+ TEGRA_PIN_PU6,
+};
+
+static const unsigned gpu7_pins[] = {
+ TEGRA_PIN_JTAG_RTCK_PU7,
+};
+
+static const unsigned gpv_pins[] = {
+ TEGRA_PIN_PV4,
+ TEGRA_PIN_PV5,
+ TEGRA_PIN_PV6,
+};
+
+static const unsigned hdint_pins[] = {
+ TEGRA_PIN_HDMI_INT_N_PN7,
+};
+
+static const unsigned i2cp_pins[] = {
+ TEGRA_PIN_PWR_I2C_SCL_PZ6,
+ TEGRA_PIN_PWR_I2C_SDA_PZ7,
+};
+
+static const unsigned irrx_pins[] = {
+ TEGRA_PIN_UART2_RTS_N_PJ6,
+};
+
+static const unsigned irtx_pins[] = {
+ TEGRA_PIN_UART2_CTS_N_PJ5,
+};
+
+static const unsigned kbca_pins[] = {
+ TEGRA_PIN_KB_ROW0_PR0,
+ TEGRA_PIN_KB_ROW1_PR1,
+ TEGRA_PIN_KB_ROW2_PR2,
+};
+
+static const unsigned kbcb_pins[] = {
+ TEGRA_PIN_KB_ROW7_PR7,
+ TEGRA_PIN_KB_ROW8_PS0,
+ TEGRA_PIN_KB_ROW9_PS1,
+ TEGRA_PIN_KB_ROW10_PS2,
+ TEGRA_PIN_KB_ROW11_PS3,
+ TEGRA_PIN_KB_ROW12_PS4,
+ TEGRA_PIN_KB_ROW13_PS5,
+ TEGRA_PIN_KB_ROW14_PS6,
+ TEGRA_PIN_KB_ROW15_PS7,
+};
+
+static const unsigned kbcc_pins[] = {
+ TEGRA_PIN_KB_COL0_PQ0,
+ TEGRA_PIN_KB_COL1_PQ1,
+};
+
+static const unsigned kbcd_pins[] = {
+ TEGRA_PIN_KB_ROW3_PR3,
+ TEGRA_PIN_KB_ROW4_PR4,
+ TEGRA_PIN_KB_ROW5_PR5,
+ TEGRA_PIN_KB_ROW6_PR6,
+};
+
+static const unsigned kbce_pins[] = {
+ TEGRA_PIN_KB_COL7_PQ7,
+};
+
+static const unsigned kbcf_pins[] = {
+ TEGRA_PIN_KB_COL2_PQ2,
+ TEGRA_PIN_KB_COL3_PQ3,
+ TEGRA_PIN_KB_COL4_PQ4,
+ TEGRA_PIN_KB_COL5_PQ5,
+ TEGRA_PIN_KB_COL6_PQ6,
+};
+
+static const unsigned lcsn_pins[] = {
+ TEGRA_PIN_LCD_CS0_N_PN4,
+};
+
+static const unsigned ld0_pins[] = {
+ TEGRA_PIN_LCD_D0_PE0,
+};
+
+static const unsigned ld1_pins[] = {
+ TEGRA_PIN_LCD_D1_PE1,
+};
+
+static const unsigned ld2_pins[] = {
+ TEGRA_PIN_LCD_D2_PE2,
+};
+
+static const unsigned ld3_pins[] = {
+ TEGRA_PIN_LCD_D3_PE3,
+};
+
+static const unsigned ld4_pins[] = {
+ TEGRA_PIN_LCD_D4_PE4,
+};
+
+static const unsigned ld5_pins[] = {
+ TEGRA_PIN_LCD_D5_PE5,
+};
+
+static const unsigned ld6_pins[] = {
+ TEGRA_PIN_LCD_D6_PE6,
+};
+
+static const unsigned ld7_pins[] = {
+ TEGRA_PIN_LCD_D7_PE7,
+};
+
+static const unsigned ld8_pins[] = {
+ TEGRA_PIN_LCD_D8_PF0,
+};
+
+static const unsigned ld9_pins[] = {
+ TEGRA_PIN_LCD_D9_PF1,
+};
+
+static const unsigned ld10_pins[] = {
+ TEGRA_PIN_LCD_D10_PF2,
+};
+
+static const unsigned ld11_pins[] = {
+ TEGRA_PIN_LCD_D11_PF3,
+};
+
+static const unsigned ld12_pins[] = {
+ TEGRA_PIN_LCD_D12_PF4,
+};
+
+static const unsigned ld13_pins[] = {
+ TEGRA_PIN_LCD_D13_PF5,
+};
+
+static const unsigned ld14_pins[] = {
+ TEGRA_PIN_LCD_D14_PF6,
+};
+
+static const unsigned ld15_pins[] = {
+ TEGRA_PIN_LCD_D15_PF7,
+};
+
+static const unsigned ld16_pins[] = {
+ TEGRA_PIN_LCD_D16_PM0,
+};
+
+static const unsigned ld17_pins[] = {
+ TEGRA_PIN_LCD_D17_PM1,
+};
+
+static const unsigned ldc_pins[] = {
+ TEGRA_PIN_LCD_DC0_PN6,
+};
+
+static const unsigned ldi_pins[] = {
+ TEGRA_PIN_LCD_D22_PM6,
+};
+
+static const unsigned lhp0_pins[] = {
+ TEGRA_PIN_LCD_D21_PM5,
+};
+
+static const unsigned lhp1_pins[] = {
+ TEGRA_PIN_LCD_D18_PM2,
+};
+
+static const unsigned lhp2_pins[] = {
+ TEGRA_PIN_LCD_D19_PM3,
+};
+
+static const unsigned lhs_pins[] = {
+ TEGRA_PIN_LCD_HSYNC_PJ3,
+};
+
+static const unsigned lm0_pins[] = {
+ TEGRA_PIN_LCD_CS1_N_PW0,
+};
+
+static const unsigned lm1_pins[] = {
+ TEGRA_PIN_LCD_M1_PW1,
+};
+
+static const unsigned lpp_pins[] = {
+ TEGRA_PIN_LCD_D23_PM7,
+};
+
+static const unsigned lpw0_pins[] = {
+ TEGRA_PIN_LCD_PWR0_PB2,
+};
+
+static const unsigned lpw1_pins[] = {
+ TEGRA_PIN_LCD_PWR1_PC1,
+};
+
+static const unsigned lpw2_pins[] = {
+ TEGRA_PIN_LCD_PWR2_PC6,
+};
+
+static const unsigned lsc0_pins[] = {
+ TEGRA_PIN_LCD_PCLK_PB3,
+};
+
+static const unsigned lsc1_pins[] = {
+ TEGRA_PIN_LCD_WR_N_PZ3,
+};
+
+static const unsigned lsck_pins[] = {
+ TEGRA_PIN_LCD_SCK_PZ4,
+};
+
+static const unsigned lsda_pins[] = {
+ TEGRA_PIN_LCD_SDOUT_PN5,
+};
+
+static const unsigned lsdi_pins[] = {
+ TEGRA_PIN_LCD_SDIN_PZ2,
+};
+
+static const unsigned lspi_pins[] = {
+ TEGRA_PIN_LCD_DE_PJ1,
+};
+
+static const unsigned lvp0_pins[] = {
+ TEGRA_PIN_LCD_DC1_PV7,
+};
+
+static const unsigned lvp1_pins[] = {
+ TEGRA_PIN_LCD_D20_PM4,
+};
+
+static const unsigned lvs_pins[] = {
+ TEGRA_PIN_LCD_VSYNC_PJ4,
+};
+
+static const unsigned ls_pins[] = {
+ TEGRA_PIN_LCD_PWR0_PB2,
+ TEGRA_PIN_LCD_PWR1_PC1,
+ TEGRA_PIN_LCD_PWR2_PC6,
+ TEGRA_PIN_LCD_SDIN_PZ2,
+ TEGRA_PIN_LCD_SDOUT_PN5,
+ TEGRA_PIN_LCD_WR_N_PZ3,
+ TEGRA_PIN_LCD_CS0_N_PN4,
+ TEGRA_PIN_LCD_DC0_PN6,
+ TEGRA_PIN_LCD_SCK_PZ4,
+};
+
+static const unsigned lc_pins[] = {
+ TEGRA_PIN_LCD_PCLK_PB3,
+ TEGRA_PIN_LCD_DE_PJ1,
+ TEGRA_PIN_LCD_HSYNC_PJ3,
+ TEGRA_PIN_LCD_VSYNC_PJ4,
+ TEGRA_PIN_LCD_CS1_N_PW0,
+ TEGRA_PIN_LCD_M1_PW1,
+ TEGRA_PIN_LCD_DC1_PV7,
+ TEGRA_PIN_HDMI_INT_N_PN7,
+};
+
+static const unsigned ld17_0_pins[] = {
+ TEGRA_PIN_LCD_D0_PE0,
+ TEGRA_PIN_LCD_D1_PE1,
+ TEGRA_PIN_LCD_D2_PE2,
+ TEGRA_PIN_LCD_D3_PE3,
+ TEGRA_PIN_LCD_D4_PE4,
+ TEGRA_PIN_LCD_D5_PE5,
+ TEGRA_PIN_LCD_D6_PE6,
+ TEGRA_PIN_LCD_D7_PE7,
+ TEGRA_PIN_LCD_D8_PF0,
+ TEGRA_PIN_LCD_D9_PF1,
+ TEGRA_PIN_LCD_D10_PF2,
+ TEGRA_PIN_LCD_D11_PF3,
+ TEGRA_PIN_LCD_D12_PF4,
+ TEGRA_PIN_LCD_D13_PF5,
+ TEGRA_PIN_LCD_D14_PF6,
+ TEGRA_PIN_LCD_D15_PF7,
+ TEGRA_PIN_LCD_D16_PM0,
+ TEGRA_PIN_LCD_D17_PM1,
+};
+
+static const unsigned ld19_18_pins[] = {
+ TEGRA_PIN_LCD_D18_PM2,
+ TEGRA_PIN_LCD_D19_PM3,
+};
+
+static const unsigned ld21_20_pins[] = {
+ TEGRA_PIN_LCD_D20_PM4,
+ TEGRA_PIN_LCD_D21_PM5,
+};
+
+static const unsigned ld23_22_pins[] = {
+ TEGRA_PIN_LCD_D22_PM6,
+ TEGRA_PIN_LCD_D23_PM7,
+};
+
+static const unsigned owc_pins[] = {
+ TEGRA_PIN_OWC,
+};
+
+static const unsigned pmc_pins[] = {
+ TEGRA_PIN_LED_BLINK_PBB0,
+ TEGRA_PIN_SYS_CLK_REQ_PZ5,
+ TEGRA_PIN_CORE_PWR_REQ,
+ TEGRA_PIN_CPU_PWR_REQ,
+ TEGRA_PIN_PWR_INT_N,
+};
+
+static const unsigned pta_pins[] = {
+ TEGRA_PIN_GEN2_I2C_SCL_PT5,
+ TEGRA_PIN_GEN2_I2C_SDA_PT6,
+};
+
+static const unsigned rm_pins[] = {
+ TEGRA_PIN_GEN1_I2C_SCL_PC4,
+ TEGRA_PIN_GEN1_I2C_SDA_PC5,
+};
+
+static const unsigned sdb_pins[] = {
+ TEGRA_PIN_SDIO3_CMD_PA7,
+};
+
+static const unsigned sdc_pins[] = {
+ TEGRA_PIN_SDIO3_DAT0_PB7,
+ TEGRA_PIN_SDIO3_DAT1_PB6,
+ TEGRA_PIN_SDIO3_DAT2_PB5,
+ TEGRA_PIN_SDIO3_DAT3_PB4,
+};
+
+static const unsigned sdd_pins[] = {
+ TEGRA_PIN_SDIO3_CLK_PA6,
+};
+
+static const unsigned sdio1_pins[] = {
+ TEGRA_PIN_SDIO1_CLK_PZ0,
+ TEGRA_PIN_SDIO1_CMD_PZ1,
+ TEGRA_PIN_SDIO1_DAT0_PY7,
+ TEGRA_PIN_SDIO1_DAT1_PY6,
+ TEGRA_PIN_SDIO1_DAT2_PY5,
+ TEGRA_PIN_SDIO1_DAT3_PY4,
+};
+
+static const unsigned slxa_pins[] = {
+ TEGRA_PIN_SDIO3_DAT4_PD1,
+};
+
+static const unsigned slxc_pins[] = {
+ TEGRA_PIN_SDIO3_DAT6_PD3,
+};
+
+static const unsigned slxd_pins[] = {
+ TEGRA_PIN_SDIO3_DAT7_PD4,
+};
+
+static const unsigned slxk_pins[] = {
+ TEGRA_PIN_SDIO3_DAT5_PD0,
+};
+
+static const unsigned spdi_pins[] = {
+ TEGRA_PIN_SPDIF_IN_PK6,
+};
+
+static const unsigned spdo_pins[] = {
+ TEGRA_PIN_SPDIF_OUT_PK5,
+};
+
+static const unsigned spia_pins[] = {
+ TEGRA_PIN_SPI2_MOSI_PX0,
+};
+
+static const unsigned spib_pins[] = {
+ TEGRA_PIN_SPI2_MISO_PX1,
+};
+
+static const unsigned spic_pins[] = {
+ TEGRA_PIN_SPI2_CS0_N_PX3,
+ TEGRA_PIN_SPI2_SCK_PX2,
+};
+
+static const unsigned spid_pins[] = {
+ TEGRA_PIN_SPI1_MOSI_PX4,
+};
+
+static const unsigned spie_pins[] = {
+ TEGRA_PIN_SPI1_CS0_N_PX6,
+ TEGRA_PIN_SPI1_SCK_PX5,
+};
+
+static const unsigned spif_pins[] = {
+ TEGRA_PIN_SPI1_MISO_PX7,
+};
+
+static const unsigned spig_pins[] = {
+ TEGRA_PIN_SPI2_CS1_N_PW2,
+};
+
+static const unsigned spih_pins[] = {
+ TEGRA_PIN_SPI2_CS2_N_PW3,
+};
+
+static const unsigned uaa_pins[] = {
+ TEGRA_PIN_ULPI_DATA0_PO1,
+ TEGRA_PIN_ULPI_DATA1_PO2,
+ TEGRA_PIN_ULPI_DATA2_PO3,
+ TEGRA_PIN_ULPI_DATA3_PO4,
+};
+
+static const unsigned uab_pins[] = {
+ TEGRA_PIN_ULPI_DATA4_PO5,
+ TEGRA_PIN_ULPI_DATA5_PO6,
+ TEGRA_PIN_ULPI_DATA6_PO7,
+ TEGRA_PIN_ULPI_DATA7_PO0,
+};
+
+static const unsigned uac_pins[] = {
+ TEGRA_PIN_PV0,
+ TEGRA_PIN_PV1,
+ TEGRA_PIN_PV2,
+ TEGRA_PIN_PV3,
+};
+
+static const unsigned ck32_pins[] = {
+ TEGRA_PIN_CLK_32_K_IN,
+};
+
+static const unsigned uad_pins[] = {
+ TEGRA_PIN_UART2_RXD_PC3,
+ TEGRA_PIN_UART2_TXD_PC2,
+};
+
+static const unsigned uca_pins[] = {
+ TEGRA_PIN_UART3_RXD_PW7,
+ TEGRA_PIN_UART3_TXD_PW6,
+};
+
+static const unsigned ucb_pins[] = {
+ TEGRA_PIN_UART3_CTS_N_PA1,
+ TEGRA_PIN_UART3_RTS_N_PC0,
+};
+
+static const unsigned uda_pins[] = {
+ TEGRA_PIN_ULPI_CLK_PY0,
+ TEGRA_PIN_ULPI_DIR_PY1,
+ TEGRA_PIN_ULPI_NXT_PY2,
+ TEGRA_PIN_ULPI_STP_PY3,
+};
+
+static const unsigned ddrc_pins[] = {
+ TEGRA_PIN_DDR_COMP_PD,
+ TEGRA_PIN_DDR_COMP_PU,
+};
+
+static const unsigned pmca_pins[] = {
+ TEGRA_PIN_LED_BLINK_PBB0,
+};
+
+static const unsigned pmcb_pins[] = {
+ TEGRA_PIN_SYS_CLK_REQ_PZ5,
+};
+
+static const unsigned pmcc_pins[] = {
+ TEGRA_PIN_CORE_PWR_REQ,
+};
+
+static const unsigned pmcd_pins[] = {
+ TEGRA_PIN_CPU_PWR_REQ,
+};
+
+static const unsigned pmce_pins[] = {
+ TEGRA_PIN_PWR_INT_N,
+};
+
+static const unsigned xm2c_pins[] = {
+ TEGRA_PIN_DDR_A0,
+ TEGRA_PIN_DDR_A1,
+ TEGRA_PIN_DDR_A2,
+ TEGRA_PIN_DDR_A3,
+ TEGRA_PIN_DDR_A4,
+ TEGRA_PIN_DDR_A5,
+ TEGRA_PIN_DDR_A6,
+ TEGRA_PIN_DDR_A7,
+ TEGRA_PIN_DDR_A8,
+ TEGRA_PIN_DDR_A9,
+ TEGRA_PIN_DDR_A10,
+ TEGRA_PIN_DDR_A11,
+ TEGRA_PIN_DDR_A12,
+ TEGRA_PIN_DDR_A13,
+ TEGRA_PIN_DDR_A14,
+ TEGRA_PIN_DDR_CAS_N,
+ TEGRA_PIN_DDR_BA0,
+ TEGRA_PIN_DDR_BA1,
+ TEGRA_PIN_DDR_BA2,
+ TEGRA_PIN_DDR_DQS0P,
+ TEGRA_PIN_DDR_DQS0N,
+ TEGRA_PIN_DDR_DQS1P,
+ TEGRA_PIN_DDR_DQS1N,
+ TEGRA_PIN_DDR_DQS2P,
+ TEGRA_PIN_DDR_DQS2N,
+ TEGRA_PIN_DDR_DQS3P,
+ TEGRA_PIN_DDR_DQS3N,
+ TEGRA_PIN_DDR_CS0_N,
+ TEGRA_PIN_DDR_CS1_N,
+ TEGRA_PIN_DDR_CKE0,
+ TEGRA_PIN_DDR_CKE1,
+ TEGRA_PIN_DDR_CLK,
+ TEGRA_PIN_DDR_CLK_N,
+ TEGRA_PIN_DDR_DM0,
+ TEGRA_PIN_DDR_DM1,
+ TEGRA_PIN_DDR_DM2,
+ TEGRA_PIN_DDR_DM3,
+ TEGRA_PIN_DDR_ODT,
+ TEGRA_PIN_DDR_RAS_N,
+ TEGRA_PIN_DDR_WE_N,
+ TEGRA_PIN_DDR_QUSE0,
+ TEGRA_PIN_DDR_QUSE1,
+ TEGRA_PIN_DDR_QUSE2,
+ TEGRA_PIN_DDR_QUSE3,
+};
+
+static const unsigned xm2d_pins[] = {
+ TEGRA_PIN_DDR_DQ0,
+ TEGRA_PIN_DDR_DQ1,
+ TEGRA_PIN_DDR_DQ2,
+ TEGRA_PIN_DDR_DQ3,
+ TEGRA_PIN_DDR_DQ4,
+ TEGRA_PIN_DDR_DQ5,
+ TEGRA_PIN_DDR_DQ6,
+ TEGRA_PIN_DDR_DQ7,
+ TEGRA_PIN_DDR_DQ8,
+ TEGRA_PIN_DDR_DQ9,
+ TEGRA_PIN_DDR_DQ10,
+ TEGRA_PIN_DDR_DQ11,
+ TEGRA_PIN_DDR_DQ12,
+ TEGRA_PIN_DDR_DQ13,
+ TEGRA_PIN_DDR_DQ14,
+ TEGRA_PIN_DDR_DQ15,
+ TEGRA_PIN_DDR_DQ16,
+ TEGRA_PIN_DDR_DQ17,
+ TEGRA_PIN_DDR_DQ18,
+ TEGRA_PIN_DDR_DQ19,
+ TEGRA_PIN_DDR_DQ20,
+ TEGRA_PIN_DDR_DQ21,
+ TEGRA_PIN_DDR_DQ22,
+ TEGRA_PIN_DDR_DQ23,
+ TEGRA_PIN_DDR_DQ24,
+ TEGRA_PIN_DDR_DQ25,
+ TEGRA_PIN_DDR_DQ26,
+ TEGRA_PIN_DDR_DQ27,
+ TEGRA_PIN_DDR_DQ28,
+ TEGRA_PIN_DDR_DQ29,
+ TEGRA_PIN_DDR_DQ30,
+ TEGRA_PIN_DDR_DQ31,
+};
+
+static const unsigned drive_ao1_pins[] = {
+ TEGRA_PIN_SYS_RESET,
+ TEGRA_PIN_PWR_I2C_SCL_PZ6,
+ TEGRA_PIN_PWR_I2C_SDA_PZ7,
+ TEGRA_PIN_KB_ROW0_PR0,
+ TEGRA_PIN_KB_ROW1_PR1,
+ TEGRA_PIN_KB_ROW2_PR2,
+ TEGRA_PIN_KB_ROW3_PR3,
+ TEGRA_PIN_KB_ROW4_PR4,
+ TEGRA_PIN_KB_ROW5_PR5,
+ TEGRA_PIN_KB_ROW6_PR6,
+ TEGRA_PIN_KB_ROW7_PR7,
+};
+
+static const unsigned drive_ao2_pins[] = {
+ TEGRA_PIN_KB_ROW8_PS0,
+ TEGRA_PIN_KB_ROW9_PS1,
+ TEGRA_PIN_KB_ROW10_PS2,
+ TEGRA_PIN_KB_ROW11_PS3,
+ TEGRA_PIN_KB_ROW12_PS4,
+ TEGRA_PIN_KB_ROW13_PS5,
+ TEGRA_PIN_KB_ROW14_PS6,
+ TEGRA_PIN_KB_ROW15_PS7,
+ TEGRA_PIN_KB_COL0_PQ0,
+ TEGRA_PIN_KB_COL1_PQ1,
+ TEGRA_PIN_KB_COL2_PQ2,
+ TEGRA_PIN_KB_COL3_PQ3,
+ TEGRA_PIN_KB_COL4_PQ4,
+ TEGRA_PIN_KB_COL5_PQ5,
+ TEGRA_PIN_KB_COL6_PQ6,
+ TEGRA_PIN_KB_COL7_PQ7,
+ TEGRA_PIN_LED_BLINK_PBB0,
+ TEGRA_PIN_SYS_CLK_REQ_PZ5,
+ TEGRA_PIN_CORE_PWR_REQ,
+ TEGRA_PIN_CPU_PWR_REQ,
+ TEGRA_PIN_PWR_INT_N,
+ TEGRA_PIN_CLK_32_K_IN,
+};
+
+static const unsigned drive_at1_pins[] = {
+ TEGRA_PIN_GMI_IORDY_PI5,
+ TEGRA_PIN_GMI_AD8_PH0,
+ TEGRA_PIN_GMI_AD9_PH1,
+ TEGRA_PIN_GMI_AD10_PH2,
+ TEGRA_PIN_GMI_AD11_PH3,
+ TEGRA_PIN_GMI_AD12_PH4,
+ TEGRA_PIN_GMI_AD13_PH5,
+ TEGRA_PIN_GMI_AD14_PH6,
+ TEGRA_PIN_GMI_AD15_PH7,
+ TEGRA_PIN_GMI_CS7_N_PI6,
+ TEGRA_PIN_GMI_DPD_PT7,
+ TEGRA_PIN_GEN2_I2C_SCL_PT5,
+ TEGRA_PIN_GEN2_I2C_SDA_PT6,
+};
+
+static const unsigned drive_at2_pins[] = {
+ TEGRA_PIN_GMI_WAIT_PI7,
+ TEGRA_PIN_GMI_ADV_N_PK0,
+ TEGRA_PIN_GMI_CLK_PK1,
+ TEGRA_PIN_GMI_CS6_N_PI3,
+ TEGRA_PIN_GMI_CS5_N_PI2,
+ TEGRA_PIN_GMI_CS4_N_PK2,
+ TEGRA_PIN_GMI_CS3_N_PK4,
+ TEGRA_PIN_GMI_CS2_N_PK3,
+ TEGRA_PIN_GMI_AD0_PG0,
+ TEGRA_PIN_GMI_AD1_PG1,
+ TEGRA_PIN_GMI_AD2_PG2,
+ TEGRA_PIN_GMI_AD3_PG3,
+ TEGRA_PIN_GMI_AD4_PG4,
+ TEGRA_PIN_GMI_AD5_PG5,
+ TEGRA_PIN_GMI_AD6_PG6,
+ TEGRA_PIN_GMI_AD7_PG7,
+ TEGRA_PIN_GMI_HIOW_N_PI0,
+ TEGRA_PIN_GMI_HIOR_N_PI1,
+ TEGRA_PIN_GMI_RST_N_PI4,
+};
+
+static const unsigned drive_cdev1_pins[] = {
+ TEGRA_PIN_DAP_MCLK1_PW4,
+};
+
+static const unsigned drive_cdev2_pins[] = {
+ TEGRA_PIN_DAP_MCLK2_PW5,
+};
+
+static const unsigned drive_csus_pins[] = {
+ TEGRA_PIN_VI_MCLK_PT1,
+};
+
+static const unsigned drive_dap1_pins[] = {
+ TEGRA_PIN_DAP1_FS_PN0,
+ TEGRA_PIN_DAP1_DIN_PN1,
+ TEGRA_PIN_DAP1_DOUT_PN2,
+ TEGRA_PIN_DAP1_SCLK_PN3,
+ TEGRA_PIN_SPDIF_OUT_PK5,
+ TEGRA_PIN_SPDIF_IN_PK6,
+};
+
+static const unsigned drive_dap2_pins[] = {
+ TEGRA_PIN_DAP2_FS_PA2,
+ TEGRA_PIN_DAP2_SCLK_PA3,
+ TEGRA_PIN_DAP2_DIN_PA4,
+ TEGRA_PIN_DAP2_DOUT_PA5,
+};
+
+static const unsigned drive_dap3_pins[] = {
+ TEGRA_PIN_DAP3_FS_PP0,
+ TEGRA_PIN_DAP3_DIN_PP1,
+ TEGRA_PIN_DAP3_DOUT_PP2,
+ TEGRA_PIN_DAP3_SCLK_PP3,
+};
+
+static const unsigned drive_dap4_pins[] = {
+ TEGRA_PIN_DAP4_FS_PP4,
+ TEGRA_PIN_DAP4_DIN_PP5,
+ TEGRA_PIN_DAP4_DOUT_PP6,
+ TEGRA_PIN_DAP4_SCLK_PP7,
+};
+
+static const unsigned drive_dbg_pins[] = {
+ TEGRA_PIN_PU0,
+ TEGRA_PIN_PU1,
+ TEGRA_PIN_PU2,
+ TEGRA_PIN_PU3,
+ TEGRA_PIN_PU4,
+ TEGRA_PIN_PU5,
+ TEGRA_PIN_PU6,
+ TEGRA_PIN_JTAG_RTCK_PU7,
+ TEGRA_PIN_GEN1_I2C_SDA_PC5,
+ TEGRA_PIN_GEN1_I2C_SCL_PC4,
+ TEGRA_PIN_JTAG_TRST_N,
+ TEGRA_PIN_JTAG_TDO,
+ TEGRA_PIN_JTAG_TMS,
+ TEGRA_PIN_JTAG_TCK,
+ TEGRA_PIN_JTAG_TDI,
+ TEGRA_PIN_TEST_MODE_EN,
+};
+
+static const unsigned drive_lcd1_pins[] = {
+ TEGRA_PIN_LCD_PWR1_PC1,
+ TEGRA_PIN_LCD_PWR2_PC6,
+ TEGRA_PIN_LCD_SDIN_PZ2,
+ TEGRA_PIN_LCD_SDOUT_PN5,
+ TEGRA_PIN_LCD_WR_N_PZ3,
+ TEGRA_PIN_LCD_CS0_N_PN4,
+ TEGRA_PIN_LCD_DC0_PN6,
+ TEGRA_PIN_LCD_SCK_PZ4,
+};
+
+static const unsigned drive_lcd2_pins[] = {
+ TEGRA_PIN_LCD_PWR0_PB2,
+ TEGRA_PIN_LCD_PCLK_PB3,
+ TEGRA_PIN_LCD_DE_PJ1,
+ TEGRA_PIN_LCD_HSYNC_PJ3,
+ TEGRA_PIN_LCD_VSYNC_PJ4,
+ TEGRA_PIN_LCD_D0_PE0,
+ TEGRA_PIN_LCD_D1_PE1,
+ TEGRA_PIN_LCD_D2_PE2,
+ TEGRA_PIN_LCD_D3_PE3,
+ TEGRA_PIN_LCD_D4_PE4,
+ TEGRA_PIN_LCD_D5_PE5,
+ TEGRA_PIN_LCD_D6_PE6,
+ TEGRA_PIN_LCD_D7_PE7,
+ TEGRA_PIN_LCD_D8_PF0,
+ TEGRA_PIN_LCD_D9_PF1,
+ TEGRA_PIN_LCD_D10_PF2,
+ TEGRA_PIN_LCD_D11_PF3,
+ TEGRA_PIN_LCD_D12_PF4,
+ TEGRA_PIN_LCD_D13_PF5,
+ TEGRA_PIN_LCD_D14_PF6,
+ TEGRA_PIN_LCD_D15_PF7,
+ TEGRA_PIN_LCD_D16_PM0,
+ TEGRA_PIN_LCD_D17_PM1,
+ TEGRA_PIN_LCD_D18_PM2,
+ TEGRA_PIN_LCD_D19_PM3,
+ TEGRA_PIN_LCD_D20_PM4,
+ TEGRA_PIN_LCD_D21_PM5,
+ TEGRA_PIN_LCD_D22_PM6,
+ TEGRA_PIN_LCD_D23_PM7,
+ TEGRA_PIN_LCD_CS1_N_PW0,
+ TEGRA_PIN_LCD_M1_PW1,
+ TEGRA_PIN_LCD_DC1_PV7,
+ TEGRA_PIN_HDMI_INT_N_PN7,
+};
+
+static const unsigned drive_sdmmc2_pins[] = {
+ TEGRA_PIN_SDIO3_DAT4_PD1,
+ TEGRA_PIN_SDIO3_DAT5_PD0,
+ TEGRA_PIN_SDIO3_DAT6_PD3,
+ TEGRA_PIN_SDIO3_DAT7_PD4,
+};
+
+static const unsigned drive_sdmmc3_pins[] = {
+ TEGRA_PIN_SDIO3_CLK_PA6,
+ TEGRA_PIN_SDIO3_CMD_PA7,
+ TEGRA_PIN_SDIO3_DAT0_PB7,
+ TEGRA_PIN_SDIO3_DAT1_PB6,
+ TEGRA_PIN_SDIO3_DAT2_PB5,
+ TEGRA_PIN_SDIO3_DAT3_PB4,
+ TEGRA_PIN_PV4,
+ TEGRA_PIN_PV5,
+ TEGRA_PIN_PV6,
+};
+
+static const unsigned drive_spi_pins[] = {
+ TEGRA_PIN_SPI2_MOSI_PX0,
+ TEGRA_PIN_SPI2_MISO_PX1,
+ TEGRA_PIN_SPI2_SCK_PX2,
+ TEGRA_PIN_SPI2_CS0_N_PX3,
+ TEGRA_PIN_SPI1_MOSI_PX4,
+ TEGRA_PIN_SPI1_SCK_PX5,
+ TEGRA_PIN_SPI1_CS0_N_PX6,
+ TEGRA_PIN_SPI1_MISO_PX7,
+ TEGRA_PIN_SPI2_CS1_N_PW2,
+ TEGRA_PIN_SPI2_CS2_N_PW3,
+};
+
+static const unsigned drive_uaa_pins[] = {
+ TEGRA_PIN_ULPI_DATA0_PO1,
+ TEGRA_PIN_ULPI_DATA1_PO2,
+ TEGRA_PIN_ULPI_DATA2_PO3,
+ TEGRA_PIN_ULPI_DATA3_PO4,
+};
+
+static const unsigned drive_uab_pins[] = {
+ TEGRA_PIN_ULPI_DATA4_PO5,
+ TEGRA_PIN_ULPI_DATA5_PO6,
+ TEGRA_PIN_ULPI_DATA6_PO7,
+ TEGRA_PIN_ULPI_DATA7_PO0,
+ TEGRA_PIN_PV0,
+ TEGRA_PIN_PV1,
+ TEGRA_PIN_PV2,
+ TEGRA_PIN_PV3,
+};
+
+static const unsigned drive_uart2_pins[] = {
+ TEGRA_PIN_UART2_TXD_PC2,
+ TEGRA_PIN_UART2_RXD_PC3,
+ TEGRA_PIN_UART2_RTS_N_PJ6,
+ TEGRA_PIN_UART2_CTS_N_PJ5,
+};
+
+static const unsigned drive_uart3_pins[] = {
+ TEGRA_PIN_UART3_TXD_PW6,
+ TEGRA_PIN_UART3_RXD_PW7,
+ TEGRA_PIN_UART3_RTS_N_PC0,
+ TEGRA_PIN_UART3_CTS_N_PA1,
+};
+
+static const unsigned drive_vi1_pins[] = {
+ TEGRA_PIN_VI_D0_PT4,
+ TEGRA_PIN_VI_D1_PD5,
+ TEGRA_PIN_VI_D2_PL0,
+ TEGRA_PIN_VI_D3_PL1,
+ TEGRA_PIN_VI_D4_PL2,
+ TEGRA_PIN_VI_D5_PL3,
+ TEGRA_PIN_VI_D6_PL4,
+ TEGRA_PIN_VI_D7_PL5,
+ TEGRA_PIN_VI_D8_PL6,
+ TEGRA_PIN_VI_D9_PL7,
+ TEGRA_PIN_VI_D10_PT2,
+ TEGRA_PIN_VI_D11_PT3,
+ TEGRA_PIN_VI_PCLK_PT0,
+ TEGRA_PIN_VI_VSYNC_PD6,
+ TEGRA_PIN_VI_HSYNC_PD7,
+};
+
+static const unsigned drive_vi2_pins[] = {
+ TEGRA_PIN_VI_GP0_PBB1,
+ TEGRA_PIN_CAM_I2C_SCL_PBB2,
+ TEGRA_PIN_CAM_I2C_SDA_PBB3,
+ TEGRA_PIN_VI_GP3_PBB4,
+ TEGRA_PIN_VI_GP4_PBB5,
+ TEGRA_PIN_VI_GP5_PD2,
+ TEGRA_PIN_VI_GP6_PA0,
+};
+
+static const unsigned drive_xm2a_pins[] = {
+ TEGRA_PIN_DDR_A0,
+ TEGRA_PIN_DDR_A1,
+ TEGRA_PIN_DDR_A2,
+ TEGRA_PIN_DDR_A3,
+ TEGRA_PIN_DDR_A4,
+ TEGRA_PIN_DDR_A5,
+ TEGRA_PIN_DDR_A6,
+ TEGRA_PIN_DDR_A7,
+ TEGRA_PIN_DDR_A8,
+ TEGRA_PIN_DDR_A9,
+ TEGRA_PIN_DDR_A10,
+ TEGRA_PIN_DDR_A11,
+ TEGRA_PIN_DDR_A12,
+ TEGRA_PIN_DDR_A13,
+ TEGRA_PIN_DDR_A14,
+ TEGRA_PIN_DDR_BA0,
+ TEGRA_PIN_DDR_BA1,
+ TEGRA_PIN_DDR_BA2,
+ TEGRA_PIN_DDR_CS0_N,
+ TEGRA_PIN_DDR_CS1_N,
+ TEGRA_PIN_DDR_ODT,
+ TEGRA_PIN_DDR_RAS_N,
+ TEGRA_PIN_DDR_CAS_N,
+ TEGRA_PIN_DDR_WE_N,
+ TEGRA_PIN_DDR_CKE0,
+ TEGRA_PIN_DDR_CKE1,
+};
+
+static const unsigned drive_xm2c_pins[] = {
+ TEGRA_PIN_DDR_DQS0P,
+ TEGRA_PIN_DDR_DQS0N,
+ TEGRA_PIN_DDR_DQS1P,
+ TEGRA_PIN_DDR_DQS1N,
+ TEGRA_PIN_DDR_DQS2P,
+ TEGRA_PIN_DDR_DQS2N,
+ TEGRA_PIN_DDR_DQS3P,
+ TEGRA_PIN_DDR_DQS3N,
+ TEGRA_PIN_DDR_QUSE0,
+ TEGRA_PIN_DDR_QUSE1,
+ TEGRA_PIN_DDR_QUSE2,
+ TEGRA_PIN_DDR_QUSE3,
+};
+
+static const unsigned drive_xm2d_pins[] = {
+ TEGRA_PIN_DDR_DQ0,
+ TEGRA_PIN_DDR_DQ1,
+ TEGRA_PIN_DDR_DQ2,
+ TEGRA_PIN_DDR_DQ3,
+ TEGRA_PIN_DDR_DQ4,
+ TEGRA_PIN_DDR_DQ5,
+ TEGRA_PIN_DDR_DQ6,
+ TEGRA_PIN_DDR_DQ7,
+ TEGRA_PIN_DDR_DQ8,
+ TEGRA_PIN_DDR_DQ9,
+ TEGRA_PIN_DDR_DQ10,
+ TEGRA_PIN_DDR_DQ11,
+ TEGRA_PIN_DDR_DQ12,
+ TEGRA_PIN_DDR_DQ13,
+ TEGRA_PIN_DDR_DQ14,
+ TEGRA_PIN_DDR_DQ15,
+ TEGRA_PIN_DDR_DQ16,
+ TEGRA_PIN_DDR_DQ17,
+ TEGRA_PIN_DDR_DQ18,
+ TEGRA_PIN_DDR_DQ19,
+ TEGRA_PIN_DDR_DQ20,
+ TEGRA_PIN_DDR_DQ21,
+ TEGRA_PIN_DDR_DQ22,
+ TEGRA_PIN_DDR_DQ23,
+ TEGRA_PIN_DDR_DQ24,
+ TEGRA_PIN_DDR_DQ25,
+ TEGRA_PIN_DDR_DQ26,
+ TEGRA_PIN_DDR_DQ27,
+ TEGRA_PIN_DDR_DQ28,
+ TEGRA_PIN_DDR_DQ29,
+ TEGRA_PIN_DDR_DQ30,
+ TEGRA_PIN_DDR_DQ31,
+ TEGRA_PIN_DDR_DM0,
+ TEGRA_PIN_DDR_DM1,
+ TEGRA_PIN_DDR_DM2,
+ TEGRA_PIN_DDR_DM3,
+};
+
+static const unsigned drive_xm2clk_pins[] = {
+ TEGRA_PIN_DDR_CLK,
+ TEGRA_PIN_DDR_CLK_N,
+};
+
+static const unsigned drive_sdio1_pins[] = {
+ TEGRA_PIN_SDIO1_CLK_PZ0,
+ TEGRA_PIN_SDIO1_CMD_PZ1,
+ TEGRA_PIN_SDIO1_DAT0_PY7,
+ TEGRA_PIN_SDIO1_DAT1_PY6,
+ TEGRA_PIN_SDIO1_DAT2_PY5,
+ TEGRA_PIN_SDIO1_DAT3_PY4,
+};
+
+static const unsigned drive_crt_pins[] = {
+ TEGRA_PIN_CRT_HSYNC,
+ TEGRA_PIN_CRT_VSYNC,
+};
+
+static const unsigned drive_ddc_pins[] = {
+ TEGRA_PIN_DDC_SCL,
+ TEGRA_PIN_DDC_SDA,
+};
+
+static const unsigned drive_gma_pins[] = {
+ TEGRA_PIN_GMI_AD20_PAA0,
+ TEGRA_PIN_GMI_AD21_PAA1,
+ TEGRA_PIN_GMI_AD22_PAA2,
+ TEGRA_PIN_GMI_AD23_PAA3,
+};
+
+static const unsigned drive_gmb_pins[] = {
+ TEGRA_PIN_GMI_WP_N_PC7,
+};
+
+static const unsigned drive_gmc_pins[] = {
+ TEGRA_PIN_GMI_AD16_PJ7,
+ TEGRA_PIN_GMI_AD17_PB0,
+ TEGRA_PIN_GMI_AD18_PB1,
+ TEGRA_PIN_GMI_AD19_PK7,
+};
+
+static const unsigned drive_gmd_pins[] = {
+ TEGRA_PIN_GMI_CS0_N_PJ0,
+ TEGRA_PIN_GMI_CS1_N_PJ2,
+};
+
+static const unsigned drive_gme_pins[] = {
+ TEGRA_PIN_GMI_AD24_PAA4,
+ TEGRA_PIN_GMI_AD25_PAA5,
+ TEGRA_PIN_GMI_AD26_PAA6,
+ TEGRA_PIN_GMI_AD27_PAA7,
+};
+
+static const unsigned drive_owr_pins[] = {
+ TEGRA_PIN_OWC,
+};
+
+static const unsigned drive_uda_pins[] = {
+ TEGRA_PIN_ULPI_CLK_PY0,
+ TEGRA_PIN_ULPI_DIR_PY1,
+ TEGRA_PIN_ULPI_NXT_PY2,
+ TEGRA_PIN_ULPI_STP_PY3,
+};
+
+enum tegra_mux {
+ TEGRA_MUX_AHB_CLK,
+ TEGRA_MUX_APB_CLK,
+ TEGRA_MUX_AUDIO_SYNC,
+ TEGRA_MUX_CRT,
+ TEGRA_MUX_DAP1,
+ TEGRA_MUX_DAP2,
+ TEGRA_MUX_DAP3,
+ TEGRA_MUX_DAP4,
+ TEGRA_MUX_DAP5,
+ TEGRA_MUX_DISPLAYA,
+ TEGRA_MUX_DISPLAYB,
+ TEGRA_MUX_EMC_TEST0_DLL,
+ TEGRA_MUX_EMC_TEST1_DLL,
+ TEGRA_MUX_GMI,
+ TEGRA_MUX_GMI_INT,
+ TEGRA_MUX_HDMI,
+ TEGRA_MUX_I2CP,
+ TEGRA_MUX_I2C1,
+ TEGRA_MUX_I2C2,
+ TEGRA_MUX_I2C3,
+ TEGRA_MUX_IDE,
+ TEGRA_MUX_IRDA,
+ TEGRA_MUX_KBC,
+ TEGRA_MUX_MIO,
+ TEGRA_MUX_MIPI_HS,
+ TEGRA_MUX_NAND,
+ TEGRA_MUX_OSC,
+ TEGRA_MUX_OWR,
+ TEGRA_MUX_PCIE,
+ TEGRA_MUX_PLLA_OUT,
+ TEGRA_MUX_PLLC_OUT1,
+ TEGRA_MUX_PLLM_OUT1,
+ TEGRA_MUX_PLLP_OUT2,
+ TEGRA_MUX_PLLP_OUT3,
+ TEGRA_MUX_PLLP_OUT4,
+ TEGRA_MUX_PWM,
+ TEGRA_MUX_PWR_INTR,
+ TEGRA_MUX_PWR_ON,
+ TEGRA_MUX_RSVD1,
+ TEGRA_MUX_RSVD2,
+ TEGRA_MUX_RSVD3,
+ TEGRA_MUX_RSVD4,
+ TEGRA_MUX_RTCK,
+ TEGRA_MUX_SDIO1,
+ TEGRA_MUX_SDIO2,
+ TEGRA_MUX_SDIO3,
+ TEGRA_MUX_SDIO4,
+ TEGRA_MUX_SFLASH,
+ TEGRA_MUX_SPDIF,
+ TEGRA_MUX_SPI1,
+ TEGRA_MUX_SPI2,
+ TEGRA_MUX_SPI2_ALT,
+ TEGRA_MUX_SPI3,
+ TEGRA_MUX_SPI4,
+ TEGRA_MUX_TRACE,
+ TEGRA_MUX_TWC,
+ TEGRA_MUX_UARTA,
+ TEGRA_MUX_UARTB,
+ TEGRA_MUX_UARTC,
+ TEGRA_MUX_UARTD,
+ TEGRA_MUX_UARTE,
+ TEGRA_MUX_ULPI,
+ TEGRA_MUX_VI,
+ TEGRA_MUX_VI_SENSOR_CLK,
+ TEGRA_MUX_XIO,
+};
+
+static const char * const ahb_clk_groups[] = {
+ "cdev2",
+};
+
+static const char * const apb_clk_groups[] = {
+ "cdev2",
+};
+
+static const char * const audio_sync_groups[] = {
+ "cdev1",
+};
+
+static const char * const crt_groups[] = {
+ "crtp",
+ "lm1",
+};
+
+static const char * const dap1_groups[] = {
+ "dap1",
+};
+
+static const char * const dap2_groups[] = {
+ "dap2",
+};
+
+static const char * const dap3_groups[] = {
+ "dap3",
+};
+
+static const char * const dap4_groups[] = {
+ "dap4",
+};
+
+static const char * const dap5_groups[] = {
+ "gme",
+};
+
+static const char * const displaya_groups[] = {
+ "lcsn",
+ "ld0",
+ "ld1",
+ "ld10",
+ "ld11",
+ "ld12",
+ "ld13",
+ "ld14",
+ "ld15",
+ "ld16",
+ "ld17",
+ "ld2",
+ "ld3",
+ "ld4",
+ "ld5",
+ "ld6",
+ "ld7",
+ "ld8",
+ "ld9",
+ "ldc",
+ "ldi",
+ "lhp0",
+ "lhp1",
+ "lhp2",
+ "lhs",
+ "lm0",
+ "lm1",
+ "lpp",
+ "lpw0",
+ "lpw1",
+ "lpw2",
+ "lsc0",
+ "lsc1",
+ "lsck",
+ "lsda",
+ "lsdi",
+ "lspi",
+ "lvp0",
+ "lvp1",
+ "lvs",
+};
+
+static const char * const displayb_groups[] = {
+ "lcsn",
+ "ld0",
+ "ld1",
+ "ld10",
+ "ld11",
+ "ld12",
+ "ld13",
+ "ld14",
+ "ld15",
+ "ld16",
+ "ld17",
+ "ld2",
+ "ld3",
+ "ld4",
+ "ld5",
+ "ld6",
+ "ld7",
+ "ld8",
+ "ld9",
+ "ldc",
+ "ldi",
+ "lhp0",
+ "lhp1",
+ "lhp2",
+ "lhs",
+ "lm0",
+ "lm1",
+ "lpp",
+ "lpw0",
+ "lpw1",
+ "lpw2",
+ "lsc0",
+ "lsc1",
+ "lsck",
+ "lsda",
+ "lsdi",
+ "lspi",
+ "lvp0",
+ "lvp1",
+ "lvs",
+};
+
+static const char * const emc_test0_dll_groups[] = {
+ "kbca",
+};
+
+static const char * const emc_test1_dll_groups[] = {
+ "kbcc",
+};
+
+static const char * const gmi_groups[] = {
+ "ata",
+ "atb",
+ "atc",
+ "atd",
+ "ate",
+ "dap1",
+ "dap2",
+ "dap4",
+ "gma",
+ "gmb",
+ "gmc",
+ "gmd",
+ "gme",
+ "gpu",
+ "irrx",
+ "irtx",
+ "pta",
+ "spia",
+ "spib",
+ "spic",
+ "spid",
+ "spie",
+ "uca",
+ "ucb",
+};
+
+static const char * const gmi_int_groups[] = {
+ "gmb",
+};
+
+static const char * const hdmi_groups[] = {
+ "hdint",
+ "lpw0",
+ "lpw2",
+ "lsc1",
+ "lsck",
+ "lsda",
+ "lspi",
+ "pta",
+};
+
+static const char * const i2cp_groups[] = {
+ "i2cp",
+};
+
+static const char * const i2c1_groups[] = {
+ "rm",
+ "spdi",
+ "spdo",
+ "spig",
+ "spih",
+};
+
+static const char * const i2c2_groups[] = {
+ "ddc",
+ "pta",
+};
+
+static const char * const i2c3_groups[] = {
+ "dtf",
+};
+
+static const char * const ide_groups[] = {
+ "ata",
+ "atb",
+ "atc",
+ "atd",
+ "ate",
+ "gmb",
+};
+
+static const char * const irda_groups[] = {
+ "uad",
+};
+
+static const char * const kbc_groups[] = {
+ "kbca",
+ "kbcb",
+ "kbcc",
+ "kbcd",
+ "kbce",
+ "kbcf",
+};
+
+static const char * const mio_groups[] = {
+ "kbcb",
+ "kbcd",
+ "kbcf",
+};
+
+static const char * const mipi_hs_groups[] = {
+ "uaa",
+ "uab",
+};
+
+static const char * const nand_groups[] = {
+ "ata",
+ "atb",
+ "atc",
+ "atd",
+ "ate",
+ "gmb",
+ "gmd",
+ "kbca",
+ "kbcb",
+ "kbcc",
+ "kbcd",
+ "kbce",
+ "kbcf",
+};
+
+static const char * const osc_groups[] = {
+ "cdev1",
+ "cdev2",
+};
+
+static const char * const owr_groups[] = {
+ "kbce",
+ "owc",
+ "uac",
+};
+
+static const char * const pcie_groups[] = {
+ "gpv",
+ "slxa",
+ "slxk",
+};
+
+static const char * const plla_out_groups[] = {
+ "cdev1",
+};
+
+static const char * const pllc_out1_groups[] = {
+ "csus",
+};
+
+static const char * const pllm_out1_groups[] = {
+ "cdev1",
+};
+
+static const char * const pllp_out2_groups[] = {
+ "csus",
+};
+
+static const char * const pllp_out3_groups[] = {
+ "csus",
+};
+
+static const char * const pllp_out4_groups[] = {
+ "cdev2",
+};
+
+static const char * const pwm_groups[] = {
+ "gpu",
+ "sdb",
+ "sdc",
+ "sdd",
+ "ucb",
+};
+
+static const char * const pwr_intr_groups[] = {
+ "pmc",
+};
+
+static const char * const pwr_on_groups[] = {
+ "pmc",
+};
+
+static const char * const rsvd1_groups[] = {
+ "dta",
+ "dtb",
+ "dtc",
+ "dtd",
+ "dte",
+ "gmd",
+ "gme",
+};
+
+static const char * const rsvd2_groups[] = {
+ "crtp",
+ "dap1",
+ "dap3",
+ "dap4",
+ "ddc",
+ "dtb",
+ "dtc",
+ "dte",
+ "dtf",
+ "gpu7",
+ "gpv",
+ "hdint",
+ "i2cp",
+ "owc",
+ "rm",
+ "sdio1",
+ "spdi",
+ "spdo",
+ "uac",
+ "uca",
+ "uda",
+};
+
+static const char * const rsvd3_groups[] = {
+ "crtp",
+ "dap2",
+ "dap3",
+ "ddc",
+ "gpu7",
+ "gpv",
+ "hdint",
+ "i2cp",
+ "ld17",
+ "ldc",
+ "ldi",
+ "lhp0",
+ "lhp1",
+ "lhp2",
+ "lm1",
+ "lpp",
+ "lpw1",
+ "lvp0",
+ "lvp1",
+ "owc",
+ "pmc",
+ "rm",
+ "uac",
+};
+
+static const char * const rsvd4_groups[] = {
+ "ata",
+ "ate",
+ "crtp",
+ "dap3",
+ "dap4",
+ "ddc",
+ "dta",
+ "dtc",
+ "dtd",
+ "dtf",
+ "gpu",
+ "gpu7",
+ "gpv",
+ "hdint",
+ "i2cp",
+ "kbce",
+ "lcsn",
+ "ld0",
+ "ld1",
+ "ld2",
+ "ld3",
+ "ld4",
+ "ld5",
+ "ld6",
+ "ld7",
+ "ld8",
+ "ld9",
+ "ld10",
+ "ld11",
+ "ld12",
+ "ld13",
+ "ld14",
+ "ld15",
+ "ld16",
+ "ld17",
+ "ldc",
+ "ldi",
+ "lhp0",
+ "lhp1",
+ "lhp2",
+ "lhs",
+ "lm0",
+ "lpp",
+ "lpw1",
+ "lsc0",
+ "lsdi",
+ "lvp0",
+ "lvp1",
+ "lvs",
+ "owc",
+ "pmc",
+ "pta",
+ "rm",
+ "spif",
+ "uac",
+ "uca",
+ "ucb",
+};
+
+static const char * const rtck_groups[] = {
+ "gpu7",
+};
+
+static const char * const sdio1_groups[] = {
+ "sdio1",
+};
+
+static const char * const sdio2_groups[] = {
+ "dap1",
+ "dta",
+ "dtd",
+ "kbca",
+ "kbcb",
+ "kbcd",
+ "spdi",
+ "spdo",
+};
+
+static const char * const sdio3_groups[] = {
+ "sdb",
+ "sdc",
+ "sdd",
+ "slxa",
+ "slxc",
+ "slxd",
+ "slxk",
+};
+
+static const char * const sdio4_groups[] = {
+ "atb",
+ "atc",
+ "atd",
+ "gma",
+ "gme",
+};
+
+static const char * const sflash_groups[] = {
+ "gmc",
+ "gmd",
+};
+
+static const char * const spdif_groups[] = {
+ "slxc",
+ "slxd",
+ "spdi",
+ "spdo",
+ "uad",
+};
+
+static const char * const spi1_groups[] = {
+ "dtb",
+ "dte",
+ "spia",
+ "spib",
+ "spic",
+ "spid",
+ "spie",
+ "spif",
+ "uda",
+};
+
+static const char * const spi2_groups[] = {
+ "sdb",
+ "slxa",
+ "slxc",
+ "slxd",
+ "slxk",
+ "spia",
+ "spib",
+ "spic",
+ "spid",
+ "spie",
+ "spif",
+ "spig",
+ "spih",
+ "uab",
+};
+
+static const char * const spi2_alt_groups[] = {
+ "spid",
+ "spie",
+ "spig",
+ "spih",
+};
+
+static const char * const spi3_groups[] = {
+ "gma",
+ "lcsn",
+ "lm0",
+ "lpw0",
+ "lpw2",
+ "lsc1",
+ "lsck",
+ "lsda",
+ "lsdi",
+ "sdc",
+ "sdd",
+ "spia",
+ "spib",
+ "spic",
+ "spif",
+ "spig",
+ "spih",
+ "uaa",
+};
+
+static const char * const spi4_groups[] = {
+ "gmc",
+ "irrx",
+ "irtx",
+ "slxa",
+ "slxc",
+ "slxd",
+ "slxk",
+ "uad",
+};
+
+static const char * const trace_groups[] = {
+ "kbcc",
+ "kbcf",
+};
+
+static const char * const twc_groups[] = {
+ "dap2",
+ "sdc",
+};
+
+static const char * const uarta_groups[] = {
+ "gpu",
+ "irrx",
+ "irtx",
+ "sdb",
+ "sdd",
+ "sdio1",
+ "uaa",
+ "uab",
+ "uad",
+};
+
+static const char * const uartb_groups[] = {
+ "irrx",
+ "irtx",
+};
+
+static const char * const uartc_groups[] = {
+ "uca",
+ "ucb",
+};
+
+static const char * const uartd_groups[] = {
+ "gmc",
+ "uda",
+};
+
+static const char * const uarte_groups[] = {
+ "gma",
+ "sdio1",
+};
+
+static const char * const ulpi_groups[] = {
+ "uaa",
+ "uab",
+ "uda",
+};
+
+static const char * const vi_groups[] = {
+ "dta",
+ "dtb",
+ "dtc",
+ "dtd",
+ "dte",
+ "dtf",
+};
+
+static const char * const vi_sensor_clk_groups[] = {
+ "csus",
+};
+
+static const char * const xio_groups[] = {
+ "ld0",
+ "ld1",
+ "ld10",
+ "ld11",
+ "ld12",
+ "ld13",
+ "ld14",
+ "ld15",
+ "ld16",
+ "ld2",
+ "ld3",
+ "ld4",
+ "ld5",
+ "ld6",
+ "ld7",
+ "ld8",
+ "ld9",
+ "lhs",
+ "lsc0",
+ "lspi",
+ "lvs",
+};
+
+#define FUNCTION(fname) \
+ { \
+ .name = #fname, \
+ .groups = fname##_groups, \
+ .ngroups = ARRAY_SIZE(fname##_groups), \
+ }
+
+static const struct tegra_function tegra20_functions[] = {
+ FUNCTION(ahb_clk),
+ FUNCTION(apb_clk),
+ FUNCTION(audio_sync),
+ FUNCTION(crt),
+ FUNCTION(dap1),
+ FUNCTION(dap2),
+ FUNCTION(dap3),
+ FUNCTION(dap4),
+ FUNCTION(dap5),
+ FUNCTION(displaya),
+ FUNCTION(displayb),
+ FUNCTION(emc_test0_dll),
+ FUNCTION(emc_test1_dll),
+ FUNCTION(gmi),
+ FUNCTION(gmi_int),
+ FUNCTION(hdmi),
+ FUNCTION(i2cp),
+ FUNCTION(i2c1),
+ FUNCTION(i2c2),
+ FUNCTION(i2c3),
+ FUNCTION(ide),
+ FUNCTION(irda),
+ FUNCTION(kbc),
+ FUNCTION(mio),
+ FUNCTION(mipi_hs),
+ FUNCTION(nand),
+ FUNCTION(osc),
+ FUNCTION(owr),
+ FUNCTION(pcie),
+ FUNCTION(plla_out),
+ FUNCTION(pllc_out1),
+ FUNCTION(pllm_out1),
+ FUNCTION(pllp_out2),
+ FUNCTION(pllp_out3),
+ FUNCTION(pllp_out4),
+ FUNCTION(pwm),
+ FUNCTION(pwr_intr),
+ FUNCTION(pwr_on),
+ FUNCTION(rsvd1),
+ FUNCTION(rsvd2),
+ FUNCTION(rsvd3),
+ FUNCTION(rsvd4),
+ FUNCTION(rtck),
+ FUNCTION(sdio1),
+ FUNCTION(sdio2),
+ FUNCTION(sdio3),
+ FUNCTION(sdio4),
+ FUNCTION(sflash),
+ FUNCTION(spdif),
+ FUNCTION(spi1),
+ FUNCTION(spi2),
+ FUNCTION(spi2_alt),
+ FUNCTION(spi3),
+ FUNCTION(spi4),
+ FUNCTION(trace),
+ FUNCTION(twc),
+ FUNCTION(uarta),
+ FUNCTION(uartb),
+ FUNCTION(uartc),
+ FUNCTION(uartd),
+ FUNCTION(uarte),
+ FUNCTION(ulpi),
+ FUNCTION(vi),
+ FUNCTION(vi_sensor_clk),
+ FUNCTION(xio),
+};
+
+#define TRISTATE_REG_A 0x14
+#define PIN_MUX_CTL_REG_A 0x80
+#define PULLUPDOWN_REG_A 0xa0
+#define PINGROUP_REG_A 0x868
+
+/* Pin group with mux control, and typically tri-state and pull-up/down too */
+#define MUX_PG(pg_name, f0, f1, f2, f3, f_safe, \
+ tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .funcs = { \
+ TEGRA_MUX_ ## f0, \
+ TEGRA_MUX_ ## f1, \
+ TEGRA_MUX_ ## f2, \
+ TEGRA_MUX_ ## f3, \
+ }, \
+ .func_safe = TEGRA_MUX_ ## f_safe, \
+ .mux_reg = ((mux_r) - PIN_MUX_CTL_REG_A), \
+ .mux_bank = 1, \
+ .mux_bit = mux_b, \
+ .pupd_reg = ((pupd_r) - PULLUPDOWN_REG_A), \
+ .pupd_bank = 2, \
+ .pupd_bit = pupd_b, \
+ .tri_reg = ((tri_r) - TRISTATE_REG_A), \
+ .tri_bank = 0, \
+ .tri_bit = tri_b, \
+ .einput_reg = -1, \
+ .odrain_reg = -1, \
+ .lock_reg = -1, \
+ .ioreset_reg = -1, \
+ .drv_reg = -1, \
+ }
+
+/* Pin groups with only pull up and pull down control */
+#define PULL_PG(pg_name, pupd_r, pupd_b) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .mux_reg = -1, \
+ .pupd_reg = ((pupd_r) - PULLUPDOWN_REG_A), \
+ .pupd_bank = 2, \
+ .pupd_bit = pupd_b, \
+ .tri_reg = -1, \
+ .einput_reg = -1, \
+ .odrain_reg = -1, \
+ .lock_reg = -1, \
+ .ioreset_reg = -1, \
+ .drv_reg = -1, \
+ }
+
+/* Pin groups for drive strength registers (configurable version) */
+#define DRV_PG_EXT(pg_name, r, hsm_b, schmitt_b, lpmd_b, \
+ drvdn_b, drvup_b, \
+ slwr_b, slwr_w, slwf_b, slwf_w) \
+ { \
+ .name = "drive_" #pg_name, \
+ .pins = drive_##pg_name##_pins, \
+ .npins = ARRAY_SIZE(drive_##pg_name##_pins), \
+ .mux_reg = -1, \
+ .pupd_reg = -1, \
+ .tri_reg = -1, \
+ .einput_reg = -1, \
+ .odrain_reg = -1, \
+ .lock_reg = -1, \
+ .ioreset_reg = -1, \
+ .drv_reg = ((r) - PINGROUP_REG_A), \
+ .drv_bank = 3, \
+ .hsm_bit = hsm_b, \
+ .schmitt_bit = schmitt_b, \
+ .lpmd_bit = lpmd_b, \
+ .drvdn_bit = drvdn_b, \
+ .drvdn_width = 5, \
+ .drvup_bit = drvup_b, \
+ .drvup_width = 5, \
+ .slwr_bit = slwr_b, \
+ .slwr_width = slwr_w, \
+ .slwf_bit = slwf_b, \
+ .slwf_width = slwf_w, \
+ }
+
+/* Pin groups for drive strength registers (simple version) */
+#define DRV_PG(pg_name, r) \
+ DRV_PG_EXT(pg_name, r, 2, 3, 4, 12, 20, 28, 2, 30, 2)
+
+static const struct tegra_pingroup tegra20_groups[] = {
+ /* name, f0, f1, f2, f3, f_safe, tri r/b, mux r/b, pupd r/b */
+ MUX_PG(ata, IDE, NAND, GMI, RSVD4, IDE, 0x14, 0, 0x80, 24, 0xa0, 0),
+ MUX_PG(atb, IDE, NAND, GMI, SDIO4, IDE, 0x14, 1, 0x80, 16, 0xa0, 2),
+ MUX_PG(atc, IDE, NAND, GMI, SDIO4, IDE, 0x14, 2, 0x80, 22, 0xa0, 4),
+ MUX_PG(atd, IDE, NAND, GMI, SDIO4, IDE, 0x14, 3, 0x80, 20, 0xa0, 6),
+ MUX_PG(ate, IDE, NAND, GMI, RSVD4, IDE, 0x18, 25, 0x80, 12, 0xa0, 8),
+ MUX_PG(cdev1, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC, 0x14, 4, 0x88, 2, 0xa8, 0),
+ MUX_PG(cdev2, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC, 0x14, 5, 0x88, 4, 0xa8, 2),
+ MUX_PG(crtp, CRT, RSVD2, RSVD3, RSVD4, RSVD2, 0x20, 14, 0x98, 20, 0xa4, 24),
+ MUX_PG(csus, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6, 0x88, 6, 0xac, 24),
+ MUX_PG(dap1, DAP1, RSVD2, GMI, SDIO2, DAP1, 0x14, 7, 0x88, 20, 0xa0, 10),
+ MUX_PG(dap2, DAP2, TWC, RSVD3, GMI, DAP2, 0x14, 8, 0x88, 22, 0xa0, 12),
+ MUX_PG(dap3, DAP3, RSVD2, RSVD3, RSVD4, DAP3, 0x14, 9, 0x88, 24, 0xa0, 14),
+ MUX_PG(dap4, DAP4, RSVD2, GMI, RSVD4, DAP4, 0x14, 10, 0x88, 26, 0xa0, 16),
+ MUX_PG(ddc, I2C2, RSVD2, RSVD3, RSVD4, RSVD4, 0x18, 31, 0x88, 0, 0xb0, 28),
+ MUX_PG(dta, RSVD1, SDIO2, VI, RSVD4, RSVD4, 0x14, 11, 0x84, 20, 0xa0, 18),
+ MUX_PG(dtb, RSVD1, RSVD2, VI, SPI1, RSVD1, 0x14, 12, 0x84, 22, 0xa0, 20),
+ MUX_PG(dtc, RSVD1, RSVD2, VI, RSVD4, RSVD1, 0x14, 13, 0x84, 26, 0xa0, 22),
+ MUX_PG(dtd, RSVD1, SDIO2, VI, RSVD4, RSVD1, 0x14, 14, 0x84, 28, 0xa0, 24),
+ MUX_PG(dte, RSVD1, RSVD2, VI, SPI1, RSVD1, 0x14, 15, 0x84, 30, 0xa0, 26),
+ MUX_PG(dtf, I2C3, RSVD2, VI, RSVD4, RSVD4, 0x20, 12, 0x98, 30, 0xa0, 28),
+ MUX_PG(gma, UARTE, SPI3, GMI, SDIO4, SPI3, 0x14, 28, 0x84, 0, 0xb0, 20),
+ MUX_PG(gmb, IDE, NAND, GMI, GMI_INT, GMI, 0x18, 29, 0x88, 28, 0xb0, 22),
+ MUX_PG(gmc, UARTD, SPI4, GMI, SFLASH, SPI4, 0x14, 29, 0x84, 2, 0xb0, 24),
+ MUX_PG(gmd, RSVD1, NAND, GMI, SFLASH, GMI, 0x18, 30, 0x88, 30, 0xb0, 26),
+ MUX_PG(gme, RSVD1, DAP5, GMI, SDIO4, GMI, 0x18, 0, 0x8c, 0, 0xa8, 24),
+ MUX_PG(gpu, PWM, UARTA, GMI, RSVD4, RSVD4, 0x14, 16, 0x8c, 4, 0xa4, 20),
+ MUX_PG(gpu7, RTCK, RSVD2, RSVD3, RSVD4, RTCK, 0x20, 11, 0x98, 28, 0xa4, 6),
+ MUX_PG(gpv, PCIE, RSVD2, RSVD3, RSVD4, PCIE, 0x14, 17, 0x8c, 2, 0xa0, 30),
+ MUX_PG(hdint, HDMI, RSVD2, RSVD3, RSVD4, HDMI, 0x1c, 23, 0x84, 4, -1, -1),
+ MUX_PG(i2cp, I2CP, RSVD2, RSVD3, RSVD4, RSVD4, 0x14, 18, 0x88, 8, 0xa4, 2),
+ MUX_PG(irrx, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 20, 0x88, 18, 0xa8, 22),
+ MUX_PG(irtx, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 19, 0x88, 16, 0xa8, 20),
+ MUX_PG(kbca, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC, 0x14, 22, 0x88, 10, 0xa4, 8),
+ MUX_PG(kbcb, KBC, NAND, SDIO2, MIO, KBC, 0x14, 21, 0x88, 12, 0xa4, 10),
+ MUX_PG(kbcc, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC, 0x18, 26, 0x88, 14, 0xa4, 12),
+ MUX_PG(kbcd, KBC, NAND, SDIO2, MIO, KBC, 0x20, 10, 0x98, 26, 0xa4, 14),
+ MUX_PG(kbce, KBC, NAND, OWR, RSVD4, KBC, 0x14, 26, 0x80, 28, 0xb0, 2),
+ MUX_PG(kbcf, KBC, NAND, TRACE, MIO, KBC, 0x14, 27, 0x80, 26, 0xb0, 0),
+ MUX_PG(lcsn, DISPLAYA, DISPLAYB, SPI3, RSVD4, RSVD4, 0x1c, 31, 0x90, 12, -1, -1),
+ MUX_PG(ld0, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 0, 0x94, 0, -1, -1),
+ MUX_PG(ld1, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 1, 0x94, 2, -1, -1),
+ MUX_PG(ld2, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 2, 0x94, 4, -1, -1),
+ MUX_PG(ld3, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 3, 0x94, 6, -1, -1),
+ MUX_PG(ld4, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 4, 0x94, 8, -1, -1),
+ MUX_PG(ld5, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 5, 0x94, 10, -1, -1),
+ MUX_PG(ld6, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 6, 0x94, 12, -1, -1),
+ MUX_PG(ld7, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 7, 0x94, 14, -1, -1),
+ MUX_PG(ld8, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 8, 0x94, 16, -1, -1),
+ MUX_PG(ld9, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 9, 0x94, 18, -1, -1),
+ MUX_PG(ld10, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 10, 0x94, 20, -1, -1),
+ MUX_PG(ld11, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 11, 0x94, 22, -1, -1),
+ MUX_PG(ld12, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 12, 0x94, 24, -1, -1),
+ MUX_PG(ld13, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 13, 0x94, 26, -1, -1),
+ MUX_PG(ld14, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 14, 0x94, 28, -1, -1),
+ MUX_PG(ld15, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 15, 0x94, 30, -1, -1),
+ MUX_PG(ld16, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 16, 0x98, 0, -1, -1),
+ MUX_PG(ld17, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 17, 0x98, 2, -1, -1),
+ MUX_PG(ldc, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 30, 0x90, 14, -1, -1),
+ MUX_PG(ldi, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x20, 6, 0x98, 16, -1, -1),
+ MUX_PG(lhp0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 18, 0x98, 10, -1, -1),
+ MUX_PG(lhp1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 19, 0x98, 4, -1, -1),
+ MUX_PG(lhp2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 20, 0x98, 6, -1, -1),
+ MUX_PG(lhs, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x20, 7, 0x90, 22, -1, -1),
+ MUX_PG(lm0, DISPLAYA, DISPLAYB, SPI3, RSVD4, RSVD4, 0x1c, 24, 0x90, 26, -1, -1),
+ MUX_PG(lm1, DISPLAYA, DISPLAYB, RSVD3, CRT, RSVD3, 0x1c, 25, 0x90, 28, -1, -1),
+ MUX_PG(lpp, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x20, 8, 0x98, 14, -1, -1),
+ MUX_PG(lpw0, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 3, 0x90, 0, -1, -1),
+ MUX_PG(lpw1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x20, 4, 0x90, 2, -1, -1),
+ MUX_PG(lpw2, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 5, 0x90, 4, -1, -1),
+ MUX_PG(lsc0, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 27, 0x90, 18, -1, -1),
+ MUX_PG(lsc1, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1c, 28, 0x90, 20, -1, -1),
+ MUX_PG(lsck, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1c, 29, 0x90, 16, -1, -1),
+ MUX_PG(lsda, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 1, 0x90, 8, -1, -1),
+ MUX_PG(lsdi, DISPLAYA, DISPLAYB, SPI3, RSVD4, DISPLAYA, 0x20, 2, 0x90, 6, -1, -1),
+ MUX_PG(lspi, DISPLAYA, DISPLAYB, XIO, HDMI, DISPLAYA, 0x20, 0, 0x90, 10, -1, -1),
+ MUX_PG(lvp0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 21, 0x90, 30, -1, -1),
+ MUX_PG(lvp1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x1c, 22, 0x98, 8, -1, -1),
+ MUX_PG(lvs, DISPLAYA, DISPLAYB, XIO, RSVD4, RSVD4, 0x1c, 26, 0x90, 24, -1, -1),
+ MUX_PG(owc, OWR, RSVD2, RSVD3, RSVD4, OWR, 0x14, 31, 0x84, 8, 0xb0, 30),
+ MUX_PG(pmc, PWR_ON, PWR_INTR, RSVD3, RSVD4, PWR_ON, 0x14, 23, 0x98, 18, -1, -1),
+ MUX_PG(pta, I2C2, HDMI, GMI, RSVD4, RSVD4, 0x14, 24, 0x98, 22, 0xa4, 4),
+ MUX_PG(rm, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x14, 25, 0x80, 14, 0xa4, 0),
+ MUX_PG(sdb, UARTA, PWM, SDIO3, SPI2, PWM, 0x20, 15, 0x8c, 10, -1, -1),
+ MUX_PG(sdc, PWM, TWC, SDIO3, SPI3, TWC, 0x18, 1, 0x8c, 12, 0xac, 28),
+ MUX_PG(sdd, UARTA, PWM, SDIO3, SPI3, PWM, 0x18, 2, 0x8c, 14, 0xac, 30),
+ MUX_PG(sdio1, SDIO1, RSVD2, UARTE, UARTA, RSVD2, 0x14, 30, 0x80, 30, 0xb0, 18),
+ MUX_PG(slxa, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 3, 0x84, 6, 0xa4, 22),
+ MUX_PG(slxc, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 5, 0x84, 10, 0xa4, 26),
+ MUX_PG(slxd, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 6, 0x84, 12, 0xa4, 28),
+ MUX_PG(slxk, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 7, 0x84, 14, 0xa4, 30),
+ MUX_PG(spdi, SPDIF, RSVD2, I2C1, SDIO2, RSVD2, 0x18, 8, 0x8c, 8, 0xa4, 16),
+ MUX_PG(spdo, SPDIF, RSVD2, I2C1, SDIO2, RSVD2, 0x18, 9, 0x8c, 6, 0xa4, 18),
+ MUX_PG(spia, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 10, 0x8c, 30, 0xa8, 4),
+ MUX_PG(spib, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 11, 0x8c, 28, 0xa8, 6),
+ MUX_PG(spic, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 12, 0x8c, 26, 0xa8, 8),
+ MUX_PG(spid, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 13, 0x8c, 24, 0xa8, 10),
+ MUX_PG(spie, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 14, 0x8c, 22, 0xa8, 12),
+ MUX_PG(spif, SPI3, SPI1, SPI2, RSVD4, RSVD4, 0x18, 15, 0x8c, 20, 0xa8, 14),
+ MUX_PG(spig, SPI3, SPI2, SPI2_ALT, I2C1, SPI2_ALT, 0x18, 16, 0x8c, 18, 0xa8, 16),
+ MUX_PG(spih, SPI3, SPI2, SPI2_ALT, I2C1, SPI2_ALT, 0x18, 17, 0x8c, 16, 0xa8, 18),
+ MUX_PG(uaa, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 18, 0x80, 0, 0xac, 0),
+ MUX_PG(uab, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 19, 0x80, 2, 0xac, 2),
+ MUX_PG(uac, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x18, 20, 0x80, 4, 0xac, 4),
+ MUX_PG(uad, IRDA, SPDIF, UARTA, SPI4, SPDIF, 0x18, 21, 0x80, 6, 0xac, 6),
+ MUX_PG(uca, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x18, 22, 0x84, 16, 0xac, 8),
+ MUX_PG(ucb, UARTC, PWM, GMI, RSVD4, RSVD4, 0x18, 23, 0x84, 18, 0xac, 10),
+ MUX_PG(uda, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x20, 13, 0x80, 8, 0xb0, 16),
+ /* pg_name, pupd_r/b */
+ PULL_PG(ck32, 0xb0, 14),
+ PULL_PG(ddrc, 0xac, 26),
+ PULL_PG(pmca, 0xb0, 4),
+ PULL_PG(pmcb, 0xb0, 6),
+ PULL_PG(pmcc, 0xb0, 8),
+ PULL_PG(pmcd, 0xb0, 10),
+ PULL_PG(pmce, 0xb0, 12),
+ PULL_PG(xm2c, 0xa8, 30),
+ PULL_PG(xm2d, 0xa8, 28),
+ PULL_PG(ls, 0xac, 20),
+ PULL_PG(lc, 0xac, 22),
+ PULL_PG(ld17_0, 0xac, 12),
+ PULL_PG(ld19_18, 0xac, 14),
+ PULL_PG(ld21_20, 0xac, 16),
+ PULL_PG(ld23_22, 0xac, 18),
+ /* pg_name, r */
+ DRV_PG(ao1, 0x868),
+ DRV_PG(ao2, 0x86c),
+ DRV_PG(at1, 0x870),
+ DRV_PG(at2, 0x874),
+ DRV_PG(cdev1, 0x878),
+ DRV_PG(cdev2, 0x87c),
+ DRV_PG(csus, 0x880),
+ DRV_PG(dap1, 0x884),
+ DRV_PG(dap2, 0x888),
+ DRV_PG(dap3, 0x88c),
+ DRV_PG(dap4, 0x890),
+ DRV_PG(dbg, 0x894),
+ DRV_PG(lcd1, 0x898),
+ DRV_PG(lcd2, 0x89c),
+ DRV_PG(sdmmc2, 0x8a0),
+ DRV_PG(sdmmc3, 0x8a4),
+ DRV_PG(spi, 0x8a8),
+ DRV_PG(uaa, 0x8ac),
+ DRV_PG(uab, 0x8b0),
+ DRV_PG(uart2, 0x8b4),
+ DRV_PG(uart3, 0x8b8),
+ DRV_PG(vi1, 0x8bc),
+ DRV_PG(vi2, 0x8c0),
+ /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvup_b, slwr_b, slwr_w, slwf_b, slwf_w */
+ DRV_PG_EXT(xm2a, 0x8c4, -1, -1, 4, 14, 19, 24, 4, 28, 4),
+ DRV_PG_EXT(xm2c, 0x8c8, -1, 3, -1, 14, 19, 24, 4, 28, 4),
+ DRV_PG_EXT(xm2d, 0x8cc, -1, 3, -1, 14, 19, 24, 4, 28, 4),
+ DRV_PG_EXT(xm2clk, 0x8d0, -1, -1, -1, 14, 19, 24, 4, 28, 4),
+ /* pg_name, r */
+ DRV_PG(sdio1, 0x8e0),
+ DRV_PG(crt, 0x8ec),
+ DRV_PG(ddc, 0x8f0),
+ DRV_PG(gma, 0x8f4),
+ DRV_PG(gmb, 0x8f8),
+ DRV_PG(gmc, 0x8fc),
+ DRV_PG(gmd, 0x900),
+ DRV_PG(gme, 0x904),
+ DRV_PG(owr, 0x908),
+ DRV_PG(uda, 0x90c),
+};
+
+static const struct tegra_pinctrl_soc_data tegra20_pinctrl = {
+ .ngpios = NUM_GPIOS,
+ .pins = tegra20_pins,
+ .npins = ARRAY_SIZE(tegra20_pins),
+ .functions = tegra20_functions,
+ .nfunctions = ARRAY_SIZE(tegra20_functions),
+ .groups = tegra20_groups,
+ .ngroups = ARRAY_SIZE(tegra20_groups),
+};
+
+void __devinit tegra20_pinctrl_init(const struct tegra_pinctrl_soc_data **soc)
+{
+ *soc = &tegra20_pinctrl;
+}
diff --git a/drivers/pinctrl/pinctrl-tegra30.c b/drivers/pinctrl/pinctrl-tegra30.c
new file mode 100644
index 000000000000..4d7571d4a431
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tegra30.c
@@ -0,0 +1,3726 @@
+/*
+ * Pinctrl data for the NVIDIA Tegra30 pinmux
+ *
+ * Copyright (c) 2011, 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "pinctrl-tegra.h"
+
+/*
+ * Most pins affected by the pinmux can also be GPIOs. Define these first.
+ * These must match how the GPIO driver names/numbers its pins.
+ */
+#define _GPIO(offset) (offset)
+
+#define TEGRA_PIN_CLK_32K_OUT_PA0 _GPIO(0)
+#define TEGRA_PIN_UART3_CTS_N_PA1 _GPIO(1)
+#define TEGRA_PIN_DAP2_FS_PA2 _GPIO(2)
+#define TEGRA_PIN_DAP2_SCLK_PA3 _GPIO(3)
+#define TEGRA_PIN_DAP2_DIN_PA4 _GPIO(4)
+#define TEGRA_PIN_DAP2_DOUT_PA5 _GPIO(5)
+#define TEGRA_PIN_SDMMC3_CLK_PA6 _GPIO(6)
+#define TEGRA_PIN_SDMMC3_CMD_PA7 _GPIO(7)
+#define TEGRA_PIN_GMI_A17_PB0 _GPIO(8)
+#define TEGRA_PIN_GMI_A18_PB1 _GPIO(9)
+#define TEGRA_PIN_LCD_PWR0_PB2 _GPIO(10)
+#define TEGRA_PIN_LCD_PCLK_PB3 _GPIO(11)
+#define TEGRA_PIN_SDMMC3_DAT3_PB4 _GPIO(12)
+#define TEGRA_PIN_SDMMC3_DAT2_PB5 _GPIO(13)
+#define TEGRA_PIN_SDMMC3_DAT1_PB6 _GPIO(14)
+#define TEGRA_PIN_SDMMC3_DAT0_PB7 _GPIO(15)
+#define TEGRA_PIN_UART3_RTS_N_PC0 _GPIO(16)
+#define TEGRA_PIN_LCD_PWR1_PC1 _GPIO(17)
+#define TEGRA_PIN_UART2_TXD_PC2 _GPIO(18)
+#define TEGRA_PIN_UART2_RXD_PC3 _GPIO(19)
+#define TEGRA_PIN_GEN1_I2C_SCL_PC4 _GPIO(20)
+#define TEGRA_PIN_GEN1_I2C_SDA_PC5 _GPIO(21)
+#define TEGRA_PIN_LCD_PWR2_PC6 _GPIO(22)
+#define TEGRA_PIN_GMI_WP_N_PC7 _GPIO(23)
+#define TEGRA_PIN_SDMMC3_DAT5_PD0 _GPIO(24)
+#define TEGRA_PIN_SDMMC3_DAT4_PD1 _GPIO(25)
+#define TEGRA_PIN_LCD_DC1_PD2 _GPIO(26)
+#define TEGRA_PIN_SDMMC3_DAT6_PD3 _GPIO(27)
+#define TEGRA_PIN_SDMMC3_DAT7_PD4 _GPIO(28)
+#define TEGRA_PIN_VI_D1_PD5 _GPIO(29)
+#define TEGRA_PIN_VI_VSYNC_PD6 _GPIO(30)
+#define TEGRA_PIN_VI_HSYNC_PD7 _GPIO(31)
+#define TEGRA_PIN_LCD_D0_PE0 _GPIO(32)
+#define TEGRA_PIN_LCD_D1_PE1 _GPIO(33)
+#define TEGRA_PIN_LCD_D2_PE2 _GPIO(34)
+#define TEGRA_PIN_LCD_D3_PE3 _GPIO(35)
+#define TEGRA_PIN_LCD_D4_PE4 _GPIO(36)
+#define TEGRA_PIN_LCD_D5_PE5 _GPIO(37)
+#define TEGRA_PIN_LCD_D6_PE6 _GPIO(38)
+#define TEGRA_PIN_LCD_D7_PE7 _GPIO(39)
+#define TEGRA_PIN_LCD_D8_PF0 _GPIO(40)
+#define TEGRA_PIN_LCD_D9_PF1 _GPIO(41)
+#define TEGRA_PIN_LCD_D10_PF2 _GPIO(42)
+#define TEGRA_PIN_LCD_D11_PF3 _GPIO(43)
+#define TEGRA_PIN_LCD_D12_PF4 _GPIO(44)
+#define TEGRA_PIN_LCD_D13_PF5 _GPIO(45)
+#define TEGRA_PIN_LCD_D14_PF6 _GPIO(46)
+#define TEGRA_PIN_LCD_D15_PF7 _GPIO(47)
+#define TEGRA_PIN_GMI_AD0_PG0 _GPIO(48)
+#define TEGRA_PIN_GMI_AD1_PG1 _GPIO(49)
+#define TEGRA_PIN_GMI_AD2_PG2 _GPIO(50)
+#define TEGRA_PIN_GMI_AD3_PG3 _GPIO(51)
+#define TEGRA_PIN_GMI_AD4_PG4 _GPIO(52)
+#define TEGRA_PIN_GMI_AD5_PG5 _GPIO(53)
+#define TEGRA_PIN_GMI_AD6_PG6 _GPIO(54)
+#define TEGRA_PIN_GMI_AD7_PG7 _GPIO(55)
+#define TEGRA_PIN_GMI_AD8_PH0 _GPIO(56)
+#define TEGRA_PIN_GMI_AD9_PH1 _GPIO(57)
+#define TEGRA_PIN_GMI_AD10_PH2 _GPIO(58)
+#define TEGRA_PIN_GMI_AD11_PH3 _GPIO(59)
+#define TEGRA_PIN_GMI_AD12_PH4 _GPIO(60)
+#define TEGRA_PIN_GMI_AD13_PH5 _GPIO(61)
+#define TEGRA_PIN_GMI_AD14_PH6 _GPIO(62)
+#define TEGRA_PIN_GMI_AD15_PH7 _GPIO(63)
+#define TEGRA_PIN_GMI_WR_N_PI0 _GPIO(64)
+#define TEGRA_PIN_GMI_OE_N_PI1 _GPIO(65)
+#define TEGRA_PIN_GMI_DQS_PI2 _GPIO(66)
+#define TEGRA_PIN_GMI_CS6_N_PI3 _GPIO(67)
+#define TEGRA_PIN_GMI_RST_N_PI4 _GPIO(68)
+#define TEGRA_PIN_GMI_IORDY_PI5 _GPIO(69)
+#define TEGRA_PIN_GMI_CS7_N_PI6 _GPIO(70)
+#define TEGRA_PIN_GMI_WAIT_PI7 _GPIO(71)
+#define TEGRA_PIN_GMI_CS0_N_PJ0 _GPIO(72)
+#define TEGRA_PIN_LCD_DE_PJ1 _GPIO(73)
+#define TEGRA_PIN_GMI_CS1_N_PJ2 _GPIO(74)
+#define TEGRA_PIN_LCD_HSYNC_PJ3 _GPIO(75)
+#define TEGRA_PIN_LCD_VSYNC_PJ4 _GPIO(76)
+#define TEGRA_PIN_UART2_CTS_N_PJ5 _GPIO(77)
+#define TEGRA_PIN_UART2_RTS_N_PJ6 _GPIO(78)
+#define TEGRA_PIN_GMI_A16_PJ7 _GPIO(79)
+#define TEGRA_PIN_GMI_ADV_N_PK0 _GPIO(80)
+#define TEGRA_PIN_GMI_CLK_PK1 _GPIO(81)
+#define TEGRA_PIN_GMI_CS4_N_PK2 _GPIO(82)
+#define TEGRA_PIN_GMI_CS2_N_PK3 _GPIO(83)
+#define TEGRA_PIN_GMI_CS3_N_PK4 _GPIO(84)
+#define TEGRA_PIN_SPDIF_OUT_PK5 _GPIO(85)
+#define TEGRA_PIN_SPDIF_IN_PK6 _GPIO(86)
+#define TEGRA_PIN_GMI_A19_PK7 _GPIO(87)
+#define TEGRA_PIN_VI_D2_PL0 _GPIO(88)
+#define TEGRA_PIN_VI_D3_PL1 _GPIO(89)
+#define TEGRA_PIN_VI_D4_PL2 _GPIO(90)
+#define TEGRA_PIN_VI_D5_PL3 _GPIO(91)
+#define TEGRA_PIN_VI_D6_PL4 _GPIO(92)
+#define TEGRA_PIN_VI_D7_PL5 _GPIO(93)
+#define TEGRA_PIN_VI_D8_PL6 _GPIO(94)
+#define TEGRA_PIN_VI_D9_PL7 _GPIO(95)
+#define TEGRA_PIN_LCD_D16_PM0 _GPIO(96)
+#define TEGRA_PIN_LCD_D17_PM1 _GPIO(97)
+#define TEGRA_PIN_LCD_D18_PM2 _GPIO(98)
+#define TEGRA_PIN_LCD_D19_PM3 _GPIO(99)
+#define TEGRA_PIN_LCD_D20_PM4 _GPIO(100)
+#define TEGRA_PIN_LCD_D21_PM5 _GPIO(101)
+#define TEGRA_PIN_LCD_D22_PM6 _GPIO(102)
+#define TEGRA_PIN_LCD_D23_PM7 _GPIO(103)
+#define TEGRA_PIN_DAP1_FS_PN0 _GPIO(104)
+#define TEGRA_PIN_DAP1_DIN_PN1 _GPIO(105)
+#define TEGRA_PIN_DAP1_DOUT_PN2 _GPIO(106)
+#define TEGRA_PIN_DAP1_SCLK_PN3 _GPIO(107)
+#define TEGRA_PIN_LCD_CS0_N_PN4 _GPIO(108)
+#define TEGRA_PIN_LCD_SDOUT_PN5 _GPIO(109)
+#define TEGRA_PIN_LCD_DC0_PN6 _GPIO(110)
+#define TEGRA_PIN_HDMI_INT_PN7 _GPIO(111)
+#define TEGRA_PIN_ULPI_DATA7_PO0 _GPIO(112)
+#define TEGRA_PIN_ULPI_DATA0_PO1 _GPIO(113)
+#define TEGRA_PIN_ULPI_DATA1_PO2 _GPIO(114)
+#define TEGRA_PIN_ULPI_DATA2_PO3 _GPIO(115)
+#define TEGRA_PIN_ULPI_DATA3_PO4 _GPIO(116)
+#define TEGRA_PIN_ULPI_DATA4_PO5 _GPIO(117)
+#define TEGRA_PIN_ULPI_DATA5_PO6 _GPIO(118)
+#define TEGRA_PIN_ULPI_DATA6_PO7 _GPIO(119)
+#define TEGRA_PIN_DAP3_FS_PP0 _GPIO(120)
+#define TEGRA_PIN_DAP3_DIN_PP1 _GPIO(121)
+#define TEGRA_PIN_DAP3_DOUT_PP2 _GPIO(122)
+#define TEGRA_PIN_DAP3_SCLK_PP3 _GPIO(123)
+#define TEGRA_PIN_DAP4_FS_PP4 _GPIO(124)
+#define TEGRA_PIN_DAP4_DIN_PP5 _GPIO(125)
+#define TEGRA_PIN_DAP4_DOUT_PP6 _GPIO(126)
+#define TEGRA_PIN_DAP4_SCLK_PP7 _GPIO(127)
+#define TEGRA_PIN_KB_COL0_PQ0 _GPIO(128)
+#define TEGRA_PIN_KB_COL1_PQ1 _GPIO(129)
+#define TEGRA_PIN_KB_COL2_PQ2 _GPIO(130)
+#define TEGRA_PIN_KB_COL3_PQ3 _GPIO(131)
+#define TEGRA_PIN_KB_COL4_PQ4 _GPIO(132)
+#define TEGRA_PIN_KB_COL5_PQ5 _GPIO(133)
+#define TEGRA_PIN_KB_COL6_PQ6 _GPIO(134)
+#define TEGRA_PIN_KB_COL7_PQ7 _GPIO(135)
+#define TEGRA_PIN_KB_ROW0_PR0 _GPIO(136)
+#define TEGRA_PIN_KB_ROW1_PR1 _GPIO(137)
+#define TEGRA_PIN_KB_ROW2_PR2 _GPIO(138)
+#define TEGRA_PIN_KB_ROW3_PR3 _GPIO(139)
+#define TEGRA_PIN_KB_ROW4_PR4 _GPIO(140)
+#define TEGRA_PIN_KB_ROW5_PR5 _GPIO(141)
+#define TEGRA_PIN_KB_ROW6_PR6 _GPIO(142)
+#define TEGRA_PIN_KB_ROW7_PR7 _GPIO(143)
+#define TEGRA_PIN_KB_ROW8_PS0 _GPIO(144)
+#define TEGRA_PIN_KB_ROW9_PS1 _GPIO(145)
+#define TEGRA_PIN_KB_ROW10_PS2 _GPIO(146)
+#define TEGRA_PIN_KB_ROW11_PS3 _GPIO(147)
+#define TEGRA_PIN_KB_ROW12_PS4 _GPIO(148)
+#define TEGRA_PIN_KB_ROW13_PS5 _GPIO(149)
+#define TEGRA_PIN_KB_ROW14_PS6 _GPIO(150)
+#define TEGRA_PIN_KB_ROW15_PS7 _GPIO(151)
+#define TEGRA_PIN_VI_PCLK_PT0 _GPIO(152)
+#define TEGRA_PIN_VI_MCLK_PT1 _GPIO(153)
+#define TEGRA_PIN_VI_D10_PT2 _GPIO(154)
+#define TEGRA_PIN_VI_D11_PT3 _GPIO(155)
+#define TEGRA_PIN_VI_D0_PT4 _GPIO(156)
+#define TEGRA_PIN_GEN2_I2C_SCL_PT5 _GPIO(157)
+#define TEGRA_PIN_GEN2_I2C_SDA_PT6 _GPIO(158)
+#define TEGRA_PIN_SDMMC4_CMD_PT7 _GPIO(159)
+#define TEGRA_PIN_PU0 _GPIO(160)
+#define TEGRA_PIN_PU1 _GPIO(161)
+#define TEGRA_PIN_PU2 _GPIO(162)
+#define TEGRA_PIN_PU3 _GPIO(163)
+#define TEGRA_PIN_PU4 _GPIO(164)
+#define TEGRA_PIN_PU5 _GPIO(165)
+#define TEGRA_PIN_PU6 _GPIO(166)
+#define TEGRA_PIN_JTAG_RTCK_PU7 _GPIO(167)
+#define TEGRA_PIN_PV0 _GPIO(168)
+#define TEGRA_PIN_PV1 _GPIO(169)
+#define TEGRA_PIN_PV2 _GPIO(170)
+#define TEGRA_PIN_PV3 _GPIO(171)
+#define TEGRA_PIN_DDC_SCL_PV4 _GPIO(172)
+#define TEGRA_PIN_DDC_SDA_PV5 _GPIO(173)
+#define TEGRA_PIN_CRT_HSYNC_PV6 _GPIO(174)
+#define TEGRA_PIN_CRT_VSYNC_PV7 _GPIO(175)
+#define TEGRA_PIN_LCD_CS1_N_PW0 _GPIO(176)
+#define TEGRA_PIN_LCD_M1_PW1 _GPIO(177)
+#define TEGRA_PIN_SPI2_CS1_N_PW2 _GPIO(178)
+#define TEGRA_PIN_SPI2_CS2_N_PW3 _GPIO(179)
+#define TEGRA_PIN_CLK1_OUT_PW4 _GPIO(180)
+#define TEGRA_PIN_CLK2_OUT_PW5 _GPIO(181)
+#define TEGRA_PIN_UART3_TXD_PW6 _GPIO(182)
+#define TEGRA_PIN_UART3_RXD_PW7 _GPIO(183)
+#define TEGRA_PIN_SPI2_MOSI_PX0 _GPIO(184)
+#define TEGRA_PIN_SPI2_MISO_PX1 _GPIO(185)
+#define TEGRA_PIN_SPI2_SCK_PX2 _GPIO(186)
+#define TEGRA_PIN_SPI2_CS0_N_PX3 _GPIO(187)
+#define TEGRA_PIN_SPI1_MOSI_PX4 _GPIO(188)
+#define TEGRA_PIN_SPI1_SCK_PX5 _GPIO(189)
+#define TEGRA_PIN_SPI1_CS0_N_PX6 _GPIO(190)
+#define TEGRA_PIN_SPI1_MISO_PX7 _GPIO(191)
+#define TEGRA_PIN_ULPI_CLK_PY0 _GPIO(192)
+#define TEGRA_PIN_ULPI_DIR_PY1 _GPIO(193)
+#define TEGRA_PIN_ULPI_NXT_PY2 _GPIO(194)
+#define TEGRA_PIN_ULPI_STP_PY3 _GPIO(195)
+#define TEGRA_PIN_SDMMC1_DAT3_PY4 _GPIO(196)
+#define TEGRA_PIN_SDMMC1_DAT2_PY5 _GPIO(197)
+#define TEGRA_PIN_SDMMC1_DAT1_PY6 _GPIO(198)
+#define TEGRA_PIN_SDMMC1_DAT0_PY7 _GPIO(199)
+#define TEGRA_PIN_SDMMC1_CLK_PZ0 _GPIO(200)
+#define TEGRA_PIN_SDMMC1_CMD_PZ1 _GPIO(201)
+#define TEGRA_PIN_LCD_SDIN_PZ2 _GPIO(202)
+#define TEGRA_PIN_LCD_WR_N_PZ3 _GPIO(203)
+#define TEGRA_PIN_LCD_SCK_PZ4 _GPIO(204)
+#define TEGRA_PIN_SYS_CLK_REQ_PZ5 _GPIO(205)
+#define TEGRA_PIN_PWR_I2C_SCL_PZ6 _GPIO(206)
+#define TEGRA_PIN_PWR_I2C_SDA_PZ7 _GPIO(207)
+#define TEGRA_PIN_SDMMC4_DAT0_PAA0 _GPIO(208)
+#define TEGRA_PIN_SDMMC4_DAT1_PAA1 _GPIO(209)
+#define TEGRA_PIN_SDMMC4_DAT2_PAA2 _GPIO(210)
+#define TEGRA_PIN_SDMMC4_DAT3_PAA3 _GPIO(211)
+#define TEGRA_PIN_SDMMC4_DAT4_PAA4 _GPIO(212)
+#define TEGRA_PIN_SDMMC4_DAT5_PAA5 _GPIO(213)
+#define TEGRA_PIN_SDMMC4_DAT6_PAA6 _GPIO(214)
+#define TEGRA_PIN_SDMMC4_DAT7_PAA7 _GPIO(215)
+#define TEGRA_PIN_PBB0 _GPIO(216)
+#define TEGRA_PIN_CAM_I2C_SCL_PBB1 _GPIO(217)
+#define TEGRA_PIN_CAM_I2C_SDA_PBB2 _GPIO(218)
+#define TEGRA_PIN_PBB3 _GPIO(219)
+#define TEGRA_PIN_PBB4 _GPIO(220)
+#define TEGRA_PIN_PBB5 _GPIO(221)
+#define TEGRA_PIN_PBB6 _GPIO(222)
+#define TEGRA_PIN_PBB7 _GPIO(223)
+#define TEGRA_PIN_CAM_MCLK_PCC0 _GPIO(224)
+#define TEGRA_PIN_PCC1 _GPIO(225)
+#define TEGRA_PIN_PCC2 _GPIO(226)
+#define TEGRA_PIN_SDMMC4_RST_N_PCC3 _GPIO(227)
+#define TEGRA_PIN_SDMMC4_CLK_PCC4 _GPIO(228)
+#define TEGRA_PIN_CLK2_REQ_PCC5 _GPIO(229)
+#define TEGRA_PIN_PEX_L2_RST_N_PCC6 _GPIO(230)
+#define TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7 _GPIO(231)
+#define TEGRA_PIN_PEX_L0_PRSNT_N_PDD0 _GPIO(232)
+#define TEGRA_PIN_PEX_L0_RST_N_PDD1 _GPIO(233)
+#define TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2 _GPIO(234)
+#define TEGRA_PIN_PEX_WAKE_N_PDD3 _GPIO(235)
+#define TEGRA_PIN_PEX_L1_PRSNT_N_PDD4 _GPIO(236)
+#define TEGRA_PIN_PEX_L1_RST_N_PDD5 _GPIO(237)
+#define TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6 _GPIO(238)
+#define TEGRA_PIN_PEX_L2_PRSNT_N_PDD7 _GPIO(239)
+#define TEGRA_PIN_CLK3_OUT_PEE0 _GPIO(240)
+#define TEGRA_PIN_CLK3_REQ_PEE1 _GPIO(241)
+#define TEGRA_PIN_CLK1_REQ_PEE2 _GPIO(242)
+#define TEGRA_PIN_HDMI_CEC_PEE3 _GPIO(243)
+#define TEGRA_PIN_PEE4 _GPIO(244)
+#define TEGRA_PIN_PEE5 _GPIO(245)
+#define TEGRA_PIN_PEE6 _GPIO(246)
+#define TEGRA_PIN_PEE7 _GPIO(247)
+
+/* All non-GPIO pins follow */
+#define NUM_GPIOS (TEGRA_PIN_PEE7 + 1)
+#define _PIN(offset) (NUM_GPIOS + (offset))
+
+/* Non-GPIO pins */
+#define TEGRA_PIN_CLK_32K_IN _PIN(0)
+#define TEGRA_PIN_CORE_PWR_REQ _PIN(1)
+#define TEGRA_PIN_CPU_PWR_REQ _PIN(2)
+#define TEGRA_PIN_JTAG_TCK _PIN(3)
+#define TEGRA_PIN_JTAG_TDI _PIN(4)
+#define TEGRA_PIN_JTAG_TDO _PIN(5)
+#define TEGRA_PIN_JTAG_TMS _PIN(6)
+#define TEGRA_PIN_JTAG_TRST_N _PIN(7)
+#define TEGRA_PIN_OWR _PIN(8)
+#define TEGRA_PIN_PWR_INT_N _PIN(9)
+#define TEGRA_PIN_SYS_RESET_N _PIN(10)
+#define TEGRA_PIN_TEST_MODE_EN _PIN(11)
+
+static const struct pinctrl_pin_desc tegra30_pins[] = {
+ PINCTRL_PIN(TEGRA_PIN_CLK_32K_OUT_PA0, "CLK_32K_OUT PA0"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_CTS_N_PA1, "UART3_CTS_N PA1"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_FS_PA2, "DAP2_FS PA2"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_SCLK_PA3, "DAP2_SCLK PA3"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_DIN_PA4, "DAP2_DIN PA4"),
+ PINCTRL_PIN(TEGRA_PIN_DAP2_DOUT_PA5, "DAP2_DOUT PA5"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_CLK_PA6, "SDMMC3_CLK PA6"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_CMD_PA7, "SDMMC3_CMD PA7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_A17_PB0, "GMI_A17 PB0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_A18_PB1, "GMI_A18 PB1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PWR0_PB2, "LCD_PWR0 PB2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PCLK_PB3, "LCD_PCLK PB3"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT3_PB4, "SDMMC3_DAT3 PB4"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT2_PB5, "SDMMC3_DAT2 PB5"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT1_PB6, "SDMMC3_DAT1 PB6"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT0_PB7, "SDMMC3_DAT0 PB7"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_RTS_N_PC0, "UART3_RTS_N PC0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PWR1_PC1, "LCD_PWR1 PC1"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_TXD_PC2, "UART2_TXD PC2"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_RXD_PC3, "UART2_RXD PC3"),
+ PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SCL_PC4, "GEN1_I2C_SCL PC4"),
+ PINCTRL_PIN(TEGRA_PIN_GEN1_I2C_SDA_PC5, "GEN1_I2C_SDA PC5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_PWR2_PC6, "LCD_PWR2 PC6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_WP_N_PC7, "GMI_WP_N PC7"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT5_PD0, "SDMMC3_DAT5 PD0"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT4_PD1, "SDMMC3_DAT4 PD1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_DC1_PD2, "LCD_DC1 PD2"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT6_PD3, "SDMMC3_DAT6 PD3"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC3_DAT7_PD4, "SDMMC3_DAT7 PD4"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D1_PD5, "VI_D1 PD5"),
+ PINCTRL_PIN(TEGRA_PIN_VI_VSYNC_PD6, "VI_VSYNC PD6"),
+ PINCTRL_PIN(TEGRA_PIN_VI_HSYNC_PD7, "VI_HSYNC PD7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D0_PE0, "LCD_D0 PE0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D1_PE1, "LCD_D1 PE1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D2_PE2, "LCD_D2 PE2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D3_PE3, "LCD_D3 PE3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D4_PE4, "LCD_D4 PE4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D5_PE5, "LCD_D5 PE5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D6_PE6, "LCD_D6 PE6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D7_PE7, "LCD_D7 PE7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D8_PF0, "LCD_D8 PF0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D9_PF1, "LCD_D9 PF1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D10_PF2, "LCD_D10 PF2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D11_PF3, "LCD_D11 PF3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D12_PF4, "LCD_D12 PF4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D13_PF5, "LCD_D13 PF5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D14_PF6, "LCD_D14 PF6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D15_PF7, "LCD_D15 PF7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD0_PG0, "GMI_AD0 PG0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD1_PG1, "GMI_AD1 PG1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD2_PG2, "GMI_AD2 PG2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD3_PG3, "GMI_AD3 PG3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD4_PG4, "GMI_AD4 PG4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD5_PG5, "GMI_AD5 PG5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD6_PG6, "GMI_AD6 PG6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD7_PG7, "GMI_AD7 PG7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD8_PH0, "GMI_AD8 PH0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD9_PH1, "GMI_AD9 PH1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD10_PH2, "GMI_AD10 PH2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD11_PH3, "GMI_AD11 PH3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD12_PH4, "GMI_AD12 PH4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD13_PH5, "GMI_AD13 PH5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD14_PH6, "GMI_AD14 PH6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_AD15_PH7, "GMI_AD15 PH7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_WR_N_PI0, "GMI_WR_N PI0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_OE_N_PI1, "GMI_OE_N PI1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_DQS_PI2, "GMI_DQS PI2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS6_N_PI3, "GMI_CS6_N PI3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_RST_N_PI4, "GMI_RST_N PI4"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_IORDY_PI5, "GMI_IORDY PI5"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS7_N_PI6, "GMI_CS7_N PI6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_WAIT_PI7, "GMI_WAIT PI7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS0_N_PJ0, "GMI_CS0_N PJ0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_DE_PJ1, "LCD_DE PJ1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS1_N_PJ2, "GMI_CS1_N PJ2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_HSYNC_PJ3, "LCD_HSYNC PJ3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_VSYNC_PJ4, "LCD_VSYNC PJ4"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_CTS_N_PJ5, "UART2_CTS_N PJ5"),
+ PINCTRL_PIN(TEGRA_PIN_UART2_RTS_N_PJ6, "UART2_RTS_N PJ6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_A16_PJ7, "GMI_A16 PJ7"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_ADV_N_PK0, "GMI_ADV_N PK0"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CLK_PK1, "GMI_CLK PK1"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS4_N_PK2, "GMI_CS4_N PK2"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS2_N_PK3, "GMI_CS2_N PK3"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_CS3_N_PK4, "GMI_CS3_N PK4"),
+ PINCTRL_PIN(TEGRA_PIN_SPDIF_OUT_PK5, "SPDIF_OUT PK5"),
+ PINCTRL_PIN(TEGRA_PIN_SPDIF_IN_PK6, "SPDIF_IN PK6"),
+ PINCTRL_PIN(TEGRA_PIN_GMI_A19_PK7, "GMI_A19 PK7"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D2_PL0, "VI_D2 PL0"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D3_PL1, "VI_D3 PL1"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D4_PL2, "VI_D4 PL2"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D5_PL3, "VI_D5 PL3"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D6_PL4, "VI_D6 PL4"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D7_PL5, "VI_D7 PL5"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D8_PL6, "VI_D8 PL6"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D9_PL7, "VI_D9 PL7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D16_PM0, "LCD_D16 PM0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D17_PM1, "LCD_D17 PM1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D18_PM2, "LCD_D18 PM2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D19_PM3, "LCD_D19 PM3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D20_PM4, "LCD_D20 PM4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D21_PM5, "LCD_D21 PM5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D22_PM6, "LCD_D22 PM6"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_D23_PM7, "LCD_D23 PM7"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_FS_PN0, "DAP1_FS PN0"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_DIN_PN1, "DAP1_DIN PN1"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_DOUT_PN2, "DAP1_DOUT PN2"),
+ PINCTRL_PIN(TEGRA_PIN_DAP1_SCLK_PN3, "DAP1_SCLK PN3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_CS0_N_PN4, "LCD_CS0_N PN4"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_SDOUT_PN5, "LCD_SDOUT PN5"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_DC0_PN6, "LCD_DC0 PN6"),
+ PINCTRL_PIN(TEGRA_PIN_HDMI_INT_PN7, "HDMI_INT PN7"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA7_PO0, "ULPI_DATA7 PO0"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA0_PO1, "ULPI_DATA0 PO1"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA1_PO2, "ULPI_DATA1 PO2"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA2_PO3, "ULPI_DATA2 PO3"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA3_PO4, "ULPI_DATA3 PO4"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA4_PO5, "ULPI_DATA4 PO5"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA5_PO6, "ULPI_DATA5 PO6"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DATA6_PO7, "ULPI_DATA6 PO7"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_FS_PP0, "DAP3_FS PP0"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_DIN_PP1, "DAP3_DIN PP1"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_DOUT_PP2, "DAP3_DOUT PP2"),
+ PINCTRL_PIN(TEGRA_PIN_DAP3_SCLK_PP3, "DAP3_SCLK PP3"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_FS_PP4, "DAP4_FS PP4"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_DIN_PP5, "DAP4_DIN PP5"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_DOUT_PP6, "DAP4_DOUT PP6"),
+ PINCTRL_PIN(TEGRA_PIN_DAP4_SCLK_PP7, "DAP4_SCLK PP7"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL0_PQ0, "KB_COL0 PQ0"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL1_PQ1, "KB_COL1 PQ1"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL2_PQ2, "KB_COL2 PQ2"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL3_PQ3, "KB_COL3 PQ3"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL4_PQ4, "KB_COL4 PQ4"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL5_PQ5, "KB_COL5 PQ5"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL6_PQ6, "KB_COL6 PQ6"),
+ PINCTRL_PIN(TEGRA_PIN_KB_COL7_PQ7, "KB_COL7 PQ7"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW0_PR0, "KB_ROW0 PR0"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW1_PR1, "KB_ROW1 PR1"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW2_PR2, "KB_ROW2 PR2"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW3_PR3, "KB_ROW3 PR3"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW4_PR4, "KB_ROW4 PR4"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW5_PR5, "KB_ROW5 PR5"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW6_PR6, "KB_ROW6 PR6"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW7_PR7, "KB_ROW7 PR7"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW8_PS0, "KB_ROW8 PS0"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW9_PS1, "KB_ROW9 PS1"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW10_PS2, "KB_ROW10 PS2"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW11_PS3, "KB_ROW11 PS3"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW12_PS4, "KB_ROW12 PS4"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW13_PS5, "KB_ROW13 PS5"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW14_PS6, "KB_ROW14 PS6"),
+ PINCTRL_PIN(TEGRA_PIN_KB_ROW15_PS7, "KB_ROW15 PS7"),
+ PINCTRL_PIN(TEGRA_PIN_VI_PCLK_PT0, "VI_PCLK PT0"),
+ PINCTRL_PIN(TEGRA_PIN_VI_MCLK_PT1, "VI_MCLK PT1"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D10_PT2, "VI_D10 PT2"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D11_PT3, "VI_D11 PT3"),
+ PINCTRL_PIN(TEGRA_PIN_VI_D0_PT4, "VI_D0 PT4"),
+ PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SCL_PT5, "GEN2_I2C_SCL PT5"),
+ PINCTRL_PIN(TEGRA_PIN_GEN2_I2C_SDA_PT6, "GEN2_I2C_SDA PT6"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_CMD_PT7, "SDMMC4_CMD PT7"),
+ PINCTRL_PIN(TEGRA_PIN_PU0, "PU0"),
+ PINCTRL_PIN(TEGRA_PIN_PU1, "PU1"),
+ PINCTRL_PIN(TEGRA_PIN_PU2, "PU2"),
+ PINCTRL_PIN(TEGRA_PIN_PU3, "PU3"),
+ PINCTRL_PIN(TEGRA_PIN_PU4, "PU4"),
+ PINCTRL_PIN(TEGRA_PIN_PU5, "PU5"),
+ PINCTRL_PIN(TEGRA_PIN_PU6, "PU6"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_RTCK_PU7, "JTAG_RTCK PU7"),
+ PINCTRL_PIN(TEGRA_PIN_PV0, "PV0"),
+ PINCTRL_PIN(TEGRA_PIN_PV1, "PV1"),
+ PINCTRL_PIN(TEGRA_PIN_PV2, "PV2"),
+ PINCTRL_PIN(TEGRA_PIN_PV3, "PV3"),
+ PINCTRL_PIN(TEGRA_PIN_DDC_SCL_PV4, "DDC_SCL PV4"),
+ PINCTRL_PIN(TEGRA_PIN_DDC_SDA_PV5, "DDC_SDA PV5"),
+ PINCTRL_PIN(TEGRA_PIN_CRT_HSYNC_PV6, "CRT_HSYNC PV6"),
+ PINCTRL_PIN(TEGRA_PIN_CRT_VSYNC_PV7, "CRT_VSYNC PV7"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_CS1_N_PW0, "LCD_CS1_N PW0"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_M1_PW1, "LCD_M1 PW1"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_CS1_N_PW2, "SPI2_CS1_N PW2"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_CS2_N_PW3, "SPI2_CS2_N PW3"),
+ PINCTRL_PIN(TEGRA_PIN_CLK1_OUT_PW4, "CLK1_OUT PW4"),
+ PINCTRL_PIN(TEGRA_PIN_CLK2_OUT_PW5, "CLK2_OUT PW5"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_TXD_PW6, "UART3_TXD PW6"),
+ PINCTRL_PIN(TEGRA_PIN_UART3_RXD_PW7, "UART3_RXD PW7"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_MOSI_PX0, "SPI2_MOSI PX0"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_MISO_PX1, "SPI2_MISO PX1"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_SCK_PX2, "SPI2_SCK PX2"),
+ PINCTRL_PIN(TEGRA_PIN_SPI2_CS0_N_PX3, "SPI2_CS0_N PX3"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_MOSI_PX4, "SPI1_MOSI PX4"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_SCK_PX5, "SPI1_SCK PX5"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_CS0_N_PX6, "SPI1_CS0_N PX6"),
+ PINCTRL_PIN(TEGRA_PIN_SPI1_MISO_PX7, "SPI1_MISO PX7"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_CLK_PY0, "ULPI_CLK PY0"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_DIR_PY1, "ULPI_DIR PY1"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_NXT_PY2, "ULPI_NXT PY2"),
+ PINCTRL_PIN(TEGRA_PIN_ULPI_STP_PY3, "ULPI_STP PY3"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT3_PY4, "SDMMC1_DAT3 PY4"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT2_PY5, "SDMMC1_DAT2 PY5"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT1_PY6, "SDMMC1_DAT1 PY6"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC1_DAT0_PY7, "SDMMC1_DAT0 PY7"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC1_CLK_PZ0, "SDMMC1_CLK PZ0"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC1_CMD_PZ1, "SDMMC1_CMD PZ1"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_SDIN_PZ2, "LCD_SDIN PZ2"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_WR_N_PZ3, "LCD_WR_N PZ3"),
+ PINCTRL_PIN(TEGRA_PIN_LCD_SCK_PZ4, "LCD_SCK PZ4"),
+ PINCTRL_PIN(TEGRA_PIN_SYS_CLK_REQ_PZ5, "SYS_CLK_REQ PZ5"),
+ PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SCL_PZ6, "PWR_I2C_SCL PZ6"),
+ PINCTRL_PIN(TEGRA_PIN_PWR_I2C_SDA_PZ7, "PWR_I2C_SDA PZ7"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT0_PAA0, "SDMMC4_DAT0 PAA0"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT1_PAA1, "SDMMC4_DAT1 PAA1"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT2_PAA2, "SDMMC4_DAT2 PAA2"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT3_PAA3, "SDMMC4_DAT3 PAA3"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT4_PAA4, "SDMMC4_DAT4 PAA4"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT5_PAA5, "SDMMC4_DAT5 PAA5"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT6_PAA6, "SDMMC4_DAT6 PAA6"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_DAT7_PAA7, "SDMMC4_DAT7 PAA7"),
+ PINCTRL_PIN(TEGRA_PIN_PBB0, "PBB0"),
+ PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SCL_PBB1, "CAM_I2C_SCL PBB1"),
+ PINCTRL_PIN(TEGRA_PIN_CAM_I2C_SDA_PBB2, "CAM_I2C_SDA PBB2"),
+ PINCTRL_PIN(TEGRA_PIN_PBB3, "PBB3"),
+ PINCTRL_PIN(TEGRA_PIN_PBB4, "PBB4"),
+ PINCTRL_PIN(TEGRA_PIN_PBB5, "PBB5"),
+ PINCTRL_PIN(TEGRA_PIN_PBB6, "PBB6"),
+ PINCTRL_PIN(TEGRA_PIN_PBB7, "PBB7"),
+ PINCTRL_PIN(TEGRA_PIN_CAM_MCLK_PCC0, "CAM_MCLK PCC0"),
+ PINCTRL_PIN(TEGRA_PIN_PCC1, "PCC1"),
+ PINCTRL_PIN(TEGRA_PIN_PCC2, "PCC2"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_RST_N_PCC3, "SDMMC4_RST_N PCC3"),
+ PINCTRL_PIN(TEGRA_PIN_SDMMC4_CLK_PCC4, "SDMMC4_CLK PCC4"),
+ PINCTRL_PIN(TEGRA_PIN_CLK2_REQ_PCC5, "CLK2_REQ PCC5"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L2_RST_N_PCC6, "PEX_L2_RST_N PCC6"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7, "PEX_L2_CLKREQ_N PCC7"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L0_PRSNT_N_PDD0, "PEX_L0_PRSNT_N PDD0"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L0_RST_N_PDD1, "PEX_L0_RST_N PDD1"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2, "PEX_L0_CLKREQ_N PDD2"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_WAKE_N_PDD3, "PEX_WAKE_N PDD3"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L1_PRSNT_N_PDD4, "PEX_L1_PRSNT_N PDD4"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L1_RST_N_PDD5, "PEX_L1_RST_N PDD5"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6, "PEX_L1_CLKREQ_N PDD6"),
+ PINCTRL_PIN(TEGRA_PIN_PEX_L2_PRSNT_N_PDD7, "PEX_L2_PRSNT_N PDD7"),
+ PINCTRL_PIN(TEGRA_PIN_CLK3_OUT_PEE0, "CLK3_OUT PEE0"),
+ PINCTRL_PIN(TEGRA_PIN_CLK3_REQ_PEE1, "CLK3_REQ PEE1"),
+ PINCTRL_PIN(TEGRA_PIN_CLK1_REQ_PEE2, "CLK1_REQ PEE2"),
+ PINCTRL_PIN(TEGRA_PIN_HDMI_CEC_PEE3, "HDMI_CEC PEE3"),
+ PINCTRL_PIN(TEGRA_PIN_PEE4, "PEE4"),
+ PINCTRL_PIN(TEGRA_PIN_PEE5, "PEE5"),
+ PINCTRL_PIN(TEGRA_PIN_PEE6, "PEE6"),
+ PINCTRL_PIN(TEGRA_PIN_PEE7, "PEE7"),
+ PINCTRL_PIN(TEGRA_PIN_CLK_32K_IN, "CLK_32K_IN"),
+ PINCTRL_PIN(TEGRA_PIN_CORE_PWR_REQ, "CORE_PWR_REQ"),
+ PINCTRL_PIN(TEGRA_PIN_CPU_PWR_REQ, "CPU_PWR_REQ"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TCK, "JTAG_TCK"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TDI, "JTAG_TDI"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TDO, "JTAG_TDO"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TMS, "JTAG_TMS"),
+ PINCTRL_PIN(TEGRA_PIN_JTAG_TRST_N, "JTAG_TRST_N"),
+ PINCTRL_PIN(TEGRA_PIN_OWR, "OWR"),
+ PINCTRL_PIN(TEGRA_PIN_PWR_INT_N, "PWR_INT_N"),
+ PINCTRL_PIN(TEGRA_PIN_SYS_RESET_N, "SYS_RESET_N"),
+ PINCTRL_PIN(TEGRA_PIN_TEST_MODE_EN, "TEST_MODE_EN"),
+};
+
+static const unsigned clk_32k_out_pa0_pins[] = {
+ TEGRA_PIN_CLK_32K_OUT_PA0,
+};
+
+static const unsigned uart3_cts_n_pa1_pins[] = {
+ TEGRA_PIN_UART3_CTS_N_PA1,
+};
+
+static const unsigned dap2_fs_pa2_pins[] = {
+ TEGRA_PIN_DAP2_FS_PA2,
+};
+
+static const unsigned dap2_sclk_pa3_pins[] = {
+ TEGRA_PIN_DAP2_SCLK_PA3,
+};
+
+static const unsigned dap2_din_pa4_pins[] = {
+ TEGRA_PIN_DAP2_DIN_PA4,
+};
+
+static const unsigned dap2_dout_pa5_pins[] = {
+ TEGRA_PIN_DAP2_DOUT_PA5,
+};
+
+static const unsigned sdmmc3_clk_pa6_pins[] = {
+ TEGRA_PIN_SDMMC3_CLK_PA6,
+};
+
+static const unsigned sdmmc3_cmd_pa7_pins[] = {
+ TEGRA_PIN_SDMMC3_CMD_PA7,
+};
+
+static const unsigned gmi_a17_pb0_pins[] = {
+ TEGRA_PIN_GMI_A17_PB0,
+};
+
+static const unsigned gmi_a18_pb1_pins[] = {
+ TEGRA_PIN_GMI_A18_PB1,
+};
+
+static const unsigned lcd_pwr0_pb2_pins[] = {
+ TEGRA_PIN_LCD_PWR0_PB2,
+};
+
+static const unsigned lcd_pclk_pb3_pins[] = {
+ TEGRA_PIN_LCD_PCLK_PB3,
+};
+
+static const unsigned sdmmc3_dat3_pb4_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT3_PB4,
+};
+
+static const unsigned sdmmc3_dat2_pb5_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT2_PB5,
+};
+
+static const unsigned sdmmc3_dat1_pb6_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT1_PB6,
+};
+
+static const unsigned sdmmc3_dat0_pb7_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT0_PB7,
+};
+
+static const unsigned uart3_rts_n_pc0_pins[] = {
+ TEGRA_PIN_UART3_RTS_N_PC0,
+};
+
+static const unsigned lcd_pwr1_pc1_pins[] = {
+ TEGRA_PIN_LCD_PWR1_PC1,
+};
+
+static const unsigned uart2_txd_pc2_pins[] = {
+ TEGRA_PIN_UART2_TXD_PC2,
+};
+
+static const unsigned uart2_rxd_pc3_pins[] = {
+ TEGRA_PIN_UART2_RXD_PC3,
+};
+
+static const unsigned gen1_i2c_scl_pc4_pins[] = {
+ TEGRA_PIN_GEN1_I2C_SCL_PC4,
+};
+
+static const unsigned gen1_i2c_sda_pc5_pins[] = {
+ TEGRA_PIN_GEN1_I2C_SDA_PC5,
+};
+
+static const unsigned lcd_pwr2_pc6_pins[] = {
+ TEGRA_PIN_LCD_PWR2_PC6,
+};
+
+static const unsigned gmi_wp_n_pc7_pins[] = {
+ TEGRA_PIN_GMI_WP_N_PC7,
+};
+
+static const unsigned sdmmc3_dat5_pd0_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT5_PD0,
+};
+
+static const unsigned sdmmc3_dat4_pd1_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT4_PD1,
+};
+
+static const unsigned lcd_dc1_pd2_pins[] = {
+ TEGRA_PIN_LCD_DC1_PD2,
+};
+
+static const unsigned sdmmc3_dat6_pd3_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT6_PD3,
+};
+
+static const unsigned sdmmc3_dat7_pd4_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT7_PD4,
+};
+
+static const unsigned vi_d1_pd5_pins[] = {
+ TEGRA_PIN_VI_D1_PD5,
+};
+
+static const unsigned vi_vsync_pd6_pins[] = {
+ TEGRA_PIN_VI_VSYNC_PD6,
+};
+
+static const unsigned vi_hsync_pd7_pins[] = {
+ TEGRA_PIN_VI_HSYNC_PD7,
+};
+
+static const unsigned lcd_d0_pe0_pins[] = {
+ TEGRA_PIN_LCD_D0_PE0,
+};
+
+static const unsigned lcd_d1_pe1_pins[] = {
+ TEGRA_PIN_LCD_D1_PE1,
+};
+
+static const unsigned lcd_d2_pe2_pins[] = {
+ TEGRA_PIN_LCD_D2_PE2,
+};
+
+static const unsigned lcd_d3_pe3_pins[] = {
+ TEGRA_PIN_LCD_D3_PE3,
+};
+
+static const unsigned lcd_d4_pe4_pins[] = {
+ TEGRA_PIN_LCD_D4_PE4,
+};
+
+static const unsigned lcd_d5_pe5_pins[] = {
+ TEGRA_PIN_LCD_D5_PE5,
+};
+
+static const unsigned lcd_d6_pe6_pins[] = {
+ TEGRA_PIN_LCD_D6_PE6,
+};
+
+static const unsigned lcd_d7_pe7_pins[] = {
+ TEGRA_PIN_LCD_D7_PE7,
+};
+
+static const unsigned lcd_d8_pf0_pins[] = {
+ TEGRA_PIN_LCD_D8_PF0,
+};
+
+static const unsigned lcd_d9_pf1_pins[] = {
+ TEGRA_PIN_LCD_D9_PF1,
+};
+
+static const unsigned lcd_d10_pf2_pins[] = {
+ TEGRA_PIN_LCD_D10_PF2,
+};
+
+static const unsigned lcd_d11_pf3_pins[] = {
+ TEGRA_PIN_LCD_D11_PF3,
+};
+
+static const unsigned lcd_d12_pf4_pins[] = {
+ TEGRA_PIN_LCD_D12_PF4,
+};
+
+static const unsigned lcd_d13_pf5_pins[] = {
+ TEGRA_PIN_LCD_D13_PF5,
+};
+
+static const unsigned lcd_d14_pf6_pins[] = {
+ TEGRA_PIN_LCD_D14_PF6,
+};
+
+static const unsigned lcd_d15_pf7_pins[] = {
+ TEGRA_PIN_LCD_D15_PF7,
+};
+
+static const unsigned gmi_ad0_pg0_pins[] = {
+ TEGRA_PIN_GMI_AD0_PG0,
+};
+
+static const unsigned gmi_ad1_pg1_pins[] = {
+ TEGRA_PIN_GMI_AD1_PG1,
+};
+
+static const unsigned gmi_ad2_pg2_pins[] = {
+ TEGRA_PIN_GMI_AD2_PG2,
+};
+
+static const unsigned gmi_ad3_pg3_pins[] = {
+ TEGRA_PIN_GMI_AD3_PG3,
+};
+
+static const unsigned gmi_ad4_pg4_pins[] = {
+ TEGRA_PIN_GMI_AD4_PG4,
+};
+
+static const unsigned gmi_ad5_pg5_pins[] = {
+ TEGRA_PIN_GMI_AD5_PG5,
+};
+
+static const unsigned gmi_ad6_pg6_pins[] = {
+ TEGRA_PIN_GMI_AD6_PG6,
+};
+
+static const unsigned gmi_ad7_pg7_pins[] = {
+ TEGRA_PIN_GMI_AD7_PG7,
+};
+
+static const unsigned gmi_ad8_ph0_pins[] = {
+ TEGRA_PIN_GMI_AD8_PH0,
+};
+
+static const unsigned gmi_ad9_ph1_pins[] = {
+ TEGRA_PIN_GMI_AD9_PH1,
+};
+
+static const unsigned gmi_ad10_ph2_pins[] = {
+ TEGRA_PIN_GMI_AD10_PH2,
+};
+
+static const unsigned gmi_ad11_ph3_pins[] = {
+ TEGRA_PIN_GMI_AD11_PH3,
+};
+
+static const unsigned gmi_ad12_ph4_pins[] = {
+ TEGRA_PIN_GMI_AD12_PH4,
+};
+
+static const unsigned gmi_ad13_ph5_pins[] = {
+ TEGRA_PIN_GMI_AD13_PH5,
+};
+
+static const unsigned gmi_ad14_ph6_pins[] = {
+ TEGRA_PIN_GMI_AD14_PH6,
+};
+
+static const unsigned gmi_ad15_ph7_pins[] = {
+ TEGRA_PIN_GMI_AD15_PH7,
+};
+
+static const unsigned gmi_wr_n_pi0_pins[] = {
+ TEGRA_PIN_GMI_WR_N_PI0,
+};
+
+static const unsigned gmi_oe_n_pi1_pins[] = {
+ TEGRA_PIN_GMI_OE_N_PI1,
+};
+
+static const unsigned gmi_dqs_pi2_pins[] = {
+ TEGRA_PIN_GMI_DQS_PI2,
+};
+
+static const unsigned gmi_cs6_n_pi3_pins[] = {
+ TEGRA_PIN_GMI_CS6_N_PI3,
+};
+
+static const unsigned gmi_rst_n_pi4_pins[] = {
+ TEGRA_PIN_GMI_RST_N_PI4,
+};
+
+static const unsigned gmi_iordy_pi5_pins[] = {
+ TEGRA_PIN_GMI_IORDY_PI5,
+};
+
+static const unsigned gmi_cs7_n_pi6_pins[] = {
+ TEGRA_PIN_GMI_CS7_N_PI6,
+};
+
+static const unsigned gmi_wait_pi7_pins[] = {
+ TEGRA_PIN_GMI_WAIT_PI7,
+};
+
+static const unsigned gmi_cs0_n_pj0_pins[] = {
+ TEGRA_PIN_GMI_CS0_N_PJ0,
+};
+
+static const unsigned lcd_de_pj1_pins[] = {
+ TEGRA_PIN_LCD_DE_PJ1,
+};
+
+static const unsigned gmi_cs1_n_pj2_pins[] = {
+ TEGRA_PIN_GMI_CS1_N_PJ2,
+};
+
+static const unsigned lcd_hsync_pj3_pins[] = {
+ TEGRA_PIN_LCD_HSYNC_PJ3,
+};
+
+static const unsigned lcd_vsync_pj4_pins[] = {
+ TEGRA_PIN_LCD_VSYNC_PJ4,
+};
+
+static const unsigned uart2_cts_n_pj5_pins[] = {
+ TEGRA_PIN_UART2_CTS_N_PJ5,
+};
+
+static const unsigned uart2_rts_n_pj6_pins[] = {
+ TEGRA_PIN_UART2_RTS_N_PJ6,
+};
+
+static const unsigned gmi_a16_pj7_pins[] = {
+ TEGRA_PIN_GMI_A16_PJ7,
+};
+
+static const unsigned gmi_adv_n_pk0_pins[] = {
+ TEGRA_PIN_GMI_ADV_N_PK0,
+};
+
+static const unsigned gmi_clk_pk1_pins[] = {
+ TEGRA_PIN_GMI_CLK_PK1,
+};
+
+static const unsigned gmi_cs4_n_pk2_pins[] = {
+ TEGRA_PIN_GMI_CS4_N_PK2,
+};
+
+static const unsigned gmi_cs2_n_pk3_pins[] = {
+ TEGRA_PIN_GMI_CS2_N_PK3,
+};
+
+static const unsigned gmi_cs3_n_pk4_pins[] = {
+ TEGRA_PIN_GMI_CS3_N_PK4,
+};
+
+static const unsigned spdif_out_pk5_pins[] = {
+ TEGRA_PIN_SPDIF_OUT_PK5,
+};
+
+static const unsigned spdif_in_pk6_pins[] = {
+ TEGRA_PIN_SPDIF_IN_PK6,
+};
+
+static const unsigned gmi_a19_pk7_pins[] = {
+ TEGRA_PIN_GMI_A19_PK7,
+};
+
+static const unsigned vi_d2_pl0_pins[] = {
+ TEGRA_PIN_VI_D2_PL0,
+};
+
+static const unsigned vi_d3_pl1_pins[] = {
+ TEGRA_PIN_VI_D3_PL1,
+};
+
+static const unsigned vi_d4_pl2_pins[] = {
+ TEGRA_PIN_VI_D4_PL2,
+};
+
+static const unsigned vi_d5_pl3_pins[] = {
+ TEGRA_PIN_VI_D5_PL3,
+};
+
+static const unsigned vi_d6_pl4_pins[] = {
+ TEGRA_PIN_VI_D6_PL4,
+};
+
+static const unsigned vi_d7_pl5_pins[] = {
+ TEGRA_PIN_VI_D7_PL5,
+};
+
+static const unsigned vi_d8_pl6_pins[] = {
+ TEGRA_PIN_VI_D8_PL6,
+};
+
+static const unsigned vi_d9_pl7_pins[] = {
+ TEGRA_PIN_VI_D9_PL7,
+};
+
+static const unsigned lcd_d16_pm0_pins[] = {
+ TEGRA_PIN_LCD_D16_PM0,
+};
+
+static const unsigned lcd_d17_pm1_pins[] = {
+ TEGRA_PIN_LCD_D17_PM1,
+};
+
+static const unsigned lcd_d18_pm2_pins[] = {
+ TEGRA_PIN_LCD_D18_PM2,
+};
+
+static const unsigned lcd_d19_pm3_pins[] = {
+ TEGRA_PIN_LCD_D19_PM3,
+};
+
+static const unsigned lcd_d20_pm4_pins[] = {
+ TEGRA_PIN_LCD_D20_PM4,
+};
+
+static const unsigned lcd_d21_pm5_pins[] = {
+ TEGRA_PIN_LCD_D21_PM5,
+};
+
+static const unsigned lcd_d22_pm6_pins[] = {
+ TEGRA_PIN_LCD_D22_PM6,
+};
+
+static const unsigned lcd_d23_pm7_pins[] = {
+ TEGRA_PIN_LCD_D23_PM7,
+};
+
+static const unsigned dap1_fs_pn0_pins[] = {
+ TEGRA_PIN_DAP1_FS_PN0,
+};
+
+static const unsigned dap1_din_pn1_pins[] = {
+ TEGRA_PIN_DAP1_DIN_PN1,
+};
+
+static const unsigned dap1_dout_pn2_pins[] = {
+ TEGRA_PIN_DAP1_DOUT_PN2,
+};
+
+static const unsigned dap1_sclk_pn3_pins[] = {
+ TEGRA_PIN_DAP1_SCLK_PN3,
+};
+
+static const unsigned lcd_cs0_n_pn4_pins[] = {
+ TEGRA_PIN_LCD_CS0_N_PN4,
+};
+
+static const unsigned lcd_sdout_pn5_pins[] = {
+ TEGRA_PIN_LCD_SDOUT_PN5,
+};
+
+static const unsigned lcd_dc0_pn6_pins[] = {
+ TEGRA_PIN_LCD_DC0_PN6,
+};
+
+static const unsigned hdmi_int_pn7_pins[] = {
+ TEGRA_PIN_HDMI_INT_PN7,
+};
+
+static const unsigned ulpi_data7_po0_pins[] = {
+ TEGRA_PIN_ULPI_DATA7_PO0,
+};
+
+static const unsigned ulpi_data0_po1_pins[] = {
+ TEGRA_PIN_ULPI_DATA0_PO1,
+};
+
+static const unsigned ulpi_data1_po2_pins[] = {
+ TEGRA_PIN_ULPI_DATA1_PO2,
+};
+
+static const unsigned ulpi_data2_po3_pins[] = {
+ TEGRA_PIN_ULPI_DATA2_PO3,
+};
+
+static const unsigned ulpi_data3_po4_pins[] = {
+ TEGRA_PIN_ULPI_DATA3_PO4,
+};
+
+static const unsigned ulpi_data4_po5_pins[] = {
+ TEGRA_PIN_ULPI_DATA4_PO5,
+};
+
+static const unsigned ulpi_data5_po6_pins[] = {
+ TEGRA_PIN_ULPI_DATA5_PO6,
+};
+
+static const unsigned ulpi_data6_po7_pins[] = {
+ TEGRA_PIN_ULPI_DATA6_PO7,
+};
+
+static const unsigned dap3_fs_pp0_pins[] = {
+ TEGRA_PIN_DAP3_FS_PP0,
+};
+
+static const unsigned dap3_din_pp1_pins[] = {
+ TEGRA_PIN_DAP3_DIN_PP1,
+};
+
+static const unsigned dap3_dout_pp2_pins[] = {
+ TEGRA_PIN_DAP3_DOUT_PP2,
+};
+
+static const unsigned dap3_sclk_pp3_pins[] = {
+ TEGRA_PIN_DAP3_SCLK_PP3,
+};
+
+static const unsigned dap4_fs_pp4_pins[] = {
+ TEGRA_PIN_DAP4_FS_PP4,
+};
+
+static const unsigned dap4_din_pp5_pins[] = {
+ TEGRA_PIN_DAP4_DIN_PP5,
+};
+
+static const unsigned dap4_dout_pp6_pins[] = {
+ TEGRA_PIN_DAP4_DOUT_PP6,
+};
+
+static const unsigned dap4_sclk_pp7_pins[] = {
+ TEGRA_PIN_DAP4_SCLK_PP7,
+};
+
+static const unsigned kb_col0_pq0_pins[] = {
+ TEGRA_PIN_KB_COL0_PQ0,
+};
+
+static const unsigned kb_col1_pq1_pins[] = {
+ TEGRA_PIN_KB_COL1_PQ1,
+};
+
+static const unsigned kb_col2_pq2_pins[] = {
+ TEGRA_PIN_KB_COL2_PQ2,
+};
+
+static const unsigned kb_col3_pq3_pins[] = {
+ TEGRA_PIN_KB_COL3_PQ3,
+};
+
+static const unsigned kb_col4_pq4_pins[] = {
+ TEGRA_PIN_KB_COL4_PQ4,
+};
+
+static const unsigned kb_col5_pq5_pins[] = {
+ TEGRA_PIN_KB_COL5_PQ5,
+};
+
+static const unsigned kb_col6_pq6_pins[] = {
+ TEGRA_PIN_KB_COL6_PQ6,
+};
+
+static const unsigned kb_col7_pq7_pins[] = {
+ TEGRA_PIN_KB_COL7_PQ7,
+};
+
+static const unsigned kb_row0_pr0_pins[] = {
+ TEGRA_PIN_KB_ROW0_PR0,
+};
+
+static const unsigned kb_row1_pr1_pins[] = {
+ TEGRA_PIN_KB_ROW1_PR1,
+};
+
+static const unsigned kb_row2_pr2_pins[] = {
+ TEGRA_PIN_KB_ROW2_PR2,
+};
+
+static const unsigned kb_row3_pr3_pins[] = {
+ TEGRA_PIN_KB_ROW3_PR3,
+};
+
+static const unsigned kb_row4_pr4_pins[] = {
+ TEGRA_PIN_KB_ROW4_PR4,
+};
+
+static const unsigned kb_row5_pr5_pins[] = {
+ TEGRA_PIN_KB_ROW5_PR5,
+};
+
+static const unsigned kb_row6_pr6_pins[] = {
+ TEGRA_PIN_KB_ROW6_PR6,
+};
+
+static const unsigned kb_row7_pr7_pins[] = {
+ TEGRA_PIN_KB_ROW7_PR7,
+};
+
+static const unsigned kb_row8_ps0_pins[] = {
+ TEGRA_PIN_KB_ROW8_PS0,
+};
+
+static const unsigned kb_row9_ps1_pins[] = {
+ TEGRA_PIN_KB_ROW9_PS1,
+};
+
+static const unsigned kb_row10_ps2_pins[] = {
+ TEGRA_PIN_KB_ROW10_PS2,
+};
+
+static const unsigned kb_row11_ps3_pins[] = {
+ TEGRA_PIN_KB_ROW11_PS3,
+};
+
+static const unsigned kb_row12_ps4_pins[] = {
+ TEGRA_PIN_KB_ROW12_PS4,
+};
+
+static const unsigned kb_row13_ps5_pins[] = {
+ TEGRA_PIN_KB_ROW13_PS5,
+};
+
+static const unsigned kb_row14_ps6_pins[] = {
+ TEGRA_PIN_KB_ROW14_PS6,
+};
+
+static const unsigned kb_row15_ps7_pins[] = {
+ TEGRA_PIN_KB_ROW15_PS7,
+};
+
+static const unsigned vi_pclk_pt0_pins[] = {
+ TEGRA_PIN_VI_PCLK_PT0,
+};
+
+static const unsigned vi_mclk_pt1_pins[] = {
+ TEGRA_PIN_VI_MCLK_PT1,
+};
+
+static const unsigned vi_d10_pt2_pins[] = {
+ TEGRA_PIN_VI_D10_PT2,
+};
+
+static const unsigned vi_d11_pt3_pins[] = {
+ TEGRA_PIN_VI_D11_PT3,
+};
+
+static const unsigned vi_d0_pt4_pins[] = {
+ TEGRA_PIN_VI_D0_PT4,
+};
+
+static const unsigned gen2_i2c_scl_pt5_pins[] = {
+ TEGRA_PIN_GEN2_I2C_SCL_PT5,
+};
+
+static const unsigned gen2_i2c_sda_pt6_pins[] = {
+ TEGRA_PIN_GEN2_I2C_SDA_PT6,
+};
+
+static const unsigned sdmmc4_cmd_pt7_pins[] = {
+ TEGRA_PIN_SDMMC4_CMD_PT7,
+};
+
+static const unsigned pu0_pins[] = {
+ TEGRA_PIN_PU0,
+};
+
+static const unsigned pu1_pins[] = {
+ TEGRA_PIN_PU1,
+};
+
+static const unsigned pu2_pins[] = {
+ TEGRA_PIN_PU2,
+};
+
+static const unsigned pu3_pins[] = {
+ TEGRA_PIN_PU3,
+};
+
+static const unsigned pu4_pins[] = {
+ TEGRA_PIN_PU4,
+};
+
+static const unsigned pu5_pins[] = {
+ TEGRA_PIN_PU5,
+};
+
+static const unsigned pu6_pins[] = {
+ TEGRA_PIN_PU6,
+};
+
+static const unsigned jtag_rtck_pu7_pins[] = {
+ TEGRA_PIN_JTAG_RTCK_PU7,
+};
+
+static const unsigned pv0_pins[] = {
+ TEGRA_PIN_PV0,
+};
+
+static const unsigned pv1_pins[] = {
+ TEGRA_PIN_PV1,
+};
+
+static const unsigned pv2_pins[] = {
+ TEGRA_PIN_PV2,
+};
+
+static const unsigned pv3_pins[] = {
+ TEGRA_PIN_PV3,
+};
+
+static const unsigned ddc_scl_pv4_pins[] = {
+ TEGRA_PIN_DDC_SCL_PV4,
+};
+
+static const unsigned ddc_sda_pv5_pins[] = {
+ TEGRA_PIN_DDC_SDA_PV5,
+};
+
+static const unsigned crt_hsync_pv6_pins[] = {
+ TEGRA_PIN_CRT_HSYNC_PV6,
+};
+
+static const unsigned crt_vsync_pv7_pins[] = {
+ TEGRA_PIN_CRT_VSYNC_PV7,
+};
+
+static const unsigned lcd_cs1_n_pw0_pins[] = {
+ TEGRA_PIN_LCD_CS1_N_PW0,
+};
+
+static const unsigned lcd_m1_pw1_pins[] = {
+ TEGRA_PIN_LCD_M1_PW1,
+};
+
+static const unsigned spi2_cs1_n_pw2_pins[] = {
+ TEGRA_PIN_SPI2_CS1_N_PW2,
+};
+
+static const unsigned spi2_cs2_n_pw3_pins[] = {
+ TEGRA_PIN_SPI2_CS2_N_PW3,
+};
+
+static const unsigned clk1_out_pw4_pins[] = {
+ TEGRA_PIN_CLK1_OUT_PW4,
+};
+
+static const unsigned clk2_out_pw5_pins[] = {
+ TEGRA_PIN_CLK2_OUT_PW5,
+};
+
+static const unsigned uart3_txd_pw6_pins[] = {
+ TEGRA_PIN_UART3_TXD_PW6,
+};
+
+static const unsigned uart3_rxd_pw7_pins[] = {
+ TEGRA_PIN_UART3_RXD_PW7,
+};
+
+static const unsigned spi2_mosi_px0_pins[] = {
+ TEGRA_PIN_SPI2_MOSI_PX0,
+};
+
+static const unsigned spi2_miso_px1_pins[] = {
+ TEGRA_PIN_SPI2_MISO_PX1,
+};
+
+static const unsigned spi2_sck_px2_pins[] = {
+ TEGRA_PIN_SPI2_SCK_PX2,
+};
+
+static const unsigned spi2_cs0_n_px3_pins[] = {
+ TEGRA_PIN_SPI2_CS0_N_PX3,
+};
+
+static const unsigned spi1_mosi_px4_pins[] = {
+ TEGRA_PIN_SPI1_MOSI_PX4,
+};
+
+static const unsigned spi1_sck_px5_pins[] = {
+ TEGRA_PIN_SPI1_SCK_PX5,
+};
+
+static const unsigned spi1_cs0_n_px6_pins[] = {
+ TEGRA_PIN_SPI1_CS0_N_PX6,
+};
+
+static const unsigned spi1_miso_px7_pins[] = {
+ TEGRA_PIN_SPI1_MISO_PX7,
+};
+
+static const unsigned ulpi_clk_py0_pins[] = {
+ TEGRA_PIN_ULPI_CLK_PY0,
+};
+
+static const unsigned ulpi_dir_py1_pins[] = {
+ TEGRA_PIN_ULPI_DIR_PY1,
+};
+
+static const unsigned ulpi_nxt_py2_pins[] = {
+ TEGRA_PIN_ULPI_NXT_PY2,
+};
+
+static const unsigned ulpi_stp_py3_pins[] = {
+ TEGRA_PIN_ULPI_STP_PY3,
+};
+
+static const unsigned sdmmc1_dat3_py4_pins[] = {
+ TEGRA_PIN_SDMMC1_DAT3_PY4,
+};
+
+static const unsigned sdmmc1_dat2_py5_pins[] = {
+ TEGRA_PIN_SDMMC1_DAT2_PY5,
+};
+
+static const unsigned sdmmc1_dat1_py6_pins[] = {
+ TEGRA_PIN_SDMMC1_DAT1_PY6,
+};
+
+static const unsigned sdmmc1_dat0_py7_pins[] = {
+ TEGRA_PIN_SDMMC1_DAT0_PY7,
+};
+
+static const unsigned sdmmc1_clk_pz0_pins[] = {
+ TEGRA_PIN_SDMMC1_CLK_PZ0,
+};
+
+static const unsigned sdmmc1_cmd_pz1_pins[] = {
+ TEGRA_PIN_SDMMC1_CMD_PZ1,
+};
+
+static const unsigned lcd_sdin_pz2_pins[] = {
+ TEGRA_PIN_LCD_SDIN_PZ2,
+};
+
+static const unsigned lcd_wr_n_pz3_pins[] = {
+ TEGRA_PIN_LCD_WR_N_PZ3,
+};
+
+static const unsigned lcd_sck_pz4_pins[] = {
+ TEGRA_PIN_LCD_SCK_PZ4,
+};
+
+static const unsigned sys_clk_req_pz5_pins[] = {
+ TEGRA_PIN_SYS_CLK_REQ_PZ5,
+};
+
+static const unsigned pwr_i2c_scl_pz6_pins[] = {
+ TEGRA_PIN_PWR_I2C_SCL_PZ6,
+};
+
+static const unsigned pwr_i2c_sda_pz7_pins[] = {
+ TEGRA_PIN_PWR_I2C_SDA_PZ7,
+};
+
+static const unsigned sdmmc4_dat0_paa0_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT0_PAA0,
+};
+
+static const unsigned sdmmc4_dat1_paa1_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT1_PAA1,
+};
+
+static const unsigned sdmmc4_dat2_paa2_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT2_PAA2,
+};
+
+static const unsigned sdmmc4_dat3_paa3_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT3_PAA3,
+};
+
+static const unsigned sdmmc4_dat4_paa4_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT4_PAA4,
+};
+
+static const unsigned sdmmc4_dat5_paa5_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT5_PAA5,
+};
+
+static const unsigned sdmmc4_dat6_paa6_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT6_PAA6,
+};
+
+static const unsigned sdmmc4_dat7_paa7_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT7_PAA7,
+};
+
+static const unsigned pbb0_pins[] = {
+ TEGRA_PIN_PBB0,
+};
+
+static const unsigned cam_i2c_scl_pbb1_pins[] = {
+ TEGRA_PIN_CAM_I2C_SCL_PBB1,
+};
+
+static const unsigned cam_i2c_sda_pbb2_pins[] = {
+ TEGRA_PIN_CAM_I2C_SDA_PBB2,
+};
+
+static const unsigned pbb3_pins[] = {
+ TEGRA_PIN_PBB3,
+};
+
+static const unsigned pbb4_pins[] = {
+ TEGRA_PIN_PBB4,
+};
+
+static const unsigned pbb5_pins[] = {
+ TEGRA_PIN_PBB5,
+};
+
+static const unsigned pbb6_pins[] = {
+ TEGRA_PIN_PBB6,
+};
+
+static const unsigned pbb7_pins[] = {
+ TEGRA_PIN_PBB7,
+};
+
+static const unsigned cam_mclk_pcc0_pins[] = {
+ TEGRA_PIN_CAM_MCLK_PCC0,
+};
+
+static const unsigned pcc1_pins[] = {
+ TEGRA_PIN_PCC1,
+};
+
+static const unsigned pcc2_pins[] = {
+ TEGRA_PIN_PCC2,
+};
+
+static const unsigned sdmmc4_rst_n_pcc3_pins[] = {
+ TEGRA_PIN_SDMMC4_RST_N_PCC3,
+};
+
+static const unsigned sdmmc4_clk_pcc4_pins[] = {
+ TEGRA_PIN_SDMMC4_CLK_PCC4,
+};
+
+static const unsigned clk2_req_pcc5_pins[] = {
+ TEGRA_PIN_CLK2_REQ_PCC5,
+};
+
+static const unsigned pex_l2_rst_n_pcc6_pins[] = {
+ TEGRA_PIN_PEX_L2_RST_N_PCC6,
+};
+
+static const unsigned pex_l2_clkreq_n_pcc7_pins[] = {
+ TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7,
+};
+
+static const unsigned pex_l0_prsnt_n_pdd0_pins[] = {
+ TEGRA_PIN_PEX_L0_PRSNT_N_PDD0,
+};
+
+static const unsigned pex_l0_rst_n_pdd1_pins[] = {
+ TEGRA_PIN_PEX_L0_RST_N_PDD1,
+};
+
+static const unsigned pex_l0_clkreq_n_pdd2_pins[] = {
+ TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2,
+};
+
+static const unsigned pex_wake_n_pdd3_pins[] = {
+ TEGRA_PIN_PEX_WAKE_N_PDD3,
+};
+
+static const unsigned pex_l1_prsnt_n_pdd4_pins[] = {
+ TEGRA_PIN_PEX_L1_PRSNT_N_PDD4,
+};
+
+static const unsigned pex_l1_rst_n_pdd5_pins[] = {
+ TEGRA_PIN_PEX_L1_RST_N_PDD5,
+};
+
+static const unsigned pex_l1_clkreq_n_pdd6_pins[] = {
+ TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6,
+};
+
+static const unsigned pex_l2_prsnt_n_pdd7_pins[] = {
+ TEGRA_PIN_PEX_L2_PRSNT_N_PDD7,
+};
+
+static const unsigned clk3_out_pee0_pins[] = {
+ TEGRA_PIN_CLK3_OUT_PEE0,
+};
+
+static const unsigned clk3_req_pee1_pins[] = {
+ TEGRA_PIN_CLK3_REQ_PEE1,
+};
+
+static const unsigned clk1_req_pee2_pins[] = {
+ TEGRA_PIN_CLK1_REQ_PEE2,
+};
+
+static const unsigned hdmi_cec_pee3_pins[] = {
+ TEGRA_PIN_HDMI_CEC_PEE3,
+};
+
+static const unsigned clk_32k_in_pins[] = {
+ TEGRA_PIN_CLK_32K_IN,
+};
+
+static const unsigned core_pwr_req_pins[] = {
+ TEGRA_PIN_CORE_PWR_REQ,
+};
+
+static const unsigned cpu_pwr_req_pins[] = {
+ TEGRA_PIN_CPU_PWR_REQ,
+};
+
+static const unsigned owr_pins[] = {
+ TEGRA_PIN_OWR,
+};
+
+static const unsigned pwr_int_n_pins[] = {
+ TEGRA_PIN_PWR_INT_N,
+};
+
+static const unsigned drive_ao1_pins[] = {
+ TEGRA_PIN_KB_ROW0_PR0,
+ TEGRA_PIN_KB_ROW1_PR1,
+ TEGRA_PIN_KB_ROW2_PR2,
+ TEGRA_PIN_KB_ROW3_PR3,
+ TEGRA_PIN_KB_ROW4_PR4,
+ TEGRA_PIN_KB_ROW5_PR5,
+ TEGRA_PIN_KB_ROW6_PR6,
+ TEGRA_PIN_KB_ROW7_PR7,
+ TEGRA_PIN_PWR_I2C_SCL_PZ6,
+ TEGRA_PIN_PWR_I2C_SDA_PZ7,
+ TEGRA_PIN_SYS_RESET_N,
+};
+
+static const unsigned drive_ao2_pins[] = {
+ TEGRA_PIN_CLK_32K_OUT_PA0,
+ TEGRA_PIN_KB_COL0_PQ0,
+ TEGRA_PIN_KB_COL1_PQ1,
+ TEGRA_PIN_KB_COL2_PQ2,
+ TEGRA_PIN_KB_COL3_PQ3,
+ TEGRA_PIN_KB_COL4_PQ4,
+ TEGRA_PIN_KB_COL5_PQ5,
+ TEGRA_PIN_KB_COL6_PQ6,
+ TEGRA_PIN_KB_COL7_PQ7,
+ TEGRA_PIN_KB_ROW8_PS0,
+ TEGRA_PIN_KB_ROW9_PS1,
+ TEGRA_PIN_KB_ROW10_PS2,
+ TEGRA_PIN_KB_ROW11_PS3,
+ TEGRA_PIN_KB_ROW12_PS4,
+ TEGRA_PIN_KB_ROW13_PS5,
+ TEGRA_PIN_KB_ROW14_PS6,
+ TEGRA_PIN_KB_ROW15_PS7,
+ TEGRA_PIN_SYS_CLK_REQ_PZ5,
+ TEGRA_PIN_CLK_32K_IN,
+ TEGRA_PIN_CORE_PWR_REQ,
+ TEGRA_PIN_CPU_PWR_REQ,
+ TEGRA_PIN_PWR_INT_N,
+};
+
+static const unsigned drive_at1_pins[] = {
+ TEGRA_PIN_GMI_AD8_PH0,
+ TEGRA_PIN_GMI_AD9_PH1,
+ TEGRA_PIN_GMI_AD10_PH2,
+ TEGRA_PIN_GMI_AD11_PH3,
+ TEGRA_PIN_GMI_AD12_PH4,
+ TEGRA_PIN_GMI_AD13_PH5,
+ TEGRA_PIN_GMI_AD14_PH6,
+ TEGRA_PIN_GMI_AD15_PH7,
+ TEGRA_PIN_GMI_IORDY_PI5,
+ TEGRA_PIN_GMI_CS7_N_PI6,
+};
+
+static const unsigned drive_at2_pins[] = {
+ TEGRA_PIN_GMI_AD0_PG0,
+ TEGRA_PIN_GMI_AD1_PG1,
+ TEGRA_PIN_GMI_AD2_PG2,
+ TEGRA_PIN_GMI_AD3_PG3,
+ TEGRA_PIN_GMI_AD4_PG4,
+ TEGRA_PIN_GMI_AD5_PG5,
+ TEGRA_PIN_GMI_AD6_PG6,
+ TEGRA_PIN_GMI_AD7_PG7,
+ TEGRA_PIN_GMI_WR_N_PI0,
+ TEGRA_PIN_GMI_OE_N_PI1,
+ TEGRA_PIN_GMI_DQS_PI2,
+ TEGRA_PIN_GMI_CS6_N_PI3,
+ TEGRA_PIN_GMI_RST_N_PI4,
+ TEGRA_PIN_GMI_WAIT_PI7,
+ TEGRA_PIN_GMI_ADV_N_PK0,
+ TEGRA_PIN_GMI_CLK_PK1,
+ TEGRA_PIN_GMI_CS4_N_PK2,
+ TEGRA_PIN_GMI_CS2_N_PK3,
+ TEGRA_PIN_GMI_CS3_N_PK4,
+};
+
+static const unsigned drive_at3_pins[] = {
+ TEGRA_PIN_GMI_WP_N_PC7,
+ TEGRA_PIN_GMI_CS0_N_PJ0,
+};
+
+static const unsigned drive_at4_pins[] = {
+ TEGRA_PIN_GMI_A17_PB0,
+ TEGRA_PIN_GMI_A18_PB1,
+ TEGRA_PIN_GMI_CS1_N_PJ2,
+ TEGRA_PIN_GMI_A16_PJ7,
+ TEGRA_PIN_GMI_A19_PK7,
+};
+
+static const unsigned drive_at5_pins[] = {
+ TEGRA_PIN_GEN2_I2C_SCL_PT5,
+ TEGRA_PIN_GEN2_I2C_SDA_PT6,
+};
+
+static const unsigned drive_cdev1_pins[] = {
+ TEGRA_PIN_CLK1_OUT_PW4,
+ TEGRA_PIN_CLK1_REQ_PEE2,
+};
+
+static const unsigned drive_cdev2_pins[] = {
+ TEGRA_PIN_CLK2_OUT_PW5,
+ TEGRA_PIN_CLK2_REQ_PCC5,
+};
+
+static const unsigned drive_cec_pins[] = {
+ TEGRA_PIN_HDMI_CEC_PEE3,
+};
+
+static const unsigned drive_crt_pins[] = {
+ TEGRA_PIN_CRT_HSYNC_PV6,
+ TEGRA_PIN_CRT_VSYNC_PV7,
+};
+
+static const unsigned drive_csus_pins[] = {
+ TEGRA_PIN_VI_MCLK_PT1,
+};
+
+static const unsigned drive_dap1_pins[] = {
+ TEGRA_PIN_SPDIF_OUT_PK5,
+ TEGRA_PIN_SPDIF_IN_PK6,
+ TEGRA_PIN_DAP1_FS_PN0,
+ TEGRA_PIN_DAP1_DIN_PN1,
+ TEGRA_PIN_DAP1_DOUT_PN2,
+ TEGRA_PIN_DAP1_SCLK_PN3,
+};
+
+static const unsigned drive_dap2_pins[] = {
+ TEGRA_PIN_DAP2_FS_PA2,
+ TEGRA_PIN_DAP2_SCLK_PA3,
+ TEGRA_PIN_DAP2_DIN_PA4,
+ TEGRA_PIN_DAP2_DOUT_PA5,
+};
+
+static const unsigned drive_dap3_pins[] = {
+ TEGRA_PIN_DAP3_FS_PP0,
+ TEGRA_PIN_DAP3_DIN_PP1,
+ TEGRA_PIN_DAP3_DOUT_PP2,
+ TEGRA_PIN_DAP3_SCLK_PP3,
+};
+
+static const unsigned drive_dap4_pins[] = {
+ TEGRA_PIN_DAP4_FS_PP4,
+ TEGRA_PIN_DAP4_DIN_PP5,
+ TEGRA_PIN_DAP4_DOUT_PP6,
+ TEGRA_PIN_DAP4_SCLK_PP7,
+};
+
+static const unsigned drive_dbg_pins[] = {
+ TEGRA_PIN_GEN1_I2C_SCL_PC4,
+ TEGRA_PIN_GEN1_I2C_SDA_PC5,
+ TEGRA_PIN_PU0,
+ TEGRA_PIN_PU1,
+ TEGRA_PIN_PU2,
+ TEGRA_PIN_PU3,
+ TEGRA_PIN_PU4,
+ TEGRA_PIN_PU5,
+ TEGRA_PIN_PU6,
+ TEGRA_PIN_JTAG_RTCK_PU7,
+ TEGRA_PIN_JTAG_TCK,
+ TEGRA_PIN_JTAG_TDI,
+ TEGRA_PIN_JTAG_TDO,
+ TEGRA_PIN_JTAG_TMS,
+ TEGRA_PIN_JTAG_TRST_N,
+ TEGRA_PIN_TEST_MODE_EN,
+};
+
+static const unsigned drive_ddc_pins[] = {
+ TEGRA_PIN_DDC_SCL_PV4,
+ TEGRA_PIN_DDC_SDA_PV5,
+};
+
+static const unsigned drive_dev3_pins[] = {
+ TEGRA_PIN_CLK3_OUT_PEE0,
+ TEGRA_PIN_CLK3_REQ_PEE1,
+};
+
+static const unsigned drive_gma_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT0_PAA0,
+ TEGRA_PIN_SDMMC4_DAT1_PAA1,
+ TEGRA_PIN_SDMMC4_DAT2_PAA2,
+ TEGRA_PIN_SDMMC4_DAT3_PAA3,
+ TEGRA_PIN_SDMMC4_RST_N_PCC3,
+};
+
+static const unsigned drive_gmb_pins[] = {
+ TEGRA_PIN_SDMMC4_DAT4_PAA4,
+ TEGRA_PIN_SDMMC4_DAT5_PAA5,
+ TEGRA_PIN_SDMMC4_DAT6_PAA6,
+ TEGRA_PIN_SDMMC4_DAT7_PAA7,
+};
+
+static const unsigned drive_gmc_pins[] = {
+ TEGRA_PIN_SDMMC4_CLK_PCC4,
+};
+
+static const unsigned drive_gmd_pins[] = {
+ TEGRA_PIN_SDMMC4_CMD_PT7,
+};
+
+static const unsigned drive_gme_pins[] = {
+ TEGRA_PIN_PBB0,
+ TEGRA_PIN_CAM_I2C_SCL_PBB1,
+ TEGRA_PIN_CAM_I2C_SDA_PBB2,
+ TEGRA_PIN_PBB3,
+ TEGRA_PIN_PCC2,
+};
+
+static const unsigned drive_gmf_pins[] = {
+ TEGRA_PIN_PBB4,
+ TEGRA_PIN_PBB5,
+ TEGRA_PIN_PBB6,
+ TEGRA_PIN_PBB7,
+};
+
+static const unsigned drive_gmg_pins[] = {
+ TEGRA_PIN_CAM_MCLK_PCC0,
+};
+
+static const unsigned drive_gmh_pins[] = {
+ TEGRA_PIN_PCC1,
+};
+
+static const unsigned drive_gpv_pins[] = {
+ TEGRA_PIN_PEX_L2_RST_N_PCC6,
+ TEGRA_PIN_PEX_L2_CLKREQ_N_PCC7,
+ TEGRA_PIN_PEX_L0_PRSNT_N_PDD0,
+ TEGRA_PIN_PEX_L0_RST_N_PDD1,
+ TEGRA_PIN_PEX_L0_CLKREQ_N_PDD2,
+ TEGRA_PIN_PEX_WAKE_N_PDD3,
+ TEGRA_PIN_PEX_L1_PRSNT_N_PDD4,
+ TEGRA_PIN_PEX_L1_RST_N_PDD5,
+ TEGRA_PIN_PEX_L1_CLKREQ_N_PDD6,
+ TEGRA_PIN_PEX_L2_PRSNT_N_PDD7,
+};
+
+static const unsigned drive_lcd1_pins[] = {
+ TEGRA_PIN_LCD_PWR1_PC1,
+ TEGRA_PIN_LCD_PWR2_PC6,
+ TEGRA_PIN_LCD_CS0_N_PN4,
+ TEGRA_PIN_LCD_SDOUT_PN5,
+ TEGRA_PIN_LCD_DC0_PN6,
+ TEGRA_PIN_LCD_SDIN_PZ2,
+ TEGRA_PIN_LCD_WR_N_PZ3,
+ TEGRA_PIN_LCD_SCK_PZ4,
+};
+
+static const unsigned drive_lcd2_pins[] = {
+ TEGRA_PIN_LCD_PWR0_PB2,
+ TEGRA_PIN_LCD_PCLK_PB3,
+ TEGRA_PIN_LCD_DC1_PD2,
+ TEGRA_PIN_LCD_D0_PE0,
+ TEGRA_PIN_LCD_D1_PE1,
+ TEGRA_PIN_LCD_D2_PE2,
+ TEGRA_PIN_LCD_D3_PE3,
+ TEGRA_PIN_LCD_D4_PE4,
+ TEGRA_PIN_LCD_D5_PE5,
+ TEGRA_PIN_LCD_D6_PE6,
+ TEGRA_PIN_LCD_D7_PE7,
+ TEGRA_PIN_LCD_D8_PF0,
+ TEGRA_PIN_LCD_D9_PF1,
+ TEGRA_PIN_LCD_D10_PF2,
+ TEGRA_PIN_LCD_D11_PF3,
+ TEGRA_PIN_LCD_D12_PF4,
+ TEGRA_PIN_LCD_D13_PF5,
+ TEGRA_PIN_LCD_D14_PF6,
+ TEGRA_PIN_LCD_D15_PF7,
+ TEGRA_PIN_LCD_DE_PJ1,
+ TEGRA_PIN_LCD_HSYNC_PJ3,
+ TEGRA_PIN_LCD_VSYNC_PJ4,
+ TEGRA_PIN_LCD_D16_PM0,
+ TEGRA_PIN_LCD_D17_PM1,
+ TEGRA_PIN_LCD_D18_PM2,
+ TEGRA_PIN_LCD_D19_PM3,
+ TEGRA_PIN_LCD_D20_PM4,
+ TEGRA_PIN_LCD_D21_PM5,
+ TEGRA_PIN_LCD_D22_PM6,
+ TEGRA_PIN_LCD_D23_PM7,
+ TEGRA_PIN_HDMI_INT_PN7,
+ TEGRA_PIN_LCD_CS1_N_PW0,
+ TEGRA_PIN_LCD_M1_PW1,
+};
+
+static const unsigned drive_owr_pins[] = {
+ TEGRA_PIN_OWR,
+};
+
+static const unsigned drive_sdio1_pins[] = {
+ TEGRA_PIN_SDMMC1_DAT3_PY4,
+ TEGRA_PIN_SDMMC1_DAT2_PY5,
+ TEGRA_PIN_SDMMC1_DAT1_PY6,
+ TEGRA_PIN_SDMMC1_DAT0_PY7,
+ TEGRA_PIN_SDMMC1_CLK_PZ0,
+ TEGRA_PIN_SDMMC1_CMD_PZ1,
+};
+
+static const unsigned drive_sdio2_pins[] = {
+ TEGRA_PIN_SDMMC3_DAT5_PD0,
+ TEGRA_PIN_SDMMC3_DAT4_PD1,
+ TEGRA_PIN_SDMMC3_DAT6_PD3,
+ TEGRA_PIN_SDMMC3_DAT7_PD4,
+};
+
+static const unsigned drive_sdio3_pins[] = {
+ TEGRA_PIN_SDMMC3_CLK_PA6,
+ TEGRA_PIN_SDMMC3_CMD_PA7,
+ TEGRA_PIN_SDMMC3_DAT3_PB4,
+ TEGRA_PIN_SDMMC3_DAT2_PB5,
+ TEGRA_PIN_SDMMC3_DAT1_PB6,
+ TEGRA_PIN_SDMMC3_DAT0_PB7,
+};
+
+static const unsigned drive_spi_pins[] = {
+ TEGRA_PIN_SPI2_CS1_N_PW2,
+ TEGRA_PIN_SPI2_CS2_N_PW3,
+ TEGRA_PIN_SPI2_MOSI_PX0,
+ TEGRA_PIN_SPI2_MISO_PX1,
+ TEGRA_PIN_SPI2_SCK_PX2,
+ TEGRA_PIN_SPI2_CS0_N_PX3,
+ TEGRA_PIN_SPI1_MOSI_PX4,
+ TEGRA_PIN_SPI1_SCK_PX5,
+ TEGRA_PIN_SPI1_CS0_N_PX6,
+ TEGRA_PIN_SPI1_MISO_PX7,
+};
+
+static const unsigned drive_uaa_pins[] = {
+ TEGRA_PIN_ULPI_DATA0_PO1,
+ TEGRA_PIN_ULPI_DATA1_PO2,
+ TEGRA_PIN_ULPI_DATA2_PO3,
+ TEGRA_PIN_ULPI_DATA3_PO4,
+};
+
+static const unsigned drive_uab_pins[] = {
+ TEGRA_PIN_ULPI_DATA7_PO0,
+ TEGRA_PIN_ULPI_DATA4_PO5,
+ TEGRA_PIN_ULPI_DATA5_PO6,
+ TEGRA_PIN_ULPI_DATA6_PO7,
+ TEGRA_PIN_PV0,
+ TEGRA_PIN_PV1,
+ TEGRA_PIN_PV2,
+ TEGRA_PIN_PV3,
+};
+
+static const unsigned drive_uart2_pins[] = {
+ TEGRA_PIN_UART2_TXD_PC2,
+ TEGRA_PIN_UART2_RXD_PC3,
+ TEGRA_PIN_UART2_CTS_N_PJ5,
+ TEGRA_PIN_UART2_RTS_N_PJ6,
+};
+
+static const unsigned drive_uart3_pins[] = {
+ TEGRA_PIN_UART3_CTS_N_PA1,
+ TEGRA_PIN_UART3_RTS_N_PC0,
+ TEGRA_PIN_UART3_TXD_PW6,
+ TEGRA_PIN_UART3_RXD_PW7,
+};
+
+static const unsigned drive_uda_pins[] = {
+ TEGRA_PIN_ULPI_CLK_PY0,
+ TEGRA_PIN_ULPI_DIR_PY1,
+ TEGRA_PIN_ULPI_NXT_PY2,
+ TEGRA_PIN_ULPI_STP_PY3,
+};
+
+static const unsigned drive_vi1_pins[] = {
+ TEGRA_PIN_VI_D1_PD5,
+ TEGRA_PIN_VI_VSYNC_PD6,
+ TEGRA_PIN_VI_HSYNC_PD7,
+ TEGRA_PIN_VI_D2_PL0,
+ TEGRA_PIN_VI_D3_PL1,
+ TEGRA_PIN_VI_D4_PL2,
+ TEGRA_PIN_VI_D5_PL3,
+ TEGRA_PIN_VI_D6_PL4,
+ TEGRA_PIN_VI_D7_PL5,
+ TEGRA_PIN_VI_D8_PL6,
+ TEGRA_PIN_VI_D9_PL7,
+ TEGRA_PIN_VI_PCLK_PT0,
+ TEGRA_PIN_VI_D10_PT2,
+ TEGRA_PIN_VI_D11_PT3,
+ TEGRA_PIN_VI_D0_PT4,
+};
+
+enum tegra_mux {
+ TEGRA_MUX_BLINK,
+ TEGRA_MUX_CEC,
+ TEGRA_MUX_CLK_12M_OUT,
+ TEGRA_MUX_CLK_32K_IN,
+ TEGRA_MUX_CORE_PWR_REQ,
+ TEGRA_MUX_CPU_PWR_REQ,
+ TEGRA_MUX_CRT,
+ TEGRA_MUX_DAP,
+ TEGRA_MUX_DDR,
+ TEGRA_MUX_DEV3,
+ TEGRA_MUX_DISPLAYA,
+ TEGRA_MUX_DISPLAYB,
+ TEGRA_MUX_DTV,
+ TEGRA_MUX_EXTPERIPH1,
+ TEGRA_MUX_EXTPERIPH2,
+ TEGRA_MUX_EXTPERIPH3,
+ TEGRA_MUX_GMI,
+ TEGRA_MUX_GMI_ALT,
+ TEGRA_MUX_HDA,
+ TEGRA_MUX_HDCP,
+ TEGRA_MUX_HDMI,
+ TEGRA_MUX_HSI,
+ TEGRA_MUX_I2C1,
+ TEGRA_MUX_I2C2,
+ TEGRA_MUX_I2C3,
+ TEGRA_MUX_I2C4,
+ TEGRA_MUX_I2CPWR,
+ TEGRA_MUX_I2S0,
+ TEGRA_MUX_I2S1,
+ TEGRA_MUX_I2S2,
+ TEGRA_MUX_I2S3,
+ TEGRA_MUX_I2S4,
+ TEGRA_MUX_INVALID,
+ TEGRA_MUX_KBC,
+ TEGRA_MUX_MIO,
+ TEGRA_MUX_NAND,
+ TEGRA_MUX_NAND_ALT,
+ TEGRA_MUX_OWR,
+ TEGRA_MUX_PCIE,
+ TEGRA_MUX_PWM0,
+ TEGRA_MUX_PWM1,
+ TEGRA_MUX_PWM2,
+ TEGRA_MUX_PWM3,
+ TEGRA_MUX_PWR_INT_N,
+ TEGRA_MUX_RSVD1,
+ TEGRA_MUX_RSVD2,
+ TEGRA_MUX_RSVD3,
+ TEGRA_MUX_RSVD4,
+ TEGRA_MUX_RTCK,
+ TEGRA_MUX_SATA,
+ TEGRA_MUX_SDMMC1,
+ TEGRA_MUX_SDMMC2,
+ TEGRA_MUX_SDMMC3,
+ TEGRA_MUX_SDMMC4,
+ TEGRA_MUX_SPDIF,
+ TEGRA_MUX_SPI1,
+ TEGRA_MUX_SPI2,
+ TEGRA_MUX_SPI2_ALT,
+ TEGRA_MUX_SPI3,
+ TEGRA_MUX_SPI4,
+ TEGRA_MUX_SPI5,
+ TEGRA_MUX_SPI6,
+ TEGRA_MUX_SYSCLK,
+ TEGRA_MUX_TEST,
+ TEGRA_MUX_TRACE,
+ TEGRA_MUX_UARTA,
+ TEGRA_MUX_UARTB,
+ TEGRA_MUX_UARTC,
+ TEGRA_MUX_UARTD,
+ TEGRA_MUX_UARTE,
+ TEGRA_MUX_ULPI,
+ TEGRA_MUX_VGP1,
+ TEGRA_MUX_VGP2,
+ TEGRA_MUX_VGP3,
+ TEGRA_MUX_VGP4,
+ TEGRA_MUX_VGP5,
+ TEGRA_MUX_VGP6,
+ TEGRA_MUX_VI,
+ TEGRA_MUX_VI_ALT1,
+ TEGRA_MUX_VI_ALT2,
+ TEGRA_MUX_VI_ALT3,
+};
+static const char * const blink_groups[] = {
+ "clk_32k_out_pa0",
+};
+
+static const char * const cec_groups[] = {
+ "hdmi_cec_pee3",
+ "owr",
+};
+
+static const char * const clk_12m_out_groups[] = {
+ "pv3",
+};
+
+static const char * const clk_32k_in_groups[] = {
+ "clk_32k_in",
+};
+
+static const char * const core_pwr_req_groups[] = {
+ "core_pwr_req",
+};
+
+static const char * const cpu_pwr_req_groups[] = {
+ "cpu_pwr_req",
+};
+
+static const char * const crt_groups[] = {
+ "crt_hsync_pv6",
+ "crt_vsync_pv7",
+};
+
+static const char * const dap_groups[] = {
+ "clk1_req_pee2",
+ "clk2_req_pcc5",
+};
+
+static const char * const ddr_groups[] = {
+ "vi_d0_pt4",
+ "vi_d1_pd5",
+ "vi_d10_pt2",
+ "vi_d11_pt3",
+ "vi_d2_pl0",
+ "vi_d3_pl1",
+ "vi_d4_pl2",
+ "vi_d5_pl3",
+ "vi_d6_pl4",
+ "vi_d7_pl5",
+ "vi_d8_pl6",
+ "vi_d9_pl7",
+ "vi_hsync_pd7",
+ "vi_vsync_pd6",
+};
+
+static const char * const dev3_groups[] = {
+ "clk3_req_pee1",
+};
+
+static const char * const displaya_groups[] = {
+ "dap3_din_pp1",
+ "dap3_dout_pp2",
+ "dap3_fs_pp0",
+ "dap3_sclk_pp3",
+ "pbb3",
+ "pbb4",
+ "pbb5",
+ "pbb6",
+ "lcd_cs0_n_pn4",
+ "lcd_cs1_n_pw0",
+ "lcd_d0_pe0",
+ "lcd_d1_pe1",
+ "lcd_d10_pf2",
+ "lcd_d11_pf3",
+ "lcd_d12_pf4",
+ "lcd_d13_pf5",
+ "lcd_d14_pf6",
+ "lcd_d15_pf7",
+ "lcd_d16_pm0",
+ "lcd_d17_pm1",
+ "lcd_d18_pm2",
+ "lcd_d19_pm3",
+ "lcd_d2_pe2",
+ "lcd_d20_pm4",
+ "lcd_d21_pm5",
+ "lcd_d22_pm6",
+ "lcd_d23_pm7",
+ "lcd_d3_pe3",
+ "lcd_d4_pe4",
+ "lcd_d5_pe5",
+ "lcd_d6_pe6",
+ "lcd_d7_pe7",
+ "lcd_d8_pf0",
+ "lcd_d9_pf1",
+ "lcd_dc0_pn6",
+ "lcd_dc1_pd2",
+ "lcd_de_pj1",
+ "lcd_hsync_pj3",
+ "lcd_m1_pw1",
+ "lcd_pclk_pb3",
+ "lcd_pwr0_pb2",
+ "lcd_pwr1_pc1",
+ "lcd_pwr2_pc6",
+ "lcd_sck_pz4",
+ "lcd_sdin_pz2",
+ "lcd_sdout_pn5",
+ "lcd_vsync_pj4",
+ "lcd_wr_n_pz3",
+};
+
+static const char * const displayb_groups[] = {
+ "dap3_din_pp1",
+ "dap3_dout_pp2",
+ "dap3_fs_pp0",
+ "dap3_sclk_pp3",
+ "pbb3",
+ "pbb4",
+ "pbb5",
+ "pbb6",
+ "lcd_cs0_n_pn4",
+ "lcd_cs1_n_pw0",
+ "lcd_d0_pe0",
+ "lcd_d1_pe1",
+ "lcd_d10_pf2",
+ "lcd_d11_pf3",
+ "lcd_d12_pf4",
+ "lcd_d13_pf5",
+ "lcd_d14_pf6",
+ "lcd_d15_pf7",
+ "lcd_d16_pm0",
+ "lcd_d17_pm1",
+ "lcd_d18_pm2",
+ "lcd_d19_pm3",
+ "lcd_d2_pe2",
+ "lcd_d20_pm4",
+ "lcd_d21_pm5",
+ "lcd_d22_pm6",
+ "lcd_d23_pm7",
+ "lcd_d3_pe3",
+ "lcd_d4_pe4",
+ "lcd_d5_pe5",
+ "lcd_d6_pe6",
+ "lcd_d7_pe7",
+ "lcd_d8_pf0",
+ "lcd_d9_pf1",
+ "lcd_dc0_pn6",
+ "lcd_dc1_pd2",
+ "lcd_de_pj1",
+ "lcd_hsync_pj3",
+ "lcd_m1_pw1",
+ "lcd_pclk_pb3",
+ "lcd_pwr0_pb2",
+ "lcd_pwr1_pc1",
+ "lcd_pwr2_pc6",
+ "lcd_sck_pz4",
+ "lcd_sdin_pz2",
+ "lcd_sdout_pn5",
+ "lcd_vsync_pj4",
+ "lcd_wr_n_pz3",
+};
+
+static const char * const dtv_groups[] = {
+ "gmi_a17_pb0",
+ "gmi_a18_pb1",
+ "gmi_cs0_n_pj0",
+ "gmi_cs1_n_pj2",
+};
+
+static const char * const extperiph1_groups[] = {
+ "clk1_out_pw4",
+};
+
+static const char * const extperiph2_groups[] = {
+ "clk2_out_pw5",
+};
+
+static const char * const extperiph3_groups[] = {
+ "clk3_out_pee0",
+};
+
+static const char * const gmi_groups[] = {
+ "dap1_din_pn1",
+ "dap1_dout_pn2",
+ "dap1_fs_pn0",
+ "dap1_sclk_pn3",
+ "dap2_din_pa4",
+ "dap2_dout_pa5",
+ "dap2_fs_pa2",
+ "dap2_sclk_pa3",
+ "dap4_din_pp5",
+ "dap4_dout_pp6",
+ "dap4_fs_pp4",
+ "dap4_sclk_pp7",
+ "gen2_i2c_scl_pt5",
+ "gen2_i2c_sda_pt6",
+ "gmi_a16_pj7",
+ "gmi_a17_pb0",
+ "gmi_a18_pb1",
+ "gmi_a19_pk7",
+ "gmi_ad0_pg0",
+ "gmi_ad1_pg1",
+ "gmi_ad10_ph2",
+ "gmi_ad11_ph3",
+ "gmi_ad12_ph4",
+ "gmi_ad13_ph5",
+ "gmi_ad14_ph6",
+ "gmi_ad15_ph7",
+ "gmi_ad2_pg2",
+ "gmi_ad3_pg3",
+ "gmi_ad4_pg4",
+ "gmi_ad5_pg5",
+ "gmi_ad6_pg6",
+ "gmi_ad7_pg7",
+ "gmi_ad8_ph0",
+ "gmi_ad9_ph1",
+ "gmi_adv_n_pk0",
+ "gmi_clk_pk1",
+ "gmi_cs0_n_pj0",
+ "gmi_cs1_n_pj2",
+ "gmi_cs2_n_pk3",
+ "gmi_cs3_n_pk4",
+ "gmi_cs4_n_pk2",
+ "gmi_cs6_n_pi3",
+ "gmi_cs7_n_pi6",
+ "gmi_dqs_pi2",
+ "gmi_iordy_pi5",
+ "gmi_oe_n_pi1",
+ "gmi_rst_n_pi4",
+ "gmi_wait_pi7",
+ "gmi_wp_n_pc7",
+ "gmi_wr_n_pi0",
+ "pu0",
+ "pu1",
+ "pu2",
+ "pu3",
+ "pu4",
+ "pu5",
+ "pu6",
+ "sdmmc4_clk_pcc4",
+ "sdmmc4_cmd_pt7",
+ "sdmmc4_dat0_paa0",
+ "sdmmc4_dat1_paa1",
+ "sdmmc4_dat2_paa2",
+ "sdmmc4_dat3_paa3",
+ "sdmmc4_dat4_paa4",
+ "sdmmc4_dat5_paa5",
+ "sdmmc4_dat6_paa6",
+ "sdmmc4_dat7_paa7",
+ "spi1_cs0_n_px6",
+ "spi1_mosi_px4",
+ "spi1_sck_px5",
+ "spi2_cs0_n_px3",
+ "spi2_miso_px1",
+ "spi2_mosi_px0",
+ "spi2_sck_px2",
+ "uart2_cts_n_pj5",
+ "uart2_rts_n_pj6",
+ "uart3_cts_n_pa1",
+ "uart3_rts_n_pc0",
+ "uart3_rxd_pw7",
+ "uart3_txd_pw6",
+};
+
+static const char * const gmi_alt_groups[] = {
+ "gmi_a16_pj7",
+ "gmi_cs3_n_pk4",
+ "gmi_cs7_n_pi6",
+ "gmi_wp_n_pc7",
+};
+
+static const char * const hda_groups[] = {
+ "clk1_req_pee2",
+ "dap1_din_pn1",
+ "dap1_dout_pn2",
+ "dap1_fs_pn0",
+ "dap1_sclk_pn3",
+ "dap2_din_pa4",
+ "dap2_dout_pa5",
+ "dap2_fs_pa2",
+ "dap2_sclk_pa3",
+ "pex_l0_clkreq_n_pdd2",
+ "pex_l0_prsnt_n_pdd0",
+ "pex_l0_rst_n_pdd1",
+ "pex_l1_clkreq_n_pdd6",
+ "pex_l1_prsnt_n_pdd4",
+ "pex_l1_rst_n_pdd5",
+ "pex_l2_clkreq_n_pcc7",
+ "pex_l2_prsnt_n_pdd7",
+ "pex_l2_rst_n_pcc6",
+ "pex_wake_n_pdd3",
+ "spdif_in_pk6",
+};
+
+static const char * const hdcp_groups[] = {
+ "gen2_i2c_scl_pt5",
+ "gen2_i2c_sda_pt6",
+ "lcd_pwr0_pb2",
+ "lcd_pwr2_pc6",
+ "lcd_sck_pz4",
+ "lcd_sdout_pn5",
+ "lcd_wr_n_pz3",
+};
+
+static const char * const hdmi_groups[] = {
+ "hdmi_int_pn7",
+};
+
+static const char * const hsi_groups[] = {
+ "ulpi_data0_po1",
+ "ulpi_data1_po2",
+ "ulpi_data2_po3",
+ "ulpi_data3_po4",
+ "ulpi_data4_po5",
+ "ulpi_data5_po6",
+ "ulpi_data6_po7",
+ "ulpi_data7_po0",
+};
+
+static const char * const i2c1_groups[] = {
+ "gen1_i2c_scl_pc4",
+ "gen1_i2c_sda_pc5",
+ "spdif_in_pk6",
+ "spdif_out_pk5",
+ "spi2_cs1_n_pw2",
+ "spi2_cs2_n_pw3",
+};
+
+static const char * const i2c2_groups[] = {
+ "gen2_i2c_scl_pt5",
+ "gen2_i2c_sda_pt6",
+};
+
+static const char * const i2c3_groups[] = {
+ "cam_i2c_scl_pbb1",
+ "cam_i2c_sda_pbb2",
+ "sdmmc4_cmd_pt7",
+ "sdmmc4_dat4_paa4",
+};
+
+static const char * const i2c4_groups[] = {
+ "ddc_scl_pv4",
+ "ddc_sda_pv5",
+};
+
+static const char * const i2cpwr_groups[] = {
+ "pwr_i2c_scl_pz6",
+ "pwr_i2c_sda_pz7",
+};
+
+static const char * const i2s0_groups[] = {
+ "dap1_din_pn1",
+ "dap1_dout_pn2",
+ "dap1_fs_pn0",
+ "dap1_sclk_pn3",
+};
+
+static const char * const i2s1_groups[] = {
+ "dap2_din_pa4",
+ "dap2_dout_pa5",
+ "dap2_fs_pa2",
+ "dap2_sclk_pa3",
+};
+
+static const char * const i2s2_groups[] = {
+ "dap3_din_pp1",
+ "dap3_dout_pp2",
+ "dap3_fs_pp0",
+ "dap3_sclk_pp3",
+};
+
+static const char * const i2s3_groups[] = {
+ "dap4_din_pp5",
+ "dap4_dout_pp6",
+ "dap4_fs_pp4",
+ "dap4_sclk_pp7",
+};
+
+static const char * const i2s4_groups[] = {
+ "pbb0",
+ "pbb7",
+ "pcc1",
+ "pcc2",
+ "sdmmc4_dat4_paa4",
+ "sdmmc4_dat5_paa5",
+ "sdmmc4_dat6_paa6",
+ "sdmmc4_dat7_paa7",
+};
+
+static const char * const invalid_groups[] = {
+ "kb_row3_pr3",
+ "sdmmc4_clk_pcc4",
+};
+
+static const char * const kbc_groups[] = {
+ "kb_col0_pq0",
+ "kb_col1_pq1",
+ "kb_col2_pq2",
+ "kb_col3_pq3",
+ "kb_col4_pq4",
+ "kb_col5_pq5",
+ "kb_col6_pq6",
+ "kb_col7_pq7",
+ "kb_row0_pr0",
+ "kb_row1_pr1",
+ "kb_row10_ps2",
+ "kb_row11_ps3",
+ "kb_row12_ps4",
+ "kb_row13_ps5",
+ "kb_row14_ps6",
+ "kb_row15_ps7",
+ "kb_row2_pr2",
+ "kb_row3_pr3",
+ "kb_row4_pr4",
+ "kb_row5_pr5",
+ "kb_row6_pr6",
+ "kb_row7_pr7",
+ "kb_row8_ps0",
+ "kb_row9_ps1",
+};
+
+static const char * const mio_groups[] = {
+ "kb_col6_pq6",
+ "kb_col7_pq7",
+ "kb_row10_ps2",
+ "kb_row11_ps3",
+ "kb_row12_ps4",
+ "kb_row13_ps5",
+ "kb_row14_ps6",
+ "kb_row15_ps7",
+ "kb_row6_pr6",
+ "kb_row7_pr7",
+ "kb_row8_ps0",
+ "kb_row9_ps1",
+};
+
+static const char * const nand_groups[] = {
+ "gmi_ad0_pg0",
+ "gmi_ad1_pg1",
+ "gmi_ad10_ph2",
+ "gmi_ad11_ph3",
+ "gmi_ad12_ph4",
+ "gmi_ad13_ph5",
+ "gmi_ad14_ph6",
+ "gmi_ad15_ph7",
+ "gmi_ad2_pg2",
+ "gmi_ad3_pg3",
+ "gmi_ad4_pg4",
+ "gmi_ad5_pg5",
+ "gmi_ad6_pg6",
+ "gmi_ad7_pg7",
+ "gmi_ad8_ph0",
+ "gmi_ad9_ph1",
+ "gmi_adv_n_pk0",
+ "gmi_clk_pk1",
+ "gmi_cs0_n_pj0",
+ "gmi_cs1_n_pj2",
+ "gmi_cs2_n_pk3",
+ "gmi_cs3_n_pk4",
+ "gmi_cs4_n_pk2",
+ "gmi_cs6_n_pi3",
+ "gmi_cs7_n_pi6",
+ "gmi_dqs_pi2",
+ "gmi_iordy_pi5",
+ "gmi_oe_n_pi1",
+ "gmi_rst_n_pi4",
+ "gmi_wait_pi7",
+ "gmi_wp_n_pc7",
+ "gmi_wr_n_pi0",
+ "kb_col0_pq0",
+ "kb_col1_pq1",
+ "kb_col2_pq2",
+ "kb_col3_pq3",
+ "kb_col4_pq4",
+ "kb_col5_pq5",
+ "kb_col6_pq6",
+ "kb_col7_pq7",
+ "kb_row0_pr0",
+ "kb_row1_pr1",
+ "kb_row10_ps2",
+ "kb_row11_ps3",
+ "kb_row12_ps4",
+ "kb_row13_ps5",
+ "kb_row14_ps6",
+ "kb_row15_ps7",
+ "kb_row2_pr2",
+ "kb_row3_pr3",
+ "kb_row4_pr4",
+ "kb_row5_pr5",
+ "kb_row6_pr6",
+ "kb_row7_pr7",
+ "kb_row8_ps0",
+ "kb_row9_ps1",
+ "sdmmc4_clk_pcc4",
+ "sdmmc4_cmd_pt7",
+};
+
+static const char * const nand_alt_groups[] = {
+ "gmi_cs6_n_pi3",
+ "gmi_cs7_n_pi6",
+ "gmi_rst_n_pi4",
+};
+
+static const char * const owr_groups[] = {
+ "pu0",
+ "pv2",
+ "kb_row5_pr5",
+ "owr",
+};
+
+static const char * const pcie_groups[] = {
+ "pex_l0_clkreq_n_pdd2",
+ "pex_l0_prsnt_n_pdd0",
+ "pex_l0_rst_n_pdd1",
+ "pex_l1_clkreq_n_pdd6",
+ "pex_l1_prsnt_n_pdd4",
+ "pex_l1_rst_n_pdd5",
+ "pex_l2_clkreq_n_pcc7",
+ "pex_l2_prsnt_n_pdd7",
+ "pex_l2_rst_n_pcc6",
+ "pex_wake_n_pdd3",
+};
+
+static const char * const pwm0_groups[] = {
+ "gmi_ad8_ph0",
+ "pu3",
+ "sdmmc3_dat3_pb4",
+ "sdmmc3_dat5_pd0",
+ "uart3_rts_n_pc0",
+};
+
+static const char * const pwm1_groups[] = {
+ "gmi_ad9_ph1",
+ "pu4",
+ "sdmmc3_dat2_pb5",
+ "sdmmc3_dat4_pd1",
+};
+
+static const char * const pwm2_groups[] = {
+ "gmi_ad10_ph2",
+ "pu5",
+ "sdmmc3_clk_pa6",
+};
+
+static const char * const pwm3_groups[] = {
+ "gmi_ad11_ph3",
+ "pu6",
+ "sdmmc3_cmd_pa7",
+};
+
+static const char * const pwr_int_n_groups[] = {
+ "pwr_int_n",
+};
+
+static const char * const rsvd1_groups[] = {
+ "gmi_ad0_pg0",
+ "gmi_ad1_pg1",
+ "gmi_ad12_ph4",
+ "gmi_ad13_ph5",
+ "gmi_ad14_ph6",
+ "gmi_ad15_ph7",
+ "gmi_ad2_pg2",
+ "gmi_ad3_pg3",
+ "gmi_ad4_pg4",
+ "gmi_ad5_pg5",
+ "gmi_ad6_pg6",
+ "gmi_ad7_pg7",
+ "gmi_adv_n_pk0",
+ "gmi_clk_pk1",
+ "gmi_cs0_n_pj0",
+ "gmi_cs1_n_pj2",
+ "gmi_cs2_n_pk3",
+ "gmi_cs3_n_pk4",
+ "gmi_cs4_n_pk2",
+ "gmi_dqs_pi2",
+ "gmi_iordy_pi5",
+ "gmi_oe_n_pi1",
+ "gmi_wait_pi7",
+ "gmi_wp_n_pc7",
+ "gmi_wr_n_pi0",
+ "pu1",
+ "pu2",
+ "pv0",
+ "pv1",
+ "sdmmc3_dat0_pb7",
+ "sdmmc3_dat1_pb6",
+ "sdmmc3_dat2_pb5",
+ "sdmmc3_dat3_pb4",
+ "vi_pclk_pt0",
+};
+
+static const char * const rsvd2_groups[] = {
+ "clk1_out_pw4",
+ "clk2_out_pw5",
+ "clk2_req_pcc5",
+ "clk3_out_pee0",
+ "clk3_req_pee1",
+ "clk_32k_in",
+ "clk_32k_out_pa0",
+ "core_pwr_req",
+ "cpu_pwr_req",
+ "crt_hsync_pv6",
+ "crt_vsync_pv7",
+ "dap3_din_pp1",
+ "dap3_dout_pp2",
+ "dap3_fs_pp0",
+ "dap3_sclk_pp3",
+ "dap4_din_pp5",
+ "dap4_dout_pp6",
+ "dap4_fs_pp4",
+ "dap4_sclk_pp7",
+ "ddc_scl_pv4",
+ "ddc_sda_pv5",
+ "gen1_i2c_scl_pc4",
+ "gen1_i2c_sda_pc5",
+ "pbb0",
+ "pbb7",
+ "pcc1",
+ "pcc2",
+ "pv0",
+ "pv1",
+ "pv2",
+ "pv3",
+ "hdmi_cec_pee3",
+ "hdmi_int_pn7",
+ "jtag_rtck_pu7",
+ "pwr_i2c_scl_pz6",
+ "pwr_i2c_sda_pz7",
+ "pwr_int_n",
+ "sdmmc1_clk_pz0",
+ "sdmmc1_cmd_pz1",
+ "sdmmc1_dat0_py7",
+ "sdmmc1_dat1_py6",
+ "sdmmc1_dat2_py5",
+ "sdmmc1_dat3_py4",
+ "sdmmc3_dat0_pb7",
+ "sdmmc3_dat1_pb6",
+ "sdmmc4_rst_n_pcc3",
+ "spdif_out_pk5",
+ "sys_clk_req_pz5",
+ "uart3_cts_n_pa1",
+ "uart3_rxd_pw7",
+ "uart3_txd_pw6",
+ "ulpi_clk_py0",
+ "ulpi_dir_py1",
+ "ulpi_nxt_py2",
+ "ulpi_stp_py3",
+ "vi_d0_pt4",
+ "vi_d10_pt2",
+ "vi_d11_pt3",
+ "vi_hsync_pd7",
+ "vi_vsync_pd6",
+};
+
+static const char * const rsvd3_groups[] = {
+ "cam_i2c_scl_pbb1",
+ "cam_i2c_sda_pbb2",
+ "clk1_out_pw4",
+ "clk1_req_pee2",
+ "clk2_out_pw5",
+ "clk2_req_pcc5",
+ "clk3_out_pee0",
+ "clk3_req_pee1",
+ "clk_32k_in",
+ "clk_32k_out_pa0",
+ "core_pwr_req",
+ "cpu_pwr_req",
+ "crt_hsync_pv6",
+ "crt_vsync_pv7",
+ "dap2_din_pa4",
+ "dap2_dout_pa5",
+ "dap2_fs_pa2",
+ "dap2_sclk_pa3",
+ "ddc_scl_pv4",
+ "ddc_sda_pv5",
+ "gen1_i2c_scl_pc4",
+ "gen1_i2c_sda_pc5",
+ "pbb0",
+ "pbb7",
+ "pcc1",
+ "pcc2",
+ "pv0",
+ "pv1",
+ "pv2",
+ "pv3",
+ "hdmi_cec_pee3",
+ "hdmi_int_pn7",
+ "jtag_rtck_pu7",
+ "kb_row0_pr0",
+ "kb_row1_pr1",
+ "kb_row2_pr2",
+ "kb_row3_pr3",
+ "lcd_d0_pe0",
+ "lcd_d1_pe1",
+ "lcd_d10_pf2",
+ "lcd_d11_pf3",
+ "lcd_d12_pf4",
+ "lcd_d13_pf5",
+ "lcd_d14_pf6",
+ "lcd_d15_pf7",
+ "lcd_d16_pm0",
+ "lcd_d17_pm1",
+ "lcd_d18_pm2",
+ "lcd_d19_pm3",
+ "lcd_d2_pe2",
+ "lcd_d20_pm4",
+ "lcd_d21_pm5",
+ "lcd_d22_pm6",
+ "lcd_d23_pm7",
+ "lcd_d3_pe3",
+ "lcd_d4_pe4",
+ "lcd_d5_pe5",
+ "lcd_d6_pe6",
+ "lcd_d7_pe7",
+ "lcd_d8_pf0",
+ "lcd_d9_pf1",
+ "lcd_dc0_pn6",
+ "lcd_dc1_pd2",
+ "lcd_de_pj1",
+ "lcd_hsync_pj3",
+ "lcd_m1_pw1",
+ "lcd_pclk_pb3",
+ "lcd_pwr1_pc1",
+ "lcd_vsync_pj4",
+ "owr",
+ "pex_l0_clkreq_n_pdd2",
+ "pex_l0_prsnt_n_pdd0",
+ "pex_l0_rst_n_pdd1",
+ "pex_l1_clkreq_n_pdd6",
+ "pex_l1_prsnt_n_pdd4",
+ "pex_l1_rst_n_pdd5",
+ "pex_l2_clkreq_n_pcc7",
+ "pex_l2_prsnt_n_pdd7",
+ "pex_l2_rst_n_pcc6",
+ "pex_wake_n_pdd3",
+ "pwr_i2c_scl_pz6",
+ "pwr_i2c_sda_pz7",
+ "pwr_int_n",
+ "sdmmc1_clk_pz0",
+ "sdmmc1_cmd_pz1",
+ "sdmmc4_rst_n_pcc3",
+ "sys_clk_req_pz5",
+};
+
+static const char * const rsvd4_groups[] = {
+ "clk1_out_pw4",
+ "clk1_req_pee2",
+ "clk2_out_pw5",
+ "clk2_req_pcc5",
+ "clk3_out_pee0",
+ "clk3_req_pee1",
+ "clk_32k_in",
+ "clk_32k_out_pa0",
+ "core_pwr_req",
+ "cpu_pwr_req",
+ "crt_hsync_pv6",
+ "crt_vsync_pv7",
+ "dap4_din_pp5",
+ "dap4_dout_pp6",
+ "dap4_fs_pp4",
+ "dap4_sclk_pp7",
+ "ddc_scl_pv4",
+ "ddc_sda_pv5",
+ "gen1_i2c_scl_pc4",
+ "gen1_i2c_sda_pc5",
+ "gen2_i2c_scl_pt5",
+ "gen2_i2c_sda_pt6",
+ "gmi_a19_pk7",
+ "gmi_ad0_pg0",
+ "gmi_ad1_pg1",
+ "gmi_ad10_ph2",
+ "gmi_ad11_ph3",
+ "gmi_ad12_ph4",
+ "gmi_ad13_ph5",
+ "gmi_ad14_ph6",
+ "gmi_ad15_ph7",
+ "gmi_ad2_pg2",
+ "gmi_ad3_pg3",
+ "gmi_ad4_pg4",
+ "gmi_ad5_pg5",
+ "gmi_ad6_pg6",
+ "gmi_ad7_pg7",
+ "gmi_ad8_ph0",
+ "gmi_ad9_ph1",
+ "gmi_adv_n_pk0",
+ "gmi_clk_pk1",
+ "gmi_cs2_n_pk3",
+ "gmi_cs4_n_pk2",
+ "gmi_dqs_pi2",
+ "gmi_iordy_pi5",
+ "gmi_oe_n_pi1",
+ "gmi_rst_n_pi4",
+ "gmi_wait_pi7",
+ "gmi_wr_n_pi0",
+ "pcc2",
+ "pu0",
+ "pu1",
+ "pu2",
+ "pu3",
+ "pu4",
+ "pu5",
+ "pu6",
+ "pv0",
+ "pv1",
+ "pv2",
+ "pv3",
+ "hdmi_cec_pee3",
+ "hdmi_int_pn7",
+ "jtag_rtck_pu7",
+ "kb_col2_pq2",
+ "kb_col3_pq3",
+ "kb_col4_pq4",
+ "kb_col5_pq5",
+ "kb_row0_pr0",
+ "kb_row1_pr1",
+ "kb_row2_pr2",
+ "kb_row4_pr4",
+ "lcd_cs0_n_pn4",
+ "lcd_cs1_n_pw0",
+ "lcd_d0_pe0",
+ "lcd_d1_pe1",
+ "lcd_d10_pf2",
+ "lcd_d11_pf3",
+ "lcd_d12_pf4",
+ "lcd_d13_pf5",
+ "lcd_d14_pf6",
+ "lcd_d15_pf7",
+ "lcd_d16_pm0",
+ "lcd_d17_pm1",
+ "lcd_d18_pm2",
+ "lcd_d19_pm3",
+ "lcd_d2_pe2",
+ "lcd_d20_pm4",
+ "lcd_d21_pm5",
+ "lcd_d22_pm6",
+ "lcd_d23_pm7",
+ "lcd_d3_pe3",
+ "lcd_d4_pe4",
+ "lcd_d5_pe5",
+ "lcd_d6_pe6",
+ "lcd_d7_pe7",
+ "lcd_d8_pf0",
+ "lcd_d9_pf1",
+ "lcd_dc0_pn6",
+ "lcd_dc1_pd2",
+ "lcd_de_pj1",
+ "lcd_hsync_pj3",
+ "lcd_m1_pw1",
+ "lcd_pclk_pb3",
+ "lcd_pwr1_pc1",
+ "lcd_sdin_pz2",
+ "lcd_vsync_pj4",
+ "owr",
+ "pex_l0_clkreq_n_pdd2",
+ "pex_l0_prsnt_n_pdd0",
+ "pex_l0_rst_n_pdd1",
+ "pex_l1_clkreq_n_pdd6",
+ "pex_l1_prsnt_n_pdd4",
+ "pex_l1_rst_n_pdd5",
+ "pex_l2_clkreq_n_pcc7",
+ "pex_l2_prsnt_n_pdd7",
+ "pex_l2_rst_n_pcc6",
+ "pex_wake_n_pdd3",
+ "pwr_i2c_scl_pz6",
+ "pwr_i2c_sda_pz7",
+ "pwr_int_n",
+ "spi1_miso_px7",
+ "sys_clk_req_pz5",
+ "uart3_cts_n_pa1",
+ "uart3_rts_n_pc0",
+ "uart3_rxd_pw7",
+ "uart3_txd_pw6",
+ "vi_d0_pt4",
+ "vi_d1_pd5",
+ "vi_d10_pt2",
+ "vi_d11_pt3",
+ "vi_d2_pl0",
+ "vi_d3_pl1",
+ "vi_d4_pl2",
+ "vi_d5_pl3",
+ "vi_d6_pl4",
+ "vi_d7_pl5",
+ "vi_d8_pl6",
+ "vi_d9_pl7",
+ "vi_hsync_pd7",
+ "vi_pclk_pt0",
+ "vi_vsync_pd6",
+};
+
+static const char * const rtck_groups[] = {
+ "jtag_rtck_pu7",
+};
+
+static const char * const sata_groups[] = {
+ "gmi_cs6_n_pi3",
+};
+
+static const char * const sdmmc1_groups[] = {
+ "sdmmc1_clk_pz0",
+ "sdmmc1_cmd_pz1",
+ "sdmmc1_dat0_py7",
+ "sdmmc1_dat1_py6",
+ "sdmmc1_dat2_py5",
+ "sdmmc1_dat3_py4",
+};
+
+static const char * const sdmmc2_groups[] = {
+ "dap1_din_pn1",
+ "dap1_dout_pn2",
+ "dap1_fs_pn0",
+ "dap1_sclk_pn3",
+ "kb_row10_ps2",
+ "kb_row11_ps3",
+ "kb_row12_ps4",
+ "kb_row13_ps5",
+ "kb_row14_ps6",
+ "kb_row15_ps7",
+ "kb_row6_pr6",
+ "kb_row7_pr7",
+ "kb_row8_ps0",
+ "kb_row9_ps1",
+ "spdif_in_pk6",
+ "spdif_out_pk5",
+ "vi_d1_pd5",
+ "vi_d2_pl0",
+ "vi_d3_pl1",
+ "vi_d4_pl2",
+ "vi_d5_pl3",
+ "vi_d6_pl4",
+ "vi_d7_pl5",
+ "vi_d8_pl6",
+ "vi_d9_pl7",
+ "vi_pclk_pt0",
+};
+
+static const char * const sdmmc3_groups[] = {
+ "sdmmc3_clk_pa6",
+ "sdmmc3_cmd_pa7",
+ "sdmmc3_dat0_pb7",
+ "sdmmc3_dat1_pb6",
+ "sdmmc3_dat2_pb5",
+ "sdmmc3_dat3_pb4",
+ "sdmmc3_dat4_pd1",
+ "sdmmc3_dat5_pd0",
+ "sdmmc3_dat6_pd3",
+ "sdmmc3_dat7_pd4",
+};
+
+static const char * const sdmmc4_groups[] = {
+ "cam_i2c_scl_pbb1",
+ "cam_i2c_sda_pbb2",
+ "cam_mclk_pcc0",
+ "pbb0",
+ "pbb3",
+ "pbb4",
+ "pbb5",
+ "pbb6",
+ "pbb7",
+ "pcc1",
+ "sdmmc4_clk_pcc4",
+ "sdmmc4_cmd_pt7",
+ "sdmmc4_dat0_paa0",
+ "sdmmc4_dat1_paa1",
+ "sdmmc4_dat2_paa2",
+ "sdmmc4_dat3_paa3",
+ "sdmmc4_dat4_paa4",
+ "sdmmc4_dat5_paa5",
+ "sdmmc4_dat6_paa6",
+ "sdmmc4_dat7_paa7",
+ "sdmmc4_rst_n_pcc3",
+};
+
+static const char * const spdif_groups[] = {
+ "sdmmc3_dat6_pd3",
+ "sdmmc3_dat7_pd4",
+ "spdif_in_pk6",
+ "spdif_out_pk5",
+ "uart2_rxd_pc3",
+ "uart2_txd_pc2",
+};
+
+static const char * const spi1_groups[] = {
+ "spi1_cs0_n_px6",
+ "spi1_miso_px7",
+ "spi1_mosi_px4",
+ "spi1_sck_px5",
+ "ulpi_clk_py0",
+ "ulpi_dir_py1",
+ "ulpi_nxt_py2",
+ "ulpi_stp_py3",
+};
+
+static const char * const spi2_groups[] = {
+ "sdmmc3_cmd_pa7",
+ "sdmmc3_dat4_pd1",
+ "sdmmc3_dat5_pd0",
+ "sdmmc3_dat6_pd3",
+ "sdmmc3_dat7_pd4",
+ "spi1_cs0_n_px6",
+ "spi1_mosi_px4",
+ "spi1_sck_px5",
+ "spi2_cs0_n_px3",
+ "spi2_cs1_n_pw2",
+ "spi2_cs2_n_pw3",
+ "spi2_miso_px1",
+ "spi2_mosi_px0",
+ "spi2_sck_px2",
+ "ulpi_data4_po5",
+ "ulpi_data5_po6",
+ "ulpi_data6_po7",
+ "ulpi_data7_po0",
+};
+
+static const char * const spi2_alt_groups[] = {
+ "spi1_cs0_n_px6",
+ "spi1_miso_px7",
+ "spi1_mosi_px4",
+ "spi1_sck_px5",
+ "spi2_cs1_n_pw2",
+ "spi2_cs2_n_pw3",
+};
+
+static const char * const spi3_groups[] = {
+ "sdmmc3_clk_pa6",
+ "sdmmc3_dat0_pb7",
+ "sdmmc3_dat1_pb6",
+ "sdmmc3_dat2_pb5",
+ "sdmmc3_dat3_pb4",
+ "sdmmc4_dat0_paa0",
+ "sdmmc4_dat1_paa1",
+ "sdmmc4_dat2_paa2",
+ "sdmmc4_dat3_paa3",
+ "spi1_miso_px7",
+ "spi2_cs0_n_px3",
+ "spi2_cs1_n_pw2",
+ "spi2_cs2_n_pw3",
+ "spi2_miso_px1",
+ "spi2_mosi_px0",
+ "spi2_sck_px2",
+ "ulpi_data0_po1",
+ "ulpi_data1_po2",
+ "ulpi_data2_po3",
+ "ulpi_data3_po4",
+};
+
+static const char * const spi4_groups[] = {
+ "gmi_a16_pj7",
+ "gmi_a17_pb0",
+ "gmi_a18_pb1",
+ "gmi_a19_pk7",
+ "sdmmc3_dat4_pd1",
+ "sdmmc3_dat5_pd0",
+ "sdmmc3_dat6_pd3",
+ "sdmmc3_dat7_pd4",
+ "uart2_cts_n_pj5",
+ "uart2_rts_n_pj6",
+ "uart2_rxd_pc3",
+ "uart2_txd_pc2",
+};
+
+static const char * const spi5_groups[] = {
+ "lcd_cs0_n_pn4",
+ "lcd_cs1_n_pw0",
+ "lcd_pwr0_pb2",
+ "lcd_pwr2_pc6",
+ "lcd_sck_pz4",
+ "lcd_sdin_pz2",
+ "lcd_sdout_pn5",
+ "lcd_wr_n_pz3",
+};
+
+static const char * const spi6_groups[] = {
+ "spi2_cs0_n_px3",
+ "spi2_miso_px1",
+ "spi2_mosi_px0",
+ "spi2_sck_px2",
+};
+
+static const char * const sysclk_groups[] = {
+ "sys_clk_req_pz5",
+};
+
+static const char * const test_groups[] = {
+ "kb_col0_pq0",
+ "kb_col1_pq1",
+};
+
+static const char * const trace_groups[] = {
+ "kb_col0_pq0",
+ "kb_col1_pq1",
+ "kb_col2_pq2",
+ "kb_col3_pq3",
+ "kb_col4_pq4",
+ "kb_col5_pq5",
+ "kb_col6_pq6",
+ "kb_col7_pq7",
+ "kb_row4_pr4",
+ "kb_row5_pr5",
+};
+
+static const char * const uarta_groups[] = {
+ "pu0",
+ "pu1",
+ "pu2",
+ "pu3",
+ "pu4",
+ "pu5",
+ "pu6",
+ "sdmmc1_clk_pz0",
+ "sdmmc1_cmd_pz1",
+ "sdmmc1_dat0_py7",
+ "sdmmc1_dat1_py6",
+ "sdmmc1_dat2_py5",
+ "sdmmc1_dat3_py4",
+ "sdmmc3_clk_pa6",
+ "sdmmc3_cmd_pa7",
+ "uart2_cts_n_pj5",
+ "uart2_rts_n_pj6",
+ "uart2_rxd_pc3",
+ "uart2_txd_pc2",
+ "ulpi_data0_po1",
+ "ulpi_data1_po2",
+ "ulpi_data2_po3",
+ "ulpi_data3_po4",
+ "ulpi_data4_po5",
+ "ulpi_data5_po6",
+ "ulpi_data6_po7",
+ "ulpi_data7_po0",
+};
+
+static const char * const uartb_groups[] = {
+ "uart2_cts_n_pj5",
+ "uart2_rts_n_pj6",
+ "uart2_rxd_pc3",
+ "uart2_txd_pc2",
+};
+
+static const char * const uartc_groups[] = {
+ "uart3_cts_n_pa1",
+ "uart3_rts_n_pc0",
+ "uart3_rxd_pw7",
+ "uart3_txd_pw6",
+};
+
+static const char * const uartd_groups[] = {
+ "gmi_a16_pj7",
+ "gmi_a17_pb0",
+ "gmi_a18_pb1",
+ "gmi_a19_pk7",
+ "ulpi_clk_py0",
+ "ulpi_dir_py1",
+ "ulpi_nxt_py2",
+ "ulpi_stp_py3",
+};
+
+static const char * const uarte_groups[] = {
+ "sdmmc1_dat0_py7",
+ "sdmmc1_dat1_py6",
+ "sdmmc1_dat2_py5",
+ "sdmmc1_dat3_py4",
+ "sdmmc4_dat0_paa0",
+ "sdmmc4_dat1_paa1",
+ "sdmmc4_dat2_paa2",
+ "sdmmc4_dat3_paa3",
+};
+
+static const char * const ulpi_groups[] = {
+ "ulpi_clk_py0",
+ "ulpi_data0_po1",
+ "ulpi_data1_po2",
+ "ulpi_data2_po3",
+ "ulpi_data3_po4",
+ "ulpi_data4_po5",
+ "ulpi_data5_po6",
+ "ulpi_data6_po7",
+ "ulpi_data7_po0",
+ "ulpi_dir_py1",
+ "ulpi_nxt_py2",
+ "ulpi_stp_py3",
+};
+
+static const char * const vgp1_groups[] = {
+ "cam_i2c_scl_pbb1",
+};
+
+static const char * const vgp2_groups[] = {
+ "cam_i2c_sda_pbb2",
+};
+
+static const char * const vgp3_groups[] = {
+ "pbb3",
+ "sdmmc4_dat5_paa5",
+};
+
+static const char * const vgp4_groups[] = {
+ "pbb4",
+ "sdmmc4_dat6_paa6",
+};
+
+static const char * const vgp5_groups[] = {
+ "pbb5",
+ "sdmmc4_dat7_paa7",
+};
+
+static const char * const vgp6_groups[] = {
+ "pbb6",
+ "sdmmc4_rst_n_pcc3",
+};
+
+static const char * const vi_groups[] = {
+ "cam_mclk_pcc0",
+ "vi_d0_pt4",
+ "vi_d1_pd5",
+ "vi_d10_pt2",
+ "vi_d11_pt3",
+ "vi_d2_pl0",
+ "vi_d3_pl1",
+ "vi_d4_pl2",
+ "vi_d5_pl3",
+ "vi_d6_pl4",
+ "vi_d7_pl5",
+ "vi_d8_pl6",
+ "vi_d9_pl7",
+ "vi_hsync_pd7",
+ "vi_mclk_pt1",
+ "vi_pclk_pt0",
+ "vi_vsync_pd6",
+};
+
+static const char * const vi_alt1_groups[] = {
+ "cam_mclk_pcc0",
+ "vi_mclk_pt1",
+};
+
+static const char * const vi_alt2_groups[] = {
+ "vi_mclk_pt1",
+};
+
+static const char * const vi_alt3_groups[] = {
+ "cam_mclk_pcc0",
+ "vi_mclk_pt1",
+};
+
+#define FUNCTION(fname) \
+ { \
+ .name = #fname, \
+ .groups = fname##_groups, \
+ .ngroups = ARRAY_SIZE(fname##_groups), \
+ }
+
+static const struct tegra_function tegra30_functions[] = {
+ FUNCTION(blink),
+ FUNCTION(cec),
+ FUNCTION(clk_12m_out),
+ FUNCTION(clk_32k_in),
+ FUNCTION(core_pwr_req),
+ FUNCTION(cpu_pwr_req),
+ FUNCTION(crt),
+ FUNCTION(dap),
+ FUNCTION(ddr),
+ FUNCTION(dev3),
+ FUNCTION(displaya),
+ FUNCTION(displayb),
+ FUNCTION(dtv),
+ FUNCTION(extperiph1),
+ FUNCTION(extperiph2),
+ FUNCTION(extperiph3),
+ FUNCTION(gmi),
+ FUNCTION(gmi_alt),
+ FUNCTION(hda),
+ FUNCTION(hdcp),
+ FUNCTION(hdmi),
+ FUNCTION(hsi),
+ FUNCTION(i2c1),
+ FUNCTION(i2c2),
+ FUNCTION(i2c3),
+ FUNCTION(i2c4),
+ FUNCTION(i2cpwr),
+ FUNCTION(i2s0),
+ FUNCTION(i2s1),
+ FUNCTION(i2s2),
+ FUNCTION(i2s3),
+ FUNCTION(i2s4),
+ FUNCTION(invalid),
+ FUNCTION(kbc),
+ FUNCTION(mio),
+ FUNCTION(nand),
+ FUNCTION(nand_alt),
+ FUNCTION(owr),
+ FUNCTION(pcie),
+ FUNCTION(pwm0),
+ FUNCTION(pwm1),
+ FUNCTION(pwm2),
+ FUNCTION(pwm3),
+ FUNCTION(pwr_int_n),
+ FUNCTION(rsvd1),
+ FUNCTION(rsvd2),
+ FUNCTION(rsvd3),
+ FUNCTION(rsvd4),
+ FUNCTION(rtck),
+ FUNCTION(sata),
+ FUNCTION(sdmmc1),
+ FUNCTION(sdmmc2),
+ FUNCTION(sdmmc3),
+ FUNCTION(sdmmc4),
+ FUNCTION(spdif),
+ FUNCTION(spi1),
+ FUNCTION(spi2),
+ FUNCTION(spi2_alt),
+ FUNCTION(spi3),
+ FUNCTION(spi4),
+ FUNCTION(spi5),
+ FUNCTION(spi6),
+ FUNCTION(sysclk),
+ FUNCTION(test),
+ FUNCTION(trace),
+ FUNCTION(uarta),
+ FUNCTION(uartb),
+ FUNCTION(uartc),
+ FUNCTION(uartd),
+ FUNCTION(uarte),
+ FUNCTION(ulpi),
+ FUNCTION(vgp1),
+ FUNCTION(vgp2),
+ FUNCTION(vgp3),
+ FUNCTION(vgp4),
+ FUNCTION(vgp5),
+ FUNCTION(vgp6),
+ FUNCTION(vi),
+ FUNCTION(vi_alt1),
+ FUNCTION(vi_alt2),
+ FUNCTION(vi_alt3),
+};
+
+#define MUXCTL_REG_A 0x3000
+#define PINGROUP_REG_A 0x868
+
+#define PINGROUP_REG_Y(r) ((r) - MUXCTL_REG_A)
+#define PINGROUP_REG_N(r) -1
+
+#define PINGROUP(pg_name, f0, f1, f2, f3, f_safe, r, od, ior) \
+ { \
+ .name = #pg_name, \
+ .pins = pg_name##_pins, \
+ .npins = ARRAY_SIZE(pg_name##_pins), \
+ .funcs = { \
+ TEGRA_MUX_ ## f0, \
+ TEGRA_MUX_ ## f1, \
+ TEGRA_MUX_ ## f2, \
+ TEGRA_MUX_ ## f3, \
+ }, \
+ .func_safe = TEGRA_MUX_ ## f_safe, \
+ .mux_reg = PINGROUP_REG_Y(r), \
+ .mux_bank = 0, \
+ .mux_bit = 0, \
+ .pupd_reg = PINGROUP_REG_Y(r), \
+ .pupd_bank = 0, \
+ .pupd_bit = 2, \
+ .tri_reg = PINGROUP_REG_Y(r), \
+ .tri_bank = 0, \
+ .tri_bit = 4, \
+ .einput_reg = PINGROUP_REG_Y(r), \
+ .einput_bank = 0, \
+ .einput_bit = 5, \
+ .odrain_reg = PINGROUP_REG_##od(r), \
+ .odrain_bank = 0, \
+ .odrain_bit = 6, \
+ .lock_reg = PINGROUP_REG_Y(r), \
+ .lock_bank = 0, \
+ .lock_bit = 7, \
+ .ioreset_reg = PINGROUP_REG_##ior(r), \
+ .ioreset_bank = 0, \
+ .ioreset_bit = 8, \
+ .drv_reg = -1, \
+ }
+
+#define DRV_PINGROUP(pg_name, r, hsm_b, schmitt_b, lpmd_b, \
+ drvdn_b, drvdn_w, drvup_b, drvup_w, \
+ slwr_b, slwr_w, slwf_b, slwf_w) \
+ { \
+ .name = "drive_" #pg_name, \
+ .pins = drive_##pg_name##_pins, \
+ .npins = ARRAY_SIZE(drive_##pg_name##_pins), \
+ .mux_reg = -1, \
+ .pupd_reg = -1, \
+ .tri_reg = -1, \
+ .einput_reg = -1, \
+ .odrain_reg = -1, \
+ .lock_reg = -1, \
+ .ioreset_reg = -1, \
+ .drv_reg = ((r) - PINGROUP_REG_A), \
+ .drv_bank = 1, \
+ .hsm_bit = hsm_b, \
+ .schmitt_bit = schmitt_b, \
+ .lpmd_bit = lpmd_b, \
+ .drvdn_bit = drvdn_b, \
+ .drvdn_width = drvdn_w, \
+ .drvup_bit = drvup_b, \
+ .drvup_width = drvup_w, \
+ .slwr_bit = slwr_b, \
+ .slwr_width = slwr_w, \
+ .slwf_bit = slwf_b, \
+ .slwf_width = slwf_w, \
+ }
+
+static const struct tegra_pingroup tegra30_groups[] = {
+ /* pg_name, f0, f1, f2, f3, safe, r, od, ior */
+ /* FIXME: Fill in correct data in safe column */
+ PINGROUP(clk_32k_out_pa0, BLINK, RSVD2, RSVD3, RSVD4, RSVD4, 0x331c, N, N),
+ PINGROUP(uart3_cts_n_pa1, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x317c, N, N),
+ PINGROUP(dap2_fs_pa2, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3358, N, N),
+ PINGROUP(dap2_sclk_pa3, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3364, N, N),
+ PINGROUP(dap2_din_pa4, I2S1, HDA, RSVD3, GMI, RSVD3, 0x335c, N, N),
+ PINGROUP(dap2_dout_pa5, I2S1, HDA, RSVD3, GMI, RSVD3, 0x3360, N, N),
+ PINGROUP(sdmmc3_clk_pa6, UARTA, PWM2, SDMMC3, SPI3, SPI3, 0x3390, N, N),
+ PINGROUP(sdmmc3_cmd_pa7, UARTA, PWM3, SDMMC3, SPI2, SPI2, 0x3394, N, N),
+ PINGROUP(gmi_a17_pb0, UARTD, SPI4, GMI, DTV, DTV, 0x3234, N, N),
+ PINGROUP(gmi_a18_pb1, UARTD, SPI4, GMI, DTV, DTV, 0x3238, N, N),
+ PINGROUP(lcd_pwr0_pb2, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x3090, N, N),
+ PINGROUP(lcd_pclk_pb3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3094, N, N),
+ PINGROUP(sdmmc3_dat3_pb4, RSVD1, PWM0, SDMMC3, SPI3, RSVD1, 0x33a4, N, N),
+ PINGROUP(sdmmc3_dat2_pb5, RSVD1, PWM1, SDMMC3, SPI3, RSVD1, 0x33a0, N, N),
+ PINGROUP(sdmmc3_dat1_pb6, RSVD1, RSVD2, SDMMC3, SPI3, RSVD2, 0x339c, N, N),
+ PINGROUP(sdmmc3_dat0_pb7, RSVD1, RSVD2, SDMMC3, SPI3, RSVD2, 0x3398, N, N),
+ PINGROUP(uart3_rts_n_pc0, UARTC, PWM0, GMI, RSVD4, RSVD4, 0x3180, N, N),
+ PINGROUP(lcd_pwr1_pc1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3070, N, N),
+ PINGROUP(uart2_txd_pc2, UARTB, SPDIF, UARTA, SPI4, SPI4, 0x3168, N, N),
+ PINGROUP(uart2_rxd_pc3, UARTB, SPDIF, UARTA, SPI4, SPI4, 0x3164, N, N),
+ PINGROUP(gen1_i2c_scl_pc4, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a4, Y, N),
+ PINGROUP(gen1_i2c_sda_pc5, I2C1, RSVD2, RSVD3, RSVD4, RSVD4, 0x31a0, Y, N),
+ PINGROUP(lcd_pwr2_pc6, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x3074, N, N),
+ PINGROUP(gmi_wp_n_pc7, RSVD1, NAND, GMI, GMI_ALT, RSVD1, 0x31c0, N, N),
+ PINGROUP(sdmmc3_dat5_pd0, PWM0, SPI4, SDMMC3, SPI2, SPI2, 0x33ac, N, N),
+ PINGROUP(sdmmc3_dat4_pd1, PWM1, SPI4, SDMMC3, SPI2, SPI2, 0x33a8, N, N),
+ PINGROUP(lcd_dc1_pd2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x310c, N, N),
+ PINGROUP(sdmmc3_dat6_pd3, SPDIF, SPI4, SDMMC3, SPI2, SPI2, 0x33b0, N, N),
+ PINGROUP(sdmmc3_dat7_pd4, SPDIF, SPI4, SDMMC3, SPI2, SPI2, 0x33b4, N, N),
+ PINGROUP(vi_d1_pd5, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3128, N, Y),
+ PINGROUP(vi_vsync_pd6, DDR, RSVD2, VI, RSVD4, RSVD4, 0x315c, N, Y),
+ PINGROUP(vi_hsync_pd7, DDR, RSVD2, VI, RSVD4, RSVD4, 0x3160, N, Y),
+ PINGROUP(lcd_d0_pe0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30a4, N, N),
+ PINGROUP(lcd_d1_pe1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30a8, N, N),
+ PINGROUP(lcd_d2_pe2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30ac, N, N),
+ PINGROUP(lcd_d3_pe3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30b0, N, N),
+ PINGROUP(lcd_d4_pe4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30b4, N, N),
+ PINGROUP(lcd_d5_pe5, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30b8, N, N),
+ PINGROUP(lcd_d6_pe6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30bc, N, N),
+ PINGROUP(lcd_d7_pe7, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30c0, N, N),
+ PINGROUP(lcd_d8_pf0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30c4, N, N),
+ PINGROUP(lcd_d9_pf1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30c8, N, N),
+ PINGROUP(lcd_d10_pf2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30cc, N, N),
+ PINGROUP(lcd_d11_pf3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30d0, N, N),
+ PINGROUP(lcd_d12_pf4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30d4, N, N),
+ PINGROUP(lcd_d13_pf5, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30d8, N, N),
+ PINGROUP(lcd_d14_pf6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30dc, N, N),
+ PINGROUP(lcd_d15_pf7, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30e0, N, N),
+ PINGROUP(gmi_ad0_pg0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f0, N, N),
+ PINGROUP(gmi_ad1_pg1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f4, N, N),
+ PINGROUP(gmi_ad2_pg2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31f8, N, N),
+ PINGROUP(gmi_ad3_pg3, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31fc, N, N),
+ PINGROUP(gmi_ad4_pg4, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3200, N, N),
+ PINGROUP(gmi_ad5_pg5, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3204, N, N),
+ PINGROUP(gmi_ad6_pg6, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3208, N, N),
+ PINGROUP(gmi_ad7_pg7, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x320c, N, N),
+ PINGROUP(gmi_ad8_ph0, PWM0, NAND, GMI, RSVD4, RSVD4, 0x3210, N, N),
+ PINGROUP(gmi_ad9_ph1, PWM1, NAND, GMI, RSVD4, RSVD4, 0x3214, N, N),
+ PINGROUP(gmi_ad10_ph2, PWM2, NAND, GMI, RSVD4, RSVD4, 0x3218, N, N),
+ PINGROUP(gmi_ad11_ph3, PWM3, NAND, GMI, RSVD4, RSVD4, 0x321c, N, N),
+ PINGROUP(gmi_ad12_ph4, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3220, N, N),
+ PINGROUP(gmi_ad13_ph5, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3224, N, N),
+ PINGROUP(gmi_ad14_ph6, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3228, N, N),
+ PINGROUP(gmi_ad15_ph7, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x322c, N, N),
+ PINGROUP(gmi_wr_n_pi0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3240, N, N),
+ PINGROUP(gmi_oe_n_pi1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3244, N, N),
+ PINGROUP(gmi_dqs_pi2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x3248, N, N),
+ PINGROUP(gmi_cs6_n_pi3, NAND, NAND_ALT, GMI, SATA, SATA, 0x31e8, N, N),
+ PINGROUP(gmi_rst_n_pi4, NAND, NAND_ALT, GMI, RSVD4, RSVD4, 0x324c, N, N),
+ PINGROUP(gmi_iordy_pi5, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31c4, N, N),
+ PINGROUP(gmi_cs7_n_pi6, NAND, NAND_ALT, GMI, GMI_ALT, GMI_ALT, 0x31ec, N, N),
+ PINGROUP(gmi_wait_pi7, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31c8, N, N),
+ PINGROUP(gmi_cs0_n_pj0, RSVD1, NAND, GMI, DTV, RSVD1, 0x31d4, N, N),
+ PINGROUP(lcd_de_pj1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3098, N, N),
+ PINGROUP(gmi_cs1_n_pj2, RSVD1, NAND, GMI, DTV, RSVD1, 0x31d8, N, N),
+ PINGROUP(lcd_hsync_pj3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x309c, N, N),
+ PINGROUP(lcd_vsync_pj4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30a0, N, N),
+ PINGROUP(uart2_cts_n_pj5, UARTA, UARTB, GMI, SPI4, SPI4, 0x3170, N, N),
+ PINGROUP(uart2_rts_n_pj6, UARTA, UARTB, GMI, SPI4, SPI4, 0x316c, N, N),
+ PINGROUP(gmi_a16_pj7, UARTD, SPI4, GMI, GMI_ALT, GMI_ALT, 0x3230, N, N),
+ PINGROUP(gmi_adv_n_pk0, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31cc, N, N),
+ PINGROUP(gmi_clk_pk1, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31d0, N, N),
+ PINGROUP(gmi_cs4_n_pk2, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31e4, N, N),
+ PINGROUP(gmi_cs2_n_pk3, RSVD1, NAND, GMI, RSVD4, RSVD4, 0x31dc, N, N),
+ PINGROUP(gmi_cs3_n_pk4, RSVD1, NAND, GMI, GMI_ALT, RSVD1, 0x31e0, N, N),
+ PINGROUP(spdif_out_pk5, SPDIF, RSVD2, I2C1, SDMMC2, RSVD2, 0x3354, N, N),
+ PINGROUP(spdif_in_pk6, SPDIF, HDA, I2C1, SDMMC2, SDMMC2, 0x3350, N, N),
+ PINGROUP(gmi_a19_pk7, UARTD, SPI4, GMI, RSVD4, RSVD4, 0x323c, N, N),
+ PINGROUP(vi_d2_pl0, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x312c, N, Y),
+ PINGROUP(vi_d3_pl1, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3130, N, Y),
+ PINGROUP(vi_d4_pl2, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3134, N, Y),
+ PINGROUP(vi_d5_pl3, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3138, N, Y),
+ PINGROUP(vi_d6_pl4, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x313c, N, Y),
+ PINGROUP(vi_d7_pl5, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3140, N, Y),
+ PINGROUP(vi_d8_pl6, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3144, N, Y),
+ PINGROUP(vi_d9_pl7, DDR, SDMMC2, VI, RSVD4, RSVD4, 0x3148, N, Y),
+ PINGROUP(lcd_d16_pm0, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30e4, N, N),
+ PINGROUP(lcd_d17_pm1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30e8, N, N),
+ PINGROUP(lcd_d18_pm2, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30ec, N, N),
+ PINGROUP(lcd_d19_pm3, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30f0, N, N),
+ PINGROUP(lcd_d20_pm4, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30f4, N, N),
+ PINGROUP(lcd_d21_pm5, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30f8, N, N),
+ PINGROUP(lcd_d22_pm6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x30fc, N, N),
+ PINGROUP(lcd_d23_pm7, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3100, N, N),
+ PINGROUP(dap1_fs_pn0, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x3338, N, N),
+ PINGROUP(dap1_din_pn1, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x333c, N, N),
+ PINGROUP(dap1_dout_pn2, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x3340, N, N),
+ PINGROUP(dap1_sclk_pn3, I2S0, HDA, GMI, SDMMC2, SDMMC2, 0x3344, N, N),
+ PINGROUP(lcd_cs0_n_pn4, DISPLAYA, DISPLAYB, SPI5, RSVD4, RSVD4, 0x3084, N, N),
+ PINGROUP(lcd_sdout_pn5, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x307c, N, N),
+ PINGROUP(lcd_dc0_pn6, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3088, N, N),
+ PINGROUP(hdmi_int_pn7, HDMI, RSVD2, RSVD3, RSVD4, RSVD4, 0x3110, N, N),
+ PINGROUP(ulpi_data7_po0, SPI2, HSI, UARTA, ULPI, ULPI, 0x301c, N, N),
+ PINGROUP(ulpi_data0_po1, SPI3, HSI, UARTA, ULPI, ULPI, 0x3000, N, N),
+ PINGROUP(ulpi_data1_po2, SPI3, HSI, UARTA, ULPI, ULPI, 0x3004, N, N),
+ PINGROUP(ulpi_data2_po3, SPI3, HSI, UARTA, ULPI, ULPI, 0x3008, N, N),
+ PINGROUP(ulpi_data3_po4, SPI3, HSI, UARTA, ULPI, ULPI, 0x300c, N, N),
+ PINGROUP(ulpi_data4_po5, SPI2, HSI, UARTA, ULPI, ULPI, 0x3010, N, N),
+ PINGROUP(ulpi_data5_po6, SPI2, HSI, UARTA, ULPI, ULPI, 0x3014, N, N),
+ PINGROUP(ulpi_data6_po7, SPI2, HSI, UARTA, ULPI, ULPI, 0x3018, N, N),
+ PINGROUP(dap3_fs_pp0, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x3030, N, N),
+ PINGROUP(dap3_din_pp1, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x3034, N, N),
+ PINGROUP(dap3_dout_pp2, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x3038, N, N),
+ PINGROUP(dap3_sclk_pp3, I2S2, RSVD2, DISPLAYA, DISPLAYB, RSVD2, 0x303c, N, N),
+ PINGROUP(dap4_fs_pp4, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31a8, N, N),
+ PINGROUP(dap4_din_pp5, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31ac, N, N),
+ PINGROUP(dap4_dout_pp6, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31b0, N, N),
+ PINGROUP(dap4_sclk_pp7, I2S3, RSVD2, GMI, RSVD4, RSVD4, 0x31b4, N, N),
+ PINGROUP(kb_col0_pq0, KBC, NAND, TRACE, TEST, TEST, 0x32fc, N, N),
+ PINGROUP(kb_col1_pq1, KBC, NAND, TRACE, TEST, TEST, 0x3300, N, N),
+ PINGROUP(kb_col2_pq2, KBC, NAND, TRACE, RSVD4, RSVD4, 0x3304, N, N),
+ PINGROUP(kb_col3_pq3, KBC, NAND, TRACE, RSVD4, RSVD4, 0x3308, N, N),
+ PINGROUP(kb_col4_pq4, KBC, NAND, TRACE, RSVD4, RSVD4, 0x330c, N, N),
+ PINGROUP(kb_col5_pq5, KBC, NAND, TRACE, RSVD4, RSVD4, 0x3310, N, N),
+ PINGROUP(kb_col6_pq6, KBC, NAND, TRACE, MIO, MIO, 0x3314, N, N),
+ PINGROUP(kb_col7_pq7, KBC, NAND, TRACE, MIO, MIO, 0x3318, N, N),
+ PINGROUP(kb_row0_pr0, KBC, NAND, RSVD3, RSVD4, RSVD4, 0x32bc, N, N),
+ PINGROUP(kb_row1_pr1, KBC, NAND, RSVD3, RSVD4, RSVD4, 0x32c0, N, N),
+ PINGROUP(kb_row2_pr2, KBC, NAND, RSVD3, RSVD4, RSVD4, 0x32c4, N, N),
+ PINGROUP(kb_row3_pr3, KBC, NAND, RSVD3, INVALID, RSVD3, 0x32c8, N, N),
+ PINGROUP(kb_row4_pr4, KBC, NAND, TRACE, RSVD4, RSVD4, 0x32cc, N, N),
+ PINGROUP(kb_row5_pr5, KBC, NAND, TRACE, OWR, OWR, 0x32d0, N, N),
+ PINGROUP(kb_row6_pr6, KBC, NAND, SDMMC2, MIO, MIO, 0x32d4, N, N),
+ PINGROUP(kb_row7_pr7, KBC, NAND, SDMMC2, MIO, MIO, 0x32d8, N, N),
+ PINGROUP(kb_row8_ps0, KBC, NAND, SDMMC2, MIO, MIO, 0x32dc, N, N),
+ PINGROUP(kb_row9_ps1, KBC, NAND, SDMMC2, MIO, MIO, 0x32e0, N, N),
+ PINGROUP(kb_row10_ps2, KBC, NAND, SDMMC2, MIO, MIO, 0x32e4, N, N),
+ PINGROUP(kb_row11_ps3, KBC, NAND, SDMMC2, MIO, MIO, 0x32e8, N, N),
+ PINGROUP(kb_row12_ps4, KBC, NAND, SDMMC2, MIO, MIO, 0x32ec, N, N),
+ PINGROUP(kb_row13_ps5, KBC, NAND, SDMMC2, MIO, MIO, 0x32f0, N, N),
+ PINGROUP(kb_row14_ps6, KBC, NAND, SDMMC2, MIO, MIO, 0x32f4, N, N),
+ PINGROUP(kb_row15_ps7, KBC, NAND, SDMMC2, MIO, MIO, 0x32f8, N, N),
+ PINGROUP(vi_pclk_pt0, RSVD1, SDMMC2, VI, RSVD4, RSVD4, 0x3154, N, Y),
+ PINGROUP(vi_mclk_pt1, VI, VI_ALT1, VI_ALT2, VI_ALT3, VI_ALT3, 0x3158, N, Y),
+ PINGROUP(vi_d10_pt2, DDR, RSVD2, VI, RSVD4, RSVD4, 0x314c, N, Y),
+ PINGROUP(vi_d11_pt3, DDR, RSVD2, VI, RSVD4, RSVD4, 0x3150, N, Y),
+ PINGROUP(vi_d0_pt4, DDR, RSVD2, VI, RSVD4, RSVD4, 0x3124, N, Y),
+ PINGROUP(gen2_i2c_scl_pt5, I2C2, HDCP, GMI, RSVD4, RSVD4, 0x3250, Y, N),
+ PINGROUP(gen2_i2c_sda_pt6, I2C2, HDCP, GMI, RSVD4, RSVD4, 0x3254, Y, N),
+ PINGROUP(sdmmc4_cmd_pt7, I2C3, NAND, GMI, SDMMC4, SDMMC4, 0x325c, N, Y),
+ PINGROUP(pu0, OWR, UARTA, GMI, RSVD4, RSVD4, 0x3184, N, N),
+ PINGROUP(pu1, RSVD1, UARTA, GMI, RSVD4, RSVD4, 0x3188, N, N),
+ PINGROUP(pu2, RSVD1, UARTA, GMI, RSVD4, RSVD4, 0x318c, N, N),
+ PINGROUP(pu3, PWM0, UARTA, GMI, RSVD4, RSVD4, 0x3190, N, N),
+ PINGROUP(pu4, PWM1, UARTA, GMI, RSVD4, RSVD4, 0x3194, N, N),
+ PINGROUP(pu5, PWM2, UARTA, GMI, RSVD4, RSVD4, 0x3198, N, N),
+ PINGROUP(pu6, PWM3, UARTA, GMI, RSVD4, RSVD4, 0x319c, N, N),
+ PINGROUP(jtag_rtck_pu7, RTCK, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b0, N, N),
+ PINGROUP(pv0, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3040, N, N),
+ PINGROUP(pv1, RSVD1, RSVD2, RSVD3, RSVD4, RSVD4, 0x3044, N, N),
+ PINGROUP(pv2, OWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x3060, N, N),
+ PINGROUP(pv3, CLK_12M_OUT, RSVD2, RSVD3, RSVD4, RSVD4, 0x3064, N, N),
+ PINGROUP(ddc_scl_pv4, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3114, N, N),
+ PINGROUP(ddc_sda_pv5, I2C4, RSVD2, RSVD3, RSVD4, RSVD4, 0x3118, N, N),
+ PINGROUP(crt_hsync_pv6, CRT, RSVD2, RSVD3, RSVD4, RSVD4, 0x311c, N, N),
+ PINGROUP(crt_vsync_pv7, CRT, RSVD2, RSVD3, RSVD4, RSVD4, 0x3120, N, N),
+ PINGROUP(lcd_cs1_n_pw0, DISPLAYA, DISPLAYB, SPI5, RSVD4, RSVD4, 0x3104, N, N),
+ PINGROUP(lcd_m1_pw1, DISPLAYA, DISPLAYB, RSVD3, RSVD4, RSVD4, 0x3108, N, N),
+ PINGROUP(spi2_cs1_n_pw2, SPI3, SPI2, SPI2_ALT, I2C1, I2C1, 0x3388, N, N),
+ PINGROUP(spi2_cs2_n_pw3, SPI3, SPI2, SPI2_ALT, I2C1, I2C1, 0x338c, N, N),
+ PINGROUP(clk1_out_pw4, EXTPERIPH1, RSVD2, RSVD3, RSVD4, RSVD4, 0x334c, N, N),
+ PINGROUP(clk2_out_pw5, EXTPERIPH2, RSVD2, RSVD3, RSVD4, RSVD4, 0x3068, N, N),
+ PINGROUP(uart3_txd_pw6, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x3174, N, N),
+ PINGROUP(uart3_rxd_pw7, UARTC, RSVD2, GMI, RSVD4, RSVD4, 0x3178, N, N),
+ PINGROUP(spi2_mosi_px0, SPI6, SPI2, SPI3, GMI, GMI, 0x3368, N, N),
+ PINGROUP(spi2_miso_px1, SPI6, SPI2, SPI3, GMI, GMI, 0x336c, N, N),
+ PINGROUP(spi2_sck_px2, SPI6, SPI2, SPI3, GMI, GMI, 0x3374, N, N),
+ PINGROUP(spi2_cs0_n_px3, SPI6, SPI2, SPI3, GMI, GMI, 0x3370, N, N),
+ PINGROUP(spi1_mosi_px4, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x3378, N, N),
+ PINGROUP(spi1_sck_px5, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x337c, N, N),
+ PINGROUP(spi1_cs0_n_px6, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x3380, N, N),
+ PINGROUP(spi1_miso_px7, SPI3, SPI1, SPI2_ALT, RSVD4, RSVD4, 0x3384, N, N),
+ PINGROUP(ulpi_clk_py0, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x3020, N, N),
+ PINGROUP(ulpi_dir_py1, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x3024, N, N),
+ PINGROUP(ulpi_nxt_py2, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x3028, N, N),
+ PINGROUP(ulpi_stp_py3, SPI1, RSVD2, UARTD, ULPI, RSVD2, 0x302c, N, N),
+ PINGROUP(sdmmc1_dat3_py4, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x3050, N, N),
+ PINGROUP(sdmmc1_dat2_py5, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x3054, N, N),
+ PINGROUP(sdmmc1_dat1_py6, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x3058, N, N),
+ PINGROUP(sdmmc1_dat0_py7, SDMMC1, RSVD2, UARTE, UARTA, RSVD2, 0x305c, N, N),
+ PINGROUP(sdmmc1_clk_pz0, SDMMC1, RSVD2, RSVD3, UARTA, RSVD3, 0x3048, N, N),
+ PINGROUP(sdmmc1_cmd_pz1, SDMMC1, RSVD2, RSVD3, UARTA, RSVD3, 0x304c, N, N),
+ PINGROUP(lcd_sdin_pz2, DISPLAYA, DISPLAYB, SPI5, RSVD4, RSVD4, 0x3078, N, N),
+ PINGROUP(lcd_wr_n_pz3, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x3080, N, N),
+ PINGROUP(lcd_sck_pz4, DISPLAYA, DISPLAYB, SPI5, HDCP, HDCP, 0x308c, N, N),
+ PINGROUP(sys_clk_req_pz5, SYSCLK, RSVD2, RSVD3, RSVD4, RSVD4, 0x3320, N, N),
+ PINGROUP(pwr_i2c_scl_pz6, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b4, Y, N),
+ PINGROUP(pwr_i2c_sda_pz7, I2CPWR, RSVD2, RSVD3, RSVD4, RSVD4, 0x32b8, Y, N),
+ PINGROUP(sdmmc4_dat0_paa0, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x3260, N, Y),
+ PINGROUP(sdmmc4_dat1_paa1, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x3264, N, Y),
+ PINGROUP(sdmmc4_dat2_paa2, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x3268, N, Y),
+ PINGROUP(sdmmc4_dat3_paa3, UARTE, SPI3, GMI, SDMMC4, SDMMC4, 0x326c, N, Y),
+ PINGROUP(sdmmc4_dat4_paa4, I2C3, I2S4, GMI, SDMMC4, SDMMC4, 0x3270, N, Y),
+ PINGROUP(sdmmc4_dat5_paa5, VGP3, I2S4, GMI, SDMMC4, SDMMC4, 0x3274, N, Y),
+ PINGROUP(sdmmc4_dat6_paa6, VGP4, I2S4, GMI, SDMMC4, SDMMC4, 0x3278, N, Y),
+ PINGROUP(sdmmc4_dat7_paa7, VGP5, I2S4, GMI, SDMMC4, SDMMC4, 0x327c, N, Y),
+ PINGROUP(pbb0, I2S4, RSVD2, RSVD3, SDMMC4, RSVD3, 0x328c, N, N),
+ PINGROUP(cam_i2c_scl_pbb1, VGP1, I2C3, RSVD3, SDMMC4, RSVD3, 0x3290, Y, N),
+ PINGROUP(cam_i2c_sda_pbb2, VGP2, I2C3, RSVD3, SDMMC4, RSVD3, 0x3294, Y, N),
+ PINGROUP(pbb3, VGP3, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x3298, N, N),
+ PINGROUP(pbb4, VGP4, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x329c, N, N),
+ PINGROUP(pbb5, VGP5, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x32a0, N, N),
+ PINGROUP(pbb6, VGP6, DISPLAYA, DISPLAYB, SDMMC4, SDMMC4, 0x32a4, N, N),
+ PINGROUP(pbb7, I2S4, RSVD2, RSVD3, SDMMC4, RSVD3, 0x32a8, N, N),
+ PINGROUP(cam_mclk_pcc0, VI, VI_ALT1, VI_ALT3, SDMMC4, SDMMC4, 0x3284, N, N),
+ PINGROUP(pcc1, I2S4, RSVD2, RSVD3, SDMMC4, RSVD3, 0x3288, N, N),
+ PINGROUP(pcc2, I2S4, RSVD2, RSVD3, RSVD4, RSVD4, 0x32ac, N, N),
+ PINGROUP(sdmmc4_rst_n_pcc3, VGP6, RSVD2, RSVD3, SDMMC4, RSVD3, 0x3280, N, Y),
+ PINGROUP(sdmmc4_clk_pcc4, INVALID, NAND, GMI, SDMMC4, SDMMC4, 0x3258, N, Y),
+ PINGROUP(clk2_req_pcc5, DAP, RSVD2, RSVD3, RSVD4, RSVD4, 0x306c, N, N),
+ PINGROUP(pex_l2_rst_n_pcc6, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33d8, N, N),
+ PINGROUP(pex_l2_clkreq_n_pcc7, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33dc, N, N),
+ PINGROUP(pex_l0_prsnt_n_pdd0, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33b8, N, N),
+ PINGROUP(pex_l0_rst_n_pdd1, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33bc, N, N),
+ PINGROUP(pex_l0_clkreq_n_pdd2, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33c0, N, N),
+ PINGROUP(pex_wake_n_pdd3, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33c4, N, N),
+ PINGROUP(pex_l1_prsnt_n_pdd4, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33c8, N, N),
+ PINGROUP(pex_l1_rst_n_pdd5, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33cc, N, N),
+ PINGROUP(pex_l1_clkreq_n_pdd6, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33d0, N, N),
+ PINGROUP(pex_l2_prsnt_n_pdd7, PCIE, HDA, RSVD3, RSVD4, RSVD4, 0x33d4, N, N),
+ PINGROUP(clk3_out_pee0, EXTPERIPH3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31b8, N, N),
+ PINGROUP(clk3_req_pee1, DEV3, RSVD2, RSVD3, RSVD4, RSVD4, 0x31bc, N, N),
+ PINGROUP(clk1_req_pee2, DAP, HDA, RSVD3, RSVD4, RSVD4, 0x3348, N, N),
+ PINGROUP(hdmi_cec_pee3, CEC, RSVD2, RSVD3, RSVD4, RSVD4, 0x33e0, Y, N),
+ PINGROUP(clk_32k_in, CLK_32K_IN, RSVD2, RSVD3, RSVD4, RSVD4, 0x3330, N, N),
+ PINGROUP(core_pwr_req, CORE_PWR_REQ, RSVD2, RSVD3, RSVD4, RSVD4, 0x3324, N, N),
+ PINGROUP(cpu_pwr_req, CPU_PWR_REQ, RSVD2, RSVD3, RSVD4, RSVD4, 0x3328, N, N),
+ PINGROUP(owr, OWR, CEC, RSVD3, RSVD4, RSVD4, 0x3334, N, N),
+ PINGROUP(pwr_int_n, PWR_INT_N, RSVD2, RSVD3, RSVD4, RSVD4, 0x332c, N, N),
+ /* pg_name, r, hsm_b, schmitt_b, lpmd_b, drvdn_b, drvdn_w, drvup_b, drvup_w, slwr_b, slwr_w, slwf_b, slwf_w */
+ DRV_PINGROUP(ao1, 0x868, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(ao2, 0x86c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(at1, 0x870, 2, 3, 4, 14, 5, 19, 5, 24, 2, 28, 2),
+ DRV_PINGROUP(at2, 0x874, 2, 3, 4, 14, 5, 19, 5, 24, 2, 28, 2),
+ DRV_PINGROUP(at3, 0x878, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(at4, 0x87c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(at5, 0x880, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(cdev1, 0x884, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(cdev2, 0x888, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(cec, 0x938, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(crt, 0x8f8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(csus, 0x88c, -1, -1, -1, 12, 5, 19, 5, 24, 4, 28, 4),
+ DRV_PINGROUP(dap1, 0x890, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(dap2, 0x894, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(dap3, 0x898, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(dap4, 0x89c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(dbg, 0x8a0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(ddc, 0x8fc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(dev3, 0x92c, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(gma, 0x900, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4),
+ DRV_PINGROUP(gmb, 0x904, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4),
+ DRV_PINGROUP(gmc, 0x908, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4),
+ DRV_PINGROUP(gmd, 0x90c, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4),
+ DRV_PINGROUP(gme, 0x910, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(gmf, 0x914, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(gmg, 0x918, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(gmh, 0x91c, 2, 3, 4, 14, 5, 19, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(gpv, 0x928, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(lcd1, 0x8a4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(lcd2, 0x8a8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(owr, 0x920, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(sdio1, 0x8ec, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2),
+ DRV_PINGROUP(sdio2, 0x8ac, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2),
+ DRV_PINGROUP(sdio3, 0x8b0, 2, 3, -1, 12, 7, 20, 7, 28, 2, 30, 2),
+ DRV_PINGROUP(spi, 0x8b4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(uaa, 0x8b8, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(uab, 0x8bc, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(uart2, 0x8c0, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(uart3, 0x8c4, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(uda, 0x924, 2, 3, 4, 12, 5, 20, 5, 28, 2, 30, 2),
+ DRV_PINGROUP(vi1, 0x8c8, -1, -1, -1, 14, 5, 19, 5, 24, 4, 28, 4),
+};
+
+static const struct tegra_pinctrl_soc_data tegra30_pinctrl = {
+ .ngpios = NUM_GPIOS,
+ .pins = tegra30_pins,
+ .npins = ARRAY_SIZE(tegra30_pins),
+ .functions = tegra30_functions,
+ .nfunctions = ARRAY_SIZE(tegra30_functions),
+ .groups = tegra30_groups,
+ .ngroups = ARRAY_SIZE(tegra30_groups),
+};
+
+void __devinit tegra30_pinctrl_init(const struct tegra_pinctrl_soc_data **soc)
+{
+ *soc = &tegra30_pinctrl;
+}
diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c
index c8d02f1c2b5e..26eb8ccd72d5 100644
--- a/drivers/pinctrl/pinctrl-u300.c
+++ b/drivers/pinctrl/pinctrl-u300.c
@@ -19,6 +19,9 @@
#include <linux/err.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include "pinctrl-coh901.h"
/*
* Register definitions for the U300 Padmux control registers in the
@@ -162,7 +165,7 @@
#define U300_SYSCON_PMC4R_APP_MISC_16_APP_UART1_CTS 0x0100
#define U300_SYSCON_PMC4R_APP_MISC_16_EMIF_1_STATIC_CS5_N 0x0200
-#define DRIVER_NAME "pinmux-u300"
+#define DRIVER_NAME "pinctrl-u300"
/*
* The DB3350 has 467 pads, I have enumerated the pads clockwise around the
@@ -1044,22 +1047,82 @@ static struct pinctrl_gpio_range u300_gpio_ranges[] = {
U300_GPIO_RANGE(25, 181, 1),
};
+static struct pinctrl_gpio_range *u300_match_gpio_range(unsigned pin)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) {
+ struct pinctrl_gpio_range *range;
+
+ range = &u300_gpio_ranges[i];
+ if (pin >= range->pin_base &&
+ pin <= (range->pin_base + range->npins - 1))
+ return range;
+ }
+ return NULL;
+}
+
+int u300_pin_config_get(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long *config)
+{
+ struct pinctrl_gpio_range *range = u300_match_gpio_range(pin);
+
+ /* We get config for those pins we CAN get it for and that's it */
+ if (!range)
+ return -ENOTSUPP;
+
+ return u300_gpio_config_get(range->gc,
+ (pin - range->pin_base + range->base),
+ config);
+}
+
+int u300_pin_config_set(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ unsigned long config)
+{
+ struct pinctrl_gpio_range *range = u300_match_gpio_range(pin);
+ int ret;
+
+ if (!range)
+ return -EINVAL;
+
+ /* Note: none of these configurations take any argument */
+ ret = u300_gpio_config_set(range->gc,
+ (pin - range->pin_base + range->base),
+ pinconf_to_config_param(config));
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct pinconf_ops u300_pconf_ops = {
+ .is_generic = true,
+ .pin_config_get = u300_pin_config_get,
+ .pin_config_set = u300_pin_config_set,
+};
+
static struct pinctrl_desc u300_pmx_desc = {
.name = DRIVER_NAME,
.pins = u300_pads,
.npins = ARRAY_SIZE(u300_pads),
.pctlops = &u300_pctrl_ops,
.pmxops = &u300_pmx_ops,
+ .confops = &u300_pconf_ops,
.owner = THIS_MODULE,
};
-static int __init u300_pmx_probe(struct platform_device *pdev)
+static int __devinit u300_pmx_probe(struct platform_device *pdev)
{
struct u300_pmx *upmx;
struct resource *res;
+ struct gpio_chip *gpio_chip = dev_get_platdata(&pdev->dev);
int ret;
int i;
+ pr_err("U300 PMX PROBE\n");
+
/* Create state holders etc for this driver */
upmx = devm_kzalloc(&pdev->dev, sizeof(*upmx), GFP_KERNEL);
if (!upmx)
@@ -1095,12 +1158,14 @@ static int __init u300_pmx_probe(struct platform_device *pdev)
}
/* We will handle a range of GPIO pins */
- for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++)
+ for (i = 0; i < ARRAY_SIZE(u300_gpio_ranges); i++) {
+ u300_gpio_ranges[i].gc = gpio_chip;
pinctrl_add_gpio_range(upmx->pctl, &u300_gpio_ranges[i]);
+ }
platform_set_drvdata(pdev, upmx);
- dev_info(&pdev->dev, "initialized U300 pinmux driver\n");
+ dev_info(&pdev->dev, "initialized U300 pin control driver\n");
return 0;
@@ -1115,7 +1180,7 @@ out_no_resource:
return ret;
}
-static int __exit u300_pmx_remove(struct platform_device *pdev)
+static int __devexit u300_pmx_remove(struct platform_device *pdev)
{
struct u300_pmx *upmx = platform_get_drvdata(pdev);
int i;
@@ -1136,12 +1201,13 @@ static struct platform_driver u300_pmx_driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
- .remove = __exit_p(u300_pmx_remove),
+ .probe = u300_pmx_probe,
+ .remove = __devexit_p(u300_pmx_remove),
};
static int __init u300_pmx_init(void)
{
- return platform_driver_probe(&u300_pmx_driver, u300_pmx_probe);
+ return platform_driver_register(&u300_pmx_driver);
}
arch_initcall(u300_pmx_init);
diff --git a/drivers/pinctrl/pinmux.c b/drivers/pinctrl/pinmux.c
index 7c3193f7a044..4e62783a573a 100644
--- a/drivers/pinctrl/pinmux.c
+++ b/drivers/pinctrl/pinmux.c
@@ -1,12 +1,14 @@
/*
* Core driver for the pin muxing portions of the pin control subsystem
*
- * Copyright (C) 2011 ST-Ericsson SA
+ * Copyright (C) 2011-2012 ST-Ericsson SA
* Written on behalf of Linaro for ST-Ericsson
* Based on bits of regulator core, gpio core and clk core
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
+ * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
+ *
* License terms: GNU General Public License (GPL) version 2
*/
#define pr_fmt(fmt) "pinmux core: " fmt
@@ -19,8 +21,6 @@
#include <linux/radix-tree.h>
#include <linux/err.h>
#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/debugfs.h>
@@ -28,80 +28,64 @@
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinmux.h>
#include "core.h"
+#include "pinmux.h"
-/* List of pinmuxes */
-static DEFINE_MUTEX(pinmux_list_mutex);
-static LIST_HEAD(pinmux_list);
+int pinmux_check_ops(struct pinctrl_dev *pctldev)
+{
+ const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned selector = 0;
-/* Global pinmux maps */
-static struct pinmux_map *pinmux_maps;
-static unsigned pinmux_maps_num;
+ /* Check that we implement required operations */
+ if (!ops->list_functions ||
+ !ops->get_function_name ||
+ !ops->get_function_groups ||
+ !ops->enable ||
+ !ops->disable)
+ return -EINVAL;
-/**
- * struct pinmux_group - group list item for pinmux groups
- * @node: pinmux group list node
- * @group_selector: the group selector for this group
- */
-struct pinmux_group {
- struct list_head node;
- unsigned group_selector;
-};
+ /* Check that all functions registered have names */
+ while (ops->list_functions(pctldev, selector) >= 0) {
+ const char *fname = ops->get_function_name(pctldev,
+ selector);
+ if (!fname) {
+ pr_err("pinmux ops has no name for function%u\n",
+ selector);
+ return -EINVAL;
+ }
+ selector++;
+ }
-/**
- * struct pinmux - per-device pinmux state holder
- * @node: global list node
- * @dev: the device using this pinmux
- * @usecount: the number of active users of this mux setting, used to keep
- * track of nested use cases
- * @pctldev: pin control device handling this pinmux
- * @func_selector: the function selector for the pinmux device handling
- * this pinmux
- * @groups: the group selectors for the pinmux device and
- * selector combination handling this pinmux, this is a list that
- * will be traversed on all pinmux operations such as
- * get/put/enable/disable
- * @mutex: a lock for the pinmux state holder
- */
-struct pinmux {
- struct list_head node;
- struct device *dev;
- unsigned usecount;
- struct pinctrl_dev *pctldev;
- unsigned func_selector;
- struct list_head groups;
- struct mutex mutex;
-};
+ return 0;
+}
-/**
- * struct pinmux_hog - a list item to stash mux hogs
- * @node: pinmux hog list node
- * @map: map entry responsible for this hogging
- * @pmx: the pinmux hogged by this item
- */
-struct pinmux_hog {
- struct list_head node;
- struct pinmux_map const *map;
- struct pinmux *pmx;
-};
+int pinmux_validate_map(struct pinctrl_map const *map, int i)
+{
+ if (!map->data.mux.function) {
+ pr_err("failed to register map %s (%d): no function given\n",
+ map->name, i);
+ return -EINVAL;
+ }
+
+ return 0;
+}
/**
* pin_request() - request a single pin to be muxed in, typically for GPIO
* @pin: the pin number in the global pin space
- * @function: a functional name to give to this pin, passed to the driver
- * so it knows what function to mux in, e.g. the string "gpioNN"
- * means that you want to mux in the pin for use as GPIO number NN
+ * @owner: a representation of the owner of this pin; typically the device
+ * name that controls its mux function, or the requested GPIO name
* @gpio_range: the range matching the GPIO pin if this is a request for a
* single GPIO pin
*/
static int pin_request(struct pinctrl_dev *pctldev,
- int pin, const char *function,
+ int pin, const char *owner,
struct pinctrl_gpio_range *gpio_range)
{
struct pin_desc *desc;
const struct pinmux_ops *ops = pctldev->desc->pmxops;
int status = -EINVAL;
- dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, function);
+ dev_dbg(pctldev->dev, "request pin %d for %s\n", pin, owner);
desc = pin_desc_get(pctldev, pin);
if (desc == NULL) {
@@ -110,20 +94,28 @@ static int pin_request(struct pinctrl_dev *pctldev,
goto out;
}
- if (!function) {
- dev_err(pctldev->dev, "no function name given\n");
- return -EINVAL;
- }
+ if (gpio_range) {
+ /* There's no need to support multiple GPIO requests */
+ if (desc->gpio_owner) {
+ dev_err(pctldev->dev,
+ "pin already requested\n");
+ goto out;
+ }
- spin_lock(&desc->lock);
- if (desc->mux_function) {
- spin_unlock(&desc->lock);
- dev_err(pctldev->dev,
- "pin already requested\n");
- goto out;
+ desc->gpio_owner = owner;
+ } else {
+ if (desc->mux_usecount && strcmp(desc->mux_owner, owner)) {
+ dev_err(pctldev->dev,
+ "pin already requested\n");
+ goto out;
+ }
+
+ desc->mux_usecount++;
+ if (desc->mux_usecount > 1)
+ return 0;
+
+ desc->mux_owner = owner;
}
- desc->mux_function = function;
- spin_unlock(&desc->lock);
/* Let each pin increase references to this module */
if (!try_module_get(pctldev->owner)) {
@@ -146,19 +138,26 @@ static int pin_request(struct pinctrl_dev *pctldev,
else
status = 0;
- if (status)
+ if (status) {
dev_err(pctldev->dev, "->request on device %s failed for pin %d\n",
pctldev->desc->name, pin);
+ module_put(pctldev->owner);
+ }
+
out_free_pin:
if (status) {
- spin_lock(&desc->lock);
- desc->mux_function = NULL;
- spin_unlock(&desc->lock);
+ if (gpio_range) {
+ desc->gpio_owner = NULL;
+ } else {
+ desc->mux_usecount--;
+ if (!desc->mux_usecount)
+ desc->mux_owner = NULL;
+ }
}
out:
if (status)
dev_err(pctldev->dev, "pin-%d (%s) status %d\n",
- pin, function ? : "?", status);
+ pin, owner, status);
return status;
}
@@ -170,8 +169,8 @@ out:
* @gpio_range: the range matching the GPIO pin if this is a request for a
* single GPIO pin
*
- * This function returns a pointer to the function name in use. This is used
- * for callers that dynamically allocate a function name so it can be freed
+ * This function returns a pointer to the previous owner. This is used
+ * for callers that dynamically allocate an owner name so it can be freed
* once the pin is free. This is done for GPIO request functions.
*/
static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
@@ -179,7 +178,7 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
struct pin_desc *desc;
- const char *func;
+ const char *owner;
desc = pin_desc_get(pctldev, pin);
if (desc == NULL) {
@@ -188,6 +187,12 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
return NULL;
}
+ if (!gpio_range) {
+ desc->mux_usecount--;
+ if (desc->mux_usecount)
+ return NULL;
+ }
+
/*
* If there is no kind of request function for the pin we just assume
* we got it by default and proceed.
@@ -197,99 +202,79 @@ static const char *pin_free(struct pinctrl_dev *pctldev, int pin,
else if (ops->free)
ops->free(pctldev, pin);
- spin_lock(&desc->lock);
- func = desc->mux_function;
- desc->mux_function = NULL;
- spin_unlock(&desc->lock);
+ if (gpio_range) {
+ owner = desc->gpio_owner;
+ desc->gpio_owner = NULL;
+ } else {
+ owner = desc->mux_owner;
+ desc->mux_owner = NULL;
+ desc->mux_setting = NULL;
+ }
+
module_put(pctldev->owner);
- return func;
+ return owner;
}
/**
- * pinmux_request_gpio() - request a single pin to be muxed in as GPIO
- * @gpio: the GPIO pin number from the GPIO subsystem number space
- *
- * This function should *ONLY* be used from gpiolib-based GPIO drivers,
- * as part of their gpio_request() semantics, platforms and individual drivers
- * shall *NOT* request GPIO pins to be muxed in.
+ * pinmux_request_gpio() - request pinmuxing for a GPIO pin
+ * @pctldev: pin controller device affected
+ * @pin: the pin to mux in for GPIO
+ * @range: the applicable GPIO range
*/
-int pinmux_request_gpio(unsigned gpio)
+int pinmux_request_gpio(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin, unsigned gpio)
{
char gpiostr[16];
- const char *function;
- struct pinctrl_dev *pctldev;
- struct pinctrl_gpio_range *range;
+ const char *owner;
int ret;
- int pin;
-
- ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
- if (ret)
- return -EINVAL;
-
- /* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
/* Conjure some name stating what chip and pin this is taken by */
snprintf(gpiostr, 15, "%s:%d", range->name, gpio);
- function = kstrdup(gpiostr, GFP_KERNEL);
- if (!function)
+ owner = kstrdup(gpiostr, GFP_KERNEL);
+ if (!owner)
return -EINVAL;
- ret = pin_request(pctldev, pin, function, range);
+ ret = pin_request(pctldev, pin, owner, range);
if (ret < 0)
- kfree(function);
+ kfree(owner);
return ret;
}
-EXPORT_SYMBOL_GPL(pinmux_request_gpio);
/**
- * pinmux_free_gpio() - free a single pin, currently used as GPIO
- * @gpio: the GPIO pin number from the GPIO subsystem number space
- *
- * This function should *ONLY* be used from gpiolib-based GPIO drivers,
- * as part of their gpio_free() semantics, platforms and individual drivers
- * shall *NOT* request GPIO pins to be muxed out.
+ * pinmux_free_gpio() - release a pin from GPIO muxing
+ * @pctldev: the pin controller device for the pin
+ * @pin: the affected currently GPIO-muxed in pin
+ * @range: applicable GPIO range
*/
-void pinmux_free_gpio(unsigned gpio)
+void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin,
+ struct pinctrl_gpio_range *range)
{
- struct pinctrl_dev *pctldev;
- struct pinctrl_gpio_range *range;
- int ret;
- int pin;
- const char *func;
+ const char *owner;
- ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
- if (ret)
- return;
-
- /* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
-
- func = pin_free(pctldev, pin, range);
- kfree(func);
+ owner = pin_free(pctldev, pin, range);
+ kfree(owner);
}
-EXPORT_SYMBOL_GPL(pinmux_free_gpio);
-static int pinmux_gpio_direction(unsigned gpio, bool input)
+/**
+ * pinmux_gpio_direction() - set the direction of a single muxed-in GPIO pin
+ * @pctldev: the pin controller handling this pin
+ * @range: applicable GPIO range
+ * @pin: the affected GPIO pin in this controller
+ * @input: true if we set the pin as input, false for output
+ */
+int pinmux_gpio_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin, bool input)
{
- struct pinctrl_dev *pctldev;
- struct pinctrl_gpio_range *range;
const struct pinmux_ops *ops;
int ret;
- int pin;
-
- ret = pinctrl_get_device_gpio_range(gpio, &pctldev, &range);
- if (ret)
- return ret;
ops = pctldev->desc->pmxops;
- /* Convert to the pin controllers number space */
- pin = gpio - range->base + range->pin_base;
-
if (ops->gpio_set_direction)
ret = ops->gpio_set_direction(pctldev, range, pin, input);
else
@@ -298,146 +283,89 @@ static int pinmux_gpio_direction(unsigned gpio, bool input)
return ret;
}
-/**
- * pinmux_gpio_direction_input() - request a GPIO pin to go into input mode
- * @gpio: the GPIO pin number from the GPIO subsystem number space
- *
- * This function should *ONLY* be used from gpiolib-based GPIO drivers,
- * as part of their gpio_direction_input() semantics, platforms and individual
- * drivers shall *NOT* touch pinmux GPIO calls.
- */
-int pinmux_gpio_direction_input(unsigned gpio)
-{
- return pinmux_gpio_direction(gpio, true);
-}
-EXPORT_SYMBOL_GPL(pinmux_gpio_direction_input);
-
-/**
- * pinmux_gpio_direction_output() - request a GPIO pin to go into output mode
- * @gpio: the GPIO pin number from the GPIO subsystem number space
- *
- * This function should *ONLY* be used from gpiolib-based GPIO drivers,
- * as part of their gpio_direction_output() semantics, platforms and individual
- * drivers shall *NOT* touch pinmux GPIO calls.
- */
-int pinmux_gpio_direction_output(unsigned gpio)
-{
- return pinmux_gpio_direction(gpio, false);
-}
-EXPORT_SYMBOL_GPL(pinmux_gpio_direction_output);
-
-/**
- * pinmux_register_mappings() - register a set of pinmux mappings
- * @maps: the pinmux mappings table to register, this should be marked with
- * __initdata so it can be discarded after boot, this function will
- * perform a shallow copy for the mapping entries.
- * @num_maps: the number of maps in the mapping table
- *
- * Only call this once during initialization of your machine, the function is
- * tagged as __init and won't be callable after init has completed. The map
- * passed into this function will be owned by the pinmux core and cannot be
- * freed.
- */
-int __init pinmux_register_mappings(struct pinmux_map const *maps,
- unsigned num_maps)
+static int pinmux_func_name_to_selector(struct pinctrl_dev *pctldev,
+ const char *function)
{
- void *tmp_maps;
- int i;
-
- pr_debug("add %d pinmux maps\n", num_maps);
-
- /* First sanity check the new mapping */
- for (i = 0; i < num_maps; i++) {
- if (!maps[i].name) {
- pr_err("failed to register map %d: no map name given\n",
- i);
- return -EINVAL;
- }
-
- if (!maps[i].ctrl_dev && !maps[i].ctrl_dev_name) {
- pr_err("failed to register map %s (%d): no pin control device given\n",
- maps[i].name, i);
- return -EINVAL;
- }
+ const struct pinmux_ops *ops = pctldev->desc->pmxops;
+ unsigned selector = 0;
- if (!maps[i].function) {
- pr_err("failed to register map %s (%d): no function ID given\n",
- maps[i].name, i);
- return -EINVAL;
- }
+ /* See if this pctldev has this function */
+ while (ops->list_functions(pctldev, selector) >= 0) {
+ const char *fname = ops->get_function_name(pctldev,
+ selector);
- if (!maps[i].dev && !maps[i].dev_name)
- pr_debug("add system map %s function %s with no device\n",
- maps[i].name,
- maps[i].function);
- else
- pr_debug("register map %s, function %s\n",
- maps[i].name,
- maps[i].function);
- }
+ if (!strcmp(function, fname))
+ return selector;
- /*
- * Make a copy of the map array - string pointers will end up in the
- * kernel const section anyway so these do not need to be deep copied.
- */
- if (!pinmux_maps_num) {
- /* On first call, just copy them */
- tmp_maps = kmemdup(maps,
- sizeof(struct pinmux_map) * num_maps,
- GFP_KERNEL);
- if (!tmp_maps)
- return -ENOMEM;
- } else {
- /* Subsequent calls, reallocate array to new size */
- size_t oldsize = sizeof(struct pinmux_map) * pinmux_maps_num;
- size_t newsize = sizeof(struct pinmux_map) * num_maps;
-
- tmp_maps = krealloc(pinmux_maps, oldsize + newsize, GFP_KERNEL);
- if (!tmp_maps)
- return -ENOMEM;
- memcpy((tmp_maps + oldsize), maps, newsize);
+ selector++;
}
- pinmux_maps = tmp_maps;
- pinmux_maps_num += num_maps;
- return 0;
+ pr_err("%s does not support function %s\n",
+ pinctrl_dev_get_name(pctldev), function);
+ return -EINVAL;
}
-/**
- * acquire_pins() - acquire all the pins for a certain function on a pinmux
- * @pctldev: the device to take the pins on
- * @func_selector: the function selector to acquire the pins for
- * @group_selector: the group selector containing the pins to acquire
- */
-static int acquire_pins(struct pinctrl_dev *pctldev,
- unsigned func_selector,
- unsigned group_selector)
+int pinmux_map_to_setting(struct pinctrl_map const *map,
+ struct pinctrl_setting *setting)
{
- const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
- const char *func = pmxops->get_function_name(pctldev,
- func_selector);
- const unsigned *pins;
- unsigned num_pins;
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ char const * const *groups;
+ unsigned num_groups;
int ret;
+ const char *group;
int i;
+ const unsigned *pins;
+ unsigned num_pins;
- ret = pctlops->get_group_pins(pctldev, group_selector,
- &pins, &num_pins);
- if (ret)
+ setting->data.mux.func =
+ pinmux_func_name_to_selector(pctldev, map->data.mux.function);
+ if (setting->data.mux.func < 0)
+ return setting->data.mux.func;
+
+ ret = pmxops->get_function_groups(pctldev, setting->data.mux.func,
+ &groups, &num_groups);
+ if (ret < 0)
return ret;
+ if (!num_groups)
+ return -EINVAL;
- dev_dbg(pctldev->dev, "requesting the %u pins from group %u\n",
- num_pins, group_selector);
+ if (map->data.mux.group) {
+ bool found = false;
+ group = map->data.mux.group;
+ for (i = 0; i < num_groups; i++) {
+ if (!strcmp(group, groups[i])) {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ return -EINVAL;
+ } else {
+ group = groups[0];
+ }
+
+ setting->data.mux.group = pinctrl_get_group_selector(pctldev, group);
+ if (setting->data.mux.group < 0)
+ return setting->data.mux.group;
+
+ ret = pctlops->get_group_pins(pctldev, setting->data.mux.group, &pins,
+ &num_pins);
+ if (ret) {
+ dev_err(pctldev->dev,
+ "could not get pins for device %s group selector %d\n",
+ pinctrl_dev_get_name(pctldev), setting->data.mux.group);
+ return -ENODEV;
+ }
/* Try to allocate all pins in this group, one by one */
for (i = 0; i < num_pins; i++) {
- ret = pin_request(pctldev, pins[i], func, NULL);
+ ret = pin_request(pctldev, pins[i], map->dev_name, NULL);
if (ret) {
dev_err(pctldev->dev,
- "could not get pin %d for function %s on device %s - conflicting mux mappings?\n",
- pins[i], func ? : "(undefined)",
- pinctrl_dev_get_name(pctldev));
+ "could not get request pin %d on device %s\n",
+ pins[i], pinctrl_dev_get_name(pctldev));
/* On error release all taken pins */
i--; /* this pin just failed */
for (; i >= 0; i--)
@@ -445,587 +373,101 @@ static int acquire_pins(struct pinctrl_dev *pctldev,
return -ENODEV;
}
}
+
return 0;
}
-/**
- * release_pins() - release pins taken by earlier acquirement
- * @pctldev: the device to free the pins on
- * @group_selector: the group selector containing the pins to free
- */
-static void release_pins(struct pinctrl_dev *pctldev,
- unsigned group_selector)
+void pinmux_free_setting(struct pinctrl_setting const *setting)
{
+ struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const unsigned *pins;
unsigned num_pins;
int ret;
int i;
- ret = pctlops->get_group_pins(pctldev, group_selector,
+ ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
&pins, &num_pins);
if (ret) {
- dev_err(pctldev->dev, "could not get pins to release for group selector %d\n",
- group_selector);
+ dev_err(pctldev->dev,
+ "could not get pins for device %s group selector %d\n",
+ pinctrl_dev_get_name(pctldev), setting->data.mux.group);
return;
}
+
for (i = 0; i < num_pins; i++)
pin_free(pctldev, pins[i], NULL);
}
-/**
- * pinmux_check_pin_group() - check function and pin group combo
- * @pctldev: device to check the pin group vs function for
- * @func_selector: the function selector to check the pin group for, we have
- * already looked this up in the calling function
- * @pin_group: the pin group to match to the function
- *
- * This function will check that the pinmux driver can supply the
- * selected pin group for a certain function, returns the group selector if
- * the group and function selector will work fine together, else returns
- * negative
- */
-static int pinmux_check_pin_group(struct pinctrl_dev *pctldev,
- unsigned func_selector,
- const char *pin_group)
+int pinmux_enable_setting(struct pinctrl_setting const *setting)
{
- const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+ struct pinctrl_dev *pctldev = setting->pctldev;
const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
- int ret;
-
- /*
- * If the driver does not support different pin groups for the
- * functions, we only support group 0, and assume this exists.
- */
- if (!pctlops || !pctlops->list_groups)
- return 0;
-
- /*
- * Passing NULL (no specific group) will select the first and
- * hopefully only group of pins available for this function.
- */
- if (!pin_group) {
- char const * const *groups;
- unsigned num_groups;
-
- ret = pmxops->get_function_groups(pctldev, func_selector,
- &groups, &num_groups);
- if (ret)
- return ret;
- if (num_groups < 1)
- return -EINVAL;
- ret = pinctrl_get_group_selector(pctldev, groups[0]);
- if (ret < 0) {
- dev_err(pctldev->dev,
- "function %s wants group %s but the pin controller does not seem to have that group\n",
- pmxops->get_function_name(pctldev, func_selector),
- groups[0]);
- return ret;
- }
-
- if (num_groups > 1)
- dev_dbg(pctldev->dev,
- "function %s support more than one group, default-selecting first group %s (%d)\n",
- pmxops->get_function_name(pctldev, func_selector),
- groups[0],
- ret);
-
- return ret;
- }
-
- dev_dbg(pctldev->dev,
- "check if we have pin group %s on controller %s\n",
- pin_group, pinctrl_dev_get_name(pctldev));
-
- ret = pinctrl_get_group_selector(pctldev, pin_group);
- if (ret < 0) {
- dev_dbg(pctldev->dev,
- "%s does not support pin group %s with function %s\n",
- pinctrl_dev_get_name(pctldev),
- pin_group,
- pmxops->get_function_name(pctldev, func_selector));
- }
- return ret;
-}
-
-/**
- * pinmux_search_function() - check pin control driver for a certain function
- * @pctldev: device to check for function and position
- * @map: function map containing the function and position to look for
- * @func_selector: returns the applicable function selector if found
- * @group_selector: returns the applicable group selector if found
- *
- * This will search the pinmux driver for an applicable
- * function with a specific pin group, returns 0 if these can be mapped
- * negative otherwise
- */
-static int pinmux_search_function(struct pinctrl_dev *pctldev,
- struct pinmux_map const *map,
- unsigned *func_selector,
- unsigned *group_selector)
-{
const struct pinmux_ops *ops = pctldev->desc->pmxops;
- unsigned selector = 0;
-
- /* See if this pctldev has this function */
- while (ops->list_functions(pctldev, selector) >= 0) {
- const char *fname = ops->get_function_name(pctldev,
- selector);
- int ret;
-
- if (!strcmp(map->function, fname)) {
- /* Found the function, check pin group */
- ret = pinmux_check_pin_group(pctldev, selector,
- map->group);
- if (ret < 0)
- return ret;
-
- /* This function and group selector can be used */
- *func_selector = selector;
- *group_selector = ret;
- return 0;
-
- }
- selector++;
- }
-
- pr_err("%s does not support function %s\n",
- pinctrl_dev_get_name(pctldev), map->function);
- return -EINVAL;
-}
-
-/**
- * pinmux_enable_muxmap() - enable a map entry for a certain pinmux
- */
-static int pinmux_enable_muxmap(struct pinctrl_dev *pctldev,
- struct pinmux *pmx,
- struct device *dev,
- const char *devname,
- struct pinmux_map const *map)
-{
- unsigned func_selector;
- unsigned group_selector;
- struct pinmux_group *grp;
int ret;
+ const unsigned *pins;
+ unsigned num_pins;
+ int i;
+ struct pin_desc *desc;
- /*
- * Note that we're not locking the pinmux mutex here, because
- * this is only called at pinmux initialization time when it
- * has not been added to any list and thus is not reachable
- * by anyone else.
- */
-
- if (pmx->pctldev && pmx->pctldev != pctldev) {
- dev_err(pctldev->dev,
- "different pin control devices given for device %s, function %s\n",
- devname, map->function);
- return -EINVAL;
- }
- pmx->dev = dev;
- pmx->pctldev = pctldev;
-
- /* Now go into the driver and try to match a function and group */
- ret = pinmux_search_function(pctldev, map, &func_selector,
- &group_selector);
- if (ret < 0)
- return ret;
-
- /*
- * If the function selector is already set, it needs to be identical,
- * we support several groups with one function but not several
- * functions with one or several groups in the same pinmux.
- */
- if (pmx->func_selector != UINT_MAX &&
- pmx->func_selector != func_selector) {
- dev_err(pctldev->dev,
- "dual function defines in the map for device %s\n",
- devname);
- return -EINVAL;
- }
- pmx->func_selector = func_selector;
-
- /* Now add this group selector, we may have many of them */
- grp = kmalloc(sizeof(struct pinmux_group), GFP_KERNEL);
- if (!grp)
- return -ENOMEM;
- grp->group_selector = group_selector;
- ret = acquire_pins(pctldev, func_selector, group_selector);
+ ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
+ &pins, &num_pins);
if (ret) {
- kfree(grp);
- return ret;
+ /* errors only affect debug data, so just warn */
+ dev_warn(pctldev->dev,
+ "could not get pins for group selector %d\n",
+ setting->data.mux.group);
+ num_pins = 0;
}
- list_add(&grp->node, &pmx->groups);
-
- return 0;
-}
-
-static void pinmux_free_groups(struct pinmux *pmx)
-{
- struct list_head *node, *tmp;
-
- list_for_each_safe(node, tmp, &pmx->groups) {
- struct pinmux_group *grp =
- list_entry(node, struct pinmux_group, node);
- /* Release all pins taken by this group */
- release_pins(pmx->pctldev, grp->group_selector);
- list_del(node);
- kfree(grp);
- }
-}
-
-/**
- * pinmux_get() - retrieves the pinmux for a certain device
- * @dev: the device to get the pinmux for
- * @name: an optional specific mux mapping name or NULL, the name is only
- * needed if you want to have more than one mapping per device, or if you
- * need an anonymous pinmux (not tied to any specific device)
- */
-struct pinmux *pinmux_get(struct device *dev, const char *name)
-{
- struct pinmux_map const *map = NULL;
- struct pinctrl_dev *pctldev = NULL;
- const char *devname = NULL;
- struct pinmux *pmx;
- bool found_map;
- unsigned num_maps = 0;
- int ret = -ENODEV;
- int i;
-
- /* We must have dev or ID or both */
- if (!dev && !name)
- return ERR_PTR(-EINVAL);
-
- if (dev)
- devname = dev_name(dev);
-
- pr_debug("get mux %s for device %s\n", name,
- devname ? devname : "(none)");
- /*
- * create the state cookie holder struct pinmux for each
- * mapping, this is what consumers will get when requesting
- * a pinmux handle with pinmux_get()
- */
- pmx = kzalloc(sizeof(struct pinmux), GFP_KERNEL);
- if (pmx == NULL)
- return ERR_PTR(-ENOMEM);
- mutex_init(&pmx->mutex);
- pmx->func_selector = UINT_MAX;
- INIT_LIST_HEAD(&pmx->groups);
-
- /* Iterate over the pinmux maps to locate the right ones */
- for (i = 0; i < pinmux_maps_num; i++) {
- map = &pinmux_maps[i];
- found_map = false;
-
- /*
- * First, try to find the pctldev given in the map
- */
- pctldev = get_pinctrl_dev_from_dev(map->ctrl_dev,
- map->ctrl_dev_name);
- if (!pctldev) {
- const char *devname = NULL;
-
- if (map->ctrl_dev)
- devname = dev_name(map->ctrl_dev);
- else if (map->ctrl_dev_name)
- devname = map->ctrl_dev_name;
-
- pr_warning("could not find a pinctrl device for pinmux function %s, fishy, they shall all have one\n",
- map->function);
- pr_warning("given pinctrl device name: %s",
- devname ? devname : "UNDEFINED");
-
- /* Continue to check the other mappings anyway... */
+ for (i = 0; i < num_pins; i++) {
+ desc = pin_desc_get(pctldev, pins[i]);
+ if (desc == NULL) {
+ dev_warn(pctldev->dev,
+ "could not get pin desc for pin %d\n",
+ pins[i]);
continue;
}
-
- pr_debug("in map, found pctldev %s to handle function %s",
- dev_name(pctldev->dev), map->function);
-
-
- /*
- * If we're looking for a specific named map, this must match,
- * else we loop and look for the next.
- */
- if (name != NULL) {
- if (map->name == NULL)
- continue;
- if (strcmp(map->name, name))
- continue;
- }
-
- /*
- * This is for the case where no device name is given, we
- * already know that the function name matches from above
- * code.
- */
- if (!map->dev_name && (name != NULL))
- found_map = true;
-
- /* If the mapping has a device set up it must match */
- if (map->dev_name &&
- (!devname || !strcmp(map->dev_name, devname)))
- /* MATCH! */
- found_map = true;
-
- /* If this map is applicable, then apply it */
- if (found_map) {
- ret = pinmux_enable_muxmap(pctldev, pmx, dev,
- devname, map);
- if (ret) {
- pinmux_free_groups(pmx);
- kfree(pmx);
- return ERR_PTR(ret);
- }
- num_maps++;
- }
- }
-
-
- /* We should have atleast one map, right */
- if (!num_maps) {
- pr_err("could not find any mux maps for device %s, ID %s\n",
- devname ? devname : "(anonymous)",
- name ? name : "(undefined)");
- kfree(pmx);
- return ERR_PTR(-EINVAL);
+ desc->mux_setting = &(setting->data.mux);
}
- pr_debug("found %u mux maps for device %s, UD %s\n",
- num_maps,
- devname ? devname : "(anonymous)",
- name ? name : "(undefined)");
-
- /* Add the pinmux to the global list */
- mutex_lock(&pinmux_list_mutex);
- list_add(&pmx->node, &pinmux_list);
- mutex_unlock(&pinmux_list_mutex);
-
- return pmx;
+ return ops->enable(pctldev, setting->data.mux.func,
+ setting->data.mux.group);
}
-EXPORT_SYMBOL_GPL(pinmux_get);
-/**
- * pinmux_put() - release a previously claimed pinmux
- * @pmx: a pinmux previously claimed by pinmux_get()
- */
-void pinmux_put(struct pinmux *pmx)
-{
- if (pmx == NULL)
- return;
-
- mutex_lock(&pmx->mutex);
- if (pmx->usecount)
- pr_warn("releasing pinmux with active users!\n");
- /* Free the groups and all acquired pins */
- pinmux_free_groups(pmx);
- mutex_unlock(&pmx->mutex);
-
- /* Remove from list */
- mutex_lock(&pinmux_list_mutex);
- list_del(&pmx->node);
- mutex_unlock(&pinmux_list_mutex);
-
- kfree(pmx);
-}
-EXPORT_SYMBOL_GPL(pinmux_put);
-
-/**
- * pinmux_enable() - enable a certain pinmux setting
- * @pmx: the pinmux to enable, previously claimed by pinmux_get()
- */
-int pinmux_enable(struct pinmux *pmx)
-{
- int ret = 0;
-
- if (pmx == NULL)
- return -EINVAL;
- mutex_lock(&pmx->mutex);
- if (pmx->usecount++ == 0) {
- struct pinctrl_dev *pctldev = pmx->pctldev;
- const struct pinmux_ops *ops = pctldev->desc->pmxops;
- struct pinmux_group *grp;
-
- list_for_each_entry(grp, &pmx->groups, node) {
- ret = ops->enable(pctldev, pmx->func_selector,
- grp->group_selector);
- if (ret) {
- /*
- * TODO: call disable() on all groups we called
- * enable() on to this point?
- */
- pmx->usecount--;
- break;
- }
- }
- }
- mutex_unlock(&pmx->mutex);
- return ret;
-}
-EXPORT_SYMBOL_GPL(pinmux_enable);
-
-/**
- * pinmux_disable() - disable a certain pinmux setting
- * @pmx: the pinmux to disable, previously claimed by pinmux_get()
- */
-void pinmux_disable(struct pinmux *pmx)
-{
- if (pmx == NULL)
- return;
-
- mutex_lock(&pmx->mutex);
- if (--pmx->usecount == 0) {
- struct pinctrl_dev *pctldev = pmx->pctldev;
- const struct pinmux_ops *ops = pctldev->desc->pmxops;
- struct pinmux_group *grp;
-
- list_for_each_entry(grp, &pmx->groups, node) {
- ops->disable(pctldev, pmx->func_selector,
- grp->group_selector);
- }
- }
- mutex_unlock(&pmx->mutex);
-}
-EXPORT_SYMBOL_GPL(pinmux_disable);
-
-int pinmux_check_ops(struct pinctrl_dev *pctldev)
+void pinmux_disable_setting(struct pinctrl_setting const *setting)
{
+ struct pinctrl_dev *pctldev = setting->pctldev;
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
const struct pinmux_ops *ops = pctldev->desc->pmxops;
- unsigned selector = 0;
-
- /* Check that we implement required operations */
- if (!ops->list_functions ||
- !ops->get_function_name ||
- !ops->get_function_groups ||
- !ops->enable ||
- !ops->disable)
- return -EINVAL;
-
- /* Check that all functions registered have names */
- while (ops->list_functions(pctldev, selector) >= 0) {
- const char *fname = ops->get_function_name(pctldev,
- selector);
- if (!fname) {
- pr_err("pinmux ops has no name for function%u\n",
- selector);
- return -EINVAL;
- }
- selector++;
- }
-
- return 0;
-}
-
-/* Hog a single map entry and add to the hoglist */
-static int pinmux_hog_map(struct pinctrl_dev *pctldev,
- struct pinmux_map const *map)
-{
- struct pinmux_hog *hog;
- struct pinmux *pmx;
int ret;
+ const unsigned *pins;
+ unsigned num_pins;
+ int i;
+ struct pin_desc *desc;
- if (map->dev || map->dev_name) {
- /*
- * TODO: the day we have device tree support, we can
- * traverse the device tree and hog to specific device nodes
- * without any problems, so then we can hog pinmuxes for
- * all devices that just want a static pin mux at this point.
- */
- dev_err(pctldev->dev, "map %s wants to hog a non-system pinmux, this is not going to work\n",
- map->name);
- return -EINVAL;
- }
-
- hog = kzalloc(sizeof(struct pinmux_hog), GFP_KERNEL);
- if (!hog)
- return -ENOMEM;
-
- pmx = pinmux_get(NULL, map->name);
- if (IS_ERR(pmx)) {
- kfree(hog);
- dev_err(pctldev->dev,
- "could not get the %s pinmux mapping for hogging\n",
- map->name);
- return PTR_ERR(pmx);
- }
-
- ret = pinmux_enable(pmx);
+ ret = pctlops->get_group_pins(pctldev, setting->data.mux.group,
+ &pins, &num_pins);
if (ret) {
- pinmux_put(pmx);
- kfree(hog);
- dev_err(pctldev->dev,
- "could not enable the %s pinmux mapping for hogging\n",
- map->name);
- return ret;
+ /* errors only affect debug data, so just warn */
+ dev_warn(pctldev->dev,
+ "could not get pins for group selector %d\n",
+ setting->data.mux.group);
+ num_pins = 0;
}
- hog->map = map;
- hog->pmx = pmx;
-
- dev_info(pctldev->dev, "hogged map %s, function %s\n", map->name,
- map->function);
- mutex_lock(&pctldev->pinmux_hogs_lock);
- list_add(&hog->node, &pctldev->pinmux_hogs);
- mutex_unlock(&pctldev->pinmux_hogs_lock);
-
- return 0;
-}
-
-/**
- * pinmux_hog_maps() - hog specific map entries on controller device
- * @pctldev: the pin control device to hog entries on
- *
- * When the pin controllers are registered, there may be some specific pinmux
- * map entries that need to be hogged, i.e. get+enabled until the system shuts
- * down.
- */
-int pinmux_hog_maps(struct pinctrl_dev *pctldev)
-{
- struct device *dev = pctldev->dev;
- const char *devname = dev_name(dev);
- int ret;
- int i;
-
- INIT_LIST_HEAD(&pctldev->pinmux_hogs);
- mutex_init(&pctldev->pinmux_hogs_lock);
-
- for (i = 0; i < pinmux_maps_num; i++) {
- struct pinmux_map const *map = &pinmux_maps[i];
-
- if (!map->hog_on_boot)
+ for (i = 0; i < num_pins; i++) {
+ desc = pin_desc_get(pctldev, pins[i]);
+ if (desc == NULL) {
+ dev_warn(pctldev->dev,
+ "could not get pin desc for pin %d\n",
+ pins[i]);
continue;
-
- if ((map->ctrl_dev == dev) ||
- (map->ctrl_dev_name &&
- !strcmp(map->ctrl_dev_name, devname))) {
- /* OK time to hog! */
- ret = pinmux_hog_map(pctldev, map);
- if (ret)
- return ret;
}
+ desc->mux_setting = NULL;
}
- return 0;
-}
-/**
- * pinmux_unhog_maps() - unhog specific map entries on controller device
- * @pctldev: the pin control device to unhog entries on
- */
-void pinmux_unhog_maps(struct pinctrl_dev *pctldev)
-{
- struct list_head *node, *tmp;
-
- mutex_lock(&pctldev->pinmux_hogs_lock);
- list_for_each_safe(node, tmp, &pctldev->pinmux_hogs) {
- struct pinmux_hog *hog =
- list_entry(node, struct pinmux_hog, node);
- pinmux_disable(hog->pmx);
- pinmux_put(hog->pmx);
- list_del(node);
- kfree(hog);
- }
- mutex_unlock(&pctldev->pinmux_hogs_lock);
+ ops->disable(pctldev, setting->data.mux.func, setting->data.mux.group);
}
#ifdef CONFIG_DEBUG_FS
@@ -1037,6 +479,8 @@ static int pinmux_functions_show(struct seq_file *s, void *what)
const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
unsigned func_selector = 0;
+ mutex_lock(&pinctrl_mutex);
+
while (pmxops->list_functions(pctldev, func_selector) >= 0) {
const char *func = pmxops->get_function_name(pctldev,
func_selector);
@@ -1057,24 +501,29 @@ static int pinmux_functions_show(struct seq_file *s, void *what)
seq_puts(s, "]\n");
func_selector++;
-
}
+ mutex_unlock(&pinctrl_mutex);
+
return 0;
}
static int pinmux_pins_show(struct seq_file *s, void *what)
{
struct pinctrl_dev *pctldev = s->private;
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
+ const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
unsigned i, pin;
seq_puts(s, "Pinmux settings per pin\n");
- seq_puts(s, "Format: pin (name): pinmuxfunction\n");
+ seq_puts(s, "Format: pin (name): mux_owner gpio_owner hog?\n");
+
+ mutex_lock(&pinctrl_mutex);
/* The pin number can be retrived from the pin controller descriptor */
for (i = 0; i < pctldev->desc->npins; i++) {
-
struct pin_desc *desc;
+ bool is_hog = false;
pin = pctldev->desc->pins[i].number;
desc = pin_desc_get(pctldev, pin);
@@ -1082,94 +531,52 @@ static int pinmux_pins_show(struct seq_file *s, void *what)
if (desc == NULL)
continue;
- seq_printf(s, "pin %d (%s): %s\n", pin,
+ if (desc->mux_owner &&
+ !strcmp(desc->mux_owner, pinctrl_dev_get_name(pctldev)))
+ is_hog = true;
+
+ seq_printf(s, "pin %d (%s): %s %s%s", pin,
desc->name ? desc->name : "unnamed",
- desc->mux_function ? desc->mux_function
- : "UNCLAIMED");
+ desc->mux_owner ? desc->mux_owner
+ : "(MUX UNCLAIMED)",
+ desc->gpio_owner ? desc->gpio_owner
+ : "(GPIO UNCLAIMED)",
+ is_hog ? " (HOG)" : "");
+
+ if (desc->mux_setting)
+ seq_printf(s, " function %s group %s\n",
+ pmxops->get_function_name(pctldev,
+ desc->mux_setting->func),
+ pctlops->get_group_name(pctldev,
+ desc->mux_setting->group));
+ else
+ seq_printf(s, "\n");
}
- return 0;
-}
-
-static int pinmux_hogs_show(struct seq_file *s, void *what)
-{
- struct pinctrl_dev *pctldev = s->private;
- struct pinmux_hog *hog;
-
- seq_puts(s, "Pinmux map hogs held by device\n");
-
- list_for_each_entry(hog, &pctldev->pinmux_hogs, node)
- seq_printf(s, "%s\n", hog->map->name);
+ mutex_unlock(&pinctrl_mutex);
return 0;
}
-static int pinmux_show(struct seq_file *s, void *what)
+void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map)
{
- struct pinmux *pmx;
-
- seq_puts(s, "Requested pinmuxes and their maps:\n");
- list_for_each_entry(pmx, &pinmux_list, node) {
- struct pinctrl_dev *pctldev = pmx->pctldev;
- const struct pinmux_ops *pmxops;
- const struct pinctrl_ops *pctlops;
- struct pinmux_group *grp;
-
- if (!pctldev) {
- seq_puts(s, "NO PIN CONTROLLER DEVICE\n");
- continue;
- }
-
- pmxops = pctldev->desc->pmxops;
- pctlops = pctldev->desc->pctlops;
-
- seq_printf(s, "device: %s function: %s (%u),",
- pinctrl_dev_get_name(pmx->pctldev),
- pmxops->get_function_name(pctldev,
- pmx->func_selector),
- pmx->func_selector);
-
- seq_printf(s, " groups: [");
- list_for_each_entry(grp, &pmx->groups, node) {
- seq_printf(s, " %s (%u)",
- pctlops->get_group_name(pctldev,
- grp->group_selector),
- grp->group_selector);
- }
- seq_printf(s, " ]");
-
- seq_printf(s, " users: %u map-> %s\n",
- pmx->usecount,
- pmx->dev ? dev_name(pmx->dev) : "(system)");
- }
-
- return 0;
+ seq_printf(s, "group %s\nfunction %s\n",
+ map->data.mux.group ? map->data.mux.group : "(default)",
+ map->data.mux.function);
}
-static int pinmux_maps_show(struct seq_file *s, void *what)
+void pinmux_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting)
{
- int i;
-
- seq_puts(s, "Pinmux maps:\n");
-
- for (i = 0; i < pinmux_maps_num; i++) {
- struct pinmux_map const *map = &pinmux_maps[i];
+ struct pinctrl_dev *pctldev = setting->pctldev;
+ const struct pinmux_ops *pmxops = pctldev->desc->pmxops;
+ const struct pinctrl_ops *pctlops = pctldev->desc->pctlops;
- seq_printf(s, "%s:\n", map->name);
- if (map->dev || map->dev_name)
- seq_printf(s, " device: %s\n",
- map->dev ? dev_name(map->dev) :
- map->dev_name);
- else
- seq_printf(s, " SYSTEM MUX\n");
- seq_printf(s, " controlling device %s\n",
- map->ctrl_dev ? dev_name(map->ctrl_dev) :
- map->ctrl_dev_name);
- seq_printf(s, " function: %s\n", map->function);
- seq_printf(s, " group: %s\n", map->group ? map->group :
- "(default)");
- }
- return 0;
+ seq_printf(s, "group: %s (%u) function: %s (%u)\n",
+ pctlops->get_group_name(pctldev, setting->data.mux.group),
+ setting->data.mux.group,
+ pmxops->get_function_name(pctldev, setting->data.mux.func),
+ setting->data.mux.func);
}
static int pinmux_functions_open(struct inode *inode, struct file *file)
@@ -1182,21 +589,6 @@ static int pinmux_pins_open(struct inode *inode, struct file *file)
return single_open(file, pinmux_pins_show, inode->i_private);
}
-static int pinmux_hogs_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinmux_hogs_show, inode->i_private);
-}
-
-static int pinmux_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinmux_show, NULL);
-}
-
-static int pinmux_maps_open(struct inode *inode, struct file *file)
-{
- return single_open(file, pinmux_maps_show, NULL);
-}
-
static const struct file_operations pinmux_functions_ops = {
.open = pinmux_functions_open,
.read = seq_read,
@@ -1211,27 +603,6 @@ static const struct file_operations pinmux_pins_ops = {
.release = single_release,
};
-static const struct file_operations pinmux_hogs_ops = {
- .open = pinmux_hogs_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinmux_ops = {
- .open = pinmux_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
-static const struct file_operations pinmux_maps_ops = {
- .open = pinmux_maps_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .release = single_release,
-};
-
void pinmux_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev)
{
@@ -1239,16 +610,6 @@ void pinmux_init_device_debugfs(struct dentry *devroot,
devroot, pctldev, &pinmux_functions_ops);
debugfs_create_file("pinmux-pins", S_IFREG | S_IRUGO,
devroot, pctldev, &pinmux_pins_ops);
- debugfs_create_file("pinmux-hogs", S_IFREG | S_IRUGO,
- devroot, pctldev, &pinmux_hogs_ops);
-}
-
-void pinmux_init_debugfs(struct dentry *subsys_root)
-{
- debugfs_create_file("pinmuxes", S_IFREG | S_IRUGO,
- subsys_root, NULL, &pinmux_ops);
- debugfs_create_file("pinmux-maps", S_IFREG | S_IRUGO,
- subsys_root, NULL, &pinmux_maps_ops);
}
#endif /* CONFIG_DEBUG_FS */
diff --git a/drivers/pinctrl/pinmux.h b/drivers/pinctrl/pinmux.h
index 97f52223fbc2..6fc47003e95d 100644
--- a/drivers/pinctrl/pinmux.h
+++ b/drivers/pinctrl/pinmux.h
@@ -13,11 +13,29 @@
#ifdef CONFIG_PINMUX
int pinmux_check_ops(struct pinctrl_dev *pctldev);
+
+int pinmux_validate_map(struct pinctrl_map const *map, int i);
+
+int pinmux_request_gpio(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin, unsigned gpio);
+void pinmux_free_gpio(struct pinctrl_dev *pctldev, unsigned pin,
+ struct pinctrl_gpio_range *range);
+int pinmux_gpio_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin, bool input);
+
+int pinmux_map_to_setting(struct pinctrl_map const *map,
+ struct pinctrl_setting *setting);
+void pinmux_free_setting(struct pinctrl_setting const *setting);
+int pinmux_enable_setting(struct pinctrl_setting const *setting);
+void pinmux_disable_setting(struct pinctrl_setting const *setting);
+
+void pinmux_show_map(struct seq_file *s, struct pinctrl_map const *map);
+void pinmux_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting);
void pinmux_init_device_debugfs(struct dentry *devroot,
struct pinctrl_dev *pctldev);
-void pinmux_init_debugfs(struct dentry *subsys_root);
-int pinmux_hog_maps(struct pinctrl_dev *pctldev);
-void pinmux_unhog_maps(struct pinctrl_dev *pctldev);
#else
@@ -26,21 +44,63 @@ static inline int pinmux_check_ops(struct pinctrl_dev *pctldev)
return 0;
}
-static inline void pinmux_init_device_debugfs(struct dentry *devroot,
- struct pinctrl_dev *pctldev)
+static inline int pinmux_validate_map(struct pinctrl_map const *map, int i)
{
+ return 0;
}
-static inline void pinmux_init_debugfs(struct dentry *subsys_root)
+static inline int pinmux_request_gpio(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin, unsigned gpio)
{
+ return 0;
}
-static inline int pinmux_hog_maps(struct pinctrl_dev *pctldev)
+static inline void pinmux_free_gpio(struct pinctrl_dev *pctldev,
+ unsigned pin,
+ struct pinctrl_gpio_range *range)
+{
+}
+
+static inline int pinmux_gpio_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned pin, bool input)
+{
+ return 0;
+}
+
+static inline int pinmux_map_to_setting(struct pinctrl_map const *map,
+ struct pinctrl_setting *setting)
{
return 0;
}
-static inline void pinmux_unhog_maps(struct pinctrl_dev *pctldev)
+static inline void pinmux_free_setting(struct pinctrl_setting const *setting)
+{
+}
+
+static inline int pinmux_enable_setting(struct pinctrl_setting const *setting)
+{
+ return 0;
+}
+
+static inline void pinmux_disable_setting(
+ struct pinctrl_setting const *setting)
+{
+}
+
+static inline void pinmux_show_map(struct seq_file *s,
+ struct pinctrl_map const *map)
+{
+}
+
+static inline void pinmux_show_setting(struct seq_file *s,
+ struct pinctrl_setting const *setting)
+{
+}
+
+static inline void pinmux_init_device_debugfs(struct dentry *devroot,
+ struct pinctrl_dev *pctldev)
{
}
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2dc02c972ce9..2a262f5c5c0c 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 bb947657d490..bf7e4f935b17 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 1e5290b5396d..c1a3fd8e1243 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 760c6d7624fe..bc8384c6f3eb 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 19170bb7700b..a514bf66fdd7 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 000000000000..8a582bdfdc76
--- /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 b7944f903886..e38f91be0b10 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 b0859d4183e8..99a30b513137 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 72d731c21d45..77aadde5281c 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;
@@ -571,7 +571,7 @@ static void asus_rfkill_hotplug(struct asus_wmi *asus)
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
@@ -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 8147c10161cc..d43b66742004 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 6f966d6c062b..000000000000
--- 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 d96734478324..1887e2f166a4 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 d93e962f2610..a05fc9c955d8 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 ea44abd8df48..dab91b48d22c 100644
--- a/drivers/platform/x86/eeepc-laptop.c
+++ b/drivers/platform/x86/eeepc-laptop.c
@@ -646,7 +646,7 @@ static void eeepc_rfkill_hotplug(struct eeepc_laptop *eeepc, acpi_handle handle)
} else {
dev = pci_get_slot(bus, 0);
if (dev) {
- pci_remove_bus_device(dev);
+ pci_stop_and_remove_bus_device(dev);
pci_dev_put(dev);
}
}
@@ -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 9f6e64302b45..656761380342 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 ba68d4e7a779..7387f97a2941 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_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c
index 0903a883e9f4..0a3594c7e912 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 2ee9766737ea..5ae9cd9c7e6e 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 6ee0b5c90933..79a0c2f6be53 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 fd73ea89b857..e2a34b42ddc1 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 c006dee5ebfe..8a51795aa02a 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 ea0c6075b720..d68c0002f4a2 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 dcdc1f4a4624..ee79ce64d9df 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 e549eeeda121..41781ed8301c 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/pnpbios/bioscalls.c b/drivers/pnp/pnpbios/bioscalls.c
index b859d16cf78c..769d265b221b 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 cfe86853feb2..9d4222648640 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/apm_power.c b/drivers/power/apm_power.c
index 8a612dec9139..39763015b360 100644
--- a/drivers/power/apm_power.c
+++ b/drivers/power/apm_power.c
@@ -10,6 +10,7 @@
*/
#include <linux/module.h>
+#include <linux/device.h>
#include <linux/power_supply.h>
#include <linux/apm-emulation.h>
diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c
index 9b3f2bf56e70..6dc01c255592 100644
--- a/drivers/power/max8998_charger.c
+++ b/drivers/power/max8998_charger.c
@@ -19,7 +19,6 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/module.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/slab.h>
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
index 018de2b26998..cc439fd89d8d 100644
--- a/drivers/power/power_supply.h
+++ b/drivers/power/power_supply.h
@@ -10,6 +10,10 @@
* You may use this code as per GPL version 2
*/
+struct device;
+struct device_type;
+struct power_supply;
+
#ifdef CONFIG_SYSFS
extern void power_supply_init_attrs(struct device_type *dev_type);
diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c
index da25eb94e5c6..995f966ed5b7 100644
--- a/drivers/power/power_supply_leds.c
+++ b/drivers/power/power_supply_leds.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
index b52b57ca3084..4368e7d61316 100644
--- a/drivers/power/power_supply_sysfs.c
+++ b/drivers/power/power_supply_sysfs.c
@@ -12,6 +12,7 @@
*/
#include <linux/ctype.h>
+#include <linux/device.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/stat.h>
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a229de98ae6f..36db5a441eba 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -258,14 +258,6 @@ config REGULATOR_DB8500_PRCMU
This driver supports the voltage domain regulators controlled by the
DB8500 PRCMU
-config REGULATOR_BQ24022
- tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC"
- help
- This driver controls a TI bq24022 Charger attached via
- GPIOs. The provided current regulator can enable/disable
- charging select between 100 mA and 500 mA charging current
- limit.
-
config REGULATOR_TPS6105X
tristate "TI TPS6105X Power regulators"
depends on TPS6105X
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index b5042c885d89..94b52745e957 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -16,7 +16,6 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
-obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c
deleted file mode 100644
index 9fab6d1bbe80..000000000000
--- a/drivers/regulator/bq24022.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater)
- * 1-Cell Li-Ion Charger connected via GPIOs.
- *
- * Copyright (c) 2008 Philipp Zabel
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <linux/module.h>
-#include <linux/gpio.h>
-#include <linux/regulator/bq24022.h>
-#include <linux/regulator/driver.h>
-
-
-static int bq24022_set_current_limit(struct regulator_dev *rdev,
- int min_uA, int max_uA)
-{
- struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "setting current limit to %s mA\n",
- max_uA >= 500000 ? "500" : "100");
-
- /* REVISIT: maybe return error if min_uA != 0 ? */
- gpio_set_value(pdata->gpio_iset2, max_uA >= 500000);
- return 0;
-}
-
-static int bq24022_get_current_limit(struct regulator_dev *rdev)
-{
- struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
-
- return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000;
-}
-
-static int bq24022_enable(struct regulator_dev *rdev)
-{
- struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "enabling charger\n");
-
- gpio_set_value(pdata->gpio_nce, 0);
- return 0;
-}
-
-static int bq24022_disable(struct regulator_dev *rdev)
-{
- struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "disabling charger\n");
-
- gpio_set_value(pdata->gpio_nce, 1);
- return 0;
-}
-
-static int bq24022_is_enabled(struct regulator_dev *rdev)
-{
- struct bq24022_mach_info *pdata = rdev_get_drvdata(rdev);
-
- return !gpio_get_value(pdata->gpio_nce);
-}
-
-static struct regulator_ops bq24022_ops = {
- .set_current_limit = bq24022_set_current_limit,
- .get_current_limit = bq24022_get_current_limit,
- .enable = bq24022_enable,
- .disable = bq24022_disable,
- .is_enabled = bq24022_is_enabled,
-};
-
-static struct regulator_desc bq24022_desc = {
- .name = "bq24022",
- .ops = &bq24022_ops,
- .type = REGULATOR_CURRENT,
- .owner = THIS_MODULE,
-};
-
-static int __init bq24022_probe(struct platform_device *pdev)
-{
- struct bq24022_mach_info *pdata = pdev->dev.platform_data;
- struct regulator_dev *bq24022;
- int ret;
-
- if (!pdata || !pdata->gpio_nce || !pdata->gpio_iset2)
- return -EINVAL;
-
- ret = gpio_request(pdata->gpio_nce, "ncharge_en");
- if (ret) {
- dev_dbg(&pdev->dev, "couldn't request nCE GPIO: %d\n",
- pdata->gpio_nce);
- goto err_ce;
- }
- ret = gpio_request(pdata->gpio_iset2, "charge_mode");
- if (ret) {
- dev_dbg(&pdev->dev, "couldn't request ISET2 GPIO: %d\n",
- pdata->gpio_iset2);
- goto err_iset2;
- }
- ret = gpio_direction_output(pdata->gpio_iset2, 0);
- ret = gpio_direction_output(pdata->gpio_nce, 1);
-
- bq24022 = regulator_register(&bq24022_desc, &pdev->dev,
- pdata->init_data, pdata, NULL);
- if (IS_ERR(bq24022)) {
- dev_dbg(&pdev->dev, "couldn't register regulator\n");
- ret = PTR_ERR(bq24022);
- goto err_reg;
- }
- platform_set_drvdata(pdev, bq24022);
- dev_dbg(&pdev->dev, "registered regulator\n");
-
- return 0;
-err_reg:
- gpio_free(pdata->gpio_iset2);
-err_iset2:
- gpio_free(pdata->gpio_nce);
-err_ce:
- return ret;
-}
-
-static int __devexit bq24022_remove(struct platform_device *pdev)
-{
- struct bq24022_mach_info *pdata = pdev->dev.platform_data;
- struct regulator_dev *bq24022 = platform_get_drvdata(pdev);
-
- regulator_unregister(bq24022);
- gpio_free(pdata->gpio_iset2);
- gpio_free(pdata->gpio_nce);
-
- return 0;
-}
-
-static struct platform_driver bq24022_driver = {
- .driver = {
- .name = "bq24022",
- },
- .remove = __devexit_p(bq24022_remove),
-};
-
-static int __init bq24022_init(void)
-{
- return platform_driver_probe(&bq24022_driver, bq24022_probe);
-}
-
-static void __exit bq24022_exit(void)
-{
- platform_driver_unregister(&bq24022_driver);
-}
-
-module_init(bq24022_init);
-module_exit(bq24022_exit);
-
-MODULE_AUTHOR("Philipp Zabel");
-MODULE_DESCRIPTION("TI bq24022 Li-Ion Charger driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
new file mode 100644
index 000000000000..24d880e78ec6
--- /dev/null
+++ b/drivers/remoteproc/Kconfig
@@ -0,0 +1,28 @@
+menu "Remoteproc drivers (EXPERIMENTAL)"
+
+# REMOTEPROC gets selected by whoever wants it
+config REMOTEPROC
+ tristate
+ depends on EXPERIMENTAL
+
+config OMAP_REMOTEPROC
+ tristate "OMAP remoteproc support"
+ depends on ARCH_OMAP4
+ depends on OMAP_IOMMU
+ select REMOTEPROC
+ select OMAP_MBOX_FWK
+ select RPMSG
+ help
+ Say y here to support OMAP's remote processors (dual M3
+ and DSP on OMAP4) via the remote processor framework.
+
+ Currently only supported on OMAP4.
+
+ Usually you want to say y here, in order to enable multimedia
+ use-cases to run on your platform (multimedia codecs are
+ offloaded to remote DSP processors using this framework).
+
+ It's safe to say n here if you're not interested in multimedia
+ offloading or just want a bare minimum kernel.
+
+endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
new file mode 100644
index 000000000000..5445d9b23294
--- /dev/null
+++ b/drivers/remoteproc/Makefile
@@ -0,0 +1,9 @@
+#
+# Generic framework for controlling remote processors
+#
+
+obj-$(CONFIG_REMOTEPROC) += remoteproc.o
+remoteproc-y := remoteproc_core.o
+remoteproc-y += remoteproc_debugfs.o
+remoteproc-y += remoteproc_virtio.o
+obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
new file mode 100644
index 000000000000..69425c4e86f3
--- /dev/null
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -0,0 +1,229 @@
+/*
+ * OMAP Remote Processor driver
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Hari Kanigeri <h-kanigeri2@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/remoteproc.h>
+
+#include <plat/mailbox.h>
+#include <plat/remoteproc.h>
+
+#include "omap_remoteproc.h"
+#include "remoteproc_internal.h"
+
+/**
+ * struct omap_rproc - omap remote processor state
+ * @mbox: omap mailbox handle
+ * @nb: notifier block that will be invoked on inbound mailbox messages
+ * @rproc: rproc handle
+ */
+struct omap_rproc {
+ struct omap_mbox *mbox;
+ struct notifier_block nb;
+ struct rproc *rproc;
+};
+
+/**
+ * omap_rproc_mbox_callback() - inbound mailbox message handler
+ * @this: notifier block
+ * @index: unused
+ * @data: mailbox payload
+ *
+ * This handler is invoked by omap's mailbox driver whenever a mailbox
+ * message is received. Usually, the mailbox payload simply contains
+ * the index of the virtqueue that is kicked by the remote processor,
+ * and we let remoteproc core handle it.
+ *
+ * In addition to virtqueue indices, we also have some out-of-band values
+ * that indicates different events. Those values are deliberately very
+ * big so they don't coincide with virtqueue indices.
+ */
+static int omap_rproc_mbox_callback(struct notifier_block *this,
+ unsigned long index, void *data)
+{
+ mbox_msg_t msg = (mbox_msg_t) data;
+ struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb);
+ struct device *dev = oproc->rproc->dev;
+ const char *name = oproc->rproc->name;
+
+ dev_dbg(dev, "mbox msg: 0x%x\n", msg);
+
+ switch (msg) {
+ case RP_MBOX_CRASH:
+ /* just log this for now. later, we'll also do recovery */
+ dev_err(dev, "omap rproc %s crashed\n", name);
+ break;
+ case RP_MBOX_ECHO_REPLY:
+ dev_info(dev, "received echo reply from %s\n", name);
+ break;
+ default:
+ /* msg contains the index of the triggered vring */
+ if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
+ dev_dbg(dev, "no message was found in vqid %d\n", msg);
+ }
+
+ return NOTIFY_DONE;
+}
+
+/* kick a virtqueue */
+static void omap_rproc_kick(struct rproc *rproc, int vqid)
+{
+ struct omap_rproc *oproc = rproc->priv;
+ int ret;
+
+ /* send the index of the triggered virtqueue in the mailbox payload */
+ ret = omap_mbox_msg_send(oproc->mbox, vqid);
+ if (ret)
+ dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret);
+}
+
+/*
+ * Power up the remote processor.
+ *
+ * This function will be invoked only after the firmware for this rproc
+ * was loaded, parsed successfully, and all of its resource requirements
+ * were met.
+ */
+static int omap_rproc_start(struct rproc *rproc)
+{
+ struct omap_rproc *oproc = rproc->priv;
+ struct platform_device *pdev = to_platform_device(rproc->dev);
+ struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+ int ret;
+
+ oproc->nb.notifier_call = omap_rproc_mbox_callback;
+
+ /* every omap rproc is assigned a mailbox instance for messaging */
+ oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb);
+ if (IS_ERR(oproc->mbox)) {
+ ret = PTR_ERR(oproc->mbox);
+ dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Ping the remote processor. this is only for sanity-sake;
+ * there is no functional effect whatsoever.
+ *
+ * Note that the reply will _not_ arrive immediately: this message
+ * will wait in the mailbox fifo until the remote processor is booted.
+ */
+ ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST);
+ if (ret) {
+ dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret);
+ goto put_mbox;
+ }
+
+ ret = pdata->device_enable(pdev);
+ if (ret) {
+ dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret);
+ goto put_mbox;
+ }
+
+ return 0;
+
+put_mbox:
+ omap_mbox_put(oproc->mbox, &oproc->nb);
+ return ret;
+}
+
+/* power off the remote processor */
+static int omap_rproc_stop(struct rproc *rproc)
+{
+ struct platform_device *pdev = to_platform_device(rproc->dev);
+ struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+ struct omap_rproc *oproc = rproc->priv;
+ int ret;
+
+ ret = pdata->device_shutdown(pdev);
+ if (ret)
+ return ret;
+
+ omap_mbox_put(oproc->mbox, &oproc->nb);
+
+ return 0;
+}
+
+static struct rproc_ops omap_rproc_ops = {
+ .start = omap_rproc_start,
+ .stop = omap_rproc_stop,
+ .kick = omap_rproc_kick,
+};
+
+static int __devinit omap_rproc_probe(struct platform_device *pdev)
+{
+ struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+ struct omap_rproc *oproc;
+ struct rproc *rproc;
+ int ret;
+
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(pdev->dev.parent, "dma_set_coherent_mask: %d\n", ret);
+ return ret;
+ }
+
+ rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops,
+ pdata->firmware, sizeof(*oproc));
+ if (!rproc)
+ return -ENOMEM;
+
+ oproc = rproc->priv;
+ oproc->rproc = rproc;
+
+ platform_set_drvdata(pdev, rproc);
+
+ ret = rproc_register(rproc);
+ if (ret)
+ goto free_rproc;
+
+ return 0;
+
+free_rproc:
+ rproc_free(rproc);
+ return ret;
+}
+
+static int __devexit omap_rproc_remove(struct platform_device *pdev)
+{
+ struct rproc *rproc = platform_get_drvdata(pdev);
+
+ return rproc_unregister(rproc);
+}
+
+static struct platform_driver omap_rproc_driver = {
+ .probe = omap_rproc_probe,
+ .remove = __devexit_p(omap_rproc_remove),
+ .driver = {
+ .name = "omap-rproc",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(omap_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP Remote Processor control driver");
diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h
new file mode 100644
index 000000000000..f6d2036d383d
--- /dev/null
+++ b/drivers/remoteproc/omap_remoteproc.h
@@ -0,0 +1,69 @@
+/*
+ * Remote processor messaging
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * 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 MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * 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 DAMAGE.
+ */
+
+#ifndef _OMAP_RPMSG_H
+#define _OMAP_RPMSG_H
+
+/*
+ * enum - Predefined Mailbox Messages
+ *
+ * @RP_MBOX_READY: informs the M3's that we're up and running. this is
+ * part of the init sequence sent that the M3 expects to see immediately
+ * after it is booted.
+ *
+ * @RP_MBOX_PENDING_MSG: informs the receiver that there is an inbound
+ * message waiting in its own receive-side vring. please note that currently
+ * this message is optional: alternatively, one can explicitly send the index
+ * of the triggered virtqueue itself. the preferred approach will be decided
+ * as we progress and experiment with those two different approaches.
+ *
+ * @RP_MBOX_CRASH: this message is sent if BIOS crashes
+ *
+ * @RP_MBOX_ECHO_REQUEST: a mailbox-level "ping" message.
+ *
+ * @RP_MBOX_ECHO_REPLY: a mailbox-level reply to a "ping"
+ *
+ * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the
+ * recovery mechanism (to some extent).
+ */
+enum omap_rp_mbox_messages {
+ RP_MBOX_READY = 0xFFFFFF00,
+ RP_MBOX_PENDING_MSG = 0xFFFFFF01,
+ RP_MBOX_CRASH = 0xFFFFFF02,
+ RP_MBOX_ECHO_REQUEST = 0xFFFFFF03,
+ RP_MBOX_ECHO_REPLY = 0xFFFFFF04,
+ RP_MBOX_ABORT_REQUEST = 0xFFFFFF05,
+};
+
+#endif /* _OMAP_RPMSG_H */
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
new file mode 100644
index 000000000000..ee15c68fb519
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -0,0 +1,1586 @@
+/*
+ * Remote Processor Framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Robert Tivy <rtivy@ti.com>
+ * Armando Uribe De Leon <x0095078@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/remoteproc.h>
+#include <linux/iommu.h>
+#include <linux/klist.h>
+#include <linux/elf.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_ring.h>
+#include <asm/byteorder.h>
+
+#include "remoteproc_internal.h"
+
+static void klist_rproc_get(struct klist_node *n);
+static void klist_rproc_put(struct klist_node *n);
+
+/*
+ * klist of the available remote processors.
+ *
+ * We need this in order to support name-based lookups (needed by the
+ * rproc_get_by_name()).
+ *
+ * That said, we don't use rproc_get_by_name() at this point.
+ * The use cases that do require its existence should be
+ * scrutinized, and hopefully migrated to rproc_boot() using device-based
+ * binding.
+ *
+ * If/when this materializes, we could drop the klist (and the by_name
+ * API).
+ */
+static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put);
+
+typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
+ struct resource_table *table, int len);
+typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
+
+/*
+ * This is the IOMMU fault handler we register with the IOMMU API
+ * (when relevant; not all remote processors access memory through
+ * an IOMMU).
+ *
+ * IOMMU core will invoke this handler whenever the remote processor
+ * will try to access an unmapped device address.
+ *
+ * Currently this is mostly a stub, but it will be later used to trigger
+ * the recovery of the remote processor.
+ */
+static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
+ unsigned long iova, int flags)
+{
+ dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);
+
+ /*
+ * Let the iommu core know we're not really handling this fault;
+ * we just plan to use this as a recovery trigger.
+ */
+ return -ENOSYS;
+}
+
+static int rproc_enable_iommu(struct rproc *rproc)
+{
+ struct iommu_domain *domain;
+ struct device *dev = rproc->dev;
+ int ret;
+
+ /*
+ * We currently use iommu_present() to decide if an IOMMU
+ * setup is needed.
+ *
+ * This works for simple cases, but will easily fail with
+ * platforms that do have an IOMMU, but not for this specific
+ * rproc.
+ *
+ * This will be easily solved by introducing hw capabilities
+ * that will be set by the remoteproc driver.
+ */
+ if (!iommu_present(dev->bus)) {
+ dev_dbg(dev, "iommu not found\n");
+ return 0;
+ }
+
+ domain = iommu_domain_alloc(dev->bus);
+ if (!domain) {
+ dev_err(dev, "can't alloc iommu domain\n");
+ return -ENOMEM;
+ }
+
+ iommu_set_fault_handler(domain, rproc_iommu_fault);
+
+ ret = iommu_attach_device(domain, dev);
+ if (ret) {
+ dev_err(dev, "can't attach iommu device: %d\n", ret);
+ goto free_domain;
+ }
+
+ rproc->domain = domain;
+
+ return 0;
+
+free_domain:
+ iommu_domain_free(domain);
+ return ret;
+}
+
+static void rproc_disable_iommu(struct rproc *rproc)
+{
+ struct iommu_domain *domain = rproc->domain;
+ struct device *dev = rproc->dev;
+
+ if (!domain)
+ return;
+
+ iommu_detach_device(domain, dev);
+ iommu_domain_free(domain);
+
+ return;
+}
+
+/*
+ * Some remote processors will ask us to allocate them physically contiguous
+ * memory regions (which we call "carveouts"), and map them to specific
+ * device addresses (which are hardcoded in the firmware).
+ *
+ * They may then ask us to copy objects into specific device addresses (e.g.
+ * code/data sections) or expose us certain symbols in other device address
+ * (e.g. their trace buffer).
+ *
+ * This function is an internal helper with which we can go over the allocated
+ * carveouts and translate specific device address to kernel virtual addresses
+ * so we can access the referenced memory.
+ *
+ * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too,
+ * but only on kernel direct mapped RAM memory. Instead, we're just using
+ * here the output of the DMA API, which should be more correct.
+ */
+static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+ struct rproc_mem_entry *carveout;
+ void *ptr = NULL;
+
+ list_for_each_entry(carveout, &rproc->carveouts, node) {
+ int offset = da - carveout->da;
+
+ /* try next carveout if da is too small */
+ if (offset < 0)
+ continue;
+
+ /* try next carveout if da is too large */
+ if (offset + len > carveout->len)
+ continue;
+
+ ptr = carveout->va + offset;
+
+ break;
+ }
+
+ return ptr;
+}
+
+/**
+ * rproc_load_segments() - load firmware segments to memory
+ * @rproc: remote processor which will be booted using these fw segments
+ * @elf_data: the content of the ELF firmware image
+ * @len: firmware size (in bytes)
+ *
+ * This function loads the firmware segments to memory, where the remote
+ * processor expects them.
+ *
+ * Some remote processors will expect their code and data to be placed
+ * in specific device addresses, and can't have them dynamically assigned.
+ *
+ * We currently support only those kind of remote processors, and expect
+ * the program header's paddr member to contain those addresses. We then go
+ * through the physically contiguous "carveout" memory regions which we
+ * allocated (and mapped) earlier on behalf of the remote processor,
+ * and "translate" device address to kernel addresses, so we can copy the
+ * segments where they are expected.
+ *
+ * Currently we only support remote processors that required carveout
+ * allocations and got them mapped onto their iommus. Some processors
+ * might be different: they might not have iommus, and would prefer to
+ * directly allocate memory for every segment/resource. This is not yet
+ * supported, though.
+ */
+static int
+rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
+{
+ struct device *dev = rproc->dev;
+ struct elf32_hdr *ehdr;
+ struct elf32_phdr *phdr;
+ int i, ret = 0;
+
+ ehdr = (struct elf32_hdr *)elf_data;
+ phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
+
+ /* go through the available ELF segments */
+ for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+ u32 da = phdr->p_paddr;
+ u32 memsz = phdr->p_memsz;
+ u32 filesz = phdr->p_filesz;
+ u32 offset = phdr->p_offset;
+ void *ptr;
+
+ if (phdr->p_type != PT_LOAD)
+ continue;
+
+ dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+ phdr->p_type, da, memsz, filesz);
+
+ if (filesz > memsz) {
+ dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+ filesz, memsz);
+ ret = -EINVAL;
+ break;
+ }
+
+ if (offset + filesz > len) {
+ dev_err(dev, "truncated fw: need 0x%x avail 0x%x\n",
+ offset + filesz, len);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* grab the kernel address for this device address */
+ ptr = rproc_da_to_va(rproc, da, memsz);
+ if (!ptr) {
+ dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* put the segment where the remote processor expects it */
+ if (phdr->p_filesz)
+ memcpy(ptr, elf_data + phdr->p_offset, filesz);
+
+ /*
+ * Zero out remaining memory for this segment.
+ *
+ * This isn't strictly required since dma_alloc_coherent already
+ * did this for us. albeit harmless, we may consider removing
+ * this.
+ */
+ if (memsz > filesz)
+ memset(ptr + filesz, 0, memsz - filesz);
+ }
+
+ return ret;
+}
+
+static int
+__rproc_handle_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
+{
+ struct rproc *rproc = rvdev->rproc;
+ struct device *dev = rproc->dev;
+ struct fw_rsc_vdev_vring *vring = &rsc->vring[i];
+ dma_addr_t dma;
+ void *va;
+ int ret, size, notifyid;
+
+ dev_dbg(dev, "vdev rsc: vring%d: da %x, qsz %d, align %d\n",
+ i, vring->da, vring->num, vring->align);
+
+ /* make sure reserved bytes are zeroes */
+ if (vring->reserved) {
+ dev_err(dev, "vring rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
+ /* verify queue size and vring alignment are sane */
+ if (!vring->num || !vring->align) {
+ dev_err(dev, "invalid qsz (%d) or alignment (%d)\n",
+ vring->num, vring->align);
+ return -EINVAL;
+ }
+
+ /* actual size of vring (in bytes) */
+ size = PAGE_ALIGN(vring_size(vring->num, vring->align));
+
+ if (!idr_pre_get(&rproc->notifyids, GFP_KERNEL)) {
+ dev_err(dev, "idr_pre_get failed\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Allocate non-cacheable memory for the vring. In the future
+ * this call will also configure the IOMMU for us
+ */
+ va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
+ if (!va) {
+ dev_err(dev, "dma_alloc_coherent failed\n");
+ return -EINVAL;
+ }
+
+ /* assign an rproc-wide unique index for this vring */
+ /* TODO: assign a notifyid for rvdev updates as well */
+ ret = idr_get_new(&rproc->notifyids, &rvdev->vring[i], &notifyid);
+ if (ret) {
+ dev_err(dev, "idr_get_new failed: %d\n", ret);
+ dma_free_coherent(dev, size, va, dma);
+ return ret;
+ }
+
+ /* let the rproc know the da and notifyid of this vring */
+ /* TODO: expose this to remote processor */
+ vring->da = dma;
+ vring->notifyid = notifyid;
+
+ dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va,
+ dma, size, notifyid);
+
+ rvdev->vring[i].len = vring->num;
+ rvdev->vring[i].align = vring->align;
+ rvdev->vring[i].va = va;
+ rvdev->vring[i].dma = dma;
+ rvdev->vring[i].notifyid = notifyid;
+ rvdev->vring[i].rvdev = rvdev;
+
+ return 0;
+}
+
+static void __rproc_free_vrings(struct rproc_vdev *rvdev, int i)
+{
+ struct rproc *rproc = rvdev->rproc;
+
+ for (i--; i > 0; i--) {
+ struct rproc_vring *rvring = &rvdev->vring[i];
+ int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
+
+ dma_free_coherent(rproc->dev, size, rvring->va, rvring->dma);
+ idr_remove(&rproc->notifyids, rvring->notifyid);
+ }
+}
+
+/**
+ * rproc_handle_vdev() - handle a vdev fw resource
+ * @rproc: the remote processor
+ * @rsc: the vring resource descriptor
+ * @avail: size of available data (for sanity checking the image)
+ *
+ * This resource entry requests the host to statically register a virtio
+ * device (vdev), and setup everything needed to support it. It contains
+ * everything needed to make it possible: the virtio device id, virtio
+ * device features, vrings information, virtio config space, etc...
+ *
+ * Before registering the vdev, the vrings are allocated from non-cacheable
+ * physically contiguous memory. Currently we only support two vrings per
+ * remote processor (temporary limitation). We might also want to consider
+ * doing the vring allocation only later when ->find_vqs() is invoked, and
+ * then release them upon ->del_vqs().
+ *
+ * Note: @da is currently not really handled correctly: we dynamically
+ * allocate it using the DMA API, ignoring requested hard coded addresses,
+ * and we don't take care of any required IOMMU programming. This is all
+ * going to be taken care of when the generic iommu-based DMA API will be
+ * merged. Meanwhile, statically-addressed iommu-based firmware images should
+ * use RSC_DEVMEM resource entries to map their required @da to the physical
+ * address of their base CMA region (ouch, hacky!).
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+ int avail)
+{
+ struct device *dev = rproc->dev;
+ struct rproc_vdev *rvdev;
+ int i, ret;
+
+ /* make sure resource isn't truncated */
+ if (sizeof(*rsc) + rsc->num_of_vrings * sizeof(struct fw_rsc_vdev_vring)
+ + rsc->config_len > avail) {
+ dev_err(rproc->dev, "vdev rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved[0] || rsc->reserved[1]) {
+ dev_err(dev, "vdev rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "vdev rsc: id %d, dfeatures %x, cfg len %d, %d vrings\n",
+ rsc->id, rsc->dfeatures, rsc->config_len, rsc->num_of_vrings);
+
+ /* we currently support only two vrings per rvdev */
+ if (rsc->num_of_vrings > ARRAY_SIZE(rvdev->vring)) {
+ dev_err(dev, "too many vrings: %d\n", rsc->num_of_vrings);
+ return -EINVAL;
+ }
+
+ rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
+ if (!rvdev)
+ return -ENOMEM;
+
+ rvdev->rproc = rproc;
+
+ /* allocate the vrings */
+ for (i = 0; i < rsc->num_of_vrings; i++) {
+ ret = __rproc_handle_vring(rvdev, rsc, i);
+ if (ret)
+ goto free_vrings;
+ }
+
+ /* remember the device features */
+ rvdev->dfeatures = rsc->dfeatures;
+
+ list_add_tail(&rvdev->node, &rproc->rvdevs);
+
+ /* it is now safe to add the virtio device */
+ ret = rproc_add_virtio_dev(rvdev, rsc->id);
+ if (ret)
+ goto free_vrings;
+
+ return 0;
+
+free_vrings:
+ __rproc_free_vrings(rvdev, i);
+ kfree(rvdev);
+ return ret;
+}
+
+/**
+ * rproc_handle_trace() - handle a shared trace buffer resource
+ * @rproc: the remote processor
+ * @rsc: the trace resource descriptor
+ * @avail: size of available data (for sanity checking the image)
+ *
+ * In case the remote processor dumps trace logs into memory,
+ * export it via debugfs.
+ *
+ * Currently, the 'da' member of @rsc should contain the device address
+ * where the remote processor is dumping the traces. Later we could also
+ * support dynamically allocating this address using the generic
+ * DMA API (but currently there isn't a use case for that).
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
+ int avail)
+{
+ struct rproc_mem_entry *trace;
+ struct device *dev = rproc->dev;
+ void *ptr;
+ char name[15];
+
+ if (sizeof(*rsc) > avail) {
+ dev_err(rproc->dev, "trace rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved) {
+ dev_err(dev, "trace rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
+ /* what's the kernel address of this resource ? */
+ ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
+ if (!ptr) {
+ dev_err(dev, "erroneous trace resource entry\n");
+ return -EINVAL;
+ }
+
+ trace = kzalloc(sizeof(*trace), GFP_KERNEL);
+ if (!trace) {
+ dev_err(dev, "kzalloc trace failed\n");
+ return -ENOMEM;
+ }
+
+ /* set the trace buffer dma properties */
+ trace->len = rsc->len;
+ trace->va = ptr;
+
+ /* make sure snprintf always null terminates, even if truncating */
+ snprintf(name, sizeof(name), "trace%d", rproc->num_traces);
+
+ /* create the debugfs entry */
+ trace->priv = rproc_create_trace_file(name, rproc, trace);
+ if (!trace->priv) {
+ trace->va = NULL;
+ kfree(trace);
+ return -EINVAL;
+ }
+
+ list_add_tail(&trace->node, &rproc->traces);
+
+ rproc->num_traces++;
+
+ dev_dbg(dev, "%s added: va %p, da 0x%x, len 0x%x\n", name, ptr,
+ rsc->da, rsc->len);
+
+ return 0;
+}
+
+/**
+ * rproc_handle_devmem() - handle devmem resource entry
+ * @rproc: remote processor handle
+ * @rsc: the devmem resource entry
+ * @avail: size of available data (for sanity checking the image)
+ *
+ * Remote processors commonly need to access certain on-chip peripherals.
+ *
+ * Some of these remote processors access memory via an iommu device,
+ * and might require us to configure their iommu before they can access
+ * the on-chip peripherals they need.
+ *
+ * This resource entry is a request to map such a peripheral device.
+ *
+ * These devmem entries will contain the physical address of the device in
+ * the 'pa' member. If a specific device address is expected, then 'da' will
+ * contain it (currently this is the only use case supported). 'len' will
+ * contain the size of the physical region we need to map.
+ *
+ * Currently we just "trust" those devmem entries to contain valid physical
+ * addresses, but this is going to change: we want the implementations to
+ * tell us ranges of physical addresses the firmware is allowed to request,
+ * and not allow firmwares to request access to physical addresses that
+ * are outside those ranges.
+ */
+static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
+ int avail)
+{
+ struct rproc_mem_entry *mapping;
+ int ret;
+
+ /* no point in handling this resource without a valid iommu domain */
+ if (!rproc->domain)
+ return -EINVAL;
+
+ if (sizeof(*rsc) > avail) {
+ dev_err(rproc->dev, "devmem rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved) {
+ dev_err(rproc->dev, "devmem rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
+ mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+ if (!mapping) {
+ dev_err(rproc->dev, "kzalloc mapping failed\n");
+ return -ENOMEM;
+ }
+
+ ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags);
+ if (ret) {
+ dev_err(rproc->dev, "failed to map devmem: %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * We'll need this info later when we'll want to unmap everything
+ * (e.g. on shutdown).
+ *
+ * We can't trust the remote processor not to change the resource
+ * table, so we must maintain this info independently.
+ */
+ mapping->da = rsc->da;
+ mapping->len = rsc->len;
+ list_add_tail(&mapping->node, &rproc->mappings);
+
+ dev_dbg(rproc->dev, "mapped devmem pa 0x%x, da 0x%x, len 0x%x\n",
+ rsc->pa, rsc->da, rsc->len);
+
+ return 0;
+
+out:
+ kfree(mapping);
+ return ret;
+}
+
+/**
+ * rproc_handle_carveout() - handle phys contig memory allocation requests
+ * @rproc: rproc handle
+ * @rsc: the resource entry
+ * @avail: size of available data (for image validation)
+ *
+ * This function will handle firmware requests for allocation of physically
+ * contiguous memory regions.
+ *
+ * These request entries should come first in the firmware's resource table,
+ * as other firmware entries might request placing other data objects inside
+ * these memory regions (e.g. data/code segments, trace resource entries, ...).
+ *
+ * Allocating memory this way helps utilizing the reserved physical memory
+ * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
+ * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
+ * pressure is important; it may have a substantial impact on performance.
+ */
+static int rproc_handle_carveout(struct rproc *rproc,
+ struct fw_rsc_carveout *rsc, int avail)
+{
+ struct rproc_mem_entry *carveout, *mapping;
+ struct device *dev = rproc->dev;
+ dma_addr_t dma;
+ void *va;
+ int ret;
+
+ if (sizeof(*rsc) > avail) {
+ dev_err(rproc->dev, "carveout rsc is truncated\n");
+ return -EINVAL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (rsc->reserved) {
+ dev_err(dev, "carveout rsc has non zero reserved bytes\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
+ rsc->da, rsc->pa, rsc->len, rsc->flags);
+
+ mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+ if (!mapping) {
+ dev_err(dev, "kzalloc mapping failed\n");
+ return -ENOMEM;
+ }
+
+ carveout = kzalloc(sizeof(*carveout), GFP_KERNEL);
+ if (!carveout) {
+ dev_err(dev, "kzalloc carveout failed\n");
+ ret = -ENOMEM;
+ goto free_mapping;
+ }
+
+ va = dma_alloc_coherent(dev, rsc->len, &dma, GFP_KERNEL);
+ if (!va) {
+ dev_err(dev, "failed to dma alloc carveout: %d\n", rsc->len);
+ ret = -ENOMEM;
+ goto free_carv;
+ }
+
+ dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len);
+
+ /*
+ * Ok, this is non-standard.
+ *
+ * Sometimes we can't rely on the generic iommu-based DMA API
+ * to dynamically allocate the device address and then set the IOMMU
+ * tables accordingly, because some remote processors might
+ * _require_ us to use hard coded device addresses that their
+ * firmware was compiled with.
+ *
+ * In this case, we must use the IOMMU API directly and map
+ * the memory to the device address as expected by the remote
+ * processor.
+ *
+ * Obviously such remote processor devices should not be configured
+ * to use the iommu-based DMA API: we expect 'dma' to contain the
+ * physical address in this case.
+ */
+ if (rproc->domain) {
+ ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len,
+ rsc->flags);
+ if (ret) {
+ dev_err(dev, "iommu_map failed: %d\n", ret);
+ goto dma_free;
+ }
+
+ /*
+ * We'll need this info later when we'll want to unmap
+ * everything (e.g. on shutdown).
+ *
+ * We can't trust the remote processor not to change the
+ * resource table, so we must maintain this info independently.
+ */
+ mapping->da = rsc->da;
+ mapping->len = rsc->len;
+ list_add_tail(&mapping->node, &rproc->mappings);
+
+ dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma);
+
+ /*
+ * Some remote processors might need to know the pa
+ * even though they are behind an IOMMU. E.g., OMAP4's
+ * remote M3 processor needs this so it can control
+ * on-chip hardware accelerators that are not behind
+ * the IOMMU, and therefor must know the pa.
+ *
+ * Generally we don't want to expose physical addresses
+ * if we don't have to (remote processors are generally
+ * _not_ trusted), so we might want to do this only for
+ * remote processor that _must_ have this (e.g. OMAP4's
+ * dual M3 subsystem).
+ */
+ rsc->pa = dma;
+ }
+
+ carveout->va = va;
+ carveout->len = rsc->len;
+ carveout->dma = dma;
+ carveout->da = rsc->da;
+
+ list_add_tail(&carveout->node, &rproc->carveouts);
+
+ return 0;
+
+dma_free:
+ dma_free_coherent(dev, rsc->len, va, dma);
+free_carv:
+ kfree(carveout);
+free_mapping:
+ kfree(mapping);
+ return ret;
+}
+
+/*
+ * A lookup table for resource handlers. The indices are defined in
+ * enum fw_resource_type.
+ */
+static rproc_handle_resource_t rproc_handle_rsc[] = {
+ [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
+ [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
+ [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
+ [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
+};
+
+/* handle firmware resource entries before booting the remote processor */
+static int
+rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
+{
+ struct device *dev = rproc->dev;
+ rproc_handle_resource_t handler;
+ int ret = 0, i;
+
+ for (i = 0; i < table->num; i++) {
+ int offset = table->offset[i];
+ struct fw_rsc_hdr *hdr = (void *)table + offset;
+ int avail = len - offset - sizeof(*hdr);
+ void *rsc = (void *)hdr + sizeof(*hdr);
+
+ /* make sure table isn't truncated */
+ if (avail < 0) {
+ dev_err(dev, "rsc table is truncated\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "rsc: type %d\n", hdr->type);
+
+ if (hdr->type >= RSC_LAST) {
+ dev_warn(dev, "unsupported resource %d\n", hdr->type);
+ continue;
+ }
+
+ handler = rproc_handle_rsc[hdr->type];
+ if (!handler)
+ continue;
+
+ ret = handler(rproc, rsc, avail);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/* handle firmware resource entries while registering the remote processor */
+static int
+rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
+{
+ struct device *dev = rproc->dev;
+ int ret = 0, i;
+
+ for (i = 0; i < table->num; i++) {
+ int offset = table->offset[i];
+ struct fw_rsc_hdr *hdr = (void *)table + offset;
+ int avail = len - offset - sizeof(*hdr);
+ struct fw_rsc_vdev *vrsc;
+
+ /* make sure table isn't truncated */
+ if (avail < 0) {
+ dev_err(dev, "rsc table is truncated\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
+
+ if (hdr->type != RSC_VDEV)
+ continue;
+
+ vrsc = (struct fw_rsc_vdev *)hdr->data;
+
+ ret = rproc_handle_vdev(rproc, vrsc, avail);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * rproc_find_rsc_table() - find the resource table
+ * @rproc: the rproc handle
+ * @elf_data: the content of the ELF firmware image
+ * @len: firmware size (in bytes)
+ * @tablesz: place holder for providing back the table size
+ *
+ * This function finds the resource table inside the remote processor's
+ * firmware. It is used both upon the registration of @rproc (in order
+ * to look for and register the supported virito devices), and when the
+ * @rproc is booted.
+ *
+ * Returns the pointer to the resource table if it is found, and write its
+ * size into @tablesz. If a valid table isn't found, NULL is returned
+ * (and @tablesz isn't set).
+ */
+static struct resource_table *
+rproc_find_rsc_table(struct rproc *rproc, const u8 *elf_data, size_t len,
+ int *tablesz)
+{
+ struct elf32_hdr *ehdr;
+ struct elf32_shdr *shdr;
+ const char *name_table;
+ struct device *dev = rproc->dev;
+ struct resource_table *table = NULL;
+ int i;
+
+ ehdr = (struct elf32_hdr *)elf_data;
+ shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
+ name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
+
+ /* look for the resource table and handle it */
+ for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
+ int size = shdr->sh_size;
+ int offset = shdr->sh_offset;
+
+ if (strcmp(name_table + shdr->sh_name, ".resource_table"))
+ continue;
+
+ table = (struct resource_table *)(elf_data + offset);
+
+ /* make sure we have the entire table */
+ if (offset + size > len) {
+ dev_err(dev, "resource table truncated\n");
+ return NULL;
+ }
+
+ /* make sure table has at least the header */
+ if (sizeof(struct resource_table) > size) {
+ dev_err(dev, "header-less resource table\n");
+ return NULL;
+ }
+
+ /* we don't support any version beyond the first */
+ if (table->ver != 1) {
+ dev_err(dev, "unsupported fw ver: %d\n", table->ver);
+ return NULL;
+ }
+
+ /* make sure reserved bytes are zeroes */
+ if (table->reserved[0] || table->reserved[1]) {
+ dev_err(dev, "non zero reserved bytes\n");
+ return NULL;
+ }
+
+ /* make sure the offsets array isn't truncated */
+ if (table->num * sizeof(table->offset[0]) +
+ sizeof(struct resource_table) > size) {
+ dev_err(dev, "resource table incomplete\n");
+ return NULL;
+ }
+
+ *tablesz = shdr->sh_size;
+ break;
+ }
+
+ return table;
+}
+
+/**
+ * rproc_resource_cleanup() - clean up and free all acquired resources
+ * @rproc: rproc handle
+ *
+ * This function will free all resources acquired for @rproc, and it
+ * is called whenever @rproc either shuts down or fails to boot.
+ */
+static void rproc_resource_cleanup(struct rproc *rproc)
+{
+ struct rproc_mem_entry *entry, *tmp;
+ struct device *dev = rproc->dev;
+
+ /* clean up debugfs trace entries */
+ list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
+ rproc_remove_trace_file(entry->priv);
+ rproc->num_traces--;
+ list_del(&entry->node);
+ kfree(entry);
+ }
+
+ /* clean up carveout allocations */
+ list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
+ dma_free_coherent(dev, entry->len, entry->va, entry->dma);
+ list_del(&entry->node);
+ kfree(entry);
+ }
+
+ /* clean up iommu mapping entries */
+ list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) {
+ size_t unmapped;
+
+ unmapped = iommu_unmap(rproc->domain, entry->da, entry->len);
+ if (unmapped != entry->len) {
+ /* nothing much to do besides complaining */
+ dev_err(dev, "failed to unmap %u/%u\n", entry->len,
+ unmapped);
+ }
+
+ list_del(&entry->node);
+ kfree(entry);
+ }
+}
+
+/* make sure this fw image is sane */
+static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
+{
+ const char *name = rproc->firmware;
+ struct device *dev = rproc->dev;
+ struct elf32_hdr *ehdr;
+ char class;
+
+ if (!fw) {
+ dev_err(dev, "failed to load %s\n", name);
+ return -EINVAL;
+ }
+
+ if (fw->size < sizeof(struct elf32_hdr)) {
+ dev_err(dev, "Image is too small\n");
+ return -EINVAL;
+ }
+
+ ehdr = (struct elf32_hdr *)fw->data;
+
+ /* We only support ELF32 at this point */
+ class = ehdr->e_ident[EI_CLASS];
+ if (class != ELFCLASS32) {
+ dev_err(dev, "Unsupported class: %d\n", class);
+ return -EINVAL;
+ }
+
+ /* We assume the firmware has the same endianess as the host */
+# ifdef __LITTLE_ENDIAN
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+# else /* BIG ENDIAN */
+ if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+# endif
+ dev_err(dev, "Unsupported firmware endianess\n");
+ return -EINVAL;
+ }
+
+ if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
+ dev_err(dev, "Image is too small\n");
+ return -EINVAL;
+ }
+
+ if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+ dev_err(dev, "Image is corrupted (bad magic)\n");
+ return -EINVAL;
+ }
+
+ if (ehdr->e_phnum == 0) {
+ dev_err(dev, "No loadable segments\n");
+ return -EINVAL;
+ }
+
+ if (ehdr->e_phoff > fw->size) {
+ dev_err(dev, "Firmware size is too small\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * take a firmware and boot a remote processor with it.
+ */
+static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
+{
+ struct device *dev = rproc->dev;
+ const char *name = rproc->firmware;
+ struct elf32_hdr *ehdr;
+ struct resource_table *table;
+ int ret, tablesz;
+
+ ret = rproc_fw_sanity_check(rproc, fw);
+ if (ret)
+ return ret;
+
+ ehdr = (struct elf32_hdr *)fw->data;
+
+ dev_info(dev, "Booting fw image %s, size %d\n", name, fw->size);
+
+ /*
+ * if enabling an IOMMU isn't relevant for this rproc, this is
+ * just a nop
+ */
+ ret = rproc_enable_iommu(rproc);
+ if (ret) {
+ dev_err(dev, "can't enable iommu: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * The ELF entry point is the rproc's boot addr (though this is not
+ * a configurable property of all remote processors: some will always
+ * boot at a specific hardcoded address).
+ */
+ rproc->bootaddr = ehdr->e_entry;
+
+ /* look for the resource table */
+ table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
+ if (!table)
+ goto clean_up;
+
+ /* handle fw resources which are required to boot rproc */
+ ret = rproc_handle_boot_rsc(rproc, table, tablesz);
+ if (ret) {
+ dev_err(dev, "Failed to process resources: %d\n", ret);
+ goto clean_up;
+ }
+
+ /* load the ELF segments to memory */
+ ret = rproc_load_segments(rproc, fw->data, fw->size);
+ if (ret) {
+ dev_err(dev, "Failed to load program segments: %d\n", ret);
+ goto clean_up;
+ }
+
+ /* power up the remote processor */
+ ret = rproc->ops->start(rproc);
+ if (ret) {
+ dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
+ goto clean_up;
+ }
+
+ rproc->state = RPROC_RUNNING;
+
+ dev_info(dev, "remote processor %s is now up\n", rproc->name);
+
+ return 0;
+
+clean_up:
+ rproc_resource_cleanup(rproc);
+ rproc_disable_iommu(rproc);
+ return ret;
+}
+
+/*
+ * take a firmware and look for virtio devices to register.
+ *
+ * Note: this function is called asynchronously upon registration of the
+ * remote processor (so we must wait until it completes before we try
+ * to unregister the device. one other option is just to use kref here,
+ * that might be cleaner).
+ */
+static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
+{
+ struct rproc *rproc = context;
+ struct resource_table *table;
+ int ret, tablesz;
+
+ if (rproc_fw_sanity_check(rproc, fw) < 0)
+ goto out;
+
+ /* look for the resource table */
+ table = rproc_find_rsc_table(rproc, fw->data, fw->size, &tablesz);
+ if (!table)
+ goto out;
+
+ /* look for virtio devices and register them */
+ ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
+ if (ret)
+ goto out;
+
+out:
+ if (fw)
+ release_firmware(fw);
+ /* allow rproc_unregister() contexts, if any, to proceed */
+ complete_all(&rproc->firmware_loading_complete);
+}
+
+/**
+ * rproc_boot() - boot a remote processor
+ * @rproc: handle of a remote processor
+ *
+ * Boot a remote processor (i.e. load its firmware, power it on, ...).
+ *
+ * If the remote processor is already powered on, this function immediately
+ * returns (successfully).
+ *
+ * Returns 0 on success, and an appropriate error value otherwise.
+ */
+int rproc_boot(struct rproc *rproc)
+{
+ const struct firmware *firmware_p;
+ struct device *dev;
+ int ret;
+
+ if (!rproc) {
+ pr_err("invalid rproc handle\n");
+ return -EINVAL;
+ }
+
+ dev = rproc->dev;
+
+ ret = mutex_lock_interruptible(&rproc->lock);
+ if (ret) {
+ dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
+ return ret;
+ }
+
+ /* loading a firmware is required */
+ if (!rproc->firmware) {
+ dev_err(dev, "%s: no firmware to load\n", __func__);
+ ret = -EINVAL;
+ goto unlock_mutex;
+ }
+
+ /* prevent underlying implementation from being removed */
+ if (!try_module_get(dev->driver->owner)) {
+ dev_err(dev, "%s: can't get owner\n", __func__);
+ ret = -EINVAL;
+ goto unlock_mutex;
+ }
+
+ /* skip the boot process if rproc is already powered up */
+ if (atomic_inc_return(&rproc->power) > 1) {
+ ret = 0;
+ goto unlock_mutex;
+ }
+
+ dev_info(dev, "powering up %s\n", rproc->name);
+
+ /* load firmware */
+ ret = request_firmware(&firmware_p, rproc->firmware, dev);
+ if (ret < 0) {
+ dev_err(dev, "request_firmware failed: %d\n", ret);
+ goto downref_rproc;
+ }
+
+ ret = rproc_fw_boot(rproc, firmware_p);
+
+ release_firmware(firmware_p);
+
+downref_rproc:
+ if (ret) {
+ module_put(dev->driver->owner);
+ atomic_dec(&rproc->power);
+ }
+unlock_mutex:
+ mutex_unlock(&rproc->lock);
+ return ret;
+}
+EXPORT_SYMBOL(rproc_boot);
+
+/**
+ * rproc_shutdown() - power off the remote processor
+ * @rproc: the remote processor
+ *
+ * Power off a remote processor (previously booted with rproc_boot()).
+ *
+ * In case @rproc is still being used by an additional user(s), then
+ * this function will just decrement the power refcount and exit,
+ * without really powering off the device.
+ *
+ * Every call to rproc_boot() must (eventually) be accompanied by a call
+ * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug.
+ *
+ * Notes:
+ * - we're not decrementing the rproc's refcount, only the power refcount.
+ * which means that the @rproc handle stays valid even after rproc_shutdown()
+ * returns, and users can still use it with a subsequent rproc_boot(), if
+ * needed.
+ * - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly
+ * because rproc_shutdown() _does not_ decrement the refcount of @rproc.
+ * To decrement the refcount of @rproc, use rproc_put() (but _only_ if
+ * you acquired @rproc using rproc_get_by_name()).
+ */
+void rproc_shutdown(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev;
+ int ret;
+
+ ret = mutex_lock_interruptible(&rproc->lock);
+ if (ret) {
+ dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
+ return;
+ }
+
+ /* if the remote proc is still needed, bail out */
+ if (!atomic_dec_and_test(&rproc->power))
+ goto out;
+
+ /* power off the remote processor */
+ ret = rproc->ops->stop(rproc);
+ if (ret) {
+ atomic_inc(&rproc->power);
+ dev_err(dev, "can't stop rproc: %d\n", ret);
+ goto out;
+ }
+
+ /* clean up all acquired resources */
+ rproc_resource_cleanup(rproc);
+
+ rproc_disable_iommu(rproc);
+
+ rproc->state = RPROC_OFFLINE;
+
+ dev_info(dev, "stopped remote processor %s\n", rproc->name);
+
+out:
+ mutex_unlock(&rproc->lock);
+ if (!ret)
+ module_put(dev->driver->owner);
+}
+EXPORT_SYMBOL(rproc_shutdown);
+
+/**
+ * rproc_release() - completely deletes the existence of a remote processor
+ * @kref: the rproc's kref
+ *
+ * This function should _never_ be called directly.
+ *
+ * The only reasonable location to use it is as an argument when kref_put'ing
+ * @rproc's refcount.
+ *
+ * This way it will be called when no one holds a valid pointer to this @rproc
+ * anymore (and obviously after it is removed from the rprocs klist).
+ *
+ * Note: this function is not static because rproc_vdev_release() needs it when
+ * it decrements @rproc's refcount.
+ */
+void rproc_release(struct kref *kref)
+{
+ struct rproc *rproc = container_of(kref, struct rproc, refcount);
+ struct rproc_vdev *rvdev, *rvtmp;
+
+ dev_info(rproc->dev, "removing %s\n", rproc->name);
+
+ rproc_delete_debug_dir(rproc);
+
+ /* clean up remote vdev entries */
+ list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) {
+ __rproc_free_vrings(rvdev, RVDEV_NUM_VRINGS);
+ list_del(&rvdev->node);
+ }
+
+ /*
+ * At this point no one holds a reference to rproc anymore,
+ * so we can directly unroll rproc_alloc()
+ */
+ rproc_free(rproc);
+}
+
+/* will be called when an rproc is added to the rprocs klist */
+static void klist_rproc_get(struct klist_node *n)
+{
+ struct rproc *rproc = container_of(n, struct rproc, node);
+
+ kref_get(&rproc->refcount);
+}
+
+/* will be called when an rproc is removed from the rprocs klist */
+static void klist_rproc_put(struct klist_node *n)
+{
+ struct rproc *rproc = container_of(n, struct rproc, node);
+
+ kref_put(&rproc->refcount, rproc_release);
+}
+
+static struct rproc *next_rproc(struct klist_iter *i)
+{
+ struct klist_node *n;
+
+ n = klist_next(i);
+ if (!n)
+ return NULL;
+
+ return container_of(n, struct rproc, node);
+}
+
+/**
+ * rproc_get_by_name() - find a remote processor by name and boot it
+ * @name: name of the remote processor
+ *
+ * Finds an rproc handle using the remote processor's name, and then
+ * boot it. If it's already powered on, then just immediately return
+ * (successfully).
+ *
+ * Returns the rproc handle on success, and NULL on failure.
+ *
+ * This function increments the remote processor's refcount, so always
+ * use rproc_put() to decrement it back once rproc isn't needed anymore.
+ *
+ * Note: currently this function (and its counterpart rproc_put()) are not
+ * being used. We need to scrutinize the use cases
+ * that still need them, and see if we can migrate them to use the non
+ * name-based boot/shutdown interface.
+ */
+struct rproc *rproc_get_by_name(const char *name)
+{
+ struct rproc *rproc;
+ struct klist_iter i;
+ int ret;
+
+ /* find the remote processor, and upref its refcount */
+ klist_iter_init(&rprocs, &i);
+ while ((rproc = next_rproc(&i)) != NULL)
+ if (!strcmp(rproc->name, name)) {
+ kref_get(&rproc->refcount);
+ break;
+ }
+ klist_iter_exit(&i);
+
+ /* can't find this rproc ? */
+ if (!rproc) {
+ pr_err("can't find remote processor %s\n", name);
+ return NULL;
+ }
+
+ ret = rproc_boot(rproc);
+ if (ret < 0) {
+ kref_put(&rproc->refcount, rproc_release);
+ return NULL;
+ }
+
+ return rproc;
+}
+EXPORT_SYMBOL(rproc_get_by_name);
+
+/**
+ * rproc_put() - decrement the refcount of a remote processor, and shut it down
+ * @rproc: the remote processor
+ *
+ * This function tries to shutdown @rproc, and it then decrements its
+ * refcount.
+ *
+ * After this function returns, @rproc may _not_ be used anymore, and its
+ * handle should be considered invalid.
+ *
+ * This function should be called _iff_ the @rproc handle was grabbed by
+ * calling rproc_get_by_name().
+ */
+void rproc_put(struct rproc *rproc)
+{
+ /* try to power off the remote processor */
+ rproc_shutdown(rproc);
+
+ /* downref rproc's refcount */
+ kref_put(&rproc->refcount, rproc_release);
+}
+EXPORT_SYMBOL(rproc_put);
+
+/**
+ * rproc_register() - register a remote processor
+ * @rproc: the remote processor handle to register
+ *
+ * Registers @rproc with the remoteproc framework, after it has been
+ * allocated with rproc_alloc().
+ *
+ * This is called by the platform-specific rproc implementation, whenever
+ * a new remote processor device is probed.
+ *
+ * Returns 0 on success and an appropriate error code otherwise.
+ *
+ * Note: this function initiates an asynchronous firmware loading
+ * context, which will look for virtio devices supported by the rproc's
+ * firmware.
+ *
+ * If found, those virtio devices will be created and added, so as a result
+ * of registering this remote processor, additional virtio drivers might be
+ * probed.
+ */
+int rproc_register(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev;
+ int ret = 0;
+
+ /* expose to rproc_get_by_name users */
+ klist_add_tail(&rproc->node, &rprocs);
+
+ dev_info(rproc->dev, "%s is available\n", rproc->name);
+
+ dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n");
+ dev_info(dev, "THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.\n");
+
+ /* create debugfs entries */
+ rproc_create_debug_dir(rproc);
+
+ /* rproc_unregister() calls must wait until async loader completes */
+ init_completion(&rproc->firmware_loading_complete);
+
+ /*
+ * We must retrieve early virtio configuration info from
+ * the firmware (e.g. whether to register a virtio device,
+ * what virtio features does it support, ...).
+ *
+ * We're initiating an asynchronous firmware loading, so we can
+ * be built-in kernel code, without hanging the boot process.
+ */
+ ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ rproc->firmware, dev, GFP_KERNEL,
+ rproc, rproc_fw_config_virtio);
+ if (ret < 0) {
+ dev_err(dev, "request_firmware_nowait failed: %d\n", ret);
+ complete_all(&rproc->firmware_loading_complete);
+ klist_remove(&rproc->node);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(rproc_register);
+
+/**
+ * rproc_alloc() - allocate a remote processor handle
+ * @dev: the underlying device
+ * @name: name of this remote processor
+ * @ops: platform-specific handlers (mainly start/stop)
+ * @firmware: name of firmware file to load
+ * @len: length of private data needed by the rproc driver (in bytes)
+ *
+ * Allocates a new remote processor handle, but does not register
+ * it yet.
+ *
+ * This function should be used by rproc implementations during initialization
+ * of the remote processor.
+ *
+ * After creating an rproc handle using this function, and when ready,
+ * implementations should then call rproc_register() to complete
+ * the registration of the remote processor.
+ *
+ * On success the new rproc is returned, and on failure, NULL.
+ *
+ * Note: _never_ directly deallocate @rproc, even if it was not registered
+ * yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free().
+ */
+struct rproc *rproc_alloc(struct device *dev, const char *name,
+ const struct rproc_ops *ops,
+ const char *firmware, int len)
+{
+ struct rproc *rproc;
+
+ if (!dev || !name || !ops)
+ return NULL;
+
+ rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+ if (!rproc) {
+ dev_err(dev, "%s: kzalloc failed\n", __func__);
+ return NULL;
+ }
+
+ rproc->dev = dev;
+ rproc->name = name;
+ rproc->ops = ops;
+ rproc->firmware = firmware;
+ rproc->priv = &rproc[1];
+
+ atomic_set(&rproc->power, 0);
+
+ kref_init(&rproc->refcount);
+
+ mutex_init(&rproc->lock);
+
+ idr_init(&rproc->notifyids);
+
+ INIT_LIST_HEAD(&rproc->carveouts);
+ INIT_LIST_HEAD(&rproc->mappings);
+ INIT_LIST_HEAD(&rproc->traces);
+ INIT_LIST_HEAD(&rproc->rvdevs);
+
+ rproc->state = RPROC_OFFLINE;
+
+ return rproc;
+}
+EXPORT_SYMBOL(rproc_alloc);
+
+/**
+ * rproc_free() - free an rproc handle that was allocated by rproc_alloc
+ * @rproc: the remote processor handle
+ *
+ * This function should _only_ be used if @rproc was only allocated,
+ * but not registered yet.
+ *
+ * If @rproc was already successfully registered (by calling rproc_register()),
+ * then use rproc_unregister() instead.
+ */
+void rproc_free(struct rproc *rproc)
+{
+ idr_remove_all(&rproc->notifyids);
+ idr_destroy(&rproc->notifyids);
+
+ kfree(rproc);
+}
+EXPORT_SYMBOL(rproc_free);
+
+/**
+ * rproc_unregister() - unregister a remote processor
+ * @rproc: rproc handle to unregister
+ *
+ * Unregisters a remote processor, and decrements its refcount.
+ * If its refcount drops to zero, then @rproc will be freed. If not,
+ * it will be freed later once the last reference is dropped.
+ *
+ * This function should be called when the platform specific rproc
+ * implementation decides to remove the rproc device. it should
+ * _only_ be called if a previous invocation of rproc_register()
+ * has completed successfully.
+ *
+ * After rproc_unregister() returns, @rproc is _not_ valid anymore and
+ * it shouldn't be used. More specifically, don't call rproc_free()
+ * or try to directly free @rproc after rproc_unregister() returns;
+ * none of these are needed, and calling them is a bug.
+ *
+ * Returns 0 on success and -EINVAL if @rproc isn't valid.
+ */
+int rproc_unregister(struct rproc *rproc)
+{
+ struct rproc_vdev *rvdev;
+
+ if (!rproc)
+ return -EINVAL;
+
+ /* if rproc is just being registered, wait */
+ wait_for_completion(&rproc->firmware_loading_complete);
+
+ /* clean up remote vdev entries */
+ list_for_each_entry(rvdev, &rproc->rvdevs, node)
+ rproc_remove_virtio_dev(rvdev);
+
+ /* the rproc is downref'ed as soon as it's removed from the klist */
+ klist_del(&rproc->node);
+
+ /* the rproc will only be released after its refcount drops to zero */
+ kref_put(&rproc->refcount, rproc_release);
+
+ return 0;
+}
+EXPORT_SYMBOL(rproc_unregister);
+
+static int __init remoteproc_init(void)
+{
+ rproc_init_debugfs();
+ return 0;
+}
+module_init(remoteproc_init);
+
+static void __exit remoteproc_exit(void)
+{
+ rproc_exit_debugfs();
+}
+module_exit(remoteproc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Generic Remote Processor Framework");
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
new file mode 100644
index 000000000000..70277a530133
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -0,0 +1,179 @@
+/*
+ * Remote Processor Framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Brian Swetland <swetland@google.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Robert Tivy <rtivy@ti.com>
+ * Armando Uribe De Leon <x0095078@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/debugfs.h>
+#include <linux/remoteproc.h>
+#include <linux/device.h>
+
+/* remoteproc debugfs parent dir */
+static struct dentry *rproc_dbg;
+
+/*
+ * Some remote processors may support dumping trace logs into a shared
+ * memory buffer. We expose this trace buffer using debugfs, so users
+ * can easily tell what's going on remotely.
+ *
+ * We will most probably improve the rproc tracing facilities later on,
+ * but this kind of lightweight and simple mechanism is always good to have,
+ * as it provides very early tracing with little to no dependencies at all.
+ */
+static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc_mem_entry *trace = filp->private_data;
+ int len = strnlen(trace->va, trace->len);
+
+ return simple_read_from_buffer(userbuf, count, ppos, trace->va, len);
+}
+
+static int rproc_open_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+
+ return 0;
+}
+
+static const struct file_operations trace_rproc_ops = {
+ .read = rproc_trace_read,
+ .open = rproc_open_generic,
+ .llseek = generic_file_llseek,
+};
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via debugfs. Always keep in sync with enum rproc_state
+ */
+static const char * const rproc_state_string[] = {
+ "offline",
+ "suspended",
+ "running",
+ "crashed",
+ "invalid",
+};
+
+/* expose the state of the remote processor via debugfs */
+static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc *rproc = filp->private_data;
+ unsigned int state;
+ char buf[30];
+ int i;
+
+ state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
+
+ i = snprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state],
+ rproc->state);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, i);
+}
+
+static const struct file_operations rproc_state_ops = {
+ .read = rproc_state_read,
+ .open = rproc_open_generic,
+ .llseek = generic_file_llseek,
+};
+
+/* expose the name of the remote processor via debugfs */
+static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc *rproc = filp->private_data;
+ /* need room for the name, a newline and a terminating null */
+ char buf[100];
+ int i;
+
+ i = snprintf(buf, sizeof(buf), "%.98s\n", rproc->name);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, i);
+}
+
+static const struct file_operations rproc_name_ops = {
+ .read = rproc_name_read,
+ .open = rproc_open_generic,
+ .llseek = generic_file_llseek,
+};
+
+void rproc_remove_trace_file(struct dentry *tfile)
+{
+ debugfs_remove(tfile);
+}
+
+struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
+ struct rproc_mem_entry *trace)
+{
+ struct dentry *tfile;
+
+ tfile = debugfs_create_file(name, 0400, rproc->dbg_dir,
+ trace, &trace_rproc_ops);
+ if (!tfile) {
+ dev_err(rproc->dev, "failed to create debugfs trace entry\n");
+ return NULL;
+ }
+
+ return tfile;
+}
+
+void rproc_delete_debug_dir(struct rproc *rproc)
+{
+ if (!rproc->dbg_dir)
+ return;
+
+ debugfs_remove_recursive(rproc->dbg_dir);
+}
+
+void rproc_create_debug_dir(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev;
+
+ if (!rproc_dbg)
+ return;
+
+ rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg);
+ if (!rproc->dbg_dir)
+ return;
+
+ debugfs_create_file("name", 0400, rproc->dbg_dir,
+ rproc, &rproc_name_ops);
+ debugfs_create_file("state", 0400, rproc->dbg_dir,
+ rproc, &rproc_state_ops);
+}
+
+void __init rproc_init_debugfs(void)
+{
+ if (debugfs_initialized()) {
+ rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (!rproc_dbg)
+ pr_err("can't create debugfs dir\n");
+ }
+}
+
+void __exit rproc_exit_debugfs(void)
+{
+ if (rproc_dbg)
+ debugfs_remove(rproc_dbg);
+}
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
new file mode 100644
index 000000000000..9f336d6bdef3
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -0,0 +1,44 @@
+/*
+ * Remote processor framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.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.
+ */
+
+#ifndef REMOTEPROC_INTERNAL_H
+#define REMOTEPROC_INTERNAL_H
+
+#include <linux/irqreturn.h>
+
+struct rproc;
+
+/* from remoteproc_core.c */
+void rproc_release(struct kref *kref);
+irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
+
+/* from remoteproc_virtio.c */
+int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id);
+void rproc_remove_virtio_dev(struct rproc_vdev *rvdev);
+
+/* from remoteproc_debugfs.c */
+void rproc_remove_trace_file(struct dentry *tfile);
+struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
+ struct rproc_mem_entry *trace);
+void rproc_delete_debug_dir(struct rproc *rproc);
+void rproc_create_debug_dir(struct rproc *rproc);
+void rproc_init_debugfs(void);
+void rproc_exit_debugfs(void);
+
+#endif /* REMOTEPROC_INTERNAL_H */
diff --git a/drivers/remoteproc/remoteproc_virtio.c b/drivers/remoteproc/remoteproc_virtio.c
new file mode 100644
index 000000000000..ecf612130750
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_virtio.c
@@ -0,0 +1,289 @@
+/*
+ * Remote processor messaging transport (OMAP platform-specific bits)
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.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/export.h>
+#include <linux/remoteproc.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_ring.h>
+#include <linux/err.h>
+#include <linux/kref.h>
+#include <linux/slab.h>
+
+#include "remoteproc_internal.h"
+
+/* kick the remote processor, and let it know which virtqueue to poke at */
+static void rproc_virtio_notify(struct virtqueue *vq)
+{
+ struct rproc_vring *rvring = vq->priv;
+ struct rproc *rproc = rvring->rvdev->rproc;
+ int notifyid = rvring->notifyid;
+
+ dev_dbg(rproc->dev, "kicking vq index: %d\n", notifyid);
+
+ rproc->ops->kick(rproc, notifyid);
+}
+
+/**
+ * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
+ * @rproc: handle to the remote processor
+ * @notifyid: index of the signalled virtqueue (unique per this @rproc)
+ *
+ * This function should be called by the platform-specific rproc driver,
+ * when the remote processor signals that a specific virtqueue has pending
+ * messages available.
+ *
+ * Returns IRQ_NONE if no message was found in the @notifyid virtqueue,
+ * and otherwise returns IRQ_HANDLED.
+ */
+irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
+{
+ struct rproc_vring *rvring;
+
+ dev_dbg(rproc->dev, "vq index %d is interrupted\n", notifyid);
+
+ rvring = idr_find(&rproc->notifyids, notifyid);
+ if (!rvring || !rvring->vq)
+ return IRQ_NONE;
+
+ return vring_interrupt(0, rvring->vq);
+}
+EXPORT_SYMBOL(rproc_vq_interrupt);
+
+static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
+ unsigned id,
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
+{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+ struct rproc *rproc = vdev_to_rproc(vdev);
+ struct rproc_vring *rvring;
+ struct virtqueue *vq;
+ void *addr;
+ int len, size;
+
+ /* we're temporarily limited to two virtqueues per rvdev */
+ if (id >= ARRAY_SIZE(rvdev->vring))
+ return ERR_PTR(-EINVAL);
+
+ rvring = &rvdev->vring[id];
+
+ addr = rvring->va;
+ len = rvring->len;
+
+ /* zero vring */
+ size = vring_size(len, rvring->align);
+ memset(addr, 0, size);
+
+ dev_dbg(rproc->dev, "vring%d: va %p qsz %d notifyid %d\n",
+ id, addr, len, rvring->notifyid);
+
+ /*
+ * Create the new vq, and tell virtio we're not interested in
+ * the 'weak' smp barriers, since we're talking with a real device.
+ */
+ vq = vring_new_virtqueue(len, rvring->align, vdev, false, addr,
+ rproc_virtio_notify, callback, name);
+ if (!vq) {
+ dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ rvring->vq = vq;
+ vq->priv = rvring;
+
+ return vq;
+}
+
+static void rproc_virtio_del_vqs(struct virtio_device *vdev)
+{
+ struct virtqueue *vq, *n;
+ struct rproc *rproc = vdev_to_rproc(vdev);
+ struct rproc_vring *rvring;
+
+ /* power down the remote processor before deleting vqs */
+ rproc_shutdown(rproc);
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
+ rvring = vq->priv;
+ rvring->vq = NULL;
+ vring_del_virtqueue(vq);
+ }
+}
+
+static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct rproc *rproc = vdev_to_rproc(vdev);
+ int i, ret;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i])) {
+ ret = PTR_ERR(vqs[i]);
+ goto error;
+ }
+ }
+
+ /* now that the vqs are all set, boot the remote processor */
+ ret = rproc_boot(rproc);
+ if (ret) {
+ dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ rproc_virtio_del_vqs(vdev);
+ return ret;
+}
+
+/*
+ * We don't support yet real virtio status semantics.
+ *
+ * The plan is to provide this via the VDEV resource entry
+ * which is part of the firmware: this way the remote processor
+ * will be able to access the status values as set by us.
+ */
+static u8 rproc_virtio_get_status(struct virtio_device *vdev)
+{
+ return 0;
+}
+
+static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
+{
+ dev_dbg(&vdev->dev, "status: %d\n", status);
+}
+
+static void rproc_virtio_reset(struct virtio_device *vdev)
+{
+ dev_dbg(&vdev->dev, "reset !\n");
+}
+
+/* provide the vdev features as retrieved from the firmware */
+static u32 rproc_virtio_get_features(struct virtio_device *vdev)
+{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+
+ return rvdev->dfeatures;
+}
+
+static void rproc_virtio_finalize_features(struct virtio_device *vdev)
+{
+ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+
+ /* Give virtio_ring a chance to accept features */
+ vring_transport_features(vdev);
+
+ /*
+ * Remember the finalized features of our vdev, and provide it
+ * to the remote processor once it is powered on.
+ *
+ * Similarly to the status field, we don't expose yet the negotiated
+ * features to the remote processors at this point. This will be
+ * fixed as part of a small resource table overhaul and then an
+ * extension of the virtio resource entries.
+ */
+ rvdev->gfeatures = vdev->features[0];
+}
+
+static struct virtio_config_ops rproc_virtio_config_ops = {
+ .get_features = rproc_virtio_get_features,
+ .finalize_features = rproc_virtio_finalize_features,
+ .find_vqs = rproc_virtio_find_vqs,
+ .del_vqs = rproc_virtio_del_vqs,
+ .reset = rproc_virtio_reset,
+ .set_status = rproc_virtio_set_status,
+ .get_status = rproc_virtio_get_status,
+};
+
+/*
+ * This function is called whenever vdev is released, and is responsible
+ * to decrement the remote processor's refcount taken when vdev was
+ * added.
+ *
+ * Never call this function directly; it will be called by the driver
+ * core when needed.
+ */
+static void rproc_vdev_release(struct device *dev)
+{
+ struct virtio_device *vdev = dev_to_virtio(dev);
+ struct rproc *rproc = vdev_to_rproc(vdev);
+
+ kref_put(&rproc->refcount, rproc_release);
+}
+
+/**
+ * rproc_add_virtio_dev() - register an rproc-induced virtio device
+ * @rvdev: the remote vdev
+ *
+ * This function registers a virtio device. This vdev's partent is
+ * the rproc device.
+ *
+ * Returns 0 on success or an appropriate error value otherwise.
+ */
+int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
+{
+ struct rproc *rproc = rvdev->rproc;
+ struct device *dev = rproc->dev;
+ struct virtio_device *vdev = &rvdev->vdev;
+ int ret;
+
+ vdev->id.device = id,
+ vdev->config = &rproc_virtio_config_ops,
+ vdev->dev.parent = dev;
+ vdev->dev.release = rproc_vdev_release;
+
+ /*
+ * We're indirectly making a non-temporary copy of the rproc pointer
+ * here, because drivers probed with this vdev will indirectly
+ * access the wrapping rproc.
+ *
+ * Therefore we must increment the rproc refcount here, and decrement
+ * it _only_ when the vdev is released.
+ */
+ kref_get(&rproc->refcount);
+
+ ret = register_virtio_device(vdev);
+ if (ret) {
+ kref_put(&rproc->refcount, rproc_release);
+ dev_err(dev, "failed to register vdev: %d\n", ret);
+ goto out;
+ }
+
+ dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
+
+out:
+ return ret;
+}
+
+/**
+ * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
+ * @rvdev: the remote vdev
+ *
+ * This function unregisters an existing virtio device.
+ */
+void rproc_remove_virtio_dev(struct rproc_vdev *rvdev)
+{
+ unregister_virtio_device(&rvdev->vdev);
+}
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
new file mode 100644
index 000000000000..32aead65735a
--- /dev/null
+++ b/drivers/rpmsg/Kconfig
@@ -0,0 +1,10 @@
+menu "Rpmsg drivers (EXPERIMENTAL)"
+
+# RPMSG always gets selected by whoever wants it
+config RPMSG
+ tristate
+ select VIRTIO
+ select VIRTIO_RING
+ depends on EXPERIMENTAL
+
+endmenu
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
new file mode 100644
index 000000000000..7617fcb8259f
--- /dev/null
+++ b/drivers/rpmsg/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
new file mode 100644
index 000000000000..75506ec2840e
--- /dev/null
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -0,0 +1,1054 @@
+/*
+ * Virtio-based remote processor messaging bus
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/scatterlist.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/rpmsg.h>
+#include <linux/mutex.h>
+
+/**
+ * struct virtproc_info - virtual remote processor state
+ * @vdev: the virtio device
+ * @rvq: rx virtqueue
+ * @svq: tx virtqueue
+ * @rbufs: kernel address of rx buffers
+ * @sbufs: kernel address of tx buffers
+ * @last_sbuf: index of last tx buffer used
+ * @bufs_dma: dma base addr of the buffers
+ * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders.
+ * sending a message might require waking up a dozing remote
+ * processor, which involves sleeping, hence the mutex.
+ * @endpoints: idr of local endpoints, allows fast retrieval
+ * @endpoints_lock: lock of the endpoints set
+ * @sendq: wait queue of sending contexts waiting for a tx buffers
+ * @sleepers: number of senders that are waiting for a tx buffer
+ * @ns_ept: the bus's name service endpoint
+ *
+ * This structure stores the rpmsg state of a given virtio remote processor
+ * device (there might be several virtio proc devices for each physical
+ * remote processor).
+ */
+struct virtproc_info {
+ struct virtio_device *vdev;
+ struct virtqueue *rvq, *svq;
+ void *rbufs, *sbufs;
+ int last_sbuf;
+ dma_addr_t bufs_dma;
+ struct mutex tx_lock;
+ struct idr endpoints;
+ struct mutex endpoints_lock;
+ wait_queue_head_t sendq;
+ atomic_t sleepers;
+ struct rpmsg_endpoint *ns_ept;
+};
+
+/**
+ * struct rpmsg_channel_info - internal channel info representation
+ * @name: name of service
+ * @src: local address
+ * @dst: destination address
+ */
+struct rpmsg_channel_info {
+ char name[RPMSG_NAME_SIZE];
+ u32 src;
+ u32 dst;
+};
+
+#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev)
+#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv)
+
+/*
+ * We're allocating 512 buffers of 512 bytes for communications, and then
+ * using the first 256 buffers for RX, and the last 256 buffers for TX.
+ *
+ * Each buffer will have 16 bytes for the msg header and 496 bytes for
+ * the payload.
+ *
+ * This will require a total space of 256KB for the buffers.
+ *
+ * We might also want to add support for user-provided buffers in time.
+ * This will allow bigger buffer size flexibility, and can also be used
+ * to achieve zero-copy messaging.
+ *
+ * Note that these numbers are purely a decision of this driver - we
+ * can change this without changing anything in the firmware of the remote
+ * processor.
+ */
+#define RPMSG_NUM_BUFS (512)
+#define RPMSG_BUF_SIZE (512)
+#define RPMSG_TOTAL_BUF_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE)
+
+/*
+ * Local addresses are dynamically allocated on-demand.
+ * We do not dynamically assign addresses from the low 1024 range,
+ * in order to reserve that address range for predefined services.
+ */
+#define RPMSG_RESERVED_ADDRESSES (1024)
+
+/* Address 53 is reserved for advertising remote services */
+#define RPMSG_NS_ADDR (53)
+
+/* sysfs show configuration fields */
+#define rpmsg_show_attr(field, path, format_string) \
+static ssize_t \
+field##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); \
+ \
+ return sprintf(buf, format_string, rpdev->path); \
+}
+
+/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
+rpmsg_show_attr(name, id.name, "%s\n");
+rpmsg_show_attr(src, src, "0x%x\n");
+rpmsg_show_attr(dst, dst, "0x%x\n");
+rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
+
+/*
+ * Unique (and free running) index for rpmsg devices.
+ *
+ * Yeah, we're not recycling those numbers (yet?). will be easy
+ * to change if/when we want to.
+ */
+static unsigned int rpmsg_dev_index;
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name);
+}
+
+static struct device_attribute rpmsg_dev_attrs[] = {
+ __ATTR_RO(name),
+ __ATTR_RO(modalias),
+ __ATTR_RO(dst),
+ __ATTR_RO(src),
+ __ATTR_RO(announce),
+ __ATTR_NULL
+};
+
+/* rpmsg devices and drivers are matched using the service name */
+static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev,
+ const struct rpmsg_device_id *id)
+{
+ return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;
+}
+
+/* match rpmsg channel and rpmsg driver */
+static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);
+ const struct rpmsg_device_id *ids = rpdrv->id_table;
+ unsigned int i;
+
+ for (i = 0; ids[i].name[0]; i++)
+ if (rpmsg_id_match(rpdev, &ids[i]))
+ return 1;
+
+ return 0;
+}
+
+static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT,
+ rpdev->id.name);
+}
+
+/* for more info, see below documentation of rpmsg_create_ept() */
+static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
+ struct rpmsg_channel *rpdev, rpmsg_rx_cb_t cb,
+ void *priv, u32 addr)
+{
+ int err, tmpaddr, request;
+ struct rpmsg_endpoint *ept;
+ struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev;
+
+ if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL))
+ return NULL;
+
+ ept = kzalloc(sizeof(*ept), GFP_KERNEL);
+ if (!ept) {
+ dev_err(dev, "failed to kzalloc a new ept\n");
+ return NULL;
+ }
+
+ ept->rpdev = rpdev;
+ ept->cb = cb;
+ ept->priv = priv;
+
+ /* do we need to allocate a local address ? */
+ request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr;
+
+ mutex_lock(&vrp->endpoints_lock);
+
+ /* bind the endpoint to an rpmsg address (and allocate one if needed) */
+ err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr);
+ if (err) {
+ dev_err(dev, "idr_get_new_above failed: %d\n", err);
+ goto free_ept;
+ }
+
+ /* make sure the user's address request is fulfilled, if relevant */
+ if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) {
+ dev_err(dev, "address 0x%x already in use\n", addr);
+ goto rem_idr;
+ }
+
+ ept->addr = tmpaddr;
+
+ mutex_unlock(&vrp->endpoints_lock);
+
+ return ept;
+
+rem_idr:
+ idr_remove(&vrp->endpoints, request);
+free_ept:
+ mutex_unlock(&vrp->endpoints_lock);
+ kfree(ept);
+ return NULL;
+}
+
+/**
+ * rpmsg_create_ept() - create a new rpmsg_endpoint
+ * @rpdev: rpmsg channel device
+ * @cb: rx callback handler
+ * @priv: private data for the driver's use
+ * @addr: local rpmsg address to bind with @cb
+ *
+ * Every rpmsg address in the system is bound to an rx callback (so when
+ * inbound messages arrive, they are dispatched by the rpmsg bus using the
+ * appropriate callback handler) by means of an rpmsg_endpoint struct.
+ *
+ * This function allows drivers to create such an endpoint, and by that,
+ * bind a callback, and possibly some private data too, to an rpmsg address
+ * (either one that is known in advance, or one that will be dynamically
+ * assigned for them).
+ *
+ * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint
+ * is already created for them when they are probed by the rpmsg bus
+ * (using the rx callback provided when they registered to the rpmsg bus).
+ *
+ * So things should just work for simple drivers: they already have an
+ * endpoint, their rx callback is bound to their rpmsg address, and when
+ * relevant inbound messages arrive (i.e. messages which their dst address
+ * equals to the src address of their rpmsg channel), the driver's handler
+ * is invoked to process it.
+ *
+ * That said, more complicated drivers might do need to allocate
+ * additional rpmsg addresses, and bind them to different rx callbacks.
+ * To accomplish that, those drivers need to call this function.
+ *
+ * Drivers should provide their @rpdev channel (so the new endpoint would belong
+ * to the same remote processor their channel belongs to), an rx callback
+ * function, an optional private data (which is provided back when the
+ * rx callback is invoked), and an address they want to bind with the
+ * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
+ * dynamically assign them an available rpmsg address (drivers should have
+ * a very good reason why not to always use RPMSG_ADDR_ANY here).
+ *
+ * Returns a pointer to the endpoint on success, or NULL on error.
+ */
+struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
+ rpmsg_rx_cb_t cb, void *priv, u32 addr)
+{
+ return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, addr);
+}
+EXPORT_SYMBOL(rpmsg_create_ept);
+
+/**
+ * __rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
+ * @vrp: virtproc which owns this ept
+ * @ept: endpoing to destroy
+ *
+ * An internal function which destroy an ept without assuming it is
+ * bound to an rpmsg channel. This is needed for handling the internal
+ * name service endpoint, which isn't bound to an rpmsg channel.
+ * See also __rpmsg_create_ept().
+ */
+static void
+__rpmsg_destroy_ept(struct virtproc_info *vrp, struct rpmsg_endpoint *ept)
+{
+ mutex_lock(&vrp->endpoints_lock);
+ idr_remove(&vrp->endpoints, ept->addr);
+ mutex_unlock(&vrp->endpoints_lock);
+
+ kfree(ept);
+}
+
+/**
+ * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
+ * @ept: endpoing to destroy
+ *
+ * Should be used by drivers to destroy an rpmsg endpoint previously
+ * created with rpmsg_create_ept().
+ */
+void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
+{
+ __rpmsg_destroy_ept(ept->rpdev->vrp, ept);
+}
+EXPORT_SYMBOL(rpmsg_destroy_ept);
+
+/*
+ * when an rpmsg driver is probed with a channel, we seamlessly create
+ * it an endpoint, binding its rx callback to a unique local rpmsg
+ * address.
+ *
+ * if we need to, we also announce about this channel to the remote
+ * processor (needed in case the driver is exposing an rpmsg service).
+ */
+static int rpmsg_dev_probe(struct device *dev)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
+ struct virtproc_info *vrp = rpdev->vrp;
+ struct rpmsg_endpoint *ept;
+ int err;
+
+ ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, rpdev->src);
+ if (!ept) {
+ dev_err(dev, "failed to create endpoint\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ rpdev->ept = ept;
+ rpdev->src = ept->addr;
+
+ err = rpdrv->probe(rpdev);
+ if (err) {
+ dev_err(dev, "%s: failed: %d\n", __func__, err);
+ rpmsg_destroy_ept(ept);
+ goto out;
+ }
+
+ /* need to tell remote processor's name service about this channel ? */
+ if (rpdev->announce &&
+ virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) {
+ struct rpmsg_ns_msg nsm;
+
+ strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
+ nsm.addr = rpdev->src;
+ nsm.flags = RPMSG_NS_CREATE;
+
+ err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR);
+ if (err)
+ dev_err(dev, "failed to announce service %d\n", err);
+ }
+
+out:
+ return err;
+}
+
+static int rpmsg_dev_remove(struct device *dev)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
+ struct virtproc_info *vrp = rpdev->vrp;
+ int err = 0;
+
+ /* tell remote processor's name service we're removing this channel */
+ if (rpdev->announce &&
+ virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) {
+ struct rpmsg_ns_msg nsm;
+
+ strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
+ nsm.addr = rpdev->src;
+ nsm.flags = RPMSG_NS_DESTROY;
+
+ err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR);
+ if (err)
+ dev_err(dev, "failed to announce service %d\n", err);
+ }
+
+ rpdrv->remove(rpdev);
+
+ rpmsg_destroy_ept(rpdev->ept);
+
+ return err;
+}
+
+static struct bus_type rpmsg_bus = {
+ .name = "rpmsg",
+ .match = rpmsg_dev_match,
+ .dev_attrs = rpmsg_dev_attrs,
+ .uevent = rpmsg_uevent,
+ .probe = rpmsg_dev_probe,
+ .remove = rpmsg_dev_remove,
+};
+
+/**
+ * register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus
+ * @rpdrv: pointer to a struct rpmsg_driver
+ *
+ * Returns 0 on success, and an appropriate error value on failure.
+ */
+int register_rpmsg_driver(struct rpmsg_driver *rpdrv)
+{
+ rpdrv->drv.bus = &rpmsg_bus;
+ return driver_register(&rpdrv->drv);
+}
+EXPORT_SYMBOL(register_rpmsg_driver);
+
+/**
+ * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus
+ * @rpdrv: pointer to a struct rpmsg_driver
+ *
+ * Returns 0 on success, and an appropriate error value on failure.
+ */
+void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv)
+{
+ driver_unregister(&rpdrv->drv);
+}
+EXPORT_SYMBOL(unregister_rpmsg_driver);
+
+static void rpmsg_release_device(struct device *dev)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ kfree(rpdev);
+}
+
+/*
+ * match an rpmsg channel with a channel info struct.
+ * this is used to make sure we're not creating rpmsg devices for channels
+ * that already exist.
+ */
+static int rpmsg_channel_match(struct device *dev, void *data)
+{
+ struct rpmsg_channel_info *chinfo = data;
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src)
+ return 0;
+
+ if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst)
+ return 0;
+
+ if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE))
+ return 0;
+
+ /* found a match ! */
+ return 1;
+}
+
+/*
+ * create an rpmsg channel using its name and address info.
+ * this function will be used to create both static and dynamic
+ * channels.
+ */
+static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp,
+ struct rpmsg_channel_info *chinfo)
+{
+ struct rpmsg_channel *rpdev;
+ struct device *tmp, *dev = &vrp->vdev->dev;
+ int ret;
+
+ /* make sure a similar channel doesn't already exist */
+ tmp = device_find_child(dev, chinfo, rpmsg_channel_match);
+ if (tmp) {
+ /* decrement the matched device's refcount back */
+ put_device(tmp);
+ dev_err(dev, "channel %s:%x:%x already exist\n",
+ chinfo->name, chinfo->src, chinfo->dst);
+ return NULL;
+ }
+
+ rpdev = kzalloc(sizeof(struct rpmsg_channel), GFP_KERNEL);
+ if (!rpdev) {
+ pr_err("kzalloc failed\n");
+ return NULL;
+ }
+
+ rpdev->vrp = vrp;
+ rpdev->src = chinfo->src;
+ rpdev->dst = chinfo->dst;
+
+ /*
+ * rpmsg server channels has predefined local address (for now),
+ * and their existence needs to be announced remotely
+ */
+ rpdev->announce = rpdev->src != RPMSG_ADDR_ANY ? true : false;
+
+ strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE);
+
+ /* very simple device indexing plumbing which is enough for now */
+ dev_set_name(&rpdev->dev, "rpmsg%d", rpmsg_dev_index++);
+
+ rpdev->dev.parent = &vrp->vdev->dev;
+ rpdev->dev.bus = &rpmsg_bus;
+ rpdev->dev.release = rpmsg_release_device;
+
+ ret = device_register(&rpdev->dev);
+ if (ret) {
+ dev_err(dev, "device_register failed: %d\n", ret);
+ put_device(&rpdev->dev);
+ return NULL;
+ }
+
+ return rpdev;
+}
+
+/*
+ * find an existing channel using its name + address properties,
+ * and destroy it
+ */
+static int rpmsg_destroy_channel(struct virtproc_info *vrp,
+ struct rpmsg_channel_info *chinfo)
+{
+ struct virtio_device *vdev = vrp->vdev;
+ struct device *dev;
+
+ dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match);
+ if (!dev)
+ return -EINVAL;
+
+ device_unregister(dev);
+
+ put_device(dev);
+
+ return 0;
+}
+
+/* super simple buffer "allocator" that is just enough for now */
+static void *get_a_tx_buf(struct virtproc_info *vrp)
+{
+ unsigned int len;
+ void *ret;
+
+ /* support multiple concurrent senders */
+ mutex_lock(&vrp->tx_lock);
+
+ /*
+ * either pick the next unused tx buffer
+ * (half of our buffers are used for sending messages)
+ */
+ if (vrp->last_sbuf < RPMSG_NUM_BUFS / 2)
+ ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++;
+ /* or recycle a used one */
+ else
+ ret = virtqueue_get_buf(vrp->svq, &len);
+
+ mutex_unlock(&vrp->tx_lock);
+
+ return ret;
+}
+
+/**
+ * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed
+ * @vrp: virtual remote processor state
+ *
+ * This function is called before a sender is blocked, waiting for
+ * a tx buffer to become available.
+ *
+ * If we already have blocking senders, this function merely increases
+ * the "sleepers" reference count, and exits.
+ *
+ * Otherwise, if this is the first sender to block, we also enable
+ * virtio's tx callbacks, so we'd be immediately notified when a tx
+ * buffer is consumed (we rely on virtio's tx callback in order
+ * to wake up sleeping senders as soon as a tx buffer is used by the
+ * remote processor).
+ */
+static void rpmsg_upref_sleepers(struct virtproc_info *vrp)
+{
+ /* support multiple concurrent senders */
+ mutex_lock(&vrp->tx_lock);
+
+ /* are we the first sleeping context waiting for tx buffers ? */
+ if (atomic_inc_return(&vrp->sleepers) == 1)
+ /* enable "tx-complete" interrupts before dozing off */
+ virtqueue_enable_cb(vrp->svq);
+
+ mutex_unlock(&vrp->tx_lock);
+}
+
+/**
+ * rpmsg_downref_sleepers() - disable "tx-complete" interrupts, if needed
+ * @vrp: virtual remote processor state
+ *
+ * This function is called after a sender, that waited for a tx buffer
+ * to become available, is unblocked.
+ *
+ * If we still have blocking senders, this function merely decreases
+ * the "sleepers" reference count, and exits.
+ *
+ * Otherwise, if there are no more blocking senders, we also disable
+ * virtio's tx callbacks, to avoid the overhead incurred with handling
+ * those (now redundant) interrupts.
+ */
+static void rpmsg_downref_sleepers(struct virtproc_info *vrp)
+{
+ /* support multiple concurrent senders */
+ mutex_lock(&vrp->tx_lock);
+
+ /* are we the last sleeping context waiting for tx buffers ? */
+ if (atomic_dec_and_test(&vrp->sleepers))
+ /* disable "tx-complete" interrupts */
+ virtqueue_disable_cb(vrp->svq);
+
+ mutex_unlock(&vrp->tx_lock);
+}
+
+/**
+ * rpmsg_send_offchannel_raw() - send a message across to the remote processor
+ * @rpdev: the rpmsg channel
+ * @src: source address
+ * @dst: destination address
+ * @data: payload of message
+ * @len: length of payload
+ * @wait: indicates whether caller should block in case no TX buffers available
+ *
+ * This function is the base implementation for all of the rpmsg sending API.
+ *
+ * It will send @data of length @len to @dst, and say it's from @src. The
+ * message will be sent to the remote processor which the @rpdev channel
+ * belongs to.
+ *
+ * The message is sent using one of the TX buffers that are available for
+ * communication with this remote processor.
+ *
+ * If @wait is true, the caller will be blocked until either a TX buffer is
+ * available, or 15 seconds elapses (we don't want callers to
+ * sleep indefinitely due to misbehaving remote processors), and in that
+ * case -ERESTARTSYS is returned. The number '15' itself was picked
+ * arbitrarily; there's little point in asking drivers to provide a timeout
+ * value themselves.
+ *
+ * Otherwise, if @wait is false, and there are no TX buffers available,
+ * the function will immediately fail, and -ENOMEM will be returned.
+ *
+ * Normally drivers shouldn't use this function directly; instead, drivers
+ * should use the appropriate rpmsg_{try}send{to, _offchannel} API
+ * (see include/linux/rpmsg.h).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+ void *data, int len, bool wait)
+{
+ struct virtproc_info *vrp = rpdev->vrp;
+ struct device *dev = &rpdev->dev;
+ struct scatterlist sg;
+ struct rpmsg_hdr *msg;
+ int err;
+
+ /* bcasting isn't allowed */
+ if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) {
+ dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst);
+ return -EINVAL;
+ }
+
+ /*
+ * We currently use fixed-sized buffers, and therefore the payload
+ * length is limited.
+ *
+ * One of the possible improvements here is either to support
+ * user-provided buffers (and then we can also support zero-copy
+ * messaging), or to improve the buffer allocator, to support
+ * variable-length buffer sizes.
+ */
+ if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) {
+ dev_err(dev, "message is too big (%d)\n", len);
+ return -EMSGSIZE;
+ }
+
+ /* grab a buffer */
+ msg = get_a_tx_buf(vrp);
+ if (!msg && !wait)
+ return -ENOMEM;
+
+ /* no free buffer ? wait for one (but bail after 15 seconds) */
+ while (!msg) {
+ /* enable "tx-complete" interrupts, if not already enabled */
+ rpmsg_upref_sleepers(vrp);
+
+ /*
+ * sleep until a free buffer is available or 15 secs elapse.
+ * the timeout period is not configurable because there's
+ * little point in asking drivers to specify that.
+ * if later this happens to be required, it'd be easy to add.
+ */
+ err = wait_event_interruptible_timeout(vrp->sendq,
+ (msg = get_a_tx_buf(vrp)),
+ msecs_to_jiffies(15000));
+
+ /* disable "tx-complete" interrupts if we're the last sleeper */
+ rpmsg_downref_sleepers(vrp);
+
+ /* timeout ? */
+ if (!err) {
+ dev_err(dev, "timeout waiting for a tx buffer\n");
+ return -ERESTARTSYS;
+ }
+ }
+
+ msg->len = len;
+ msg->flags = 0;
+ msg->src = src;
+ msg->dst = dst;
+ msg->reserved = 0;
+ memcpy(msg->data, data, len);
+
+ dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n",
+ msg->src, msg->dst, msg->len,
+ msg->flags, msg->reserved);
+ print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1,
+ msg, sizeof(*msg) + msg->len, true);
+
+ sg_init_one(&sg, msg, sizeof(*msg) + len);
+
+ mutex_lock(&vrp->tx_lock);
+
+ /* add message to the remote processor's virtqueue */
+ err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL);
+ if (err < 0) {
+ /*
+ * need to reclaim the buffer here, otherwise it's lost
+ * (memory won't leak, but rpmsg won't use it again for TX).
+ * this will wait for a buffer management overhaul.
+ */
+ dev_err(dev, "virtqueue_add_buf failed: %d\n", err);
+ goto out;
+ }
+
+ /* tell the remote processor it has a pending message to read */
+ virtqueue_kick(vrp->svq);
+
+ err = 0;
+out:
+ mutex_unlock(&vrp->tx_lock);
+ return err;
+}
+EXPORT_SYMBOL(rpmsg_send_offchannel_raw);
+
+/* called when an rx buffer is used, and it's time to digest a message */
+static void rpmsg_recv_done(struct virtqueue *rvq)
+{
+ struct rpmsg_hdr *msg;
+ unsigned int len;
+ struct rpmsg_endpoint *ept;
+ struct scatterlist sg;
+ struct virtproc_info *vrp = rvq->vdev->priv;
+ struct device *dev = &rvq->vdev->dev;
+ int err;
+
+ msg = virtqueue_get_buf(rvq, &len);
+ if (!msg) {
+ dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
+ return;
+ }
+
+ dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
+ msg->src, msg->dst, msg->len,
+ msg->flags, msg->reserved);
+ print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
+ msg, sizeof(*msg) + msg->len, true);
+
+ /*
+ * We currently use fixed-sized buffers, so trivially sanitize
+ * the reported payload length.
+ */
+ if (len > RPMSG_BUF_SIZE ||
+ msg->len > (len - sizeof(struct rpmsg_hdr))) {
+ dev_warn(dev, "inbound msg too big: (%d, %d)\n", len, msg->len);
+ return;
+ }
+
+ /* use the dst addr to fetch the callback of the appropriate user */
+ mutex_lock(&vrp->endpoints_lock);
+ ept = idr_find(&vrp->endpoints, msg->dst);
+ mutex_unlock(&vrp->endpoints_lock);
+
+ if (ept && ept->cb)
+ ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src);
+ else
+ dev_warn(dev, "msg received with no recepient\n");
+
+ /* publish the real size of the buffer */
+ sg_init_one(&sg, msg, RPMSG_BUF_SIZE);
+
+ /* add the buffer back to the remote processor's virtqueue */
+ err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
+ if (err < 0) {
+ dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
+ return;
+ }
+
+ /* tell the remote processor we added another available rx buffer */
+ virtqueue_kick(vrp->rvq);
+}
+
+/*
+ * This is invoked whenever the remote processor completed processing
+ * a TX msg we just sent it, and the buffer is put back to the used ring.
+ *
+ * Normally, though, we suppress this "tx complete" interrupt in order to
+ * avoid the incurred overhead.
+ */
+static void rpmsg_xmit_done(struct virtqueue *svq)
+{
+ struct virtproc_info *vrp = svq->vdev->priv;
+
+ dev_dbg(&svq->vdev->dev, "%s\n", __func__);
+
+ /* wake up potential senders that are waiting for a tx buffer */
+ wake_up_interruptible(&vrp->sendq);
+}
+
+/* invoked when a name service announcement arrives */
+static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct rpmsg_ns_msg *msg = data;
+ struct rpmsg_channel *newch;
+ struct rpmsg_channel_info chinfo;
+ struct virtproc_info *vrp = priv;
+ struct device *dev = &vrp->vdev->dev;
+ int ret;
+
+ print_hex_dump(KERN_DEBUG, "NS announcement: ",
+ DUMP_PREFIX_NONE, 16, 1,
+ data, len, true);
+
+ if (len != sizeof(*msg)) {
+ dev_err(dev, "malformed ns msg (%d)\n", len);
+ return;
+ }
+
+ /*
+ * the name service ept does _not_ belong to a real rpmsg channel,
+ * and is handled by the rpmsg bus itself.
+ * for sanity reasons, make sure a valid rpdev has _not_ sneaked
+ * in somehow.
+ */
+ if (rpdev) {
+ dev_err(dev, "anomaly: ns ept has an rpdev handle\n");
+ return;
+ }
+
+ /* don't trust the remote processor for null terminating the name */
+ msg->name[RPMSG_NAME_SIZE - 1] = '\0';
+
+ dev_info(dev, "%sing channel %s addr 0x%x\n",
+ msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat",
+ msg->name, msg->addr);
+
+ strncpy(chinfo.name, msg->name, sizeof(chinfo.name));
+ chinfo.src = RPMSG_ADDR_ANY;
+ chinfo.dst = msg->addr;
+
+ if (msg->flags & RPMSG_NS_DESTROY) {
+ ret = rpmsg_destroy_channel(vrp, &chinfo);
+ if (ret)
+ dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);
+ } else {
+ newch = rpmsg_create_channel(vrp, &chinfo);
+ if (!newch)
+ dev_err(dev, "rpmsg_create_channel failed\n");
+ }
+}
+
+static int rpmsg_probe(struct virtio_device *vdev)
+{
+ vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };
+ const char *names[] = { "input", "output" };
+ struct virtqueue *vqs[2];
+ struct virtproc_info *vrp;
+ void *bufs_va;
+ int err = 0, i;
+
+ vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
+ if (!vrp)
+ return -ENOMEM;
+
+ vrp->vdev = vdev;
+
+ idr_init(&vrp->endpoints);
+ mutex_init(&vrp->endpoints_lock);
+ mutex_init(&vrp->tx_lock);
+ init_waitqueue_head(&vrp->sendq);
+
+ /* We expect two virtqueues, rx and tx (and in this order) */
+ err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names);
+ if (err)
+ goto free_vrp;
+
+ vrp->rvq = vqs[0];
+ vrp->svq = vqs[1];
+
+ /* allocate coherent memory for the buffers */
+ bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+ &vrp->bufs_dma, GFP_KERNEL);
+ if (!bufs_va)
+ goto vqs_del;
+
+ dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%llx\n", bufs_va,
+ (unsigned long long)vrp->bufs_dma);
+
+ /* half of the buffers is dedicated for RX */
+ vrp->rbufs = bufs_va;
+
+ /* and half is dedicated for TX */
+ vrp->sbufs = bufs_va + RPMSG_TOTAL_BUF_SPACE / 2;
+
+ /* set up the receive buffers */
+ for (i = 0; i < RPMSG_NUM_BUFS / 2; i++) {
+ struct scatterlist sg;
+ void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE;
+
+ sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE);
+
+ err = virtqueue_add_buf(vrp->rvq, &sg, 0, 1, cpu_addr,
+ GFP_KERNEL);
+ WARN_ON(err < 0); /* sanity check; this can't really happen */
+ }
+
+ /* suppress "tx-complete" interrupts */
+ virtqueue_disable_cb(vrp->svq);
+
+ vdev->priv = vrp;
+
+ /* if supported by the remote processor, enable the name service */
+ if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) {
+ /* a dedicated endpoint handles the name service msgs */
+ vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb,
+ vrp, RPMSG_NS_ADDR);
+ if (!vrp->ns_ept) {
+ dev_err(&vdev->dev, "failed to create the ns ept\n");
+ err = -ENOMEM;
+ goto free_coherent;
+ }
+ }
+
+ /* tell the remote processor it can start sending messages */
+ virtqueue_kick(vrp->rvq);
+
+ dev_info(&vdev->dev, "rpmsg host is online\n");
+
+ return 0;
+
+free_coherent:
+ dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va,
+ vrp->bufs_dma);
+vqs_del:
+ vdev->config->del_vqs(vrp->vdev);
+free_vrp:
+ kfree(vrp);
+ return err;
+}
+
+static int rpmsg_remove_device(struct device *dev, void *data)
+{
+ device_unregister(dev);
+
+ return 0;
+}
+
+static void __devexit rpmsg_remove(struct virtio_device *vdev)
+{
+ struct virtproc_info *vrp = vdev->priv;
+ int ret;
+
+ vdev->config->reset(vdev);
+
+ ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device);
+ if (ret)
+ dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret);
+
+ if (vrp->ns_ept)
+ __rpmsg_destroy_ept(vrp, vrp->ns_ept);
+
+ idr_remove_all(&vrp->endpoints);
+ idr_destroy(&vrp->endpoints);
+
+ vdev->config->del_vqs(vrp->vdev);
+
+ dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+ vrp->rbufs, vrp->bufs_dma);
+
+ kfree(vrp);
+}
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static unsigned int features[] = {
+ VIRTIO_RPMSG_F_NS,
+};
+
+static struct virtio_driver virtio_ipc_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = rpmsg_probe,
+ .remove = __devexit_p(rpmsg_remove),
+};
+
+static int __init rpmsg_init(void)
+{
+ int ret;
+
+ ret = bus_register(&rpmsg_bus);
+ if (ret) {
+ pr_err("failed to register rpmsg bus: %d\n", ret);
+ return ret;
+ }
+
+ ret = register_virtio_driver(&virtio_ipc_driver);
+ if (ret) {
+ pr_err("failed to register virtio driver: %d\n", ret);
+ bus_unregister(&rpmsg_bus);
+ }
+
+ return ret;
+}
+module_init(rpmsg_init);
+
+static void __exit rpmsg_fini(void)
+{
+ unregister_virtio_driver(&virtio_ipc_driver);
+ bus_unregister(&rpmsg_bus);
+}
+module_exit(rpmsg_fini);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio-based remote processor messaging bus");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 3a125b835546..8c8377d50c4c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -554,6 +554,13 @@ config RTC_DRV_DS1742
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.
+config RTC_DRV_DA9052
+ tristate "Dialog DA9052/DA9053 RTC"
+ depends on PMIC_DA9052
+ help
+ Say y here to support the RTC driver for Dialog Semiconductor
+ DA9052-BC and DA9053-AA/Bx PMICs.
+
config RTC_DRV_EFI
tristate "EFI RTC"
depends on IA64
@@ -748,7 +755,7 @@ config HAVE_S3C_RTC
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
- depends on ARCH_S3C2410 || ARCH_S3C64XX || HAVE_S3C_RTC
+ depends on ARCH_S3C64XX || HAVE_S3C_RTC
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
@@ -773,8 +780,8 @@ config RTC_DRV_EP93XX
will be called rtc-ep93xx.
config RTC_DRV_SA1100
- tristate "SA11x0/PXA2xx"
- depends on ARCH_SA1100 || ARCH_PXA
+ tristate "SA11x0/PXA2xx/PXA910"
+ depends on ARCH_SA1100 || ARCH_PXA || ARCH_MMP
help
If you say Y here you will get access to the real time clock
built into your SA11x0 or PXA2xx CPU.
@@ -1070,4 +1077,14 @@ config RTC_DRV_PUV3
This drive can also be built as a module. If so, the module
will be called rtc-puv3.
+config RTC_DRV_LOONGSON1
+ tristate "loongson1 RTC support"
+ depends on MACH_LOONGSON1
+ help
+ This is a driver for the loongson1 on-chip Counter0 (Time-Of-Year
+ counter) to be used as a RTC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ls1x.
+
endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 6e6982335c10..727ae7786e6c 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_COH901331) += rtc-coh901331.o
+obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DAVINCI) += rtc-davinci.o
obj-$(CONFIG_RTC_DRV_DM355EVM) += rtc-dm355evm.o
obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
@@ -53,6 +54,7 @@ obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_ISL12022) += rtc-isl12022.o
obj-$(CONFIG_RTC_DRV_JZ4740) += rtc-jz4740.o
obj-$(CONFIG_RTC_DRV_LPC32XX) += rtc-lpc32xx.o
+obj-$(CONFIG_RTC_DRV_LOONGSON1) += rtc-ls1x.o
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
obj-$(CONFIG_RTC_DRV_M41T93) += rtc-m41t93.o
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index dc87eda65814..eb415bd76494 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 f04761e6622d..afee0e8ae714 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-at91sam9.c b/drivers/rtc/rtc-at91sam9.c
index ee3c122c0599..831868904e02 100644
--- a/drivers/rtc/rtc-at91sam9.c
+++ b/drivers/rtc/rtc-at91sam9.c
@@ -57,6 +57,7 @@ struct sam9_rtc {
void __iomem *rtt;
struct rtc_device *rtcdev;
u32 imr;
+ void __iomem *gpbr;
};
#define rtt_readl(rtc, field) \
@@ -65,9 +66,9 @@ struct sam9_rtc {
__raw_writel((val), (rtc)->rtt + AT91_RTT_ ## field)
#define gpbr_readl(rtc) \
- at91_sys_read(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR)
+ __raw_readl((rtc)->gpbr)
#define gpbr_writel(rtc, val) \
- at91_sys_write(AT91_GPBR + 4 * CONFIG_RTC_DRV_AT91SAM9_GPBR, (val))
+ __raw_writel((val), (rtc)->gpbr)
/*
* Read current time and date in RTC
@@ -287,16 +288,19 @@ static const struct rtc_class_ops at91_rtc_ops = {
/*
* Initialize and install RTC driver
*/
-static int __init at91_rtc_probe(struct platform_device *pdev)
+static int __devinit at91_rtc_probe(struct platform_device *pdev)
{
- struct resource *r;
+ struct resource *r, *r_gpbr;
struct sam9_rtc *rtc;
int ret;
u32 mr;
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!r)
+ r_gpbr = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!r || !r_gpbr) {
+ dev_err(&pdev->dev, "need 2 ressources\n");
return -ENODEV;
+ }
rtc = kzalloc(sizeof *rtc, GFP_KERNEL);
if (!rtc)
@@ -314,6 +318,13 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
goto fail;
}
+ rtc->gpbr = ioremap(r_gpbr->start, resource_size(r_gpbr));
+ if (!rtc->gpbr) {
+ dev_err(&pdev->dev, "failed to map gpbr registers, aborting.\n");
+ ret = -ENOMEM;
+ goto fail_gpbr;
+ }
+
mr = rtt_readl(rtc, MR);
/* unless RTT is counting at 1 Hz, re-initialize it */
@@ -335,12 +346,12 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
/* register irq handler after we know what name we'll use */
ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
- IRQF_DISABLED | IRQF_SHARED,
+ IRQF_SHARED,
dev_name(&rtc->rtcdev->dev), rtc);
if (ret) {
dev_dbg(&pdev->dev, "can't share IRQ %d?\n", AT91_ID_SYS);
rtc_device_unregister(rtc->rtcdev);
- goto fail;
+ goto fail_register;
}
/* NOTE: sam9260 rev A silicon has a ROM bug which resets the
@@ -356,6 +367,8 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
return 0;
fail_register:
+ iounmap(rtc->gpbr);
+fail_gpbr:
iounmap(rtc->rtt);
fail:
platform_set_drvdata(pdev, NULL);
@@ -366,7 +379,7 @@ fail:
/*
* Disable and remove the RTC driver
*/
-static int __exit at91_rtc_remove(struct platform_device *pdev)
+static int __devexit at91_rtc_remove(struct platform_device *pdev)
{
struct sam9_rtc *rtc = platform_get_drvdata(pdev);
u32 mr = rtt_readl(rtc, MR);
@@ -377,6 +390,7 @@ static int __exit at91_rtc_remove(struct platform_device *pdev)
rtc_device_unregister(rtc->rtcdev);
+ iounmap(rtc->gpbr);
iounmap(rtc->rtt);
platform_set_drvdata(pdev, NULL);
kfree(rtc);
@@ -440,63 +454,20 @@ static int at91_rtc_resume(struct platform_device *pdev)
#endif
static struct platform_driver at91_rtc_driver = {
- .driver.name = "rtc-at91sam9",
- .driver.owner = THIS_MODULE,
- .remove = __exit_p(at91_rtc_remove),
+ .probe = at91_rtc_probe,
+ .remove = __devexit_p(at91_rtc_remove),
.shutdown = at91_rtc_shutdown,
.suspend = at91_rtc_suspend,
.resume = at91_rtc_resume,
+ .driver = {
+ .name = "rtc-at91sam9",
+ .owner = THIS_MODULE,
+ },
};
-/* Chips can have more than one RTT module, and they can be used for more
- * than just RTCs. So we can't just register as "the" RTT driver.
- *
- * A normal approach in such cases is to create a library to allocate and
- * free the modules. Here we just use bus_find_device() as like such a
- * library, binding directly ... no runtime "library" footprint is needed.
- */
-static int __init at91_rtc_match(struct device *dev, void *v)
-{
- struct platform_device *pdev = to_platform_device(dev);
- int ret;
-
- /* continue searching if this isn't the RTT we need */
- if (strcmp("at91_rtt", pdev->name) != 0
- || pdev->id != CONFIG_RTC_DRV_AT91SAM9_RTT)
- goto fail;
-
- /* else we found it ... but fail unless we can bind to the RTC driver */
- if (dev->driver) {
- dev_dbg(dev, "busy, can't use as RTC!\n");
- goto fail;
- }
- dev->driver = &at91_rtc_driver.driver;
- if (device_attach(dev) == 0) {
- dev_dbg(dev, "can't attach RTC!\n");
- goto fail;
- }
- ret = at91_rtc_probe(pdev);
- if (ret == 0)
- return true;
-
- dev_dbg(dev, "RTC probe err %d!\n", ret);
-fail:
- return false;
-}
-
static int __init at91_rtc_init(void)
{
- int status;
- struct device *rtc;
-
- status = platform_driver_register(&at91_rtc_driver);
- if (status)
- return status;
- rtc = bus_find_device(&platform_bus_type, NULL,
- NULL, at91_rtc_match);
- if (!rtc)
- platform_driver_unregister(&at91_rtc_driver);
- return rtc ? 0 : -ENODEV;
+ return platform_driver_register(&at91_rtc_driver);
}
module_init(at91_rtc_init);
diff --git a/drivers/rtc/rtc-bq32k.c b/drivers/rtc/rtc-bq32k.c
index 408cc8f735be..f090159dce4a 100644
--- a/drivers/rtc/rtc-bq32k.c
+++ b/drivers/rtc/rtc-bq32k.c
@@ -187,17 +187,7 @@ static struct i2c_driver bq32k_driver = {
.id_table = bq32k_id,
};
-static __init int bq32k_init(void)
-{
- return i2c_add_driver(&bq32k_driver);
-}
-module_init(bq32k_init);
-
-static __exit void bq32k_exit(void)
-{
- i2c_del_driver(&bq32k_driver);
-}
-module_exit(bq32k_exit);
+module_i2c_driver(bq32k_driver);
MODULE_AUTHOR("Semihalf, Piotr Ziecik <kosmo@semihalf.com>");
MODULE_DESCRIPTION("TI BQ32000 I2C RTC driver");
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index d7782aa09943..7d5f56edb8ef 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -714,7 +714,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
rtc_cmos_int_handler = cmos_interrupt;
retval = request_irq(rtc_irq, rtc_cmos_int_handler,
- IRQF_DISABLED, dev_name(&cmos_rtc.rtc->dev),
+ 0, dev_name(&cmos_rtc.rtc->dev),
cmos_rtc.rtc);
if (retval < 0) {
dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c
index 80f9c88214c5..a5b8a0c4ea84 100644
--- a/drivers/rtc/rtc-coh901331.c
+++ b/drivers/rtc/rtc-coh901331.c
@@ -199,7 +199,7 @@ static int __init coh901331_probe(struct platform_device *pdev)
}
rtap->irq = platform_get_irq(pdev, 0);
- if (request_irq(rtap->irq, coh901331_interrupt, IRQF_DISABLED,
+ if (request_irq(rtap->irq, coh901331_interrupt, 0,
"RTC COH 901 331 Alarm", rtap)) {
ret = -EIO;
goto out_no_irq;
diff --git a/drivers/rtc/rtc-da9052.c b/drivers/rtc/rtc-da9052.c
new file mode 100644
index 000000000000..da6ab5291a41
--- /dev/null
+++ b/drivers/rtc/rtc-da9052.c
@@ -0,0 +1,293 @@
+/*
+ * Real time clock driver for DA9052
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: Dajun Dajun Chen <dajun.chen@diasemi.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+#define rtc_err(da9052, fmt, ...) \
+ dev_err(da9052->dev, "%s: " fmt, __func__, ##__VA_ARGS__)
+
+struct da9052_rtc {
+ struct rtc_device *rtc;
+ struct da9052 *da9052;
+ int irq;
+};
+
+static int da9052_rtc_enable_alarm(struct da9052 *da9052, bool enable)
+{
+ int ret;
+ if (enable) {
+ ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
+ DA9052_ALARM_Y_ALARM_ON,
+ DA9052_ALARM_Y_ALARM_ON);
+ if (ret != 0)
+ rtc_err(da9052, "Failed to enable ALM: %d\n", ret);
+ } else {
+ ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
+ DA9052_ALARM_Y_ALARM_ON, 0);
+ if (ret != 0)
+ rtc_err(da9052, "Write error: %d\n", ret);
+ }
+ return ret;
+}
+
+static irqreturn_t da9052_rtc_irq(int irq, void *data)
+{
+ struct da9052_rtc *rtc = data;
+ int ret;
+
+ ret = da9052_reg_read(rtc->da9052, DA9052_ALARM_MI_REG);
+ if (ret < 0) {
+ rtc_err(rtc->da9052, "Read error: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ if (ret & DA9052_ALARMMI_ALARMTYPE) {
+ da9052_rtc_enable_alarm(rtc->da9052, 0);
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
+ } else
+ rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_PF);
+
+ return IRQ_HANDLED;
+}
+
+static int da9052_read_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm)
+{
+ int ret;
+ uint8_t v[5];
+
+ ret = da9052_group_read(da9052, DA9052_ALARM_MI_REG, 5, v);
+ if (ret != 0) {
+ rtc_err(da9052, "Failed to group read ALM: %d\n", ret);
+ return ret;
+ }
+
+ rtc_tm->tm_year = (v[4] & DA9052_RTC_YEAR) + 100;
+ rtc_tm->tm_mon = (v[3] & DA9052_RTC_MONTH) - 1;
+ rtc_tm->tm_mday = v[2] & DA9052_RTC_DAY;
+ rtc_tm->tm_hour = v[1] & DA9052_RTC_HOUR;
+ rtc_tm->tm_min = v[0] & DA9052_RTC_MIN;
+
+ ret = rtc_valid_tm(rtc_tm);
+ if (ret != 0)
+ return ret;
+ return ret;
+}
+
+static int da9052_set_alarm(struct da9052 *da9052, struct rtc_time *rtc_tm)
+{
+ int ret;
+ uint8_t v[3];
+
+ rtc_tm->tm_year -= 100;
+ rtc_tm->tm_mon += 1;
+
+ ret = da9052_reg_update(da9052, DA9052_ALARM_MI_REG,
+ DA9052_RTC_MIN, rtc_tm->tm_min);
+ if (ret != 0) {
+ rtc_err(da9052, "Failed to write ALRM MIN: %d\n", ret);
+ return ret;
+ }
+
+ v[0] = rtc_tm->tm_hour;
+ v[1] = rtc_tm->tm_mday;
+ v[2] = rtc_tm->tm_mon;
+
+ ret = da9052_group_write(da9052, DA9052_ALARM_H_REG, 3, v);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_reg_update(da9052, DA9052_ALARM_Y_REG,
+ DA9052_RTC_YEAR, rtc_tm->tm_year);
+ if (ret != 0)
+ rtc_err(da9052, "Failed to write ALRM YEAR: %d\n", ret);
+
+ return ret;
+}
+
+static int da9052_rtc_get_alarm_status(struct da9052 *da9052)
+{
+ int ret;
+
+ ret = da9052_reg_read(da9052, DA9052_ALARM_Y_REG);
+ if (ret < 0) {
+ rtc_err(da9052, "Failed to read ALM: %d\n", ret);
+ return ret;
+ }
+ ret &= DA9052_ALARM_Y_ALARM_ON;
+ return (ret > 0) ? 1 : 0;
+}
+
+static int da9052_rtc_read_time(struct device *dev, struct rtc_time *rtc_tm)
+{
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+ uint8_t v[6];
+ int ret;
+
+ ret = da9052_group_read(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
+ if (ret < 0) {
+ rtc_err(rtc->da9052, "Failed to read RTC time : %d\n", ret);
+ return ret;
+ }
+
+ rtc_tm->tm_year = (v[5] & DA9052_RTC_YEAR) + 100;
+ rtc_tm->tm_mon = (v[4] & DA9052_RTC_MONTH) - 1;
+ rtc_tm->tm_mday = v[3] & DA9052_RTC_DAY;
+ rtc_tm->tm_hour = v[2] & DA9052_RTC_HOUR;
+ rtc_tm->tm_min = v[1] & DA9052_RTC_MIN;
+ rtc_tm->tm_sec = v[0] & DA9052_RTC_SEC;
+
+ ret = rtc_valid_tm(rtc_tm);
+ if (ret != 0) {
+ rtc_err(rtc->da9052, "rtc_valid_tm failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int da9052_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct da9052_rtc *rtc;
+ uint8_t v[6];
+
+ rtc = dev_get_drvdata(dev);
+
+ v[0] = tm->tm_sec;
+ v[1] = tm->tm_min;
+ v[2] = tm->tm_hour;
+ v[3] = tm->tm_mday;
+ v[4] = tm->tm_mon + 1;
+ v[5] = tm->tm_year - 100;
+
+ return da9052_group_write(rtc->da9052, DA9052_COUNT_S_REG, 6, v);
+}
+
+static int da9052_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+
+ ret = da9052_read_alarm(rtc->da9052, tm);
+
+ if (ret)
+ return ret;
+
+ alrm->enabled = da9052_rtc_get_alarm_status(rtc->da9052);
+
+ return 0;
+}
+
+static int da9052_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
+{
+ int ret;
+ struct rtc_time *tm = &alrm->time;
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+
+ ret = da9052_rtc_enable_alarm(rtc->da9052, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = da9052_set_alarm(rtc->da9052, tm);
+ if (ret)
+ return ret;
+
+ ret = da9052_rtc_enable_alarm(rtc->da9052, 1);
+
+ return ret;
+}
+
+static int da9052_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct da9052_rtc *rtc = dev_get_drvdata(dev);
+
+ return da9052_rtc_enable_alarm(rtc->da9052, enabled);
+}
+
+static const struct rtc_class_ops da9052_rtc_ops = {
+ .read_time = da9052_rtc_read_time,
+ .set_time = da9052_rtc_set_time,
+ .read_alarm = da9052_rtc_read_alarm,
+ .set_alarm = da9052_rtc_set_alarm,
+ .alarm_irq_enable = da9052_rtc_alarm_irq_enable,
+};
+
+static int __devinit da9052_rtc_probe(struct platform_device *pdev)
+{
+ struct da9052_rtc *rtc;
+ int ret;
+
+ rtc = devm_kzalloc(&pdev->dev, sizeof(struct da9052_rtc), GFP_KERNEL);
+ if (!rtc)
+ return -ENOMEM;
+
+ rtc->da9052 = dev_get_drvdata(pdev->dev.parent);
+ platform_set_drvdata(pdev, rtc);
+ rtc->irq = platform_get_irq_byname(pdev, "ALM");
+ ret = request_threaded_irq(rtc->irq, NULL, da9052_rtc_irq,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "ALM", rtc);
+ if (ret != 0) {
+ rtc_err(rtc->da9052, "irq registration failed: %d\n", ret);
+ goto err_mem;
+ }
+
+ rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &da9052_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc->rtc)) {
+ ret = PTR_ERR(rtc->rtc);
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(rtc->irq, rtc);
+err_mem:
+ devm_kfree(&pdev->dev, rtc);
+ return ret;
+}
+
+static int __devexit da9052_rtc_remove(struct platform_device *pdev)
+{
+ struct da9052_rtc *rtc = pdev->dev.platform_data;
+
+ rtc_device_unregister(rtc->rtc);
+ free_irq(rtc->irq, rtc);
+ platform_set_drvdata(pdev, NULL);
+ devm_kfree(&pdev->dev, rtc);
+
+ return 0;
+}
+
+static struct platform_driver da9052_rtc_driver = {
+ .probe = da9052_rtc_probe,
+ .remove = __devexit_p(da9052_rtc_remove),
+ .driver = {
+ .name = "da9052-rtc",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(da9052_rtc_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("RTC driver for Dialog DA9052 PMIC");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-rtc");
diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c
index 755e1fe914af..14c2109dbaa3 100644
--- a/drivers/rtc/rtc-davinci.c
+++ b/drivers/rtc/rtc-davinci.c
@@ -542,7 +542,7 @@ static int __init davinci_rtc_probe(struct platform_device *pdev)
rtcss_write(davinci_rtc, 0, PRTCSS_RTC_CCTRL);
ret = request_irq(davinci_rtc->irq, davinci_rtc_interrupt,
- IRQF_DISABLED, "davinci_rtc", davinci_rtc);
+ 0, "davinci_rtc", davinci_rtc);
if (ret < 0) {
dev_err(dev, "unable to register davinci RTC interrupt\n");
goto fail4;
diff --git a/drivers/rtc/rtc-ds1305.c b/drivers/rtc/rtc-ds1305.c
index 3a33b1fdbe0f..686a865913e1 100644
--- a/drivers/rtc/rtc-ds1305.c
+++ b/drivers/rtc/rtc-ds1305.c
@@ -814,17 +814,7 @@ static struct spi_driver ds1305_driver = {
/* REVISIT add suspend/resume */
};
-static int __init ds1305_init(void)
-{
- return spi_register_driver(&ds1305_driver);
-}
-module_init(ds1305_init);
-
-static void __exit ds1305_exit(void)
-{
- spi_unregister_driver(&ds1305_driver);
-}
-module_exit(ds1305_exit);
+module_spi_driver(ds1305_driver);
MODULE_DESCRIPTION("RTC driver for DS1305 and DS1306 chips");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index 62b0763b7b9a..cd188ab72f79 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -20,7 +20,8 @@
-/* We can't determine type by probing, but if we expect pre-Linux code
+/*
+ * We can't determine type by probing, but if we expect pre-Linux code
* to have set the chip up as a clock (turning on the oscillator and
* setting the date and time), Linux can ignore the non-clock features.
* That's a natural job for a factory or repair bench.
@@ -36,7 +37,8 @@ enum ds_type {
m41t00,
mcp7941x,
rx_8025,
- // rs5c372 too? different address...
+ last_ds_type /* always last */
+ /* rs5c372 too? different address... */
};
@@ -58,7 +60,8 @@ enum ds_type {
# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
#define DS1307_REG_YEAR 0x06 /* 00-99 */
-/* Other registers (control, status, alarms, trickle charge, NVRAM, etc)
+/*
+ * Other registers (control, status, alarms, trickle charge, NVRAM, etc)
* start at 7, and they differ a LOT. Only control and status matter for
* basic RTC date and time functionality; be careful using them.
*/
@@ -102,6 +105,8 @@ enum ds_type {
struct ds1307 {
u8 offset; /* register's offset */
u8 regs[11];
+ u16 nvram_offset;
+ struct bin_attribute *nvram;
enum ds_type type;
unsigned long flags;
#define HAS_NVRAM 0 /* bit 0 == sysfs file active */
@@ -116,34 +121,35 @@ struct ds1307 {
};
struct chip_desc {
- unsigned nvram56:1;
unsigned alarm:1;
+ u16 nvram_offset;
+ u16 nvram_size;
};
-static const struct chip_desc chips[] = {
-[ds_1307] = {
- .nvram56 = 1,
-},
-[ds_1337] = {
- .alarm = 1,
-},
-[ds_1338] = {
- .nvram56 = 1,
-},
-[ds_1339] = {
- .alarm = 1,
-},
-[ds_1340] = {
-},
-[ds_3231] = {
- .alarm = 1,
-},
-[m41t00] = {
-},
-[mcp7941x] = {
-},
-[rx_8025] = {
-}, };
+static const struct chip_desc chips[last_ds_type] = {
+ [ds_1307] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1337] = {
+ .alarm = 1,
+ },
+ [ds_1338] = {
+ .nvram_offset = 8,
+ .nvram_size = 56,
+ },
+ [ds_1339] = {
+ .alarm = 1,
+ },
+ [ds_3231] = {
+ .alarm = 1,
+ },
+ [mcp7941x] = {
+ /* this is battery backed SRAM */
+ .nvram_offset = 0x20,
+ .nvram_size = 0x40,
+ },
+};
static const struct i2c_device_id ds1307_id[] = {
{ "ds1307", ds_1307 },
@@ -372,6 +378,11 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
| DS1340_BIT_CENTURY;
break;
case mcp7941x:
+ /*
+ * these bits were cleared when preparing the date/time
+ * values and need to be set again before writing the
+ * buffer out to the device.
+ */
buf[DS1307_REG_SECS] |= MCP7941X_BIT_ST;
buf[DS1307_REG_WDAY] |= MCP7941X_BIT_VBATEN;
break;
@@ -417,7 +428,8 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
ds1307->regs[6], ds1307->regs[7],
ds1307->regs[8]);
- /* report alarm time (ALARM1); assume 24 hour and day-of-month modes,
+ /*
+ * report alarm time (ALARM1); assume 24 hour and day-of-month modes,
* and that all four fields are checked matches
*/
t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f);
@@ -445,7 +457,7 @@ static int ds1337_read_alarm(struct device *dev, struct rtc_wkalrm *t)
static int ds1337_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev);
struct ds1307 *ds1307 = i2c_get_clientdata(client);
unsigned char *buf = ds1307->regs;
u8 control, status;
@@ -541,8 +553,6 @@ static const struct rtc_class_ops ds13xx_rtc_ops = {
/*----------------------------------------------------------------------*/
-#define NVRAM_SIZE 56
-
static ssize_t
ds1307_nvram_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr,
@@ -555,14 +565,15 @@ ds1307_nvram_read(struct file *filp, struct kobject *kobj,
client = kobj_to_i2c_client(kobj);
ds1307 = i2c_get_clientdata(client);
- if (unlikely(off >= NVRAM_SIZE))
+ if (unlikely(off >= ds1307->nvram->size))
return 0;
- if ((off + count) > NVRAM_SIZE)
- count = NVRAM_SIZE - off;
+ if ((off + count) > ds1307->nvram->size)
+ count = ds1307->nvram->size - off;
if (unlikely(!count))
return count;
- result = ds1307->read_block_data(client, 8 + off, count, buf);
+ result = ds1307->read_block_data(client, ds1307->nvram_offset + off,
+ count, buf);
if (result < 0)
dev_err(&client->dev, "%s error %d\n", "nvram read", result);
return result;
@@ -580,14 +591,15 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj,
client = kobj_to_i2c_client(kobj);
ds1307 = i2c_get_clientdata(client);
- if (unlikely(off >= NVRAM_SIZE))
+ if (unlikely(off >= ds1307->nvram->size))
return -EFBIG;
- if ((off + count) > NVRAM_SIZE)
- count = NVRAM_SIZE - off;
+ if ((off + count) > ds1307->nvram->size)
+ count = ds1307->nvram->size - off;
if (unlikely(!count))
return count;
- result = ds1307->write_block_data(client, 8 + off, count, buf);
+ result = ds1307->write_block_data(client, ds1307->nvram_offset + off,
+ count, buf);
if (result < 0) {
dev_err(&client->dev, "%s error %d\n", "nvram write", result);
return result;
@@ -595,21 +607,8 @@ ds1307_nvram_write(struct file *filp, struct kobject *kobj,
return count;
}
-static struct bin_attribute nvram = {
- .attr = {
- .name = "nvram",
- .mode = S_IRUGO | S_IWUSR,
- },
-
- .read = ds1307_nvram_read,
- .write = ds1307_nvram_write,
- .size = NVRAM_SIZE,
-};
-
/*----------------------------------------------------------------------*/
-static struct i2c_driver ds1307_driver;
-
static int __devinit ds1307_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -630,7 +629,8 @@ static int __devinit ds1307_probe(struct i2c_client *client,
&& !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
return -EIO;
- if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL)))
+ ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL);
+ if (!ds1307)
return -ENOMEM;
i2c_set_clientdata(client, ds1307);
@@ -652,11 +652,6 @@ static int __devinit ds1307_probe(struct i2c_client *client,
case ds_1337:
case ds_1339:
case ds_3231:
- /* has IRQ? */
- if (ds1307->client->irq > 0 && chip->alarm) {
- INIT_WORK(&ds1307->work, ds1307_work);
- want_irq = true;
- }
/* get registers that the "rtc" read below won't read... */
tmp = ds1307->read_block_data(ds1307->client,
DS1337_REG_CONTROL, 2, buf);
@@ -670,14 +665,19 @@ static int __devinit ds1307_probe(struct i2c_client *client,
if (ds1307->regs[0] & DS1337_BIT_nEOSC)
ds1307->regs[0] &= ~DS1337_BIT_nEOSC;
- /* Using IRQ? Disable the square wave and both alarms.
+ /*
+ * Using IRQ? Disable the square wave and both alarms.
* For some variants, be sure alarms can trigger when we're
* running on Vbackup (BBSQI/BBSQW)
*/
- if (want_irq) {
+ if (ds1307->client->irq > 0 && chip->alarm) {
+ INIT_WORK(&ds1307->work, ds1307_work);
+
ds1307->regs[0] |= DS1337_BIT_INTCN
| bbsqi_bitpos[ds1307->type];
ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
+
+ want_irq = true;
}
i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
@@ -772,7 +772,8 @@ read_rtc:
goto exit_free;
}
- /* minimal sanity checking; some chips (like DS1340) don't
+ /*
+ * minimal sanity checking; some chips (like DS1340) don't
* specify the extra bits as must-be-zero, but there are
* still a few values that are clearly out-of-range.
*/
@@ -836,11 +837,7 @@ read_rtc:
}
break;
- case rx_8025:
- case ds_1337:
- case ds_1339:
- case ds_1388:
- case ds_3231:
+ default:
break;
}
@@ -848,7 +845,8 @@ read_rtc:
switch (ds1307->type) {
case ds_1340:
case m41t00:
- /* NOTE: ignores century bits; fix before deploying
+ /*
+ * NOTE: ignores century bits; fix before deploying
* systems that will run through year 2100.
*/
break;
@@ -858,7 +856,8 @@ read_rtc:
if (!(tmp & DS1307_BIT_12HR))
break;
- /* Be sure we're in 24 hour mode. Multi-master systems
+ /*
+ * Be sure we're in 24 hour mode. Multi-master systems
* take note...
*/
tmp = bcd2bin(tmp & 0x1f);
@@ -894,16 +893,31 @@ read_rtc:
dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
}
- if (chip->nvram56) {
- err = sysfs_create_bin_file(&client->dev.kobj, &nvram);
- if (err == 0) {
- set_bit(HAS_NVRAM, &ds1307->flags);
- dev_info(&client->dev, "56 bytes nvram\n");
+ if (chip->nvram_size) {
+ ds1307->nvram = kzalloc(sizeof(struct bin_attribute),
+ GFP_KERNEL);
+ if (!ds1307->nvram) {
+ err = -ENOMEM;
+ goto exit_nvram;
+ }
+ ds1307->nvram->attr.name = "nvram";
+ ds1307->nvram->attr.mode = S_IRUGO | S_IWUSR;
+ ds1307->nvram->read = ds1307_nvram_read,
+ ds1307->nvram->write = ds1307_nvram_write,
+ ds1307->nvram->size = chip->nvram_size;
+ ds1307->nvram_offset = chip->nvram_offset;
+ err = sysfs_create_bin_file(&client->dev.kobj, ds1307->nvram);
+ if (err) {
+ kfree(ds1307->nvram);
+ goto exit_nvram;
}
+ set_bit(HAS_NVRAM, &ds1307->flags);
+ dev_info(&client->dev, "%zu bytes nvram\n", ds1307->nvram->size);
}
return 0;
+exit_nvram:
exit_irq:
rtc_device_unregister(ds1307->rtc);
exit_free:
@@ -913,15 +927,17 @@ exit_free:
static int __devexit ds1307_remove(struct i2c_client *client)
{
- struct ds1307 *ds1307 = i2c_get_clientdata(client);
+ struct ds1307 *ds1307 = i2c_get_clientdata(client);
if (test_and_clear_bit(HAS_ALARM, &ds1307->flags)) {
free_irq(client->irq, client);
cancel_work_sync(&ds1307->work);
}
- if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags))
- sysfs_remove_bin_file(&client->dev.kobj, &nvram);
+ if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags)) {
+ sysfs_remove_bin_file(&client->dev.kobj, ds1307->nvram);
+ kfree(ds1307->nvram);
+ }
rtc_device_unregister(ds1307->rtc);
kfree(ds1307);
@@ -938,17 +954,7 @@ static struct i2c_driver ds1307_driver = {
.id_table = ds1307_id,
};
-static int __init ds1307_init(void)
-{
- return i2c_add_driver(&ds1307_driver);
-}
-module_init(ds1307_init);
-
-static void __exit ds1307_exit(void)
-{
- i2c_del_driver(&ds1307_driver);
-}
-module_exit(ds1307_exit);
+module_i2c_driver(ds1307_driver);
MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c
index e6e71deb188f..966316088b7f 100644
--- a/drivers/rtc/rtc-ds1374.c
+++ b/drivers/rtc/rtc-ds1374.c
@@ -446,18 +446,7 @@ static struct i2c_driver ds1374_driver = {
.id_table = ds1374_id,
};
-static int __init ds1374_init(void)
-{
- return i2c_add_driver(&ds1374_driver);
-}
-
-static void __exit ds1374_exit(void)
-{
- i2c_del_driver(&ds1374_driver);
-}
-
-module_init(ds1374_init);
-module_exit(ds1374_exit);
+module_i2c_driver(ds1374_driver);
MODULE_AUTHOR("Scott Wood <scottwood@freescale.com>");
MODULE_DESCRIPTION("Maxim/Dallas DS1374 RTC Driver");
diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c
index b038d2cfef26..b0a99e1b25be 100644
--- a/drivers/rtc/rtc-ds1390.c
+++ b/drivers/rtc/rtc-ds1390.c
@@ -175,17 +175,7 @@ static struct spi_driver ds1390_driver = {
.remove = __devexit_p(ds1390_remove),
};
-static __init int ds1390_init(void)
-{
- return spi_register_driver(&ds1390_driver);
-}
-module_init(ds1390_init);
-
-static __exit void ds1390_exit(void)
-{
- spi_unregister_driver(&ds1390_driver);
-}
-module_exit(ds1390_exit);
+module_spi_driver(ds1390_driver);
MODULE_DESCRIPTION("Dallas/Maxim DS1390/93/94 SPI RTC driver");
MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>");
diff --git a/drivers/rtc/rtc-ds1511.c b/drivers/rtc/rtc-ds1511.c
index 761f36bc83a9..1f675f5294f5 100644
--- a/drivers/rtc/rtc-ds1511.c
+++ b/drivers/rtc/rtc-ds1511.c
@@ -532,7 +532,7 @@ ds1511_rtc_probe(struct platform_device *pdev)
if (pdata->irq > 0) {
rtc_read(RTC_CMD1);
if (devm_request_irq(&pdev->dev, pdata->irq, ds1511_interrupt,
- IRQF_DISABLED | IRQF_SHARED, pdev->name, pdev) < 0) {
+ IRQF_SHARED, pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = 0;
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 6f0a1b530f2e..6ccedbbf923c 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -320,7 +320,7 @@ static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
writeb(0, ioaddr + RTC_INTERRUPTS);
if (devm_request_irq(&pdev->dev, pdata->irq,
ds1553_rtc_interrupt,
- IRQF_DISABLED, pdev->name, pdev) < 0) {
+ 0, pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = 0;
}
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index a319402a5447..7fa67d0df172 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -202,20 +202,9 @@ static struct i2c_driver ds1672_driver = {
.id_table = ds1672_id,
};
-static int __init ds1672_init(void)
-{
- return i2c_add_driver(&ds1672_driver);
-}
-
-static void __exit ds1672_exit(void)
-{
- i2c_del_driver(&ds1672_driver);
-}
+module_i2c_driver(ds1672_driver);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(ds1672_init);
-module_exit(ds1672_exit);
diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c
index 27b7bf672ac6..e1945095814e 100644
--- a/drivers/rtc/rtc-ds3232.c
+++ b/drivers/rtc/rtc-ds3232.c
@@ -473,18 +473,7 @@ static struct i2c_driver ds3232_driver = {
.id_table = ds3232_id,
};
-static int __init ds3232_init(void)
-{
- return i2c_add_driver(&ds3232_driver);
-}
-
-static void __exit ds3232_exit(void)
-{
- i2c_del_driver(&ds3232_driver);
-}
-
-module_init(ds3232_init);
-module_exit(ds3232_exit);
+module_i2c_driver(ds3232_driver);
MODULE_AUTHOR("Srikanth Srinivasan <srikanth.srinivasan@freescale.com>");
MODULE_DESCRIPTION("Maxim/Dallas DS3232 RTC Driver");
diff --git a/drivers/rtc/rtc-ds3234.c b/drivers/rtc/rtc-ds3234.c
index bbd26228f532..fda707926f02 100644
--- a/drivers/rtc/rtc-ds3234.c
+++ b/drivers/rtc/rtc-ds3234.c
@@ -173,17 +173,7 @@ static struct spi_driver ds3234_driver = {
.remove = __devexit_p(ds3234_remove),
};
-static __init int ds3234_init(void)
-{
- return spi_register_driver(&ds3234_driver);
-}
-module_init(ds3234_init);
-
-static __exit void ds3234_exit(void)
-{
- spi_unregister_driver(&ds3234_driver);
-}
-module_exit(ds3234_exit);
+module_spi_driver(ds3234_driver);
MODULE_DESCRIPTION("DS3234 SPI RTC driver");
MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>");
diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c
index 8414dea5fb14..0104ea7ebe50 100644
--- a/drivers/rtc/rtc-em3027.c
+++ b/drivers/rtc/rtc-em3027.c
@@ -144,19 +144,8 @@ static struct i2c_driver em3027_driver = {
.id_table = em3027_id,
};
-static int __init em3027_init(void)
-{
- return i2c_add_driver(&em3027_driver);
-}
-
-static void __exit em3027_exit(void)
-{
- i2c_del_driver(&em3027_driver);
-}
+module_i2c_driver(em3027_driver);
MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>");
MODULE_DESCRIPTION("EM Microelectronic EM3027 RTC driver");
MODULE_LICENSE("GPL");
-
-module_init(em3027_init);
-module_exit(em3027_exit);
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c
index 4cf2e70c5078..86b6ecce99f0 100644
--- a/drivers/rtc/rtc-fm3130.c
+++ b/drivers/rtc/rtc-fm3130.c
@@ -565,17 +565,7 @@ static struct i2c_driver fm3130_driver = {
.id_table = fm3130_id,
};
-static int __init fm3130_init(void)
-{
- return i2c_add_driver(&fm3130_driver);
-}
-module_init(fm3130_init);
-
-static void __exit fm3130_exit(void)
-{
- i2c_del_driver(&fm3130_driver);
-}
-module_exit(fm3130_exit);
+module_i2c_driver(fm3130_driver);
MODULE_DESCRIPTION("RTC driver for FM3130");
MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
diff --git a/drivers/rtc/rtc-isl12022.c b/drivers/rtc/rtc-isl12022.c
index 6186833973ee..1850104705c0 100644
--- a/drivers/rtc/rtc-isl12022.c
+++ b/drivers/rtc/rtc-isl12022.c
@@ -309,18 +309,7 @@ static struct i2c_driver isl12022_driver = {
.id_table = isl12022_id,
};
-static int __init isl12022_init(void)
-{
- return i2c_add_driver(&isl12022_driver);
-}
-
-static void __exit isl12022_exit(void)
-{
- i2c_del_driver(&isl12022_driver);
-}
-
-module_init(isl12022_init);
-module_exit(isl12022_exit);
+module_i2c_driver(isl12022_driver);
MODULE_AUTHOR("roman.fietze@telemotive.de");
MODULE_DESCRIPTION("ISL 12022 RTC driver");
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index da8beb8cae51..dd2aeee6c66a 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -710,22 +710,9 @@ static struct i2c_driver isl1208_driver = {
.id_table = isl1208_id,
};
-static int __init
-isl1208_init(void)
-{
- return i2c_add_driver(&isl1208_driver);
-}
-
-static void __exit
-isl1208_exit(void)
-{
- i2c_del_driver(&isl1208_driver);
-}
+module_i2c_driver(isl1208_driver);
MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
MODULE_DESCRIPTION("Intersil ISL1208 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(isl1208_init);
-module_exit(isl1208_exit);
diff --git a/drivers/rtc/rtc-lpc32xx.c b/drivers/rtc/rtc-lpc32xx.c
index ecc1713b2b4f..63c72189c64b 100644
--- a/drivers/rtc/rtc-lpc32xx.c
+++ b/drivers/rtc/rtc-lpc32xx.c
@@ -287,7 +287,7 @@ static int __devinit lpc32xx_rtc_probe(struct platform_device *pdev)
if (rtc->irq >= 0) {
if (devm_request_irq(&pdev->dev, rtc->irq,
lpc32xx_rtc_alarm_interrupt,
- IRQF_DISABLED, pdev->name, rtc) < 0) {
+ 0, pdev->name, rtc) < 0) {
dev_warn(&pdev->dev, "Can't request interrupt.\n");
rtc->irq = -1;
} else {
diff --git a/drivers/rtc/rtc-ls1x.c b/drivers/rtc/rtc-ls1x.c
new file mode 100644
index 000000000000..07e81c5f8247
--- /dev/null
+++ b/drivers/rtc/rtc-ls1x.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2011 Zhao Zhang <zhzhl555@gmail.com>
+ *
+ * Derived from driver/rtc/rtc-au1xxx.c
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/io.h>
+#include <asm/mach-loongson1/loongson1.h>
+
+#define LS1X_RTC_REG_OFFSET (LS1X_RTC_BASE + 0x20)
+#define LS1X_RTC_REGS(x) \
+ ((void __iomem *)KSEG1ADDR(LS1X_RTC_REG_OFFSET + (x)))
+
+/*RTC programmable counters 0 and 1*/
+#define SYS_COUNTER_CNTRL (LS1X_RTC_REGS(0x20))
+#define SYS_CNTRL_ERS (1 << 23)
+#define SYS_CNTRL_RTS (1 << 20)
+#define SYS_CNTRL_RM2 (1 << 19)
+#define SYS_CNTRL_RM1 (1 << 18)
+#define SYS_CNTRL_RM0 (1 << 17)
+#define SYS_CNTRL_RS (1 << 16)
+#define SYS_CNTRL_BP (1 << 14)
+#define SYS_CNTRL_REN (1 << 13)
+#define SYS_CNTRL_BRT (1 << 12)
+#define SYS_CNTRL_TEN (1 << 11)
+#define SYS_CNTRL_BTT (1 << 10)
+#define SYS_CNTRL_E0 (1 << 8)
+#define SYS_CNTRL_ETS (1 << 7)
+#define SYS_CNTRL_32S (1 << 5)
+#define SYS_CNTRL_TTS (1 << 4)
+#define SYS_CNTRL_TM2 (1 << 3)
+#define SYS_CNTRL_TM1 (1 << 2)
+#define SYS_CNTRL_TM0 (1 << 1)
+#define SYS_CNTRL_TS (1 << 0)
+
+/* Programmable Counter 0 Registers */
+#define SYS_TOYTRIM (LS1X_RTC_REGS(0))
+#define SYS_TOYWRITE0 (LS1X_RTC_REGS(4))
+#define SYS_TOYWRITE1 (LS1X_RTC_REGS(8))
+#define SYS_TOYREAD0 (LS1X_RTC_REGS(0xC))
+#define SYS_TOYREAD1 (LS1X_RTC_REGS(0x10))
+#define SYS_TOYMATCH0 (LS1X_RTC_REGS(0x14))
+#define SYS_TOYMATCH1 (LS1X_RTC_REGS(0x18))
+#define SYS_TOYMATCH2 (LS1X_RTC_REGS(0x1C))
+
+/* Programmable Counter 1 Registers */
+#define SYS_RTCTRIM (LS1X_RTC_REGS(0x40))
+#define SYS_RTCWRITE0 (LS1X_RTC_REGS(0x44))
+#define SYS_RTCREAD0 (LS1X_RTC_REGS(0x48))
+#define SYS_RTCMATCH0 (LS1X_RTC_REGS(0x4C))
+#define SYS_RTCMATCH1 (LS1X_RTC_REGS(0x50))
+#define SYS_RTCMATCH2 (LS1X_RTC_REGS(0x54))
+
+#define LS1X_SEC_OFFSET (4)
+#define LS1X_MIN_OFFSET (10)
+#define LS1X_HOUR_OFFSET (16)
+#define LS1X_DAY_OFFSET (21)
+#define LS1X_MONTH_OFFSET (26)
+
+
+#define LS1X_SEC_MASK (0x3f)
+#define LS1X_MIN_MASK (0x3f)
+#define LS1X_HOUR_MASK (0x1f)
+#define LS1X_DAY_MASK (0x1f)
+#define LS1X_MONTH_MASK (0x3f)
+#define LS1X_YEAR_MASK (0xffffffff)
+
+#define ls1x_get_sec(t) (((t) >> LS1X_SEC_OFFSET) & LS1X_SEC_MASK)
+#define ls1x_get_min(t) (((t) >> LS1X_MIN_OFFSET) & LS1X_MIN_MASK)
+#define ls1x_get_hour(t) (((t) >> LS1X_HOUR_OFFSET) & LS1X_HOUR_MASK)
+#define ls1x_get_day(t) (((t) >> LS1X_DAY_OFFSET) & LS1X_DAY_MASK)
+#define ls1x_get_month(t) (((t) >> LS1X_MONTH_OFFSET) & LS1X_MONTH_MASK)
+
+#define RTC_CNTR_OK (SYS_CNTRL_E0 | SYS_CNTRL_32S)
+
+static int ls1x_rtc_read_time(struct device *dev, struct rtc_time *rtm)
+{
+ unsigned long v, t;
+
+ v = readl(SYS_TOYREAD0);
+ t = readl(SYS_TOYREAD1);
+
+ memset(rtm, 0, sizeof(struct rtc_time));
+ t = mktime((t & LS1X_YEAR_MASK), ls1x_get_month(v),
+ ls1x_get_day(v), ls1x_get_hour(v),
+ ls1x_get_min(v), ls1x_get_sec(v));
+ rtc_time_to_tm(t, rtm);
+
+ return rtc_valid_tm(rtm);
+}
+
+static int ls1x_rtc_set_time(struct device *dev, struct rtc_time *rtm)
+{
+ unsigned long v, t, c;
+ int ret = -ETIMEDOUT;
+
+ v = ((rtm->tm_mon + 1) << LS1X_MONTH_OFFSET)
+ | (rtm->tm_mday << LS1X_DAY_OFFSET)
+ | (rtm->tm_hour << LS1X_HOUR_OFFSET)
+ | (rtm->tm_min << LS1X_MIN_OFFSET)
+ | (rtm->tm_sec << LS1X_SEC_OFFSET);
+
+ writel(v, SYS_TOYWRITE0);
+ c = 0x10000;
+ /* add timeout check counter, for more safe */
+ while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c)
+ usleep_range(1000, 3000);
+
+ if (!c) {
+ dev_err(dev, "set time timeout!\n");
+ goto err;
+ }
+
+ t = rtm->tm_year + 1900;
+ writel(t, SYS_TOYWRITE1);
+ c = 0x10000;
+ while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TS) && --c)
+ usleep_range(1000, 3000);
+
+ if (!c) {
+ dev_err(dev, "set time timeout!\n");
+ goto err;
+ }
+ return 0;
+err:
+ return ret;
+}
+
+static struct rtc_class_ops ls1x_rtc_ops = {
+ .read_time = ls1x_rtc_read_time,
+ .set_time = ls1x_rtc_set_time,
+};
+
+static int __devinit ls1x_rtc_probe(struct platform_device *pdev)
+{
+ struct rtc_device *rtcdev;
+ unsigned long v;
+ int ret;
+
+ v = readl(SYS_COUNTER_CNTRL);
+ if (!(v & RTC_CNTR_OK)) {
+ dev_err(&pdev->dev, "rtc counters not working\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ ret = -ETIMEDOUT;
+ /* set to 1 HZ if needed */
+ if (readl(SYS_TOYTRIM) != 32767) {
+ v = 0x100000;
+ while ((readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS) && --v)
+ usleep_range(1000, 3000);
+
+ if (!v) {
+ dev_err(&pdev->dev, "time out\n");
+ goto err;
+ }
+ writel(32767, SYS_TOYTRIM);
+ }
+ /* this loop coundn't be endless */
+ while (readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_TTS)
+ usleep_range(1000, 3000);
+
+ rtcdev = rtc_device_register("ls1x-rtc", &pdev->dev,
+ &ls1x_rtc_ops , THIS_MODULE);
+ if (IS_ERR(rtcdev)) {
+ ret = PTR_ERR(rtcdev);
+ goto err;
+ }
+
+ platform_set_drvdata(pdev, rtcdev);
+ return 0;
+err:
+ return ret;
+}
+
+static int __devexit ls1x_rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_device *rtcdev = platform_get_drvdata(pdev);
+
+ rtc_device_unregister(rtcdev);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver ls1x_rtc_driver = {
+ .driver = {
+ .name = "ls1x-rtc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __devexit_p(ls1x_rtc_remove),
+ .probe = ls1x_rtc_probe,
+};
+
+module_platform_driver(ls1x_rtc_driver);
+
+MODULE_AUTHOR("zhao zhang <zhzhl555@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 64aedd8cc095..4e0f84af99a7 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -900,20 +900,9 @@ static struct i2c_driver m41t80_driver = {
.id_table = m41t80_id,
};
-static int __init m41t80_rtc_init(void)
-{
- return i2c_add_driver(&m41t80_driver);
-}
-
-static void __exit m41t80_rtc_exit(void)
-{
- i2c_del_driver(&m41t80_driver);
-}
+module_i2c_driver(m41t80_driver);
MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>");
MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(m41t80_rtc_init);
-module_exit(m41t80_rtc_exit);
diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c
index ef71132ff205..10f1c29436ec 100644
--- a/drivers/rtc/rtc-m41t93.c
+++ b/drivers/rtc/rtc-m41t93.c
@@ -206,17 +206,7 @@ static struct spi_driver m41t93_driver = {
.remove = __devexit_p(m41t93_remove),
};
-static __init int m41t93_init(void)
-{
- return spi_register_driver(&m41t93_driver);
-}
-module_init(m41t93_init);
-
-static __exit void m41t93_exit(void)
-{
- spi_unregister_driver(&m41t93_driver);
-}
-module_exit(m41t93_exit);
+module_spi_driver(m41t93_driver);
MODULE_AUTHOR("Nikolaus Voss <n.voss@weinmann.de>");
MODULE_DESCRIPTION("Driver for ST M41T93 SPI RTC");
diff --git a/drivers/rtc/rtc-m41t94.c b/drivers/rtc/rtc-m41t94.c
index 2a4721f61797..6e78193e026b 100644
--- a/drivers/rtc/rtc-m41t94.c
+++ b/drivers/rtc/rtc-m41t94.c
@@ -153,19 +153,7 @@ static struct spi_driver m41t94_driver = {
.remove = __devexit_p(m41t94_remove),
};
-static __init int m41t94_init(void)
-{
- return spi_register_driver(&m41t94_driver);
-}
-
-module_init(m41t94_init);
-
-static __exit void m41t94_exit(void)
-{
- spi_unregister_driver(&m41t94_driver);
-}
-
-module_exit(m41t94_exit);
+module_spi_driver(m41t94_driver);
MODULE_AUTHOR("Kim B. Heino <Kim.Heino@bluegiga.com>");
MODULE_DESCRIPTION("Driver for ST M41T94 SPI RTC");
diff --git a/drivers/rtc/rtc-max6900.c b/drivers/rtc/rtc-max6900.c
index 486142c2637a..a00e33204b91 100644
--- a/drivers/rtc/rtc-max6900.c
+++ b/drivers/rtc/rtc-max6900.c
@@ -261,20 +261,9 @@ static struct i2c_driver max6900_driver = {
.id_table = max6900_id,
};
-static int __init max6900_init(void)
-{
- return i2c_add_driver(&max6900_driver);
-}
-
-static void __exit max6900_exit(void)
-{
- i2c_del_driver(&max6900_driver);
-}
+module_i2c_driver(max6900_driver);
MODULE_DESCRIPTION("Maxim MAX6900 RTC driver");
MODULE_AUTHOR("Dale Farnsworth <dale@farnsworth.org>");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(max6900_init);
-module_exit(max6900_exit);
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
index 1f6b3cc58e8a..36c74d22e8b5 100644
--- a/drivers/rtc/rtc-max6902.c
+++ b/drivers/rtc/rtc-max6902.c
@@ -160,17 +160,7 @@ static struct spi_driver max6902_driver = {
.remove = __devexit_p(max6902_remove),
};
-static __init int max6902_init(void)
-{
- return spi_register_driver(&max6902_driver);
-}
-module_init(max6902_init);
-
-static __exit void max6902_exit(void)
-{
- spi_unregister_driver(&max6902_driver);
-}
-module_exit(max6902_exit);
+module_spi_driver(max6902_driver);
MODULE_DESCRIPTION ("max6902 spi RTC driver");
MODULE_AUTHOR ("Raphael Assenat");
diff --git a/drivers/rtc/rtc-max8925.c b/drivers/rtc/rtc-max8925.c
index 2d71943bc436..1459055a83aa 100644
--- a/drivers/rtc/rtc-max8925.c
+++ b/drivers/rtc/rtc-max8925.c
@@ -193,10 +193,17 @@ static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
ret = max8925_reg_read(info->rtc, MAX8925_RTC_IRQ_MASK);
if (ret < 0)
goto out;
- if ((ret & ALARM0_IRQ) == 0)
- alrm->enabled = 1;
- else
+ if (ret & ALARM0_IRQ) {
alrm->enabled = 0;
+ } else {
+ ret = max8925_reg_read(info->rtc, MAX8925_ALARM0_CNTL);
+ if (ret < 0)
+ goto out;
+ if (!ret)
+ alrm->enabled = 0;
+ else
+ alrm->enabled = 1;
+ }
ret = max8925_reg_read(info->rtc, MAX8925_RTC_STATUS);
if (ret < 0)
goto out;
@@ -204,6 +211,7 @@ static int max8925_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
alrm->pending = 1;
else
alrm->pending = 0;
+ return 0;
out:
return ret;
}
@@ -220,8 +228,11 @@ static int max8925_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
ret = max8925_bulk_write(info->rtc, MAX8925_ALARM0_SEC, TIME_NUM, buf);
if (ret < 0)
goto out;
- /* only enable alarm on year/month/day/hour/min/sec */
- ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
+ if (alrm->enabled)
+ /* only enable alarm on year/month/day/hour/min/sec */
+ ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x77);
+ else
+ ret = max8925_reg_write(info->rtc, MAX8925_ALARM0_CNTL, 0x0);
if (ret < 0)
goto out;
out:
diff --git a/drivers/rtc/rtc-mpc5121.c b/drivers/rtc/rtc-mpc5121.c
index 9d3caccfc250..42f5f829b3ee 100644
--- a/drivers/rtc/rtc-mpc5121.c
+++ b/drivers/rtc/rtc-mpc5121.c
@@ -327,7 +327,7 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op)
dev_set_drvdata(&op->dev, rtc);
rtc->irq = irq_of_parse_and_map(op->dev.of_node, 1);
- err = request_irq(rtc->irq, mpc5121_rtc_handler, IRQF_DISABLED,
+ err = request_irq(rtc->irq, mpc5121_rtc_handler, 0,
"mpc5121-rtc", &op->dev);
if (err) {
dev_err(&op->dev, "%s: could not request irq: %i\n",
@@ -337,7 +337,7 @@ static int __devinit mpc5121_rtc_probe(struct platform_device *op)
rtc->irq_periodic = irq_of_parse_and_map(op->dev.of_node, 0);
err = request_irq(rtc->irq_periodic, mpc5121_rtc_handler_upd,
- IRQF_DISABLED, "mpc5121-rtc_upd", &op->dev);
+ 0, "mpc5121-rtc_upd", &op->dev);
if (err) {
dev_err(&op->dev, "%s: could not request irq: %i\n",
__func__, rtc->irq_periodic);
@@ -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-mrst.c b/drivers/rtc/rtc-mrst.c
index 6cd6c7235344..f51719bf4a75 100644
--- a/drivers/rtc/rtc-mrst.c
+++ b/drivers/rtc/rtc-mrst.c
@@ -366,7 +366,7 @@ vrtc_mrst_do_probe(struct device *dev, struct resource *iomem, int rtc_irq)
if (rtc_irq) {
retval = request_irq(rtc_irq, mrst_rtc_irq,
- IRQF_DISABLED, dev_name(&mrst_rtc.rtc->dev),
+ 0, dev_name(&mrst_rtc.rtc->dev),
mrst_rtc.rtc);
if (retval < 0) {
dev_dbg(dev, "IRQ %d is already in use, err %d\n",
diff --git a/drivers/rtc/rtc-mv.c b/drivers/rtc/rtc-mv.c
index 768e2edb9678..b2185f4255aa 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>
@@ -273,7 +274,7 @@ static int __devinit mv_rtc_probe(struct platform_device *pdev)
if (pdata->irq >= 0) {
writel(0, pdata->ioaddr + RTC_ALARM_INTERRUPT_MASK_REG_OFFS);
if (devm_request_irq(&pdev->dev, pdata->irq, mv_rtc_interrupt,
- IRQF_DISABLED | IRQF_SHARED,
+ IRQF_SHARED,
pdev->name, pdata) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = -1;
@@ -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-nuc900.c b/drivers/rtc/rtc-nuc900.c
index 781068d62f23..b79010987d1e 100644
--- a/drivers/rtc/rtc-nuc900.c
+++ b/drivers/rtc/rtc-nuc900.c
@@ -269,7 +269,7 @@ static int __devinit nuc900_rtc_probe(struct platform_device *pdev)
nuc900_rtc->irq_num = platform_get_irq(pdev, 0);
if (request_irq(nuc900_rtc->irq_num, nuc900_rtc_interrupt,
- IRQF_DISABLED, "nuc900rtc", nuc900_rtc)) {
+ 0, "nuc900rtc", nuc900_rtc)) {
dev_err(&pdev->dev, "NUC900 RTC request irq failed\n");
err = -EBUSY;
goto fail4;
diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c
index 7789002bdd5c..0b614e32653d 100644
--- a/drivers/rtc/rtc-omap.c
+++ b/drivers/rtc/rtc-omap.c
@@ -348,14 +348,14 @@ static int __init omap_rtc_probe(struct platform_device *pdev)
rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
/* handle periodic and alarm irqs */
- if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,
+ if (request_irq(omap_rtc_timer, rtc_irq, 0,
dev_name(&rtc->dev), rtc)) {
pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_timer);
goto fail1;
}
if ((omap_rtc_timer != omap_rtc_alarm) &&
- (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,
+ (request_irq(omap_rtc_alarm, rtc_irq, 0,
dev_name(&rtc->dev), rtc))) {
pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_alarm);
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index b46c4004d8fe..836118795c0b 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -346,20 +346,9 @@ static struct spi_driver pcf2123_driver = {
.remove = __devexit_p(pcf2123_remove),
};
-static int __init pcf2123_init(void)
-{
- return spi_register_driver(&pcf2123_driver);
-}
-
-static void __exit pcf2123_exit(void)
-{
- spi_unregister_driver(&pcf2123_driver);
-}
+module_spi_driver(pcf2123_driver);
MODULE_AUTHOR("Chris Verges <chrisv@cyberswitching.com>");
MODULE_DESCRIPTION("NXP PCF2123 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(pcf2123_init);
-module_exit(pcf2123_exit);
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index 606fdfab34e2..bc0677de1996 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -252,20 +252,9 @@ static struct i2c_driver pcf8563_driver = {
.id_table = pcf8563_id,
};
-static int __init pcf8563_init(void)
-{
- return i2c_add_driver(&pcf8563_driver);
-}
-
-static void __exit pcf8563_exit(void)
-{
- i2c_del_driver(&pcf8563_driver);
-}
+module_i2c_driver(pcf8563_driver);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(pcf8563_init);
-module_exit(pcf8563_exit);
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index 2d201afead3b..019ff3571168 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -320,18 +320,7 @@ static struct i2c_driver pcf8583_driver = {
.id_table = pcf8583_id,
};
-static __init int pcf8583_init(void)
-{
- return i2c_add_driver(&pcf8583_driver);
-}
-
-static __exit void pcf8583_exit(void)
-{
- i2c_del_driver(&pcf8583_driver);
-}
-
-module_init(pcf8583_init);
-module_exit(pcf8583_exit);
+module_i2c_driver(pcf8583_driver);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("PCF8583 I2C RTC driver");
diff --git a/drivers/rtc/rtc-pl030.c b/drivers/rtc/rtc-pl030.c
index 02111fee077e..22bacdbf9139 100644
--- a/drivers/rtc/rtc-pl030.c
+++ b/drivers/rtc/rtc-pl030.c
@@ -123,7 +123,7 @@ static int pl030_probe(struct amba_device *dev, const struct amba_id *id)
amba_set_drvdata(dev, rtc);
- ret = request_irq(dev->irq[0], pl030_interrupt, IRQF_DISABLED,
+ ret = request_irq(dev->irq[0], pl030_interrupt, 0,
"rtc-pl030", rtc);
if (ret)
goto err_irq;
@@ -185,18 +185,7 @@ static struct amba_driver pl030_driver = {
.id_table = pl030_ids,
};
-static int __init pl030_init(void)
-{
- return amba_driver_register(&pl030_driver);
-}
-
-static void __exit pl030_exit(void)
-{
- amba_driver_unregister(&pl030_driver);
-}
-
-module_init(pl030_init);
-module_exit(pl030_exit);
+module_amba_driver(pl030_driver);
MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
MODULE_DESCRIPTION("ARM AMBA PL030 RTC Driver");
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index a952c8de1dd7..692de7360e94 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -352,7 +352,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
}
if (request_irq(adev->irq[0], pl031_interrupt,
- IRQF_DISABLED, "rtc-pl031", ldata)) {
+ 0, "rtc-pl031", ldata)) {
ret = -EIO;
goto out_no_irq;
}
@@ -431,18 +431,7 @@ static struct amba_driver pl031_driver = {
.remove = pl031_remove,
};
-static int __init pl031_init(void)
-{
- return amba_driver_register(&pl031_driver);
-}
-
-static void __exit pl031_exit(void)
-{
- amba_driver_unregister(&pl031_driver);
-}
-
-module_init(pl031_init);
-module_exit(pl031_exit);
+module_amba_driver(pl031_driver);
MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net");
MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");
diff --git a/drivers/rtc/rtc-pm8xxx.c b/drivers/rtc/rtc-pm8xxx.c
index 9f1d6bcbdf6c..d00bd24342a3 100644
--- a/drivers/rtc/rtc-pm8xxx.c
+++ b/drivers/rtc/rtc-pm8xxx.c
@@ -520,7 +520,7 @@ static int pm8xxx_rtc_suspend(struct device *dev)
}
#endif
-SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume);
+static SIMPLE_DEV_PM_OPS(pm8xxx_rtc_pm_ops, pm8xxx_rtc_suspend, pm8xxx_rtc_resume);
static struct platform_driver pm8xxx_rtc_driver = {
.probe = pm8xxx_rtc_probe,
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index fc9f4991574b..0075c8fd93d8 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -174,14 +174,14 @@ static int pxa_rtc_open(struct device *dev)
struct pxa_rtc *pxa_rtc = dev_get_drvdata(dev);
int ret;
- ret = request_irq(pxa_rtc->irq_1Hz, pxa_rtc_irq, IRQF_DISABLED,
+ ret = request_irq(pxa_rtc->irq_1Hz, pxa_rtc_irq, 0,
"rtc 1Hz", dev);
if (ret < 0) {
dev_err(dev, "can't get irq %i, err %d\n", pxa_rtc->irq_1Hz,
ret);
goto err_irq_1Hz;
}
- ret = request_irq(pxa_rtc->irq_Alrm, pxa_rtc_irq, IRQF_DISABLED,
+ ret = request_irq(pxa_rtc->irq_Alrm, pxa_rtc_irq, 0,
"rtc Alrm", dev);
if (ret < 0) {
dev_err(dev, "can't get irq %i, err %d\n", pxa_rtc->irq_Alrm,
diff --git a/drivers/rtc/rtc-r9701.c b/drivers/rtc/rtc-r9701.c
index 2853c2a6f10f..7f8e6c247935 100644
--- a/drivers/rtc/rtc-r9701.c
+++ b/drivers/rtc/rtc-r9701.c
@@ -159,17 +159,7 @@ static struct spi_driver r9701_driver = {
.remove = __devexit_p(r9701_remove),
};
-static __init int r9701_init(void)
-{
- return spi_register_driver(&r9701_driver);
-}
-module_init(r9701_init);
-
-static __exit void r9701_exit(void)
-{
- spi_unregister_driver(&r9701_driver);
-}
-module_exit(r9701_exit);
+module_spi_driver(r9701_driver);
MODULE_DESCRIPTION("r9701 spi RTC driver");
MODULE_AUTHOR("Magnus Damm <damm@opensource.se>");
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index ce2ca8523ddd..77074ccd2850 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -235,18 +235,7 @@ static struct spi_driver rs5c348_driver = {
.remove = __devexit_p(rs5c348_remove),
};
-static __init int rs5c348_init(void)
-{
- return spi_register_driver(&rs5c348_driver);
-}
-
-static __exit void rs5c348_exit(void)
-{
- spi_unregister_driver(&rs5c348_driver);
-}
-
-module_init(rs5c348_init);
-module_exit(rs5c348_exit);
+module_spi_driver(rs5c348_driver);
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver");
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index d29f5432c6e8..fb4842c3544e 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -689,18 +689,7 @@ static struct i2c_driver rs5c372_driver = {
.id_table = rs5c372_id,
};
-static __init int rs5c372_init(void)
-{
- return i2c_add_driver(&rs5c372_driver);
-}
-
-static __exit void rs5c372_exit(void)
-{
- i2c_del_driver(&rs5c372_driver);
-}
-
-module_init(rs5c372_init);
-module_exit(rs5c372_exit);
+module_i2c_driver(rs5c372_driver);
MODULE_AUTHOR(
"Pavel Mironchik <pmironchik@optifacio.net>, "
diff --git a/drivers/rtc/rtc-rv3029c2.c b/drivers/rtc/rtc-rv3029c2.c
index ea09ff211dc6..0fbe57b2f6d2 100644
--- a/drivers/rtc/rtc-rv3029c2.c
+++ b/drivers/rtc/rtc-rv3029c2.c
@@ -436,18 +436,7 @@ static struct i2c_driver rv3029c2_driver = {
.id_table = rv3029c2_id,
};
-static int __init rv3029c2_init(void)
-{
- return i2c_add_driver(&rv3029c2_driver);
-}
-
-static void __exit rv3029c2_exit(void)
-{
- i2c_del_driver(&rv3029c2_driver);
-}
-
-module_init(rv3029c2_init);
-module_exit(rv3029c2_exit);
+module_i2c_driver(rv3029c2_driver);
MODULE_AUTHOR("Gregory Hermant <gregory.hermant@calao-systems.com>");
MODULE_DESCRIPTION("Micro Crystal RV3029C2 RTC driver");
diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c
index fde172fb2abe..0de902dc1cd5 100644
--- a/drivers/rtc/rtc-rx8025.c
+++ b/drivers/rtc/rtc-rx8025.c
@@ -644,19 +644,8 @@ static struct i2c_driver rx8025_driver = {
.id_table = rx8025_id,
};
-static int __init rx8025_init(void)
-{
- return i2c_add_driver(&rx8025_driver);
-}
-
-static void __exit rx8025_exit(void)
-{
- i2c_del_driver(&rx8025_driver);
-}
+module_i2c_driver(rx8025_driver);
MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>");
MODULE_DESCRIPTION("RX-8025 SA/NB RTC driver");
MODULE_LICENSE("GPL");
-
-module_init(rx8025_init);
-module_exit(rx8025_exit);
diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c
index 600b890a3c15..d84825124a7a 100644
--- a/drivers/rtc/rtc-rx8581.c
+++ b/drivers/rtc/rtc-rx8581.c
@@ -276,20 +276,9 @@ static struct i2c_driver rx8581_driver = {
.id_table = rx8581_id,
};
-static int __init rx8581_init(void)
-{
- return i2c_add_driver(&rx8581_driver);
-}
-
-static void __exit rx8581_exit(void)
-{
- i2c_del_driver(&rx8581_driver);
-}
+module_i2c_driver(rx8581_driver);
MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");
MODULE_DESCRIPTION("Epson RX-8581 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(rx8581_init);
-module_exit(rx8581_exit);
diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c
index f789e002c9b0..c9562ceedef3 100644
--- a/drivers/rtc/rtc-s35390a.c
+++ b/drivers/rtc/rtc-s35390a.c
@@ -304,19 +304,8 @@ static struct i2c_driver s35390a_driver = {
.id_table = s35390a_id,
};
-static int __init s35390a_rtc_init(void)
-{
- return i2c_add_driver(&s35390a_driver);
-}
-
-static void __exit s35390a_rtc_exit(void)
-{
- i2c_del_driver(&s35390a_driver);
-}
+module_i2c_driver(s35390a_driver);
MODULE_AUTHOR("Byron Bradley <byron.bbradley@gmail.com>");
MODULE_DESCRIPTION("S35390A RTC driver");
MODULE_LICENSE("GPL");
-
-module_init(s35390a_rtc_init);
-module_exit(s35390a_rtc_exit);
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index aef40bd2957b..9ccea134a996 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -35,6 +35,8 @@
enum s3c_cpu_type {
TYPE_S3C2410,
+ TYPE_S3C2416,
+ TYPE_S3C2443,
TYPE_S3C64XX,
};
@@ -132,6 +134,7 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
unsigned int tmp = 0;
+ int val;
if (!is_power_of_2(freq))
return -EINVAL;
@@ -139,12 +142,22 @@ static int s3c_rtc_setfreq(struct device *dev, int freq)
clk_enable(rtc_clk);
spin_lock_irq(&s3c_rtc_pie_lock);
- if (s3c_rtc_cpu_type == TYPE_S3C2410) {
+ if (s3c_rtc_cpu_type != TYPE_S3C64XX) {
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
tmp &= S3C2410_TICNT_ENABLE;
}
- tmp |= (rtc_dev->max_user_freq / freq)-1;
+ val = (rtc_dev->max_user_freq / freq) - 1;
+
+ if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) {
+ tmp |= S3C2443_TICNT_PART(val);
+ writel(S3C2443_TICNT1_PART(val), s3c_rtc_base + S3C2443_TICNT1);
+
+ if (s3c_rtc_cpu_type == TYPE_S3C2416)
+ writel(S3C2416_TICNT2_PART(val), s3c_rtc_base + S3C2416_TICNT2);
+ } else {
+ tmp |= val;
+ }
writel(tmp, s3c_rtc_base + S3C2410_TICNT);
spin_unlock_irq(&s3c_rtc_pie_lock);
@@ -371,7 +384,7 @@ static void s3c_rtc_enable(struct platform_device *pdev, int en)
tmp &= ~S3C2410_RTCCON_RTCEN;
writew(tmp, base + S3C2410_RTCCON);
- if (s3c_rtc_cpu_type == TYPE_S3C2410) {
+ if (s3c_rtc_cpu_type != TYPE_S3C64XX) {
tmp = readb(base + S3C2410_TICNT);
tmp &= ~S3C2410_TICNT_ENABLE;
writeb(tmp, base + S3C2410_TICNT);
@@ -428,12 +441,27 @@ static int __devexit s3c_rtc_remove(struct platform_device *dev)
return 0;
}
+static const struct of_device_id s3c_rtc_dt_match[];
+
+static inline int s3c_rtc_get_driver_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_OF
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node);
+ return match->data;
+ }
+#endif
+ return platform_get_device_id(pdev)->driver_data;
+}
+
static int __devinit s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct rtc_time rtc_tm;
struct resource *res;
int ret;
+ int tmp;
pr_debug("%s: probe=%p\n", __func__, pdev);
@@ -508,13 +536,7 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
goto err_nortc;
}
-#ifdef CONFIG_OF
- if (pdev->dev.of_node)
- s3c_rtc_cpu_type = of_device_is_compatible(pdev->dev.of_node,
- "samsung,s3c6410-rtc") ? TYPE_S3C64XX : TYPE_S3C2410;
- else
-#endif
- s3c_rtc_cpu_type = platform_get_device_id(pdev)->driver_data;
+ s3c_rtc_cpu_type = s3c_rtc_get_driver_data(pdev);
/* Check RTC Time */
@@ -533,24 +555,30 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n");
}
- if (s3c_rtc_cpu_type == TYPE_S3C64XX)
+ if (s3c_rtc_cpu_type != TYPE_S3C2410)
rtc->max_user_freq = 32768;
else
rtc->max_user_freq = 128;
+ if (s3c_rtc_cpu_type == TYPE_S3C2416 || s3c_rtc_cpu_type == TYPE_S3C2443) {
+ tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
+ tmp |= S3C2443_RTCCON_TICSEL;
+ writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
+ }
+
platform_set_drvdata(pdev, rtc);
s3c_rtc_setfreq(&pdev->dev, 1);
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
- IRQF_DISABLED, "s3c2410-rtc alarm", rtc);
+ 0, "s3c2410-rtc alarm", rtc);
if (ret) {
dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
goto err_alarm_irq;
}
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
- IRQF_DISABLED, "s3c2410-rtc tick", rtc);
+ 0, "s3c2410-rtc tick", rtc);
if (ret) {
dev_err(&pdev->dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
free_irq(s3c_rtc_alarmno, rtc);
@@ -638,8 +666,19 @@ static int s3c_rtc_resume(struct platform_device *pdev)
#ifdef CONFIG_OF
static const struct of_device_id s3c_rtc_dt_match[] = {
- { .compatible = "samsung,s3c2410-rtc" },
- { .compatible = "samsung,s3c6410-rtc" },
+ {
+ .compatible = "samsung,s3c2410-rtc"
+ .data = TYPE_S3C2410,
+ }, {
+ .compatible = "samsung,s3c2416-rtc"
+ .data = TYPE_S3C2416,
+ }, {
+ .compatible = "samsung,s3c2443-rtc"
+ .data = TYPE_S3C2443,
+ }, {
+ .compatible = "samsung,s3c6410-rtc"
+ .data = TYPE_S3C64XX,
+ },
{},
};
MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match);
@@ -652,6 +691,12 @@ static struct platform_device_id s3c_rtc_driver_ids[] = {
.name = "s3c2410-rtc",
.driver_data = TYPE_S3C2410,
}, {
+ .name = "s3c2416-rtc",
+ .driver_data = TYPE_S3C2416,
+ }, {
+ .name = "s3c2443-rtc",
+ .driver_data = TYPE_S3C2443,
+ }, {
.name = "s3c64xx-rtc",
.driver_data = TYPE_S3C64XX,
},
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index cb9a585312cc..4940fa8c4e10 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -23,94 +23,44 @@
#include <linux/platform_device.h>
#include <linux/module.h>
+#include <linux/clk.h>
#include <linux/rtc.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
+#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/of.h>
#include <linux/pm.h>
#include <linux/bitops.h>
#include <mach/hardware.h>
-#include <asm/irq.h>
+#include <mach/irqs.h>
-#ifdef CONFIG_ARCH_PXA
+#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)
#include <mach/regs-rtc.h>
#endif
#define RTC_DEF_DIVIDER (32768 - 1)
#define RTC_DEF_TRIM 0
-
-static const unsigned long RTC_FREQ = 1024;
-static struct rtc_time rtc_alarm;
-static DEFINE_SPINLOCK(sa1100_rtc_lock);
-
-static inline int rtc_periodic_alarm(struct rtc_time *tm)
-{
- return (tm->tm_year == -1) ||
- ((unsigned)tm->tm_mon >= 12) ||
- ((unsigned)(tm->tm_mday - 1) >= 31) ||
- ((unsigned)tm->tm_hour > 23) ||
- ((unsigned)tm->tm_min > 59) ||
- ((unsigned)tm->tm_sec > 59);
-}
-
-/*
- * Calculate the next alarm time given the requested alarm time mask
- * and the current time.
- */
-static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
- struct rtc_time *alrm)
-{
- unsigned long next_time;
- unsigned long now_time;
-
- next->tm_year = now->tm_year;
- next->tm_mon = now->tm_mon;
- next->tm_mday = now->tm_mday;
- next->tm_hour = alrm->tm_hour;
- next->tm_min = alrm->tm_min;
- next->tm_sec = alrm->tm_sec;
-
- rtc_tm_to_time(now, &now_time);
- rtc_tm_to_time(next, &next_time);
-
- if (next_time < now_time) {
- /* Advance one day */
- next_time += 60 * 60 * 24;
- rtc_time_to_tm(next_time, next);
- }
-}
-
-static int rtc_update_alarm(struct rtc_time *alrm)
-{
- struct rtc_time alarm_tm, now_tm;
- unsigned long now, time;
- int ret;
-
- do {
- now = RCNR;
- rtc_time_to_tm(now, &now_tm);
- rtc_next_alarm_time(&alarm_tm, &now_tm, alrm);
- ret = rtc_tm_to_time(&alarm_tm, &time);
- if (ret != 0)
- break;
-
- RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
- RTAR = time;
- } while (now != RCNR);
-
- return ret;
-}
+#define RTC_FREQ 1024
+
+struct sa1100_rtc {
+ spinlock_t lock;
+ int irq_1hz;
+ int irq_alarm;
+ struct rtc_device *rtc;
+ struct clk *clk;
+};
static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
{
- struct platform_device *pdev = to_platform_device(dev_id);
- struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct sa1100_rtc *info = dev_get_drvdata(dev_id);
+ struct rtc_device *rtc = info->rtc;
unsigned int rtsr;
unsigned long events = 0;
- spin_lock(&sa1100_rtc_lock);
+ spin_lock(&info->lock);
rtsr = RTSR;
/* clear interrupt sources */
@@ -146,30 +96,28 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
rtc_update_irq(rtc, 1, events);
- if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))
- rtc_update_alarm(&rtc_alarm);
-
- spin_unlock(&sa1100_rtc_lock);
+ spin_unlock(&info->lock);
return IRQ_HANDLED;
}
static int sa1100_rtc_open(struct device *dev)
{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+ struct rtc_device *rtc = info->rtc;
int ret;
- struct platform_device *plat_dev = to_platform_device(dev);
- struct rtc_device *rtc = platform_get_drvdata(plat_dev);
- ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED,
- "rtc 1Hz", dev);
+ ret = clk_prepare_enable(info->clk);
+ if (ret)
+ goto fail_clk;
+ ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev);
if (ret) {
- dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz);
+ dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz);
goto fail_ui;
}
- ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED,
- "rtc Alrm", dev);
+ ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev);
if (ret) {
- dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm);
+ dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm);
goto fail_ai;
}
rtc->max_user_freq = RTC_FREQ;
@@ -178,29 +126,36 @@ static int sa1100_rtc_open(struct device *dev)
return 0;
fail_ai:
- free_irq(IRQ_RTC1Hz, dev);
+ free_irq(info->irq_1hz, dev);
fail_ui:
+ clk_disable_unprepare(info->clk);
+ fail_clk:
return ret;
}
static void sa1100_rtc_release(struct device *dev)
{
- spin_lock_irq(&sa1100_rtc_lock);
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+
+ spin_lock_irq(&info->lock);
RTSR = 0;
- spin_unlock_irq(&sa1100_rtc_lock);
+ spin_unlock_irq(&info->lock);
- free_irq(IRQ_RTCAlrm, dev);
- free_irq(IRQ_RTC1Hz, dev);
+ free_irq(info->irq_alarm, dev);
+ free_irq(info->irq_1hz, dev);
+ clk_disable_unprepare(info->clk);
}
static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
- spin_lock_irq(&sa1100_rtc_lock);
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+
+ spin_lock_irq(&info->lock);
if (enabled)
RTSR |= RTSR_ALE;
else
RTSR &= ~RTSR_ALE;
- spin_unlock_irq(&sa1100_rtc_lock);
+ spin_unlock_irq(&info->lock);
return 0;
}
@@ -225,7 +180,6 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
u32 rtsr;
- memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));
rtsr = RTSR;
alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0;
alrm->pending = (rtsr & RTSR_AL) ? 1 : 0;
@@ -234,17 +188,22 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
+ unsigned long time;
int ret;
- spin_lock_irq(&sa1100_rtc_lock);
- ret = rtc_update_alarm(&alrm->time);
- if (ret == 0) {
- if (alrm->enabled)
- RTSR |= RTSR_ALE;
- else
- RTSR &= ~RTSR_ALE;
- }
- spin_unlock_irq(&sa1100_rtc_lock);
+ spin_lock_irq(&info->lock);
+ ret = rtc_tm_to_time(&alrm->time, &time);
+ if (ret != 0)
+ goto out;
+ RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
+ RTAR = time;
+ if (alrm->enabled)
+ RTSR |= RTSR_ALE;
+ else
+ RTSR &= ~RTSR_ALE;
+out:
+ spin_unlock_irq(&info->lock);
return ret;
}
@@ -271,6 +230,27 @@ static const struct rtc_class_ops sa1100_rtc_ops = {
static int sa1100_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
+ struct sa1100_rtc *info;
+ int irq_1hz, irq_alarm, ret = 0;
+
+ irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz");
+ irq_alarm = platform_get_irq_byname(pdev, "rtc alarm");
+ if (irq_1hz < 0 || irq_alarm < 0)
+ return -ENODEV;
+
+ info = kzalloc(sizeof(struct sa1100_rtc), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->clk)) {
+ dev_err(&pdev->dev, "failed to find rtc clock source\n");
+ ret = PTR_ERR(info->clk);
+ goto err_clk;
+ }
+ info->irq_1hz = irq_1hz;
+ info->irq_alarm = irq_alarm;
+ spin_lock_init(&info->lock);
+ platform_set_drvdata(pdev, info);
/*
* According to the manual we should be able to let RTTR be zero
@@ -292,10 +272,11 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops,
THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
-
- platform_set_drvdata(pdev, rtc);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto err_dev;
+ }
+ info->rtc = rtc;
/* Fix for a nasty initialization problem the in SA11xx RTSR register.
* See also the comments in sa1100_rtc_interrupt().
@@ -322,14 +303,24 @@ static int sa1100_rtc_probe(struct platform_device *pdev)
RTSR = RTSR_AL | RTSR_HZ;
return 0;
+err_dev:
+ platform_set_drvdata(pdev, NULL);
+ clk_put(info->clk);
+err_clk:
+ kfree(info);
+ return ret;
}
static int sa1100_rtc_remove(struct platform_device *pdev)
{
- struct rtc_device *rtc = platform_get_drvdata(pdev);
+ struct sa1100_rtc *info = platform_get_drvdata(pdev);
- if (rtc)
- rtc_device_unregister(rtc);
+ if (info) {
+ rtc_device_unregister(info->rtc);
+ clk_put(info->clk);
+ platform_set_drvdata(pdev, NULL);
+ kfree(info);
+ }
return 0;
}
@@ -337,15 +328,17 @@ static int sa1100_rtc_remove(struct platform_device *pdev)
#ifdef CONFIG_PM
static int sa1100_rtc_suspend(struct device *dev)
{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
- enable_irq_wake(IRQ_RTCAlrm);
+ enable_irq_wake(info->irq_alarm);
return 0;
}
static int sa1100_rtc_resume(struct device *dev)
{
+ struct sa1100_rtc *info = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
- disable_irq_wake(IRQ_RTCAlrm);
+ disable_irq_wake(info->irq_alarm);
return 0;
}
@@ -355,6 +348,13 @@ static const struct dev_pm_ops sa1100_rtc_pm_ops = {
};
#endif
+static struct of_device_id sa1100_rtc_dt_ids[] = {
+ { .compatible = "mrvl,sa1100-rtc", },
+ { .compatible = "mrvl,mmp-rtc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids);
+
static struct platform_driver sa1100_rtc_driver = {
.probe = sa1100_rtc_probe,
.remove = sa1100_rtc_remove,
@@ -363,6 +363,7 @@ static struct platform_driver sa1100_rtc_driver = {
#ifdef CONFIG_PM
.pm = &sa1100_rtc_pm_ops,
#endif
+ .of_match_table = sa1100_rtc_dt_ids,
},
};
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
index 6ac55fd48413..e55a7635ae5f 100644
--- a/drivers/rtc/rtc-sh.c
+++ b/drivers/rtc/rtc-sh.c
@@ -666,7 +666,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
if (rtc->carry_irq <= 0) {
/* register shared periodic/carry/alarm irq */
ret = request_irq(rtc->periodic_irq, sh_rtc_shared,
- IRQF_DISABLED, "sh-rtc", rtc);
+ 0, "sh-rtc", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request IRQ failed with %d, IRQ %d\n", ret,
@@ -676,7 +676,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
} else {
/* register periodic/carry/alarm irqs */
ret = request_irq(rtc->periodic_irq, sh_rtc_periodic,
- IRQF_DISABLED, "sh-rtc period", rtc);
+ 0, "sh-rtc period", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request period IRQ failed with %d, IRQ %d\n",
@@ -685,7 +685,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
}
ret = request_irq(rtc->carry_irq, sh_rtc_interrupt,
- IRQF_DISABLED, "sh-rtc carry", rtc);
+ 0, "sh-rtc carry", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request carry IRQ failed with %d, IRQ %d\n",
@@ -695,7 +695,7 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
}
ret = request_irq(rtc->alarm_irq, sh_rtc_alarm,
- IRQF_DISABLED, "sh-rtc alarm", rtc);
+ 0, "sh-rtc alarm", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request alarm IRQ failed with %d, IRQ %d\n",
diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c
index 19a28a671a8e..e38da0dc4187 100644
--- a/drivers/rtc/rtc-spear.c
+++ b/drivers/rtc/rtc-spear.c
@@ -77,9 +77,11 @@
#define STATUS_FAIL (LOST_WR_TIME | LOST_WR_DATE)
struct spear_rtc_config {
+ struct rtc_device *rtc;
struct clk *clk;
spinlock_t lock;
void __iomem *ioaddr;
+ unsigned int irq_wake;
};
static inline void spear_rtc_clear_interrupt(struct spear_rtc_config *config)
@@ -149,8 +151,7 @@ static void rtc_wait_not_busy(struct spear_rtc_config *config)
static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
{
- struct rtc_device *rtc = (struct rtc_device *)dev_id;
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = dev_id;
unsigned long flags, events = 0;
unsigned int irq_data;
@@ -161,7 +162,7 @@ static irqreturn_t spear_rtc_irq(int irq, void *dev_id)
if ((irq_data & RTC_INT_MASK)) {
spear_rtc_clear_interrupt(config);
events = RTC_IRQF | RTC_AF;
- rtc_update_irq(rtc, 1, events);
+ rtc_update_irq(config->rtc, 1, events);
return IRQ_HANDLED;
} else
return IRQ_NONE;
@@ -203,9 +204,7 @@ static void bcd2tm(struct rtc_time *tm)
*/
static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
unsigned int time, date;
/* we don't report wday/yday/isdst ... */
@@ -234,9 +233,7 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
*/
static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
unsigned int time, date, err = 0;
if (tm2bcd(tm) < 0)
@@ -266,9 +263,7 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
*/
static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
unsigned int time, date;
rtc_wait_not_busy(config);
@@ -298,9 +293,7 @@ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
*/
static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
- struct platform_device *pdev = to_platform_device(dev);
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
unsigned int time, date, err = 0;
if (tm2bcd(&alm->time) < 0)
@@ -326,17 +319,42 @@ static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
return 0;
}
+
+static int spear_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+ struct spear_rtc_config *config = dev_get_drvdata(dev);
+ int ret = 0;
+
+ spear_rtc_clear_interrupt(config);
+
+ switch (enabled) {
+ case 0:
+ /* alarm off */
+ spear_rtc_disable_interrupt(config);
+ break;
+ case 1:
+ /* alarm on */
+ spear_rtc_enable_interrupt(config);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
static struct rtc_class_ops spear_rtc_ops = {
.read_time = spear_rtc_read_time,
.set_time = spear_rtc_set_time,
.read_alarm = spear_rtc_read_alarm,
.set_alarm = spear_rtc_set_alarm,
+ .alarm_irq_enable = spear_alarm_irq_enable,
};
static int __devinit spear_rtc_probe(struct platform_device *pdev)
{
struct resource *res;
- struct rtc_device *rtc;
struct spear_rtc_config *config;
unsigned int status = 0;
int irq;
@@ -376,19 +394,17 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev)
}
spin_lock_init(&config->lock);
+ platform_set_drvdata(pdev, config);
- rtc = rtc_device_register(pdev->name, &pdev->dev, &spear_rtc_ops,
- THIS_MODULE);
- if (IS_ERR(rtc)) {
+ config->rtc = rtc_device_register(pdev->name, &pdev->dev,
+ &spear_rtc_ops, THIS_MODULE);
+ if (IS_ERR(config->rtc)) {
dev_err(&pdev->dev, "can't register RTC device, err %ld\n",
- PTR_ERR(rtc));
- status = PTR_ERR(rtc);
+ PTR_ERR(config->rtc));
+ status = PTR_ERR(config->rtc);
goto err_iounmap;
}
- platform_set_drvdata(pdev, rtc);
- dev_set_drvdata(&rtc->dev, config);
-
/* alarm irqs */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -397,7 +413,7 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev)
goto err_clear_platdata;
}
- status = request_irq(irq, spear_rtc_irq, 0, pdev->name, rtc);
+ status = request_irq(irq, spear_rtc_irq, 0, pdev->name, config);
if (status) {
dev_err(&pdev->dev, "Alarm interrupt IRQ%d already \
claimed\n", irq);
@@ -411,8 +427,7 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev)
err_clear_platdata:
platform_set_drvdata(pdev, NULL);
- dev_set_drvdata(&rtc->dev, NULL);
- rtc_device_unregister(rtc);
+ rtc_device_unregister(config->rtc);
err_iounmap:
iounmap(config->ioaddr);
err_disable_clock:
@@ -429,8 +444,7 @@ err_release_region:
static int __devexit spear_rtc_remove(struct platform_device *pdev)
{
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
int irq;
struct resource *res;
@@ -448,8 +462,7 @@ static int __devexit spear_rtc_remove(struct platform_device *pdev)
if (res)
release_mem_region(res->start, resource_size(res));
platform_set_drvdata(pdev, NULL);
- dev_set_drvdata(&rtc->dev, NULL);
- rtc_device_unregister(rtc);
+ rtc_device_unregister(config->rtc);
return 0;
}
@@ -458,14 +471,14 @@ static int __devexit spear_rtc_remove(struct platform_device *pdev)
static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
int irq;
irq = platform_get_irq(pdev, 0);
- if (device_may_wakeup(&pdev->dev))
- enable_irq_wake(irq);
- else {
+ if (device_may_wakeup(&pdev->dev)) {
+ if (!enable_irq_wake(irq))
+ config->irq_wake = 1;
+ } else {
spear_rtc_disable_interrupt(config);
clk_disable(config->clk);
}
@@ -475,15 +488,17 @@ static int spear_rtc_suspend(struct platform_device *pdev, pm_message_t state)
static int spear_rtc_resume(struct platform_device *pdev)
{
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
int irq;
irq = platform_get_irq(pdev, 0);
- if (device_may_wakeup(&pdev->dev))
- disable_irq_wake(irq);
- else {
+ if (device_may_wakeup(&pdev->dev)) {
+ if (config->irq_wake) {
+ disable_irq_wake(irq);
+ config->irq_wake = 0;
+ }
+ } else {
clk_enable(config->clk);
spear_rtc_enable_interrupt(config);
}
@@ -498,8 +513,7 @@ static int spear_rtc_resume(struct platform_device *pdev)
static void spear_rtc_shutdown(struct platform_device *pdev)
{
- struct rtc_device *rtc = platform_get_drvdata(pdev);
- struct spear_rtc_config *config = dev_get_drvdata(&rtc->dev);
+ struct spear_rtc_config *config = platform_get_drvdata(pdev);
spear_rtc_disable_interrupt(config);
clk_disable(config->clk);
diff --git a/drivers/rtc/rtc-stk17ta8.c b/drivers/rtc/rtc-stk17ta8.c
index 7621116bd20d..279f5cfa691a 100644
--- a/drivers/rtc/rtc-stk17ta8.c
+++ b/drivers/rtc/rtc-stk17ta8.c
@@ -329,7 +329,7 @@ static int __devinit stk17ta8_rtc_probe(struct platform_device *pdev)
writeb(0, ioaddr + RTC_INTERRUPTS);
if (devm_request_irq(&pdev->dev, pdata->irq,
stk17ta8_rtc_interrupt,
- IRQF_DISABLED | IRQF_SHARED,
+ IRQF_SHARED,
pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = 0;
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index d43b4f6eb4e4..4c2c6df2a9ef 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -176,6 +176,10 @@ static int set_rtc_irq_bit(unsigned char bit)
unsigned char val;
int ret;
+ /* if the bit is set, return from here */
+ if (rtc_irq_bits & bit)
+ return 0;
+
val = rtc_irq_bits | bit;
val &= ~BIT_RTC_INTERRUPTS_REG_EVERY_M;
ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
@@ -193,6 +197,10 @@ static int mask_rtc_irq_bit(unsigned char bit)
unsigned char val;
int ret;
+ /* if the bit is clear, return from here */
+ if (!(rtc_irq_bits & bit))
+ return 0;
+
val = rtc_irq_bits & ~bit;
ret = twl_rtc_write_u8(val, REG_RTC_INTERRUPTS_REG);
if (ret == 0)
@@ -357,7 +365,7 @@ out:
static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
{
- unsigned long events = 0;
+ unsigned long events;
int ret = IRQ_NONE;
int res;
u8 rd_reg;
@@ -372,11 +380,11 @@ static irqreturn_t twl_rtc_interrupt(int irq, void *rtc)
* by reading RTS_INTERRUPTS_REGISTER[IT_TIMER,IT_ALARM]
*/
if (rd_reg & BIT_RTC_STATUS_REG_ALARM_M)
- events |= RTC_IRQF | RTC_AF;
+ events = RTC_IRQF | RTC_AF;
else
- events |= RTC_IRQF | RTC_UF;
+ events = RTC_IRQF | RTC_PF;
- res = twl_rtc_write_u8(rd_reg | BIT_RTC_STATUS_REG_ALARM_M,
+ res = twl_rtc_write_u8(BIT_RTC_STATUS_REG_ALARM_M,
REG_RTC_STATUS_REG);
if (res)
goto out;
@@ -449,19 +457,11 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
REG_INT_MSK_STS_A);
}
- /* Check RTC module status, Enable if it is off */
- ret = twl_rtc_read_u8(&rd_reg, REG_RTC_CTRL_REG);
+ dev_info(&pdev->dev, "Enabling TWL-RTC\n");
+ ret = twl_rtc_write_u8(BIT_RTC_CTRL_REG_STOP_RTC_M, REG_RTC_CTRL_REG);
if (ret < 0)
goto out1;
- if (!(rd_reg & BIT_RTC_CTRL_REG_STOP_RTC_M)) {
- dev_info(&pdev->dev, "Enabling TWL-RTC.\n");
- rd_reg = BIT_RTC_CTRL_REG_STOP_RTC_M;
- ret = twl_rtc_write_u8(rd_reg, REG_RTC_CTRL_REG);
- if (ret < 0)
- goto out1;
- }
-
/* init cached IRQ enable bits */
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
diff --git a/drivers/rtc/rtc-tx4939.c b/drivers/rtc/rtc-tx4939.c
index aac0ffed4345..a12bfac49d36 100644
--- a/drivers/rtc/rtc-tx4939.c
+++ b/drivers/rtc/rtc-tx4939.c
@@ -266,7 +266,7 @@ static int __init tx4939_rtc_probe(struct platform_device *pdev)
spin_lock_init(&pdata->lock);
tx4939_rtc_cmd(pdata->rtcreg, TX4939_RTCCTL_COMMAND_NOP);
if (devm_request_irq(&pdev->dev, irq, tx4939_rtc_interrupt,
- IRQF_DISABLED, pdev->name, &pdev->dev) < 0)
+ 0, pdev->name, &pdev->dev) < 0)
return -EBUSY;
rtc = rtc_device_register(pdev->name, &pdev->dev,
&tx4939_rtc_ops, THIS_MODULE);
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index fcbfdda2993b..5f60a7c6a155 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -333,7 +333,7 @@ static int __devinit rtc_probe(struct platform_device *pdev)
goto err_device_unregister;
}
- retval = request_irq(aie_irq, elapsedtime_interrupt, IRQF_DISABLED,
+ retval = request_irq(aie_irq, elapsedtime_interrupt, 0,
"elapsed_time", pdev);
if (retval < 0)
goto err_device_unregister;
@@ -342,7 +342,7 @@ static int __devinit rtc_probe(struct platform_device *pdev)
if (pie_irq <= 0)
goto err_free_irq;
- retval = request_irq(pie_irq, rtclong1_interrupt, IRQF_DISABLED,
+ retval = request_irq(pie_irq, rtclong1_interrupt, 0,
"rtclong1", pdev);
if (retval < 0)
goto err_free_irq;
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 8c051d3179db..403b3d41d101 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -623,15 +623,7 @@ static struct i2c_driver x1205_driver = {
.id_table = x1205_id,
};
-static int __init x1205_init(void)
-{
- return i2c_add_driver(&x1205_driver);
-}
-
-static void __exit x1205_exit(void)
-{
- i2c_del_driver(&x1205_driver);
-}
+module_i2c_driver(x1205_driver);
MODULE_AUTHOR(
"Karen Spearel <kas111 at gmail dot com>, "
@@ -639,6 +631,3 @@ MODULE_AUTHOR(
MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
-
-module_init(x1205_init);
-module_exit(x1205_exit);
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 110137e7ec81..f3509120a507 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -640,6 +640,10 @@ void dasd_enable_device(struct dasd_device *device)
dasd_set_target_state(device, DASD_STATE_NEW);
/* Now wait for the devices to come up. */
wait_event(dasd_init_waitq, _wait_for_device(device));
+
+ dasd_reload_device(device);
+ if (device->discipline->kick_validate)
+ device->discipline->kick_validate(device);
}
/*
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 46784b83c5c4..0cea7e98f464 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -229,7 +229,7 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
}
/* Handle external interruption. */
-static void dasd_ext_handler(unsigned int ext_int_code,
+static void dasd_ext_handler(struct ext_code ext_code,
unsigned int param32, unsigned long param64)
{
struct dasd_ccw_req *cqr, *next;
@@ -239,7 +239,7 @@ static void dasd_ext_handler(unsigned int ext_int_code,
addr_t ip;
int rc;
- switch (ext_int_code >> 24) {
+ switch (ext_code.subcode >> 8) {
case DASD_DIAG_CODE_31BIT:
ip = (addr_t) param32;
break;
@@ -280,7 +280,7 @@ static void dasd_ext_handler(unsigned int ext_int_code,
cqr->stopclk = get_clock();
expires = 0;
- if ((ext_int_code & 0xff0000) == 0) {
+ if ((ext_code.subcode & 0xff) == 0) {
cqr->status = DASD_CQR_SUCCESS;
/* Start first request on queue if possible -> fast_io. */
if (!list_empty(&device->ccw_queue)) {
@@ -296,7 +296,7 @@ static void dasd_ext_handler(unsigned int ext_int_code,
cqr->status = DASD_CQR_QUEUED;
DBF_DEV_EVENT(DBF_DEBUG, device, "interrupt status for "
"request %p was %d (%d retries left)", cqr,
- (ext_int_code >> 16) & 0xff, cqr->retries);
+ ext_code.subcode & 0xff, cqr->retries);
dasd_diag_erp(device);
}
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 2617b1ed4709..c21871a4e73d 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1564,6 +1564,12 @@ static void dasd_eckd_do_validate_server(struct work_struct *work)
static void dasd_eckd_kick_validate_server(struct dasd_device *device)
{
dasd_get_device(device);
+ /* exit if device not online or in offline processing */
+ if (test_bit(DASD_FLAG_OFFLINE, &device->flags) ||
+ device->state < DASD_STATE_ONLINE) {
+ dasd_put_device(device);
+ return;
+ }
/* queue call to do_validate_server to the kernel event daemon. */
schedule_work(&device->kick_validate);
}
@@ -1993,6 +1999,7 @@ static int dasd_eckd_ready_to_online(struct dasd_device *device)
static int dasd_eckd_online_to_ready(struct dasd_device *device)
{
cancel_work_sync(&device->reload_device);
+ cancel_work_sync(&device->kick_validate);
return dasd_alias_remove_device(device);
};
@@ -2263,6 +2270,7 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
* and only if not suspended
*/
if (!device->block && private->lcu &&
+ device->state == DASD_STATE_ONLINE &&
!test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
!test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
/*
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index eaa7e78186f9..30f29a0020a1 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -393,7 +393,7 @@ __sclp_find_req(u32 sccb)
/* Handler for external interruption. Perform request post-processing.
* Prepare read event data request if necessary. Start processing of next
* request on queue. */
-static void sclp_interrupt_handler(unsigned int ext_int_code,
+static void sclp_interrupt_handler(struct ext_code ext_code,
unsigned int param32, unsigned long param64)
{
struct sclp_req *req;
@@ -818,7 +818,7 @@ EXPORT_SYMBOL(sclp_reactivate);
/* Handler for external interruption used during initialization. Modify
* request state to done. */
-static void sclp_check_handler(unsigned int ext_int_code,
+static void sclp_check_handler(struct ext_code ext_code,
unsigned int param32, unsigned long param64)
{
u32 finished_sccb;
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
index 0b54a91f8dcd..231a1d85127b 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"
@@ -441,9 +442,8 @@ static int sclp_mem_notifier(struct notifier_block *nb,
start = arg->start_pfn << PAGE_SHIFT;
size = arg->nr_pages << PAGE_SHIFT;
mutex_lock(&sclp_mem_mutex);
- for (id = 0; id <= sclp_max_storage_id; id++)
- if (!test_bit(id, sclp_storage_ids))
- sclp_attach_storage(id);
+ for_each_clear_bit(id, sclp_storage_ids, sclp_max_storage_id + 1)
+ sclp_attach_storage(id);
switch (action) {
case MEM_ONLINE:
case MEM_GOING_OFFLINE:
diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c
index 87fc0ac11e67..69df137310bc 100644
--- a/drivers/s390/char/sclp_quiesce.c
+++ b/drivers/s390/char/sclp_quiesce.c
@@ -15,7 +15,6 @@
#include <linux/reboot.h>
#include <linux/atomic.h>
#include <asm/ptrace.h>
-#include <asm/sigp.h>
#include <asm/smp.h>
#include "sclp.h"
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index fa733ecd3d70..69e6c50d4cfb 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -8,6 +8,7 @@
#define KMSG_COMPONENT "sclp_sdias"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+#include <linux/completion.h>
#include <linux/sched.h>
#include <asm/sclp.h>
#include <asm/debug.h>
@@ -62,15 +63,29 @@ struct sdias_sccb {
} __attribute__((packed));
static struct sdias_sccb sccb __attribute__((aligned(4096)));
+static struct sdias_evbuf sdias_evbuf;
-static int sclp_req_done;
-static wait_queue_head_t sdias_wq;
+static DECLARE_COMPLETION(evbuf_accepted);
+static DECLARE_COMPLETION(evbuf_done);
static DEFINE_MUTEX(sdias_mutex);
+/*
+ * Called by SCLP base when read event data has been completed (async mode only)
+ */
+static void sclp_sdias_receiver_fn(struct evbuf_header *evbuf)
+{
+ memcpy(&sdias_evbuf, evbuf,
+ min_t(unsigned long, sizeof(sdias_evbuf), evbuf->length));
+ complete(&evbuf_done);
+ TRACE("sclp_sdias_receiver_fn done\n");
+}
+
+/*
+ * Called by SCLP base when sdias event has been accepted
+ */
static void sdias_callback(struct sclp_req *request, void *data)
{
- sclp_req_done = 1;
- wake_up(&sdias_wq); /* Inform caller, that request is complete */
+ complete(&evbuf_accepted);
TRACE("callback done\n");
}
@@ -80,7 +95,6 @@ static int sdias_sclp_send(struct sclp_req *req)
int rc;
for (retries = SDIAS_RETRIES; retries; retries--) {
- sclp_req_done = 0;
TRACE("add request\n");
rc = sclp_add_request(req);
if (rc) {
@@ -91,16 +105,31 @@ static int sdias_sclp_send(struct sclp_req *req)
continue;
}
/* initiated, wait for completion of service call */
- wait_event(sdias_wq, (sclp_req_done == 1));
+ wait_for_completion(&evbuf_accepted);
if (req->status == SCLP_REQ_FAILED) {
TRACE("sclp request failed\n");
- rc = -EIO;
continue;
}
+ /* if not accepted, retry */
+ if (!(sccb.evbuf.hdr.flags & 0x80)) {
+ TRACE("sclp request failed: flags=%x\n",
+ sccb.evbuf.hdr.flags);
+ continue;
+ }
+ /*
+ * for the sync interface the response is in the initial sccb
+ */
+ if (!sclp_sdias_register.receiver_fn) {
+ memcpy(&sdias_evbuf, &sccb.evbuf, sizeof(sdias_evbuf));
+ TRACE("sync request done\n");
+ return 0;
+ }
+ /* otherwise we wait for completion */
+ wait_for_completion(&evbuf_done);
TRACE("request done\n");
- break;
+ return 0;
}
- return rc;
+ return -EIO;
}
/*
@@ -140,13 +169,12 @@ int sclp_sdias_blk_count(void)
goto out;
}
- switch (sccb.evbuf.event_status) {
+ switch (sdias_evbuf.event_status) {
case 0:
- rc = sccb.evbuf.blk_cnt;
+ rc = sdias_evbuf.blk_cnt;
break;
default:
- pr_err("SCLP error: %x\n",
- sccb.evbuf.event_status);
+ pr_err("SCLP error: %x\n", sdias_evbuf.event_status);
rc = -EIO;
goto out;
}
@@ -211,18 +239,18 @@ int sclp_sdias_copy(void *dest, int start_blk, int nr_blks)
goto out;
}
- switch (sccb.evbuf.event_status) {
+ switch (sdias_evbuf.event_status) {
case EVSTATE_ALL_STORED:
TRACE("all stored\n");
case EVSTATE_PART_STORED:
- TRACE("part stored: %i\n", sccb.evbuf.blk_cnt);
+ TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
break;
case EVSTATE_NO_DATA:
TRACE("no data\n");
default:
pr_err("Error from SCLP while copying hsa. "
"Event status = %x\n",
- sccb.evbuf.event_status);
+ sdias_evbuf.event_status);
rc = -EIO;
}
out:
@@ -230,19 +258,50 @@ out:
return rc;
}
-int __init sclp_sdias_init(void)
+static int __init sclp_sdias_register_check(void)
{
int rc;
+ rc = sclp_register(&sclp_sdias_register);
+ if (rc)
+ return rc;
+ if (sclp_sdias_blk_count() == 0) {
+ sclp_unregister(&sclp_sdias_register);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int __init sclp_sdias_init_sync(void)
+{
+ TRACE("Try synchronous mode\n");
+ sclp_sdias_register.receive_mask = 0;
+ sclp_sdias_register.receiver_fn = NULL;
+ return sclp_sdias_register_check();
+}
+
+static int __init sclp_sdias_init_async(void)
+{
+ TRACE("Try asynchronous mode\n");
+ sclp_sdias_register.receive_mask = EVTYP_SDIAS_MASK;
+ sclp_sdias_register.receiver_fn = sclp_sdias_receiver_fn;
+ return sclp_sdias_register_check();
+}
+
+int __init sclp_sdias_init(void)
+{
if (ipl_info.type != IPL_TYPE_FCP_DUMP)
return 0;
sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long));
debug_register_view(sdias_dbf, &debug_sprintf_view);
debug_set_level(sdias_dbf, 6);
- rc = sclp_register(&sclp_sdias_register);
- if (rc)
- return rc;
- init_waitqueue_head(&sdias_wq);
+ if (sclp_sdias_init_sync() == 0)
+ goto out;
+ if (sclp_sdias_init_async() == 0)
+ goto out;
+ TRACE("init failed\n");
+ return -ENODEV;
+out:
TRACE("init done\n");
return 0;
}
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 1b6d9247fdc7..3303d66b2794 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -21,7 +21,6 @@
#include <asm/ipl.h>
#include <asm/sclp.h>
#include <asm/setup.h>
-#include <asm/sigp.h>
#include <asm/uaccess.h>
#include <asm/debug.h>
#include <asm/processor.h>
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index dc67c397449e..a49c46c91983 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -601,8 +601,6 @@ void __irq_entry do_IRQ(struct pt_regs *regs)
struct pt_regs *old_regs;
old_regs = set_irq_regs(regs);
- s390_idle_check(regs, S390_lowcore.int_clock,
- S390_lowcore.async_enter_timer);
irq_enter();
__this_cpu_write(s390_idle.nohz_delay, 1);
if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator)
diff --git a/drivers/s390/cio/crw.c b/drivers/s390/cio/crw.c
index 425f741a280c..d0a2dff43fb4 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 770a740a393c..35c685c374e9 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -18,6 +18,7 @@
#include <linux/atomic.h>
#include <asm/debug.h>
#include <asm/qdio.h>
+#include <asm/ipl.h>
#include "cio.h"
#include "css.h"
@@ -1093,6 +1094,11 @@ static void qdio_handle_activate_check(struct ccw_device *cdev,
q->nr, q->first_to_kick, count, irq_ptr->int_parm);
no_handler:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
+ /*
+ * In case of z/VM LGR (Live Guest Migration) QDIO recovery will happen.
+ * Therefore we call the LGR detection function here.
+ */
+ lgr_info_log();
}
static void qdio_establish_handle_irq(struct ccw_device *cdev, int cstat,
@@ -1452,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 452989a7ec13..ecf12f0aca7b 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/Makefile b/drivers/s390/crypto/Makefile
index f0a12d2eb780..af3c7f16ea88 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -2,16 +2,6 @@
# S/390 crypto devices
#
-ifdef CONFIG_ZCRYPT_MONOLITHIC
-
-z90crypt-objs := zcrypt_mono.o ap_bus.o zcrypt_api.o \
- zcrypt_pcica.o zcrypt_pcicc.o zcrypt_pcixcc.o zcrypt_cex2a.o
-obj-$(CONFIG_ZCRYPT) += z90crypt.o
-
-else
-
ap-objs := ap_bus.o
obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
-
-endif
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 96bbe9d12a79..7e9a72eb2fe0 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"
@@ -1862,7 +1862,5 @@ void ap_module_exit(void)
}
}
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init(ap_module_init);
module_exit(ap_module_exit);
-#endif
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 88ad33ed5d38..88523208d47d 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -1220,7 +1220,5 @@ void zcrypt_api_exit(void)
misc_deregister(&zcrypt_misc_device);
}
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init(zcrypt_api_init);
module_exit(zcrypt_api_exit);
-#endif
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index da171b5f3996..084286728166 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -63,13 +63,11 @@ static struct ap_device_id zcrypt_cex2a_ids[] = {
{ /* end of list */ },
};
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, "
"Copyright 2001, 2006 IBM Corporation");
MODULE_LICENSE("GPL");
-#endif
static int zcrypt_cex2a_probe(struct ap_device *ap_dev);
static void zcrypt_cex2a_remove(struct ap_device *ap_dev);
@@ -496,7 +494,5 @@ void __exit zcrypt_cex2a_exit(void)
ap_driver_unregister(&zcrypt_cex2a_driver);
}
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init(zcrypt_cex2a_init);
module_exit(zcrypt_cex2a_exit);
-#endif
diff --git a/drivers/s390/crypto/zcrypt_mono.c b/drivers/s390/crypto/zcrypt_mono.c
deleted file mode 100644
index eb313c3fb2d1..000000000000
--- a/drivers/s390/crypto/zcrypt_mono.c
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * linux/drivers/s390/crypto/zcrypt_mono.c
- *
- * zcrypt 2.1.0
- *
- * Copyright (C) 2001, 2006 IBM Corporation
- * Author(s): Robert Burroughs
- * Eric Rossman (edrossma@us.ibm.com)
- *
- * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/miscdevice.h>
-#include <linux/fs.h>
-#include <linux/proc_fs.h>
-#include <linux/compat.h>
-#include <linux/atomic.h>
-#include <asm/uaccess.h>
-
-#include "ap_bus.h"
-#include "zcrypt_api.h"
-#include "zcrypt_pcica.h"
-#include "zcrypt_pcicc.h"
-#include "zcrypt_pcixcc.h"
-#include "zcrypt_cex2a.h"
-
-/**
- * The module initialization code.
- */
-static int __init zcrypt_init(void)
-{
- int rc;
-
- rc = ap_module_init();
- if (rc)
- goto out;
- rc = zcrypt_api_init();
- if (rc)
- goto out_ap;
- rc = zcrypt_pcica_init();
- if (rc)
- goto out_api;
- rc = zcrypt_pcicc_init();
- if (rc)
- goto out_pcica;
- rc = zcrypt_pcixcc_init();
- if (rc)
- goto out_pcicc;
- rc = zcrypt_cex2a_init();
- if (rc)
- goto out_pcixcc;
- return 0;
-
-out_pcixcc:
- zcrypt_pcixcc_exit();
-out_pcicc:
- zcrypt_pcicc_exit();
-out_pcica:
- zcrypt_pcica_exit();
-out_api:
- zcrypt_api_exit();
-out_ap:
- ap_module_exit();
-out:
- return rc;
-}
-
-/**
- * The module termination code.
- */
-static void __exit zcrypt_exit(void)
-{
- zcrypt_cex2a_exit();
- zcrypt_pcixcc_exit();
- zcrypt_pcicc_exit();
- zcrypt_pcica_exit();
- zcrypt_api_exit();
- ap_module_exit();
-}
-
-module_init(zcrypt_init);
-module_exit(zcrypt_exit);
diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c
index d84816f144df..0effca925451 100644
--- a/drivers/s390/crypto/zcrypt_pcica.c
+++ b/drivers/s390/crypto/zcrypt_pcica.c
@@ -53,13 +53,11 @@ static struct ap_device_id zcrypt_pcica_ids[] = {
{ /* end of list */ },
};
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
MODULE_DEVICE_TABLE(ap, zcrypt_pcica_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("PCICA Cryptographic Coprocessor device driver, "
"Copyright 2001, 2006 IBM Corporation");
MODULE_LICENSE("GPL");
-#endif
static int zcrypt_pcica_probe(struct ap_device *ap_dev);
static void zcrypt_pcica_remove(struct ap_device *ap_dev);
@@ -408,7 +406,5 @@ void zcrypt_pcica_exit(void)
ap_driver_unregister(&zcrypt_pcica_driver);
}
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init(zcrypt_pcica_init);
module_exit(zcrypt_pcica_exit);
-#endif
diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c
index bdbdbe192993..f9523c0cc8d2 100644
--- a/drivers/s390/crypto/zcrypt_pcicc.c
+++ b/drivers/s390/crypto/zcrypt_pcicc.c
@@ -65,13 +65,11 @@ static struct ap_device_id zcrypt_pcicc_ids[] = {
{ /* end of list */ },
};
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
MODULE_DEVICE_TABLE(ap, zcrypt_pcicc_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("PCICC Cryptographic Coprocessor device driver, "
"Copyright 2001, 2006 IBM Corporation");
MODULE_LICENSE("GPL");
-#endif
static int zcrypt_pcicc_probe(struct ap_device *ap_dev);
static void zcrypt_pcicc_remove(struct ap_device *ap_dev);
@@ -614,7 +612,5 @@ void zcrypt_pcicc_exit(void)
ap_driver_unregister(&zcrypt_pcicc_driver);
}
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init(zcrypt_pcicc_init);
module_exit(zcrypt_pcicc_exit);
-#endif
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index 077b7d109fde..cf1cbd4747f4 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -75,13 +75,11 @@ static struct ap_device_id zcrypt_pcixcc_ids[] = {
{ /* end of list */ },
};
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids);
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, "
"Copyright 2001, 2006 IBM Corporation");
MODULE_LICENSE("GPL");
-#endif
static int zcrypt_pcixcc_probe(struct ap_device *ap_dev);
static void zcrypt_pcixcc_remove(struct ap_device *ap_dev);
@@ -1121,7 +1119,5 @@ void zcrypt_pcixcc_exit(void)
ap_driver_unregister(&zcrypt_pcixcc_driver);
}
-#ifndef CONFIG_ZCRYPT_MONOLITHIC
module_init(zcrypt_pcixcc_init);
module_exit(zcrypt_pcixcc_exit);
-#endif
diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c
index 7bc1955337ea..d74e9ae6dfb3 100644
--- a/drivers/s390/kvm/kvm_virtio.c
+++ b/drivers/s390/kvm/kvm_virtio.c
@@ -380,15 +380,13 @@ static void hotplug_devices(struct work_struct *dummy)
/*
* we emulate the request_irq behaviour on top of s390 extints
*/
-static void kvm_extint_handler(unsigned int ext_int_code,
+static void kvm_extint_handler(struct ext_code ext_code,
unsigned int param32, unsigned long param64)
{
struct virtqueue *vq;
- u16 subcode;
u32 param;
- subcode = ext_int_code >> 16;
- if ((subcode & 0xff00) != VIRTIO_SUBCODE_64)
+ if ((ext_code.subcode & 0xff00) != VIRTIO_SUBCODE_64)
return;
kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++;
diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c
index 826157f38694..327657e2e264 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 8d6e508222b8..2236aea3ca2f 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 0b31658ccde5..a9e468cc1cac 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 f672491774eb..a3adfb4357f5 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 f66c33b9ab41..d4da3708763b 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 4e89103204dc..a06e608789e3 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -1903,6 +1903,14 @@ config SCSI_BFA_FC
To compile this driver as a module, choose M here. The module will
be called bfa.
+config SCSI_VIRTIO
+ tristate "virtio-scsi support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && VIRTIO
+ help
+ This is the virtual HBA driver for virtio. If the kernel will
+ be used in a virtual machine, say Y or M.
+
+
endif # SCSI_LOWLEVEL
source "drivers/scsi/pcmcia/Kconfig"
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index e4c1a69f8fab..ad24e065b1e5 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -141,6 +141,7 @@ obj-$(CONFIG_SCSI_CXGB4_ISCSI) += libiscsi.o libiscsi_tcp.o cxgbi/
obj-$(CONFIG_SCSI_BNX2_ISCSI) += libiscsi.o bnx2i/
obj-$(CONFIG_BE2ISCSI) += libiscsi.o be2iscsi/
obj-$(CONFIG_SCSI_PMCRAID) += pmcraid.o
+obj-$(CONFIG_SCSI_VIRTIO) += virtio_scsi.o
obj-$(CONFIG_VMWARE_PVSCSI) += vmw_pvscsi.o
obj-$(CONFIG_HYPERV_STORAGE) += hv_storvsc.o
diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
index 409f5805bdd6..52551662d107 100644
--- a/drivers/scsi/aacraid/aachba.c
+++ b/drivers/scsi/aacraid/aachba.c
@@ -151,7 +151,11 @@ int aac_msi;
int aac_commit = -1;
int startup_timeout = 180;
int aif_timeout = 120;
+int aac_sync_mode; /* Only Sync. transfer - disabled */
+module_param(aac_sync_mode, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(aac_sync_mode, "Force sync. transfer mode"
+ " 0=off, 1=on");
module_param(nondasd, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices."
" 0=off, 1=on");
diff --git a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
index ffb587817efc..3fcf62724fad 100644
--- a/drivers/scsi/aacraid/aacraid.h
+++ b/drivers/scsi/aacraid/aacraid.h
@@ -12,7 +12,7 @@
*----------------------------------------------------------------------------*/
#ifndef AAC_DRIVER_BUILD
-# define AAC_DRIVER_BUILD 28000
+# define AAC_DRIVER_BUILD 28900
# define AAC_DRIVER_BRANCH "-ms"
#endif
#define MAXIMUM_NUM_CONTAINERS 32
@@ -756,8 +756,16 @@ struct src_mu_registers {
struct src_registers {
struct src_mu_registers MUnit; /* 00h - c7h */
- __le32 reserved1[130790]; /* c8h - 7fc5fh */
- struct src_inbound IndexRegs; /* 7fc60h */
+ union {
+ struct {
+ __le32 reserved1[130790]; /* c8h - 7fc5fh */
+ struct src_inbound IndexRegs; /* 7fc60h */
+ } tupelo;
+ struct {
+ __le32 reserved1[974]; /* c8h - fffh */
+ struct src_inbound IndexRegs; /* 1000h */
+ } denali;
+ } u;
};
#define src_readb(AEP, CSR) readb(&((AEP)->regs.src.bar0->CSR))
@@ -999,6 +1007,10 @@ struct aac_bus_info_response {
#define AAC_OPT_NEW_COMM cpu_to_le32(1<<17)
#define AAC_OPT_NEW_COMM_64 cpu_to_le32(1<<18)
#define AAC_OPT_NEW_COMM_TYPE1 cpu_to_le32(1<<28)
+#define AAC_OPT_NEW_COMM_TYPE2 cpu_to_le32(1<<29)
+#define AAC_OPT_NEW_COMM_TYPE3 cpu_to_le32(1<<30)
+#define AAC_OPT_NEW_COMM_TYPE4 cpu_to_le32(1<<31)
+
struct aac_dev
{
@@ -1076,6 +1088,8 @@ struct aac_dev
# define AAC_MIN_FOOTPRINT_SIZE 8192
# define AAC_MIN_SRC_BAR0_SIZE 0x400000
# define AAC_MIN_SRC_BAR1_SIZE 0x800
+# define AAC_MIN_SRCV_BAR0_SIZE 0x100000
+# define AAC_MIN_SRCV_BAR1_SIZE 0x400
#endif
union
{
@@ -1116,7 +1130,10 @@ struct aac_dev
u8 msi;
int management_fib_count;
spinlock_t manage_lock;
-
+ spinlock_t sync_lock;
+ int sync_mode;
+ struct fib *sync_fib;
+ struct list_head sync_fib_list;
};
#define aac_adapter_interrupt(dev) \
@@ -1163,6 +1180,7 @@ struct aac_dev
#define FIB_CONTEXT_FLAG_TIMED_OUT (0x00000001)
#define FIB_CONTEXT_FLAG (0x00000002)
+#define FIB_CONTEXT_FLAG_WAIT (0x00000004)
/*
* Define the command values
@@ -1970,6 +1988,7 @@ int aac_rkt_init(struct aac_dev *dev);
int aac_nark_init(struct aac_dev *dev);
int aac_sa_init(struct aac_dev *dev);
int aac_src_init(struct aac_dev *dev);
+int aac_srcv_init(struct aac_dev *dev);
int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * hw_fib, int wait, struct fib * fibptr, unsigned long *nonotify);
unsigned int aac_response_normal(struct aac_queue * q);
unsigned int aac_command_normal(struct aac_queue * q);
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index 7ac8fdb5577b..a35f54ebdce0 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -325,12 +325,14 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
{
u32 status[5];
struct Scsi_Host * host = dev->scsi_host_ptr;
+ extern int aac_sync_mode;
/*
* Check the preferred comm settings, defaults from template.
*/
dev->management_fib_count = 0;
spin_lock_init(&dev->manage_lock);
+ spin_lock_init(&dev->sync_lock);
dev->max_fib_size = sizeof(struct hw_fib);
dev->sg_tablesize = host->sg_tablesize = (dev->max_fib_size
- sizeof(struct aac_fibhdr)
@@ -344,13 +346,21 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
(status[0] == 0x00000001)) {
if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_64))
dev->raw_io_64 = 1;
- if (dev->a_ops.adapter_comm) {
- if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE1)) {
- dev->comm_interface = AAC_COMM_MESSAGE_TYPE1;
- dev->raw_io_interface = 1;
- } else if (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM)) {
+ dev->sync_mode = aac_sync_mode;
+ if (dev->a_ops.adapter_comm &&
+ (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM))) {
dev->comm_interface = AAC_COMM_MESSAGE;
dev->raw_io_interface = 1;
+ if ((status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE1))) {
+ /* driver supports TYPE1 (Tupelo) */
+ dev->comm_interface = AAC_COMM_MESSAGE_TYPE1;
+ } else if ((status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE4)) ||
+ (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE3)) ||
+ (status[1] & le32_to_cpu(AAC_OPT_NEW_COMM_TYPE2))) {
+ /* driver doesn't support TYPE2 (Series7), TYPE3 and TYPE4 */
+ /* switch to sync. mode */
+ dev->comm_interface = AAC_COMM_MESSAGE_TYPE1;
+ dev->sync_mode = 1;
}
}
if ((dev->comm_interface == AAC_COMM_MESSAGE) &&
@@ -455,6 +465,7 @@ struct aac_dev *aac_init_adapter(struct aac_dev *dev)
}
INIT_LIST_HEAD(&dev->fib_list);
+ INIT_LIST_HEAD(&dev->sync_fib_list);
return dev;
}
diff --git a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
index e5f2d7d9002e..4b32ca442433 100644
--- a/drivers/scsi/aacraid/commsup.c
+++ b/drivers/scsi/aacraid/commsup.c
@@ -416,6 +416,7 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
unsigned long flags = 0;
unsigned long qflags;
unsigned long mflags = 0;
+ unsigned long sflags = 0;
if (!(hw_fib->header.XferState & cpu_to_le32(HostOwned)))
@@ -512,6 +513,31 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
spin_lock_irqsave(&fibptr->event_lock, flags);
}
+ if (dev->sync_mode) {
+ if (wait)
+ spin_unlock_irqrestore(&fibptr->event_lock, flags);
+ spin_lock_irqsave(&dev->sync_lock, sflags);
+ if (dev->sync_fib) {
+ list_add_tail(&fibptr->fiblink, &dev->sync_fib_list);
+ spin_unlock_irqrestore(&dev->sync_lock, sflags);
+ } else {
+ dev->sync_fib = fibptr;
+ spin_unlock_irqrestore(&dev->sync_lock, sflags);
+ aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB,
+ (u32)fibptr->hw_fib_pa, 0, 0, 0, 0, 0,
+ NULL, NULL, NULL, NULL, NULL);
+ }
+ if (wait) {
+ fibptr->flags |= FIB_CONTEXT_FLAG_WAIT;
+ if (down_interruptible(&fibptr->event_wait)) {
+ fibptr->flags &= ~FIB_CONTEXT_FLAG_WAIT;
+ return -EFAULT;
+ }
+ return 0;
+ }
+ return -EINPROGRESS;
+ }
+
if (aac_adapter_deliver(fibptr) != 0) {
printk(KERN_ERR "aac_fib_send: returned -EBUSY\n");
if (wait) {
diff --git a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
index 705e13e470af..0d279c445a30 100644
--- a/drivers/scsi/aacraid/linit.c
+++ b/drivers/scsi/aacraid/linit.c
@@ -56,7 +56,7 @@
#include "aacraid.h"
-#define AAC_DRIVER_VERSION "1.1-7"
+#define AAC_DRIVER_VERSION "1.2-0"
#ifndef AAC_DRIVER_BRANCH
#define AAC_DRIVER_BRANCH ""
#endif
@@ -162,7 +162,10 @@ static const struct pci_device_id aac_pci_tbl[] __devinitdata = {
{ 0x9005, 0x0285, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 59 }, /* Adaptec Catch All */
{ 0x9005, 0x0286, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 60 }, /* Adaptec Rocket Catch All */
{ 0x9005, 0x0288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 61 }, /* Adaptec NEMER/ARK Catch All */
- { 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Catch All */
+ { 0x9005, 0x028b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 62 }, /* Adaptec PMC Series 6 (Tupelo) */
+ { 0x9005, 0x028c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 63 }, /* Adaptec PMC Series 7 (Denali) */
+ { 0x9005, 0x028d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 64 }, /* Adaptec PMC Series 8 */
+ { 0x9005, 0x028f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 65 }, /* Adaptec PMC Series 9 */
{ 0,}
};
MODULE_DEVICE_TABLE(pci, aac_pci_tbl);
@@ -238,7 +241,10 @@ static struct aac_driver_ident aac_drivers[] = {
{ aac_rx_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec Catch All */
{ aac_rkt_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec Rocket Catch All */
{ aac_nark_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec NEMER/ARK Catch All */
- { aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec PMC Catch All */
+ { aac_src_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec PMC Series 6 (Tupelo) */
+ { aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec PMC Series 7 (Denali) */
+ { aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2 }, /* Adaptec PMC Series 8 */
+ { aac_srcv_init, "aacraid", "ADAPTEC ", "RAID ", 2 } /* Adaptec PMC Series 9 */
};
/**
@@ -1102,6 +1108,7 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
int error = -ENODEV;
int unique_id = 0;
u64 dmamask;
+ extern int aac_sync_mode;
list_for_each_entry(aac, &aac_devices, entry) {
if (aac->id > unique_id)
@@ -1162,6 +1169,21 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
if ((*aac_drivers[index].init)(aac))
goto out_unmap;
+ if (aac->sync_mode) {
+ if (aac_sync_mode)
+ printk(KERN_INFO "%s%d: Sync. mode enforced "
+ "by driver parameter. This will cause "
+ "a significant performance decrease!\n",
+ aac->name,
+ aac->id);
+ else
+ printk(KERN_INFO "%s%d: Async. mode not supported "
+ "by current driver, sync. mode enforced."
+ "\nPlease update driver to get full performance.\n",
+ aac->name,
+ aac->id);
+ }
+
/*
* Start any kernel threads needed
*/
diff --git a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
index ce530f113fdb..b029c7cc785b 100644
--- a/drivers/scsi/aacraid/rx.c
+++ b/drivers/scsi/aacraid/rx.c
@@ -643,6 +643,7 @@ int _aac_rx_init(struct aac_dev *dev)
if (aac_init_adapter(dev) == NULL)
goto error_iounmap;
aac_adapter_comm(dev, dev->comm_interface);
+ dev->sync_mode = 0; /* sync. mode not supported */
dev->msi = aac_msi && !pci_enable_msi(dev->pdev);
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
IRQF_SHARED|IRQF_DISABLED, "aacraid", dev) < 0) {
diff --git a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
index e5d4457121ea..beb533630d4b 100644
--- a/drivers/scsi/aacraid/sa.c
+++ b/drivers/scsi/aacraid/sa.c
@@ -385,6 +385,7 @@ int aac_sa_init(struct aac_dev *dev)
if(aac_init_adapter(dev) == NULL)
goto error_irq;
+ dev->sync_mode = 0; /* sync. mode not supported */
if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
IRQF_SHARED|IRQF_DISABLED,
"aacraid", (void *)dev ) < 0) {
diff --git a/drivers/scsi/aacraid/src.c b/drivers/scsi/aacraid/src.c
index 957595a7a45c..2bee51506a91 100644
--- a/drivers/scsi/aacraid/src.c
+++ b/drivers/scsi/aacraid/src.c
@@ -96,6 +96,38 @@ static irqreturn_t aac_src_intr_message(int irq, void *dev_id)
our_interrupt = 1;
/* handle AIF */
aac_intr_normal(dev, 0, 2, 0, NULL);
+ } else if (bellbits_shifted & OUTBOUNDDOORBELL_0) {
+ unsigned long sflags;
+ struct list_head *entry;
+ int send_it = 0;
+
+ if (dev->sync_fib) {
+ our_interrupt = 1;
+ if (dev->sync_fib->callback)
+ dev->sync_fib->callback(dev->sync_fib->callback_data,
+ dev->sync_fib);
+ spin_lock_irqsave(&dev->sync_fib->event_lock, sflags);
+ if (dev->sync_fib->flags & FIB_CONTEXT_FLAG_WAIT) {
+ dev->management_fib_count--;
+ up(&dev->sync_fib->event_wait);
+ }
+ spin_unlock_irqrestore(&dev->sync_fib->event_lock, sflags);
+ spin_lock_irqsave(&dev->sync_lock, sflags);
+ if (!list_empty(&dev->sync_fib_list)) {
+ entry = dev->sync_fib_list.next;
+ dev->sync_fib = list_entry(entry, struct fib, fiblink);
+ list_del(entry);
+ send_it = 1;
+ } else {
+ dev->sync_fib = NULL;
+ }
+ spin_unlock_irqrestore(&dev->sync_lock, sflags);
+ if (send_it) {
+ aac_adapter_sync_cmd(dev, SEND_SYNCHRONOUS_FIB,
+ (u32)dev->sync_fib->hw_fib_pa, 0, 0, 0, 0, 0,
+ NULL, NULL, NULL, NULL, NULL);
+ }
+ }
}
}
@@ -177,56 +209,63 @@ static int src_sync_cmd(struct aac_dev *dev, u32 command,
*/
src_writel(dev, MUnit.IDR, INBOUNDDOORBELL_0 << SRC_IDR_SHIFT);
- ok = 0;
- start = jiffies;
+ if (!dev->sync_mode || command != SEND_SYNCHRONOUS_FIB) {
+ ok = 0;
+ start = jiffies;
- /*
- * Wait up to 30 seconds
- */
- while (time_before(jiffies, start+30*HZ)) {
- /* Delay 5 microseconds to let Mon960 get info. */
- udelay(5);
-
- /* Mon960 will set doorbell0 bit
- * when it has completed the command
+ /*
+ * Wait up to 5 minutes
*/
- if ((src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT) & OUTBOUNDDOORBELL_0) {
- /* Clear the doorbell */
- src_writel(dev,
- MUnit.ODR_C,
- OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
- ok = 1;
- break;
+ while (time_before(jiffies, start+300*HZ)) {
+ udelay(5); /* Delay 5 microseconds to let Mon960 get info. */
+ /*
+ * Mon960 will set doorbell0 bit when it has completed the command.
+ */
+ if ((src_readl(dev, MUnit.ODR_R) >> SRC_ODR_SHIFT) & OUTBOUNDDOORBELL_0) {
+ /*
+ * Clear the doorbell.
+ */
+ src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
+ ok = 1;
+ break;
+ }
+ /*
+ * Yield the processor in case we are slow
+ */
+ msleep(1);
}
-
- /* Yield the processor in case we are slow */
- msleep(1);
- }
- if (unlikely(ok != 1)) {
- /* Restore interrupt mask even though we timed out */
- aac_adapter_enable_int(dev);
- return -ETIMEDOUT;
+ if (unlikely(ok != 1)) {
+ /*
+ * Restore interrupt mask even though we timed out
+ */
+ aac_adapter_enable_int(dev);
+ return -ETIMEDOUT;
+ }
+ /*
+ * Pull the synch status from Mailbox 0.
+ */
+ if (status)
+ *status = readl(&dev->IndexRegs->Mailbox[0]);
+ if (r1)
+ *r1 = readl(&dev->IndexRegs->Mailbox[1]);
+ if (r2)
+ *r2 = readl(&dev->IndexRegs->Mailbox[2]);
+ if (r3)
+ *r3 = readl(&dev->IndexRegs->Mailbox[3]);
+ if (r4)
+ *r4 = readl(&dev->IndexRegs->Mailbox[4]);
+
+ /*
+ * Clear the synch command doorbell.
+ */
+ src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
}
- /* Pull the synch status from Mailbox 0 */
- if (status)
- *status = readl(&dev->IndexRegs->Mailbox[0]);
- if (r1)
- *r1 = readl(&dev->IndexRegs->Mailbox[1]);
- if (r2)
- *r2 = readl(&dev->IndexRegs->Mailbox[2]);
- if (r3)
- *r3 = readl(&dev->IndexRegs->Mailbox[3]);
- if (r4)
- *r4 = readl(&dev->IndexRegs->Mailbox[4]);
-
- /* Clear the synch command doorbell */
- src_writel(dev, MUnit.ODR_C, OUTBOUNDDOORBELL_0 << SRC_ODR_SHIFT);
-
- /* Restore interrupt mask */
+ /*
+ * Restore interrupt mask
+ */
aac_adapter_enable_int(dev);
return 0;
-
}
/**
@@ -386,9 +425,7 @@ static int aac_src_ioremap(struct aac_dev *dev, u32 size)
{
if (!size) {
iounmap(dev->regs.src.bar0);
- dev->regs.src.bar0 = NULL;
- iounmap(dev->base);
- dev->base = NULL;
+ dev->base = dev->regs.src.bar0 = NULL;
return 0;
}
dev->regs.src.bar1 = ioremap(pci_resource_start(dev->pdev, 2),
@@ -404,7 +441,27 @@ static int aac_src_ioremap(struct aac_dev *dev, u32 size)
return -1;
}
dev->IndexRegs = &((struct src_registers __iomem *)
- dev->base)->IndexRegs;
+ dev->base)->u.tupelo.IndexRegs;
+ return 0;
+}
+
+/**
+ * aac_srcv_ioremap
+ * @size: mapping resize request
+ *
+ */
+static int aac_srcv_ioremap(struct aac_dev *dev, u32 size)
+{
+ if (!size) {
+ iounmap(dev->regs.src.bar0);
+ dev->base = dev->regs.src.bar0 = NULL;
+ return 0;
+ }
+ dev->base = dev->regs.src.bar0 = ioremap(dev->scsi_host_ptr->base, size);
+ if (dev->base == NULL)
+ return -1;
+ dev->IndexRegs = &((struct src_registers __iomem *)
+ dev->base)->u.denali.IndexRegs;
return 0;
}
@@ -419,7 +476,7 @@ static int aac_src_restart_adapter(struct aac_dev *dev, int bled)
bled = aac_adapter_sync_cmd(dev, IOP_RESET_ALWAYS,
0, 0, 0, 0, 0, 0, &var, &reset_mask, NULL, NULL, NULL);
if (bled || (var != 0x00000001))
- bled = -EINVAL;
+ return -EINVAL;
if (dev->supplement_adapter_info.SupportedOptions2 &
AAC_OPTION_DOORBELL_RESET) {
src_writel(dev, MUnit.IDR, reset_mask);
@@ -579,15 +636,149 @@ int aac_src_init(struct aac_dev *dev)
dev->dbg_size = AAC_MIN_SRC_BAR1_SIZE;
aac_adapter_enable_int(dev);
+
+ if (!dev->sync_mode) {
+ /*
+ * Tell the adapter that all is configured, and it can
+ * start accepting requests
+ */
+ aac_src_start_adapter(dev);
+ }
+ return 0;
+
+error_iounmap:
+
+ return -1;
+}
+
+/**
+ * aac_srcv_init - initialize an SRCv card
+ * @dev: device to configure
+ *
+ */
+
+int aac_srcv_init(struct aac_dev *dev)
+{
+ unsigned long start;
+ unsigned long status;
+ int restart = 0;
+ int instance = dev->id;
+ const char *name = dev->name;
+
+ dev->a_ops.adapter_ioremap = aac_srcv_ioremap;
+ dev->a_ops.adapter_comm = aac_src_select_comm;
+
+ dev->base_size = AAC_MIN_SRCV_BAR0_SIZE;
+ if (aac_adapter_ioremap(dev, dev->base_size)) {
+ printk(KERN_WARNING "%s: unable to map adapter.\n", name);
+ goto error_iounmap;
+ }
+
+ /* Failure to reset here is an option ... */
+ dev->a_ops.adapter_sync_cmd = src_sync_cmd;
+ dev->a_ops.adapter_enable_int = aac_src_disable_interrupt;
+ if ((aac_reset_devices || reset_devices) &&
+ !aac_src_restart_adapter(dev, 0))
+ ++restart;
/*
- * Tell the adapter that all is configured, and it can
- * start accepting requests
+ * Check to see if the board panic'd while booting.
*/
- aac_src_start_adapter(dev);
+ status = src_readl(dev, MUnit.OMR);
+ if (status & KERNEL_PANIC) {
+ if (aac_src_restart_adapter(dev, aac_src_check_health(dev)))
+ goto error_iounmap;
+ ++restart;
+ }
+ /*
+ * Check to see if the board failed any self tests.
+ */
+ status = src_readl(dev, MUnit.OMR);
+ if (status & SELF_TEST_FAILED) {
+ printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ /*
+ * Check to see if the monitor panic'd while booting.
+ */
+ if (status & MONITOR_PANIC) {
+ printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
+ goto error_iounmap;
+ }
+ start = jiffies;
+ /*
+ * Wait for the adapter to be up and running. Wait up to 3 minutes
+ */
+ while (!((status = src_readl(dev, MUnit.OMR)) & KERNEL_UP_AND_RUNNING)) {
+ if ((restart &&
+ (status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC))) ||
+ time_after(jiffies, start+HZ*startup_timeout)) {
+ printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %lx.\n",
+ dev->name, instance, status);
+ goto error_iounmap;
+ }
+ if (!restart &&
+ ((status & (KERNEL_PANIC|SELF_TEST_FAILED|MONITOR_PANIC)) ||
+ time_after(jiffies, start + HZ *
+ ((startup_timeout > 60)
+ ? (startup_timeout - 60)
+ : (startup_timeout / 2))))) {
+ if (likely(!aac_src_restart_adapter(dev, aac_src_check_health(dev))))
+ start = jiffies;
+ ++restart;
+ }
+ msleep(1);
+ }
+ if (restart && aac_commit)
+ aac_commit = 1;
+ /*
+ * Fill in the common function dispatch table.
+ */
+ dev->a_ops.adapter_interrupt = aac_src_interrupt_adapter;
+ dev->a_ops.adapter_disable_int = aac_src_disable_interrupt;
+ dev->a_ops.adapter_notify = aac_src_notify_adapter;
+ dev->a_ops.adapter_sync_cmd = src_sync_cmd;
+ dev->a_ops.adapter_check_health = aac_src_check_health;
+ dev->a_ops.adapter_restart = aac_src_restart_adapter;
+
+ /*
+ * First clear out all interrupts. Then enable the one's that we
+ * can handle.
+ */
+ aac_adapter_comm(dev, AAC_COMM_MESSAGE);
+ aac_adapter_disable_int(dev);
+ src_writel(dev, MUnit.ODR_C, 0xffffffff);
+ aac_adapter_enable_int(dev);
+ if (aac_init_adapter(dev) == NULL)
+ goto error_iounmap;
+ if (dev->comm_interface != AAC_COMM_MESSAGE_TYPE1)
+ goto error_iounmap;
+ dev->msi = aac_msi && !pci_enable_msi(dev->pdev);
+ if (request_irq(dev->pdev->irq, dev->a_ops.adapter_intr,
+ IRQF_SHARED|IRQF_DISABLED, "aacraid", dev) < 0) {
+ if (dev->msi)
+ pci_disable_msi(dev->pdev);
+ printk(KERN_ERR "%s%d: Interrupt unavailable.\n",
+ name, instance);
+ goto error_iounmap;
+ }
+ dev->dbg_base = dev->scsi_host_ptr->base;
+ dev->dbg_base_mapped = dev->base;
+ dev->dbg_size = dev->base_size;
+
+ aac_adapter_enable_int(dev);
+
+ if (!dev->sync_mode) {
+ /*
+ * Tell the adapter that all is configured, and it can
+ * start accepting requests
+ */
+ aac_src_start_adapter(dev);
+ }
return 0;
error_iounmap:
return -1;
}
+
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index bfd618a69499..374c4edf4fcb 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 f17c92cf808b..19a36945e6fd 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 ed119cedaae0..ede91f378000 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 1c10b796c1a2..a3e6ed353917 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/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h
index 2863a9d22851..66cda669b417 100644
--- a/drivers/scsi/aic94xx/aic94xx.h
+++ b/drivers/scsi/aic94xx/aic94xx.h
@@ -80,6 +80,8 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
int asd_execute_task(struct sas_task *, int num, gfp_t gfp_flags);
+void asd_set_dmamode(struct domain_device *dev);
+
/* ---------- TMFs ---------- */
int asd_abort_task(struct sas_task *);
int asd_abort_task_set(struct domain_device *, u8 *lun);
diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c
index 2e2ddec9c0b6..64136c56e706 100644
--- a/drivers/scsi/aic94xx/aic94xx_dev.c
+++ b/drivers/scsi/aic94xx/aic94xx_dev.c
@@ -109,26 +109,37 @@ static int asd_init_sata_tag_ddb(struct domain_device *dev)
return 0;
}
-static int asd_init_sata(struct domain_device *dev)
+void asd_set_dmamode(struct domain_device *dev)
{
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ struct ata_device *ata_dev = sas_to_ata_dev(dev);
int ddb = (int) (unsigned long) dev->lldd_dev;
u32 qdepth = 0;
- int res = 0;
- asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
- if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) &&
- dev->sata_dev.identify_device &&
- dev->sata_dev.identify_device[10] != 0) {
- u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]);
- u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]);
-
- if (w76 & 0x100) /* NCQ? */
- qdepth = (w75 & 0x1F) + 1;
+ if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) {
+ if (ata_id_has_ncq(ata_dev->id))
+ qdepth = ata_id_queue_depth(ata_dev->id);
asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
(1ULL<<qdepth)-1);
asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth);
}
+
+ if (qdepth > 0)
+ if (asd_init_sata_tag_ddb(dev) != 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
+ ata_dev->flags |= ATA_DFLAG_NCQ_OFF;
+ spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
+ }
+}
+
+static int asd_init_sata(struct domain_device *dev)
+{
+ struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+ int ddb = (int) (unsigned long) dev->lldd_dev;
+
+ asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
dev->dev_type == SATA_PM_PORT) {
struct dev_to_host_fis *fis = (struct dev_to_host_fis *)
@@ -136,9 +147,8 @@ static int asd_init_sata(struct domain_device *dev)
asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status);
}
asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF);
- if (qdepth > 0)
- res = asd_init_sata_tag_ddb(dev);
- return res;
+
+ return 0;
}
static int asd_init_target_ddb(struct domain_device *dev)
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
index d5ff142c93a2..ff80552ead84 100644
--- a/drivers/scsi/aic94xx/aic94xx_init.c
+++ b/drivers/scsi/aic94xx/aic94xx_init.c
@@ -68,7 +68,6 @@ static struct scsi_host_template aic94xx_sht = {
.queuecommand = sas_queuecommand,
.target_alloc = sas_target_alloc,
.slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
.scan_finished = asd_scan_finished,
.scan_start = asd_scan_start,
.change_queue_depth = sas_change_queue_depth,
@@ -82,7 +81,6 @@ static struct scsi_host_template aic94xx_sht = {
.use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = sas_eh_device_reset_handler,
.eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
};
@@ -972,7 +970,7 @@ static int asd_scan_finished(struct Scsi_Host *shost, unsigned long time)
if (time < HZ)
return 0;
/* Wait for discovery to finish */
- scsi_flush_work(shost);
+ sas_drain_work(SHOST_TO_SAS_HA(shost));
return 1;
}
@@ -1010,6 +1008,8 @@ static struct sas_domain_function_template aic94xx_transport_functions = {
.lldd_clear_nexus_ha = asd_clear_nexus_ha,
.lldd_control_phy = asd_control_phy,
+
+ .lldd_ata_set_dmamode = asd_set_dmamode,
};
static const struct pci_device_id aic94xx_pci_table[] __devinitdata = {
diff --git a/drivers/scsi/aic94xx/aic94xx_tmf.c b/drivers/scsi/aic94xx/aic94xx_tmf.c
index 0add73bdf2a4..cf9040933da6 100644
--- a/drivers/scsi/aic94xx/aic94xx_tmf.c
+++ b/drivers/scsi/aic94xx/aic94xx_tmf.c
@@ -181,7 +181,7 @@ static int asd_clear_nexus_I_T(struct domain_device *dev,
int asd_I_T_nexus_reset(struct domain_device *dev)
{
int res, tmp_res, i;
- struct sas_phy *phy = sas_find_local_phy(dev);
+ struct sas_phy *phy = sas_get_local_phy(dev);
/* Standard mandates link reset for ATA (type 0) and
* hard reset for SSP (type 1) */
int reset_type = (dev->dev_type == SATA_DEV ||
@@ -192,7 +192,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev)
ASD_DPRINTK("sending %s reset to %s\n",
reset_type ? "hard" : "soft", dev_name(&phy->dev));
res = sas_phy_reset(phy, reset_type);
- if (res == TMF_RESP_FUNC_COMPLETE) {
+ if (res == TMF_RESP_FUNC_COMPLETE || res == -ENODEV) {
/* wait for the maximum settle time */
msleep(500);
/* clear all outstanding commands (keep nexus suspended) */
@@ -201,7 +201,7 @@ int asd_I_T_nexus_reset(struct domain_device *dev)
for (i = 0 ; i < 3; i++) {
tmp_res = asd_clear_nexus_I_T(dev, NEXUS_PHASE_RESUME);
if (tmp_res == TC_RESUME)
- return res;
+ goto out;
msleep(500);
}
@@ -211,7 +211,10 @@ int asd_I_T_nexus_reset(struct domain_device *dev)
dev_printk(KERN_ERR, &phy->dev,
"Failed to resume nexus after reset 0x%x\n", tmp_res);
- return TMF_RESP_FUNC_FAILED;
+ res = TMF_RESP_FUNC_FAILED;
+ out:
+ sas_put_local_phy(phy);
+ return res;
}
static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
diff --git a/drivers/scsi/arcmsr/arcmsr_hba.c b/drivers/scsi/arcmsr/arcmsr_hba.c
index 2fe9e90e53d9..cbde1dca45ad 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 c454e44cf51c..b330438ac662 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 a750aa72b8ef..2a28b4ad1975 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 a3398fe70a9c..c3b99c93637a 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 e85c40b6e19b..6206a666a8ec 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 84b7127c0121..df2e1b3ddfe2 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 849cdf89f7bb..d25f944b59c2 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 7e6eca4a125e..f29d5121d5ed 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>
diff --git a/drivers/scsi/bfa/bfad_bsg.c b/drivers/scsi/bfa/bfad_bsg.c
index 530de2b1200a..8005c6c5a080 100644
--- a/drivers/scsi/bfa/bfad_bsg.c
+++ b/drivers/scsi/bfa/bfad_bsg.c
@@ -3047,8 +3047,7 @@ bfad_im_bsg_els_ct_request(struct fc_bsg_job *job)
* Allocate buffer for bsg_fcpt and do a copy_from_user op for payload
* buffer of size bsg_data->payload_len
*/
- bsg_fcpt = (struct bfa_bsg_fcpt_s *)
- kzalloc(bsg_data->payload_len, GFP_KERNEL);
+ bsg_fcpt = kzalloc(bsg_data->payload_len, GFP_KERNEL);
if (!bsg_fcpt)
goto out;
@@ -3060,6 +3059,7 @@ bfad_im_bsg_els_ct_request(struct fc_bsg_job *job)
drv_fcxp = kzalloc(sizeof(struct bfad_fcxp), GFP_KERNEL);
if (drv_fcxp == NULL) {
+ kfree(bsg_fcpt);
rc = -ENOMEM;
goto out;
}
diff --git a/drivers/scsi/bnx2fc/bnx2fc.h b/drivers/scsi/bnx2fc/bnx2fc.h
index 049ea907e04a..a4953ef9e53a 100644
--- a/drivers/scsi/bnx2fc/bnx2fc.h
+++ b/drivers/scsi/bnx2fc/bnx2fc.h
@@ -62,7 +62,7 @@
#include "bnx2fc_constants.h"
#define BNX2FC_NAME "bnx2fc"
-#define BNX2FC_VERSION "1.0.9"
+#define BNX2FC_VERSION "1.0.10"
#define PFX "bnx2fc: "
@@ -114,6 +114,8 @@
#define BNX2FC_HASH_TBL_CHUNK_SIZE (16 * 1024)
#define BNX2FC_MAX_SEQS 255
+#define BNX2FC_MAX_RETRY_CNT 3
+#define BNX2FC_MAX_RPORT_RETRY_CNT 255
#define BNX2FC_READ (1 << 1)
#define BNX2FC_WRITE (1 << 0)
@@ -121,8 +123,10 @@
#define BNX2FC_MIN_XID 0
#define BNX2FC_MAX_XID \
(BNX2FC_MAX_OUTSTANDING_CMNDS + BNX2FC_ELSTM_XIDS - 1)
+#define FCOE_MAX_NUM_XIDS 0x2000
#define FCOE_MIN_XID (BNX2FC_MAX_XID + 1)
-#define FCOE_MAX_XID (FCOE_MIN_XID + 4095)
+#define FCOE_MAX_XID (FCOE_MIN_XID + FCOE_MAX_NUM_XIDS - 1)
+#define FCOE_XIDS_PER_CPU (FCOE_MIN_XID + (512 * nr_cpu_ids) - 1)
#define BNX2FC_MAX_LUN 0xFFFF
#define BNX2FC_MAX_FCP_TGT 256
#define BNX2FC_MAX_CMD_LEN 16
diff --git a/drivers/scsi/bnx2fc/bnx2fc_constants.h b/drivers/scsi/bnx2fc/bnx2fc_constants.h
index c12702bb16d6..dad9924abbbb 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_constants.h
+++ b/drivers/scsi/bnx2fc/bnx2fc_constants.h
@@ -47,6 +47,7 @@
#define FCOE_KCQE_COMPLETION_STATUS_CTX_FREE_FAILURE (0x4)
#define FCOE_KCQE_COMPLETION_STATUS_NIC_ERROR (0x5)
#define FCOE_KCQE_COMPLETION_STATUS_WRONG_HSI_VERSION (0x6)
+#define FCOE_KCQE_COMPLETION_STATUS_PARITY_ERROR (0x81)
/* CQE type */
#define FCOE_PENDING_CQE_TYPE 0
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index a9af42e83632..abd72a01856d 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -22,7 +22,7 @@ DEFINE_PER_CPU(struct bnx2fc_percpu_s, bnx2fc_percpu);
#define DRV_MODULE_NAME "bnx2fc"
#define DRV_MODULE_VERSION BNX2FC_VERSION
-#define DRV_MODULE_RELDATE "Oct 21, 2011"
+#define DRV_MODULE_RELDATE "Jan 22, 2011"
static char version[] __devinitdata =
@@ -939,8 +939,14 @@ static int bnx2fc_libfc_config(struct fc_lport *lport)
static int bnx2fc_em_config(struct fc_lport *lport)
{
+ int max_xid;
+
+ if (nr_cpu_ids <= 2)
+ max_xid = FCOE_XIDS_PER_CPU;
+ else
+ max_xid = FCOE_MAX_XID;
if (!fc_exch_mgr_alloc(lport, FC_CLASS_3, FCOE_MIN_XID,
- FCOE_MAX_XID, NULL)) {
+ max_xid, NULL)) {
printk(KERN_ERR PFX "em_config:fc_exch_mgr_alloc failed\n");
return -ENOMEM;
}
@@ -952,8 +958,8 @@ static int bnx2fc_lport_config(struct fc_lport *lport)
{
lport->link_up = 0;
lport->qfull = 0;
- lport->max_retry_count = 3;
- lport->max_rport_retry_count = 3;
+ lport->max_retry_count = BNX2FC_MAX_RETRY_CNT;
+ lport->max_rport_retry_count = BNX2FC_MAX_RPORT_RETRY_CNT;
lport->e_d_tov = 2 * 1000;
lport->r_a_tov = 10 * 1000;
@@ -1536,6 +1542,7 @@ static void __bnx2fc_destroy(struct bnx2fc_interface *interface)
static int bnx2fc_destroy(struct net_device *netdev)
{
struct bnx2fc_interface *interface = NULL;
+ struct workqueue_struct *timer_work_queue;
int rc = 0;
rtnl_lock();
@@ -1548,9 +1555,9 @@ static int bnx2fc_destroy(struct net_device *netdev)
goto netdev_err;
}
-
- destroy_workqueue(interface->timer_work_queue);
+ timer_work_queue = interface->timer_work_queue;
__bnx2fc_destroy(interface);
+ destroy_workqueue(timer_work_queue);
netdev_err:
mutex_unlock(&bnx2fc_dev_lock);
@@ -2054,6 +2061,7 @@ if_create_err:
ifput_err:
bnx2fc_net_cleanup(interface);
bnx2fc_interface_put(interface);
+ goto mod_err;
netdev_err:
module_put(THIS_MODULE);
mod_err:
diff --git a/drivers/scsi/bnx2fc/bnx2fc_io.c b/drivers/scsi/bnx2fc/bnx2fc_io.c
index 84a78af83f90..e897ce975bb8 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_io.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_io.c
@@ -1682,9 +1682,7 @@ void bnx2fc_build_fcp_cmnd(struct bnx2fc_cmd *io_req,
memset(fcp_cmnd, 0, sizeof(struct fcp_cmnd));
- int_to_scsilun(sc_cmd->device->lun,
- (struct scsi_lun *) fcp_cmnd->fc_lun);
-
+ int_to_scsilun(sc_cmd->device->lun, &fcp_cmnd->fc_lun);
fcp_cmnd->fc_dl = htonl(io_req->data_xfer_len);
memcpy(fcp_cmnd->fc_cdb, sc_cmd->cmnd, sc_cmd->cmd_len);
diff --git a/drivers/scsi/bnx2i/57xx_iscsi_constants.h b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
index 57515f1f1690..495a841645f9 100644
--- a/drivers/scsi/bnx2i/57xx_iscsi_constants.h
+++ b/drivers/scsi/bnx2i/57xx_iscsi_constants.h
@@ -122,6 +122,7 @@
#define ISCSI_KCQE_COMPLETION_STATUS_LOM_ISCSI_NOT_ENABLED (0x51)
#define ISCSI_KCQE_COMPLETION_STATUS_CID_BUSY (0x80)
+#define ISCSI_KCQE_COMPLETION_STATUS_PARITY_ERR (0x81)
/* SQ/RQ/CQ DB structure sizes */
#define ISCSI_SQ_DB_SIZE (16)
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
index 1ad0b8225560..f9d6f4129093 100644
--- a/drivers/scsi/bnx2i/bnx2i_hwi.c
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -1312,14 +1312,18 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_EXP_DATASN) |
/* EMC */
(1ULL << ISCSI_KCQE_COMPLETION_STATUS_PROTOCOL_ERR_LUN));
- if (error_mask1)
+ if (error_mask1) {
iscsi_init2.error_bit_map[0] = error_mask1;
- else
+ mask64 &= (u32)(~mask64);
+ mask64 |= error_mask1;
+ } else
iscsi_init2.error_bit_map[0] = (u32) mask64;
- if (error_mask2)
+ if (error_mask2) {
iscsi_init2.error_bit_map[1] = error_mask2;
- else
+ mask64 &= 0xffffffff;
+ mask64 |= ((u64)error_mask2 << 32);
+ } else
iscsi_init2.error_bit_map[1] = (u32) (mask64 >> 32);
iscsi_error_mask = mask64;
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index 1a947f1b9729..4927cca733d3 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -49,11 +49,11 @@ module_param(en_tcp_dack, int, 0664);
MODULE_PARM_DESC(en_tcp_dack, "Enable TCP Delayed ACK");
unsigned int error_mask1 = 0x00;
-module_param(error_mask1, int, 0664);
+module_param(error_mask1, uint, 0664);
MODULE_PARM_DESC(error_mask1, "Config FW iSCSI Error Mask #1");
unsigned int error_mask2 = 0x00;
-module_param(error_mask2, int, 0664);
+module_param(error_mask2, uint, 0664);
MODULE_PARM_DESC(error_mask2, "Config FW iSCSI Error Mask #2");
unsigned int sq_size;
@@ -393,8 +393,9 @@ static void bnx2i_percpu_thread_create(unsigned int cpu)
p = &per_cpu(bnx2i_percpu, cpu);
- thread = kthread_create(bnx2i_percpu_io_thread, (void *)p,
- "bnx2i_thread/%d", cpu);
+ thread = kthread_create_on_node(bnx2i_percpu_io_thread, (void *)p,
+ cpu_to_node(cpu),
+ "bnx2i_thread/%d", cpu);
/* bind thread to the cpu */
if (likely(!IS_ERR(thread))) {
kthread_bind(thread, cpu);
diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c
index 89afd6d21d89..d9253db1d0e2 100644
--- a/drivers/scsi/cxgbi/libcxgbi.c
+++ b/drivers/scsi/cxgbi/libcxgbi.c
@@ -2147,11 +2147,10 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf, int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct cxgbi_conn *cconn = tcp_conn->dd_data;
struct cxgbi_sock *csk = cconn->cep->csk;
- int value, err = 0;
+ int err;
log_debug(1 << CXGBI_DBG_ISCSI,
"cls_conn 0x%p, param %d, buf(%d) %s.\n",
@@ -2173,15 +2172,7 @@ int cxgbi_set_conn_param(struct iscsi_cls_conn *cls_conn,
conn->datadgst_en, 0);
break;
case ISCSI_PARAM_MAX_R2T:
- sscanf(buf, "%d", &value);
- if (value <= 0 || !is_power_of_2(value))
- return -EINVAL;
- if (session->max_r2t == value)
- break;
- iscsi_tcp_r2tpool_free(session);
- err = iscsi_set_param(cls_conn, param, buf, buflen);
- if (!err && iscsi_tcp_r2tpool_alloc(session))
- return -ENOMEM;
+ return iscsi_tcp_set_max_r2t(conn, buf);
case ISCSI_PARAM_MAX_RECV_DLENGTH:
err = iscsi_set_param(cls_conn, param, buf, buflen);
if (!err)
diff --git a/drivers/scsi/dtc.c b/drivers/scsi/dtc.c
index c2677ba29c74..4b11bb04f5c4 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 cc75cbea936b..ae7d15c44e2a 100644
--- a/drivers/scsi/fcoe/fcoe.c
+++ b/drivers/scsi/fcoe/fcoe.c
@@ -168,6 +168,14 @@ static struct fc_function_template fcoe_nport_fc_functions = {
.show_host_supported_fc4s = 1,
.show_host_active_fc4s = 1,
.show_host_maxframe_size = 1,
+ .show_host_serial_number = 1,
+ .show_host_manufacturer = 1,
+ .show_host_model = 1,
+ .show_host_model_description = 1,
+ .show_host_hardware_version = 1,
+ .show_host_driver_version = 1,
+ .show_host_firmware_version = 1,
+ .show_host_optionrom_version = 1,
.show_host_port_id = 1,
.show_host_supported_speeds = 1,
@@ -208,6 +216,14 @@ static struct fc_function_template fcoe_vport_fc_functions = {
.show_host_supported_fc4s = 1,
.show_host_active_fc4s = 1,
.show_host_maxframe_size = 1,
+ .show_host_serial_number = 1,
+ .show_host_manufacturer = 1,
+ .show_host_model = 1,
+ .show_host_model_description = 1,
+ .show_host_hardware_version = 1,
+ .show_host_driver_version = 1,
+ .show_host_firmware_version = 1,
+ .show_host_optionrom_version = 1,
.show_host_port_id = 1,
.show_host_supported_speeds = 1,
@@ -364,11 +380,10 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
if (!fcoe) {
FCOE_NETDEV_DBG(netdev, "Could not allocate fcoe structure\n");
fcoe = ERR_PTR(-ENOMEM);
- goto out_nomod;
+ goto out_putmod;
}
dev_hold(netdev);
- kref_init(&fcoe->kref);
/*
* Initialize FIP.
@@ -384,54 +399,18 @@ static struct fcoe_interface *fcoe_interface_create(struct net_device *netdev,
kfree(fcoe);
dev_put(netdev);
fcoe = ERR_PTR(err);
- goto out_nomod;
+ goto out_putmod;
}
goto out;
-out_nomod:
+out_putmod:
module_put(THIS_MODULE);
out:
return fcoe;
}
/**
- * fcoe_interface_release() - fcoe_port kref release function
- * @kref: Embedded reference count in an fcoe_interface struct
- */
-static void fcoe_interface_release(struct kref *kref)
-{
- struct fcoe_interface *fcoe;
- struct net_device *netdev;
-
- fcoe = container_of(kref, struct fcoe_interface, kref);
- netdev = fcoe->netdev;
- /* tear-down the FCoE controller */
- fcoe_ctlr_destroy(&fcoe->ctlr);
- kfree(fcoe);
- dev_put(netdev);
- module_put(THIS_MODULE);
-}
-
-/**
- * fcoe_interface_get() - Get a reference to a FCoE interface
- * @fcoe: The FCoE interface to be held
- */
-static inline void fcoe_interface_get(struct fcoe_interface *fcoe)
-{
- kref_get(&fcoe->kref);
-}
-
-/**
- * fcoe_interface_put() - Put a reference to a FCoE interface
- * @fcoe: The FCoE interface to be released
- */
-static inline void fcoe_interface_put(struct fcoe_interface *fcoe)
-{
- kref_put(&fcoe->kref, fcoe_interface_release);
-}
-
-/**
* fcoe_interface_cleanup() - Clean up a FCoE interface
* @fcoe: The FCoE interface to be cleaned up
*
@@ -478,7 +457,11 @@ static void fcoe_interface_cleanup(struct fcoe_interface *fcoe)
rtnl_unlock();
/* Release the self-reference taken during fcoe_interface_create() */
- fcoe_interface_put(fcoe);
+ /* tear-down the FCoE controller */
+ fcoe_ctlr_destroy(fip);
+ kfree(fcoe);
+ dev_put(netdev);
+ module_put(THIS_MODULE);
}
/**
@@ -734,6 +717,85 @@ static int fcoe_shost_config(struct fc_lport *lport, struct device *dev)
return 0;
}
+
+/**
+ * fcoe_fdmi_info() - Get FDMI related info from net devive for SW FCoE
+ * @lport: The local port that is associated with the net device
+ * @netdev: The associated net device
+ *
+ * Must be called after fcoe_shost_config() as it will use local port mutex
+ *
+ */
+static void fcoe_fdmi_info(struct fc_lport *lport, struct net_device *netdev)
+{
+ struct fcoe_interface *fcoe;
+ struct fcoe_port *port;
+ struct net_device *realdev;
+ int rc;
+ struct netdev_fcoe_hbainfo fdmi;
+
+ port = lport_priv(lport);
+ fcoe = port->priv;
+ realdev = fcoe->realdev;
+
+ if (!realdev)
+ return;
+
+ /* No FDMI state m/c for NPIV ports */
+ if (lport->vport)
+ return;
+
+ if (realdev->netdev_ops->ndo_fcoe_get_hbainfo) {
+ memset(&fdmi, 0, sizeof(fdmi));
+ rc = realdev->netdev_ops->ndo_fcoe_get_hbainfo(realdev,
+ &fdmi);
+ if (rc) {
+ printk(KERN_INFO "fcoe: Failed to retrieve FDMI "
+ "information from netdev.\n");
+ return;
+ }
+
+ snprintf(fc_host_serial_number(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%s",
+ fdmi.serial_number);
+ snprintf(fc_host_manufacturer(lport->host),
+ FC_SERIAL_NUMBER_SIZE,
+ "%s",
+ fdmi.manufacturer);
+ snprintf(fc_host_model(lport->host),
+ FC_SYMBOLIC_NAME_SIZE,
+ "%s",
+ fdmi.model);
+ snprintf(fc_host_model_description(lport->host),
+ FC_SYMBOLIC_NAME_SIZE,
+ "%s",
+ fdmi.model_description);
+ snprintf(fc_host_hardware_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi.hardware_version);
+ snprintf(fc_host_driver_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi.driver_version);
+ snprintf(fc_host_optionrom_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi.optionrom_version);
+ snprintf(fc_host_firmware_version(lport->host),
+ FC_VERSION_STRING_SIZE,
+ "%s",
+ fdmi.firmware_version);
+
+ /* Enable FDMI lport states */
+ lport->fdmi_enabled = 1;
+ } else {
+ lport->fdmi_enabled = 0;
+ printk(KERN_INFO "fcoe: No FDMI support.\n");
+ }
+}
+
/**
* fcoe_oem_match() - The match routine for the offloaded exchange manager
* @fp: The I/O frame
@@ -881,9 +943,6 @@ static void fcoe_if_destroy(struct fc_lport *lport)
dev_uc_del(netdev, port->data_src_addr);
rtnl_unlock();
- /* Release reference held in fcoe_if_create() */
- fcoe_interface_put(fcoe);
-
/* Free queued packets for the per-CPU receive threads */
fcoe_percpu_clean(lport);
@@ -1047,6 +1106,9 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_lp_destroy;
}
+ /* Initialized FDMI information */
+ fcoe_fdmi_info(lport, netdev);
+
/*
* fcoe_em_alloc() and fcoe_hostlist_add() both
* need to be atomic with respect to other changes to the
@@ -1070,7 +1132,6 @@ static struct fc_lport *fcoe_if_create(struct fcoe_interface *fcoe,
goto out_lp_destroy;
}
- fcoe_interface_get(fcoe);
return lport;
out_lp_destroy:
@@ -2009,20 +2070,13 @@ static void fcoe_destroy_work(struct work_struct *work)
{
struct fcoe_port *port;
struct fcoe_interface *fcoe;
- int npiv = 0;
port = container_of(work, struct fcoe_port, destroy_work);
mutex_lock(&fcoe_config_mutex);
- /* set if this is an NPIV port */
- npiv = port->lport->vport ? 1 : 0;
-
fcoe = port->priv;
fcoe_if_destroy(port->lport);
-
- /* Do not tear down the fcoe interface for NPIV port */
- if (!npiv)
- fcoe_interface_cleanup(fcoe);
+ fcoe_interface_cleanup(fcoe);
mutex_unlock(&fcoe_config_mutex);
}
@@ -2593,12 +2647,15 @@ static int fcoe_vport_destroy(struct fc_vport *vport)
struct Scsi_Host *shost = vport_to_shost(vport);
struct fc_lport *n_port = shost_priv(shost);
struct fc_lport *vn_port = vport->dd_data;
- struct fcoe_port *port = lport_priv(vn_port);
mutex_lock(&n_port->lp_mutex);
list_del(&vn_port->list);
mutex_unlock(&n_port->lp_mutex);
- queue_work(fcoe_wq, &port->destroy_work);
+
+ mutex_lock(&fcoe_config_mutex);
+ fcoe_if_destroy(vn_port);
+ mutex_unlock(&fcoe_config_mutex);
+
return 0;
}
diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h
index bcc89e639495..3c2733a12aa1 100644
--- a/drivers/scsi/fcoe/fcoe.h
+++ b/drivers/scsi/fcoe/fcoe.h
@@ -71,8 +71,6 @@ do { \
* @ctlr: The FCoE controller (for FIP)
* @oem: The offload exchange manager for all local port
* instances associated with this port
- * @kref: The kernel reference
- *
* This structure is 1:1 with a net devive.
*/
struct fcoe_interface {
@@ -83,7 +81,6 @@ struct fcoe_interface {
struct packet_type fip_packet_type;
struct fcoe_ctlr ctlr;
struct fc_exch_mgr *oem;
- struct kref kref;
};
#define fcoe_from_ctlr(fip) container_of(fip, struct fcoe_interface, ctlr)
diff --git a/drivers/scsi/fcoe/fcoe_transport.c b/drivers/scsi/fcoe/fcoe_transport.c
index 4d119a326d3b..710e149d41b6 100644
--- a/drivers/scsi/fcoe/fcoe_transport.c
+++ b/drivers/scsi/fcoe/fcoe_transport.c
@@ -619,8 +619,8 @@ static int libfcoe_device_notification(struct notifier_block *notifier,
switch (event) {
case NETDEV_UNREGISTER:
- printk(KERN_ERR "libfcoe_device_notification: NETDEV_UNREGISTER %s\n",
- netdev->name);
+ LIBFCOE_TRANSPORT_DBG("NETDEV_UNREGISTER %s\n",
+ netdev->name);
fcoe_del_netdev_mapping(netdev);
break;
}
diff --git a/drivers/scsi/fd_mcs.c b/drivers/scsi/fd_mcs.c
index a2c6135d337e..53bfcaa86f09 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 643f6d500fe7..1a2a1e5824e3 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 81182badfeb1..1a5954f0915a 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 d42ec921de46..5d72274c507f 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/hpsa.c b/drivers/scsi/hpsa.c
index b96962c39449..500e20dd56ec 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -56,6 +56,7 @@
/* HPSA_DRIVER_VERSION must be 3 byte values (0-255) separated by '.' */
#define HPSA_DRIVER_VERSION "2.0.2-1"
#define DRIVER_NAME "HP HPSA Driver (v " HPSA_DRIVER_VERSION ")"
+#define HPSA "hpsa"
/* How long to wait (in milliseconds) for board to go into simple mode */
#define MAX_CONFIG_WAIT 30000
@@ -202,30 +203,31 @@ static int check_for_unit_attention(struct ctlr_info *h,
switch (c->err_info->SenseInfo[12]) {
case STATE_CHANGED:
- dev_warn(&h->pdev->dev, "hpsa%d: a state change "
+ dev_warn(&h->pdev->dev, HPSA "%d: a state change "
"detected, command retried\n", h->ctlr);
break;
case LUN_FAILED:
- dev_warn(&h->pdev->dev, "hpsa%d: LUN failure "
+ dev_warn(&h->pdev->dev, HPSA "%d: LUN failure "
"detected, action required\n", h->ctlr);
break;
case REPORT_LUNS_CHANGED:
- dev_warn(&h->pdev->dev, "hpsa%d: report LUN data "
+ dev_warn(&h->pdev->dev, HPSA "%d: report LUN data "
"changed, action required\n", h->ctlr);
/*
- * Note: this REPORT_LUNS_CHANGED condition only occurs on the MSA2012.
+ * Note: this REPORT_LUNS_CHANGED condition only occurs on the external
+ * target (array) devices.
*/
break;
case POWER_OR_RESET:
- dev_warn(&h->pdev->dev, "hpsa%d: a power on "
+ dev_warn(&h->pdev->dev, HPSA "%d: a power on "
"or device reset detected\n", h->ctlr);
break;
case UNIT_ATTENTION_CLEARED:
- dev_warn(&h->pdev->dev, "hpsa%d: unit attention "
+ dev_warn(&h->pdev->dev, HPSA "%d: unit attention "
"cleared by another initiator\n", h->ctlr);
break;
default:
- dev_warn(&h->pdev->dev, "hpsa%d: unknown "
+ dev_warn(&h->pdev->dev, HPSA "%d: unknown "
"unit attention detected\n", h->ctlr);
break;
}
@@ -296,11 +298,23 @@ static u32 unresettable_controller[] = {
0x40800E11, /* Smart Array 5i */
0x409C0E11, /* Smart Array 6400 */
0x409D0E11, /* Smart Array 6400 EM */
+ 0x40700E11, /* Smart Array 5300 */
+ 0x40820E11, /* Smart Array 532 */
+ 0x40830E11, /* Smart Array 5312 */
+ 0x409A0E11, /* Smart Array 641 */
+ 0x409B0E11, /* Smart Array 642 */
+ 0x40910E11, /* Smart Array 6i */
};
/* List of controllers which cannot even be soft reset */
static u32 soft_unresettable_controller[] = {
0x40800E11, /* Smart Array 5i */
+ 0x40700E11, /* Smart Array 5300 */
+ 0x40820E11, /* Smart Array 532 */
+ 0x40830E11, /* Smart Array 5312 */
+ 0x409A0E11, /* Smart Array 641 */
+ 0x409B0E11, /* Smart Array 642 */
+ 0x40910E11, /* Smart Array 6i */
/* Exclude 640x boards. These are two pci devices in one slot
* which share a battery backed cache module. One controls the
* cache, the other accesses the cache through the one that controls
@@ -475,8 +489,8 @@ static struct device_attribute *hpsa_shost_attrs[] = {
static struct scsi_host_template hpsa_driver_template = {
.module = THIS_MODULE,
- .name = "hpsa",
- .proc_name = "hpsa",
+ .name = HPSA,
+ .proc_name = HPSA,
.queuecommand = hpsa_scsi_queue_command,
.scan_start = hpsa_scan_start,
.scan_finished = hpsa_scan_finished,
@@ -577,21 +591,19 @@ static int hpsa_find_target_lun(struct ctlr_info *h,
int i, found = 0;
DECLARE_BITMAP(lun_taken, HPSA_MAX_DEVICES);
- memset(&lun_taken[0], 0, HPSA_MAX_DEVICES >> 3);
+ bitmap_zero(lun_taken, HPSA_MAX_DEVICES);
for (i = 0; i < h->ndevices; i++) {
if (h->dev[i]->bus == bus && h->dev[i]->target != -1)
- set_bit(h->dev[i]->target, lun_taken);
+ __set_bit(h->dev[i]->target, lun_taken);
}
- for (i = 0; i < HPSA_MAX_DEVICES; i++) {
- if (!test_bit(i, lun_taken)) {
- /* *bus = 1; */
- *target = i;
- *lun = 0;
- found = 1;
- break;
- }
+ i = find_first_zero_bit(lun_taken, HPSA_MAX_DEVICES);
+ if (i < HPSA_MAX_DEVICES) {
+ /* *bus = 1; */
+ *target = i;
+ *lun = 0;
+ found = 1;
}
return !found;
}
@@ -675,6 +687,20 @@ lun_assigned:
return 0;
}
+/* Update an entry in h->dev[] array. */
+static void hpsa_scsi_update_entry(struct ctlr_info *h, int hostno,
+ int entry, struct hpsa_scsi_dev_t *new_entry)
+{
+ /* assumes h->devlock is held */
+ BUG_ON(entry < 0 || entry >= HPSA_MAX_DEVICES);
+
+ /* Raid level changed. */
+ h->dev[entry]->raid_level = new_entry->raid_level;
+ dev_info(&h->pdev->dev, "%s device c%db%dt%dl%d updated.\n",
+ scsi_device_type(new_entry->devtype), hostno, new_entry->bus,
+ new_entry->target, new_entry->lun);
+}
+
/* Replace an entry from h->dev[] array. */
static void hpsa_scsi_replace_entry(struct ctlr_info *h, int hostno,
int entry, struct hpsa_scsi_dev_t *new_entry,
@@ -781,10 +807,25 @@ static inline int device_is_the_same(struct hpsa_scsi_dev_t *dev1,
return 1;
}
+static inline int device_updated(struct hpsa_scsi_dev_t *dev1,
+ struct hpsa_scsi_dev_t *dev2)
+{
+ /* Device attributes that can change, but don't mean
+ * that the device is a different device, nor that the OS
+ * needs to be told anything about the change.
+ */
+ if (dev1->raid_level != dev2->raid_level)
+ return 1;
+ return 0;
+}
+
/* Find needle in haystack. If exact match found, return DEVICE_SAME,
* and return needle location in *index. If scsi3addr matches, but not
* vendor, model, serial num, etc. return DEVICE_CHANGED, and return needle
- * location in *index. If needle not found, return DEVICE_NOT_FOUND.
+ * location in *index.
+ * In the case of a minor device attribute change, such as RAID level, just
+ * return DEVICE_UPDATED, along with the updated device's location in index.
+ * If needle not found, return DEVICE_NOT_FOUND.
*/
static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
struct hpsa_scsi_dev_t *haystack[], int haystack_size,
@@ -794,15 +835,19 @@ static int hpsa_scsi_find_entry(struct hpsa_scsi_dev_t *needle,
#define DEVICE_NOT_FOUND 0
#define DEVICE_CHANGED 1
#define DEVICE_SAME 2
+#define DEVICE_UPDATED 3
for (i = 0; i < haystack_size; i++) {
if (haystack[i] == NULL) /* previously removed. */
continue;
if (SCSI3ADDR_EQ(needle->scsi3addr, haystack[i]->scsi3addr)) {
*index = i;
- if (device_is_the_same(needle, haystack[i]))
+ if (device_is_the_same(needle, haystack[i])) {
+ if (device_updated(needle, haystack[i]))
+ return DEVICE_UPDATED;
return DEVICE_SAME;
- else
+ } else {
return DEVICE_CHANGED;
+ }
}
}
*index = -1;
@@ -838,6 +883,8 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
* sd[] and remove them from h->dev[], and for any
* devices which have changed, remove the old device
* info and add the new device info.
+ * If minor device attributes change, just update
+ * the existing device structure.
*/
i = 0;
nremoved = 0;
@@ -858,6 +905,8 @@ static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
* at the bottom of hpsa_update_scsi_devices()
*/
sd[entry] = NULL;
+ } else if (device_change == DEVICE_UPDATED) {
+ hpsa_scsi_update_entry(h, hostno, i, sd[entry]);
}
i++;
}
@@ -1257,46 +1306,6 @@ static void complete_scsi_command(struct CommandList *cp)
cmd_free(h, cp);
}
-static int hpsa_scsi_detect(struct ctlr_info *h)
-{
- struct Scsi_Host *sh;
- int error;
-
- sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h));
- if (sh == NULL)
- goto fail;
-
- sh->io_port = 0;
- sh->n_io_port = 0;
- sh->this_id = -1;
- sh->max_channel = 3;
- sh->max_cmd_len = MAX_COMMAND_SIZE;
- sh->max_lun = HPSA_MAX_LUN;
- sh->max_id = HPSA_MAX_LUN;
- sh->can_queue = h->nr_cmds;
- sh->cmd_per_lun = h->nr_cmds;
- sh->sg_tablesize = h->maxsgentries;
- h->scsi_host = sh;
- sh->hostdata[0] = (unsigned long) h;
- sh->irq = h->intr[h->intr_mode];
- sh->unique_id = sh->irq;
- error = scsi_add_host(sh, &h->pdev->dev);
- if (error)
- goto fail_host_put;
- scsi_scan_host(sh);
- return 0;
-
- fail_host_put:
- dev_err(&h->pdev->dev, "hpsa_scsi_detect: scsi_add_host"
- " failed for controller %d\n", h->ctlr);
- scsi_host_put(sh);
- return error;
- fail:
- dev_err(&h->pdev->dev, "hpsa_scsi_detect: scsi_host_alloc"
- " failed for controller %d\n", h->ctlr);
- return -ENOMEM;
-}
-
static void hpsa_pci_unmap(struct pci_dev *pdev,
struct CommandList *c, int sg_used, int data_direction)
{
@@ -1641,7 +1650,7 @@ bail_out:
return 1;
}
-static unsigned char *msa2xxx_model[] = {
+static unsigned char *ext_target_model[] = {
"MSA2012",
"MSA2024",
"MSA2312",
@@ -1650,78 +1659,54 @@ static unsigned char *msa2xxx_model[] = {
NULL,
};
-static int is_msa2xxx(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
+static int is_ext_target(struct ctlr_info *h, struct hpsa_scsi_dev_t *device)
{
int i;
- for (i = 0; msa2xxx_model[i]; i++)
- if (strncmp(device->model, msa2xxx_model[i],
- strlen(msa2xxx_model[i])) == 0)
+ for (i = 0; ext_target_model[i]; i++)
+ if (strncmp(device->model, ext_target_model[i],
+ strlen(ext_target_model[i])) == 0)
return 1;
return 0;
}
/* Helper function to assign bus, target, lun mapping of devices.
- * Puts non-msa2xxx logical volumes on bus 0, msa2xxx logical
+ * Puts non-external target logical volumes on bus 0, external target logical
* volumes on bus 1, physical devices on bus 2. and the hba on bus 3.
* Logical drive target and lun are assigned at this time, but
* physical device lun and target assignment are deferred (assigned
* in hpsa_find_target_lun, called by hpsa_scsi_add_entry.)
*/
static void figure_bus_target_lun(struct ctlr_info *h,
- u8 *lunaddrbytes, int *bus, int *target, int *lun,
- struct hpsa_scsi_dev_t *device)
+ u8 *lunaddrbytes, struct hpsa_scsi_dev_t *device)
{
- u32 lunid;
+ u32 lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
- if (is_logical_dev_addr_mode(lunaddrbytes)) {
- /* logical device */
- if (unlikely(is_scsi_rev_5(h))) {
- /* p1210m, logical drives lun assignments
- * match SCSI REPORT LUNS data.
- */
- lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
- *bus = 0;
- *target = 0;
- *lun = (lunid & 0x3fff) + 1;
- } else {
- /* not p1210m... */
- lunid = le32_to_cpu(*((__le32 *) lunaddrbytes));
- if (is_msa2xxx(h, device)) {
- /* msa2xxx way, put logicals on bus 1
- * and match target/lun numbers box
- * reports.
- */
- *bus = 1;
- *target = (lunid >> 16) & 0x3fff;
- *lun = lunid & 0x00ff;
- } else {
- /* Traditional smart array way. */
- *bus = 0;
- *lun = 0;
- *target = lunid & 0x3fff;
- }
- }
- } else {
- /* physical device */
+ if (!is_logical_dev_addr_mode(lunaddrbytes)) {
+ /* physical device, target and lun filled in later */
if (is_hba_lunid(lunaddrbytes))
- if (unlikely(is_scsi_rev_5(h))) {
- *bus = 0; /* put p1210m ctlr at 0,0,0 */
- *target = 0;
- *lun = 0;
- return;
- } else
- *bus = 3; /* traditional smartarray */
+ hpsa_set_bus_target_lun(device, 3, 0, lunid & 0x3fff);
else
- *bus = 2; /* physical disk */
- *target = -1;
- *lun = -1; /* we will fill these in later. */
+ /* defer target, lun assignment for physical devices */
+ hpsa_set_bus_target_lun(device, 2, -1, -1);
+ return;
+ }
+ /* It's a logical device */
+ if (is_ext_target(h, device)) {
+ /* external target way, put logicals on bus 1
+ * and match target/lun numbers box
+ * reports, other smart array, bus 0, target 0, match lunid
+ */
+ hpsa_set_bus_target_lun(device,
+ 1, (lunid >> 16) & 0x3fff, lunid & 0x00ff);
+ return;
}
+ hpsa_set_bus_target_lun(device, 0, 0, lunid & 0x3fff);
}
/*
* If there is no lun 0 on a target, linux won't find any devices.
- * For the MSA2xxx boxes, we have to manually detect the enclosure
+ * For the external targets (arrays), we have to manually detect the enclosure
* which is at lun zero, as CCISS_REPORT_PHYSICAL_LUNS doesn't report
* it for some reason. *tmpdevice is the target we're adding,
* this_device is a pointer into the current element of currentsd[]
@@ -1730,46 +1715,46 @@ static void figure_bus_target_lun(struct ctlr_info *h,
* lun 0 assigned.
* Returns 1 if an enclosure was added, 0 if not.
*/
-static int add_msa2xxx_enclosure_device(struct ctlr_info *h,
+static int add_ext_target_dev(struct ctlr_info *h,
struct hpsa_scsi_dev_t *tmpdevice,
struct hpsa_scsi_dev_t *this_device, u8 *lunaddrbytes,
- int bus, int target, int lun, unsigned long lunzerobits[],
- int *nmsa2xxx_enclosures)
+ unsigned long lunzerobits[], int *n_ext_target_devs)
{
unsigned char scsi3addr[8];
- if (test_bit(target, lunzerobits))
+ if (test_bit(tmpdevice->target, lunzerobits))
return 0; /* There is already a lun 0 on this target. */
if (!is_logical_dev_addr_mode(lunaddrbytes))
return 0; /* It's the logical targets that may lack lun 0. */
- if (!is_msa2xxx(h, tmpdevice))
- return 0; /* It's only the MSA2xxx that have this problem. */
+ if (!is_ext_target(h, tmpdevice))
+ return 0; /* Only external target devices have this problem. */
- if (lun == 0) /* if lun is 0, then obviously we have a lun 0. */
+ if (tmpdevice->lun == 0) /* if lun is 0, then we have a lun 0. */
return 0;
memset(scsi3addr, 0, 8);
- scsi3addr[3] = target;
+ scsi3addr[3] = tmpdevice->target;
if (is_hba_lunid(scsi3addr))
return 0; /* Don't add the RAID controller here. */
if (is_scsi_rev_5(h))
return 0; /* p1210m doesn't need to do this. */
- if (*nmsa2xxx_enclosures >= MAX_MSA2XXX_ENCLOSURES) {
- dev_warn(&h->pdev->dev, "Maximum number of MSA2XXX "
- "enclosures exceeded. Check your hardware "
+ if (*n_ext_target_devs >= MAX_EXT_TARGETS) {
+ dev_warn(&h->pdev->dev, "Maximum number of external "
+ "target devices exceeded. Check your hardware "
"configuration.");
return 0;
}
if (hpsa_update_device_info(h, scsi3addr, this_device, NULL))
return 0;
- (*nmsa2xxx_enclosures)++;
- hpsa_set_bus_target_lun(this_device, bus, target, 0);
- set_bit(target, lunzerobits);
+ (*n_ext_target_devs)++;
+ hpsa_set_bus_target_lun(this_device,
+ tmpdevice->bus, tmpdevice->target, 0);
+ set_bit(tmpdevice->target, lunzerobits);
return 1;
}
@@ -1863,10 +1848,9 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
struct hpsa_scsi_dev_t **currentsd, *this_device, *tmpdevice;
int ncurrent = 0;
int reportlunsize = sizeof(*physdev_list) + HPSA_MAX_PHYS_LUN * 8;
- int i, nmsa2xxx_enclosures, ndevs_to_allocate;
- int bus, target, lun;
+ int i, n_ext_target_devs, ndevs_to_allocate;
int raid_ctlr_position;
- DECLARE_BITMAP(lunzerobits, HPSA_MAX_TARGETS_PER_CTLR);
+ DECLARE_BITMAP(lunzerobits, MAX_EXT_TARGETS);
currentsd = kzalloc(sizeof(*currentsd) * HPSA_MAX_DEVICES, GFP_KERNEL);
physdev_list = kzalloc(reportlunsize, GFP_KERNEL);
@@ -1883,11 +1867,11 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
logdev_list, &nlogicals))
goto out;
- /* We might see up to 32 MSA2xxx enclosures, actually 8 of them
- * but each of them 4 times through different paths. The plus 1
- * is for the RAID controller.
+ /* We might see up to the maximum number of logical and physical disks
+ * plus external target devices, and a device for the local RAID
+ * controller.
*/
- ndevs_to_allocate = nphysicals + nlogicals + MAX_MSA2XXX_ENCLOSURES + 1;
+ ndevs_to_allocate = nphysicals + nlogicals + MAX_EXT_TARGETS + 1;
/* Allocate the per device structures */
for (i = 0; i < ndevs_to_allocate; i++) {
@@ -1913,7 +1897,7 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
raid_ctlr_position = nphysicals + nlogicals;
/* adjust our table of devices */
- nmsa2xxx_enclosures = 0;
+ n_ext_target_devs = 0;
for (i = 0; i < nphysicals + nlogicals + 1; i++) {
u8 *lunaddrbytes, is_OBDR = 0;
@@ -1929,26 +1913,24 @@ static void hpsa_update_scsi_devices(struct ctlr_info *h, int hostno)
if (hpsa_update_device_info(h, lunaddrbytes, tmpdevice,
&is_OBDR))
continue; /* skip it if we can't talk to it. */
- figure_bus_target_lun(h, lunaddrbytes, &bus, &target, &lun,
- tmpdevice);
+ figure_bus_target_lun(h, lunaddrbytes, tmpdevice);
this_device = currentsd[ncurrent];
/*
- * For the msa2xxx boxes, we have to insert a LUN 0 which
+ * For external target devices, we have to insert a LUN 0 which
* doesn't show up in CCISS_REPORT_PHYSICAL data, but there
* is nonetheless an enclosure device there. We have to
* present that otherwise linux won't find anything if
* there is no lun 0.
*/
- if (add_msa2xxx_enclosure_device(h, tmpdevice, this_device,
- lunaddrbytes, bus, target, lun, lunzerobits,
- &nmsa2xxx_enclosures)) {
+ if (add_ext_target_dev(h, tmpdevice, this_device,
+ lunaddrbytes, lunzerobits,
+ &n_ext_target_devs)) {
ncurrent++;
this_device = currentsd[ncurrent];
}
*this_device = *tmpdevice;
- hpsa_set_bus_target_lun(this_device, bus, target, lun);
switch (this_device->devtype) {
case TYPE_ROM:
@@ -2228,13 +2210,42 @@ static void hpsa_unregister_scsi(struct ctlr_info *h)
static int hpsa_register_scsi(struct ctlr_info *h)
{
- int rc;
+ struct Scsi_Host *sh;
+ int error;
- rc = hpsa_scsi_detect(h);
- if (rc != 0)
- dev_err(&h->pdev->dev, "hpsa_register_scsi: failed"
- " hpsa_scsi_detect(), rc is %d\n", rc);
- return rc;
+ sh = scsi_host_alloc(&hpsa_driver_template, sizeof(h));
+ if (sh == NULL)
+ goto fail;
+
+ sh->io_port = 0;
+ sh->n_io_port = 0;
+ sh->this_id = -1;
+ sh->max_channel = 3;
+ sh->max_cmd_len = MAX_COMMAND_SIZE;
+ sh->max_lun = HPSA_MAX_LUN;
+ sh->max_id = HPSA_MAX_LUN;
+ sh->can_queue = h->nr_cmds;
+ sh->cmd_per_lun = h->nr_cmds;
+ sh->sg_tablesize = h->maxsgentries;
+ h->scsi_host = sh;
+ sh->hostdata[0] = (unsigned long) h;
+ sh->irq = h->intr[h->intr_mode];
+ sh->unique_id = sh->irq;
+ error = scsi_add_host(sh, &h->pdev->dev);
+ if (error)
+ goto fail_host_put;
+ scsi_scan_host(sh);
+ return 0;
+
+ fail_host_put:
+ dev_err(&h->pdev->dev, "%s: scsi_add_host"
+ " failed for controller %d\n", __func__, h->ctlr);
+ scsi_host_put(sh);
+ return error;
+ fail:
+ dev_err(&h->pdev->dev, "%s: scsi_host_alloc"
+ " failed for controller %d\n", __func__, h->ctlr);
+ return -ENOMEM;
}
static int wait_for_device_to_become_ready(struct ctlr_info *h,
@@ -2700,16 +2711,16 @@ static int hpsa_big_passthru_ioctl(struct ctlr_info *h, void __user *argp)
status = -EINVAL;
goto cleanup1;
}
- if (ioc->buf_size > ioc->malloc_size * MAXSGENTRIES) {
+ if (ioc->buf_size > ioc->malloc_size * SG_ENTRIES_IN_CMD) {
status = -EINVAL;
goto cleanup1;
}
- buff = kzalloc(MAXSGENTRIES * sizeof(char *), GFP_KERNEL);
+ buff = kzalloc(SG_ENTRIES_IN_CMD * sizeof(char *), GFP_KERNEL);
if (!buff) {
status = -ENOMEM;
goto cleanup1;
}
- buff_size = kmalloc(MAXSGENTRIES * sizeof(int), GFP_KERNEL);
+ buff_size = kmalloc(SG_ENTRIES_IN_CMD * sizeof(int), GFP_KERNEL);
if (!buff_size) {
status = -ENOMEM;
goto cleanup1;
@@ -3354,7 +3365,7 @@ static int hpsa_controller_hard_reset(struct pci_dev *pdev,
static __devinit void init_driver_version(char *driver_version, int len)
{
memset(driver_version, 0, len);
- strncpy(driver_version, "hpsa " HPSA_DRIVER_VERSION, len - 1);
+ strncpy(driver_version, HPSA " " HPSA_DRIVER_VERSION, len - 1);
}
static __devinit int write_driver_ver_to_cfgtable(
@@ -3935,7 +3946,7 @@ static int __devinit hpsa_pci_init(struct ctlr_info *h)
return err;
}
- err = pci_request_regions(h->pdev, "hpsa");
+ err = pci_request_regions(h->pdev, HPSA);
if (err) {
dev_err(&h->pdev->dev,
"cannot obtain PCI resources, aborting\n");
@@ -4253,7 +4264,7 @@ static void start_controller_lockup_detector(struct ctlr_info *h)
spin_lock_init(&lockup_detector_lock);
hpsa_lockup_detector =
kthread_run(detect_controller_lockup_thread,
- NULL, "hpsa");
+ NULL, HPSA);
}
if (!hpsa_lockup_detector) {
dev_warn(&h->pdev->dev,
@@ -4325,7 +4336,7 @@ reinit_after_soft_reset:
if (rc != 0)
goto clean1;
- sprintf(h->devname, "hpsa%d", number_of_controllers);
+ sprintf(h->devname, HPSA "%d", number_of_controllers);
h->ctlr = number_of_controllers;
number_of_controllers++;
@@ -4482,6 +4493,14 @@ static void hpsa_shutdown(struct pci_dev *pdev)
#endif /* CONFIG_PCI_MSI */
}
+static void __devexit hpsa_free_device_info(struct ctlr_info *h)
+{
+ int i;
+
+ for (i = 0; i < h->ndevices; i++)
+ kfree(h->dev[i]);
+}
+
static void __devexit hpsa_remove_one(struct pci_dev *pdev)
{
struct ctlr_info *h;
@@ -4497,6 +4516,7 @@ static void __devexit hpsa_remove_one(struct pci_dev *pdev)
iounmap(h->vaddr);
iounmap(h->transtable);
iounmap(h->cfgtable);
+ hpsa_free_device_info(h);
hpsa_free_sg_chain_blocks(h);
pci_free_consistent(h->pdev,
h->nr_cmds * sizeof(struct CommandList),
@@ -4530,7 +4550,7 @@ static int hpsa_resume(__attribute__((unused)) struct pci_dev *pdev)
}
static struct pci_driver hpsa_pci_driver = {
- .name = "hpsa",
+ .name = HPSA,
.probe = hpsa_init_one,
.remove = __devexit_p(hpsa_remove_one),
.id_table = hpsa_pci_device_id, /* id_table */
@@ -4592,15 +4612,15 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h,
* Each SG entry requires 16 bytes. The eight registers are programmed
* with the number of 16-byte blocks a command of that size requires.
* The smallest command possible requires 5 such 16 byte blocks.
- * the largest command possible requires MAXSGENTRIES + 4 16-byte
+ * the largest command possible requires SG_ENTRIES_IN_CMD + 4 16-byte
* blocks. Note, this only extends to the SG entries contained
* within the command block, and does not extend to chained blocks
* of SG elements. bft[] contains the eight values we write to
* the registers. They are not evenly distributed, but have more
* sizes for small commands, and fewer sizes for larger commands.
*/
- int bft[8] = {5, 6, 8, 10, 12, 20, 28, MAXSGENTRIES + 4};
- BUILD_BUG_ON(28 > MAXSGENTRIES + 4);
+ int bft[8] = {5, 6, 8, 10, 12, 20, 28, SG_ENTRIES_IN_CMD + 4};
+ BUILD_BUG_ON(28 > SG_ENTRIES_IN_CMD + 4);
/* 5 = 1 s/g entry or 4k
* 6 = 2 s/g entry or 8k
* 8 = 4 s/g entry or 16k
@@ -4613,8 +4633,9 @@ static __devinit void hpsa_enter_performant_mode(struct ctlr_info *h,
memset(h->reply_pool, 0, h->reply_pool_size);
h->reply_pool_head = h->reply_pool;
- bft[7] = h->max_sg_entries + 4;
- calc_bucket_map(bft, ARRAY_SIZE(bft), 32, h->blockFetchTable);
+ bft[7] = SG_ENTRIES_IN_CMD + 4;
+ calc_bucket_map(bft, ARRAY_SIZE(bft),
+ SG_ENTRIES_IN_CMD, h->blockFetchTable);
for (i = 0; i < 8; i++)
writel(bft[i], &h->transtable->BlockFetch[i]);
@@ -4652,14 +4673,13 @@ static __devinit void hpsa_put_ctlr_into_performant_mode(struct ctlr_info *h)
return;
hpsa_get_max_perf_mode_cmds(h);
- h->max_sg_entries = 32;
/* Performant mode ring buffer and supporting data structures */
h->reply_pool_size = h->max_commands * sizeof(u64);
h->reply_pool = pci_alloc_consistent(h->pdev, h->reply_pool_size,
&(h->reply_pool_dhandle));
/* Need a block fetch table for performant mode */
- h->blockFetchTable = kmalloc(((h->max_sg_entries+1) *
+ h->blockFetchTable = kmalloc(((SG_ENTRIES_IN_CMD + 1) *
sizeof(u32)), GFP_KERNEL);
if ((h->reply_pool == NULL)
diff --git a/drivers/scsi/hpsa.h b/drivers/scsi/hpsa.h
index 91edafb8c7e6..7b28d54fa878 100644
--- a/drivers/scsi/hpsa.h
+++ b/drivers/scsi/hpsa.h
@@ -58,7 +58,6 @@ struct ctlr_info {
unsigned long paddr;
int nr_cmds; /* Number of commands allowed on this controller */
struct CfgTable __iomem *cfgtable;
- int max_sg_entries;
int interrupts_enabled;
int major;
int max_commands;
@@ -317,7 +316,7 @@ static unsigned long SA5_completed(struct ctlr_info *h)
dev_dbg(&h->pdev->dev, "Read %lx back from board\n",
register_value);
else
- dev_dbg(&h->pdev->dev, "hpsa: FIFO Empty read\n");
+ dev_dbg(&h->pdev->dev, "FIFO Empty read\n");
#endif
return register_value;
diff --git a/drivers/scsi/hpsa_cmd.h b/drivers/scsi/hpsa_cmd.h
index 3fd4715935c2..8049815d8c1e 100644
--- a/drivers/scsi/hpsa_cmd.h
+++ b/drivers/scsi/hpsa_cmd.h
@@ -23,7 +23,7 @@
/* general boundary defintions */
#define SENSEINFOBYTES 32 /* may vary between hbas */
-#define MAXSGENTRIES 32
+#define SG_ENTRIES_IN_CMD 32 /* Max SG entries excluding chain blocks */
#define HPSA_SG_CHAIN 0x80000000
#define MAXREPLYQS 256
@@ -122,12 +122,11 @@ union u64bit {
};
/* FIXME this is a per controller value (barf!) */
-#define HPSA_MAX_TARGETS_PER_CTLR 16
#define HPSA_MAX_LUN 1024
#define HPSA_MAX_PHYS_LUN 1024
-#define MAX_MSA2XXX_ENCLOSURES 32
+#define MAX_EXT_TARGETS 32
#define HPSA_MAX_DEVICES (HPSA_MAX_PHYS_LUN + HPSA_MAX_LUN + \
- MAX_MSA2XXX_ENCLOSURES + 1) /* + 1 is for the controller itself */
+ MAX_EXT_TARGETS + 1) /* + 1 is for the controller itself */
/* SCSI-3 Commands */
#pragma pack(1)
@@ -282,7 +281,7 @@ struct CommandList {
struct CommandListHeader Header;
struct RequestBlock Request;
struct ErrDescriptor ErrDesc;
- struct SGDescriptor SG[MAXSGENTRIES];
+ struct SGDescriptor SG[SG_ENTRIES_IN_CMD];
/* information associated with the command */
u32 busaddr; /* physical addr of this record */
struct ErrorInfo *err_info; /* pointer to the allocated mem */
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 67fc8ffd52e6..cd09132d5d7d 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 bdfa223a7dbb..134a0ae85bb7 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 e984951baeb6..3a6c4742951e 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 2256babe0474..aa7ed81e9237 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 112f1bec7756..deb5b6d8398e 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 b538f0883fd2..cdfe5a16de2a 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -183,7 +183,7 @@ static const struct ipr_chip_t ipr_chip[] = {
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
{ PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, IPR_USE_LSI, IPR_SIS32, IPR_PCI_CFG, &ipr_chip_cfg[1] },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
};
static int ipr_max_bus_speeds [] = {
@@ -9191,15 +9191,15 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = {
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C3, 0, 0, 0 },
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_FPGA_E2,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C4, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B4, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57B1, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C6, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
- PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_575D, 0, 0, 0 },
- { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROC_ASIC_E2,
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
+ PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57C8, 0, 0, 0 },
+ { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE,
PCI_VENDOR_ID_IBM, IPR_SUBS_DEV_ID_57CE, 0, 0, 0 },
{ }
};
diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h
index b13f9cc12279..f94eaee2ff16 100644
--- a/drivers/scsi/ipr.h
+++ b/drivers/scsi/ipr.h
@@ -58,7 +58,7 @@
#define PCI_DEVICE_ID_IBM_OBSIDIAN_E 0x0339
#define PCI_DEVICE_ID_IBM_CROC_FPGA_E2 0x033D
-#define PCI_DEVICE_ID_IBM_CROC_ASIC_E2 0x034A
+#define PCI_DEVICE_ID_IBM_CROCODILE 0x034A
#define IPR_SUBS_DEV_ID_2780 0x0264
#define IPR_SUBS_DEV_ID_5702 0x0266
@@ -92,7 +92,7 @@
#define IPR_SUBS_DEV_ID_57B1 0x0355
#define IPR_SUBS_DEV_ID_574D 0x0356
-#define IPR_SUBS_DEV_ID_575D 0x035D
+#define IPR_SUBS_DEV_ID_57C8 0x035D
#define IPR_NAME "ipr"
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 6ca9b26bb2fb..d4bf9c12ecd4 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -649,15 +649,13 @@ static void isci_host_start_complete(struct isci_host *ihost, enum sci_status co
int isci_host_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
- struct isci_host *ihost = SHOST_TO_SAS_HA(shost)->lldd_ha;
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct isci_host *ihost = ha->lldd_ha;
if (test_bit(IHOST_START_PENDING, &ihost->flags))
return 0;
- /* todo: use sas_flush_discovery once it is upstream */
- scsi_flush_work(shost);
-
- scsi_flush_work(shost);
+ sas_drain_work(ha);
dev_dbg(&ihost->pdev->dev,
"%s: ihost->status = %d, time = %ld\n",
@@ -1490,6 +1488,15 @@ sci_controller_set_interrupt_coalescence(struct isci_host *ihost,
static void sci_controller_ready_state_enter(struct sci_base_state_machine *sm)
{
struct isci_host *ihost = container_of(sm, typeof(*ihost), sm);
+ u32 val;
+
+ /* enable clock gating for power control of the scu unit */
+ val = readl(&ihost->smu_registers->clock_gating_control);
+ val &= ~(SMU_CGUCR_GEN_BIT(REGCLK_ENABLE) |
+ SMU_CGUCR_GEN_BIT(TXCLK_ENABLE) |
+ SMU_CGUCR_GEN_BIT(XCLK_ENABLE));
+ val |= SMU_CGUCR_GEN_BIT(IDLE_ENABLE);
+ writel(val, &ihost->smu_registers->clock_gating_control);
/* set the default interrupt coalescence number and timeout value. */
sci_controller_set_interrupt_coalescence(ihost, 0, 0);
diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h
index 5477f0fa8233..adbad69d1069 100644
--- a/drivers/scsi/isci/host.h
+++ b/drivers/scsi/isci/host.h
@@ -187,6 +187,7 @@ struct isci_host {
int id; /* unique within a given pci device */
struct isci_phy phys[SCI_MAX_PHYS];
struct isci_port ports[SCI_MAX_PORTS + 1]; /* includes dummy port */
+ struct asd_sas_port sas_ports[SCI_MAX_PORTS];
struct sas_ha_struct sas_ha;
spinlock_t state_lock;
@@ -393,24 +394,6 @@ static inline int sci_remote_device_node_count(struct isci_remote_device *idev)
#define sci_controller_clear_invalid_phy(controller, phy) \
((controller)->invalid_phy_mask &= ~(1 << (phy)->phy_index))
-static inline struct device *sciphy_to_dev(struct isci_phy *iphy)
-{
-
- if (!iphy || !iphy->isci_port || !iphy->isci_port->isci_host)
- return NULL;
-
- return &iphy->isci_port->isci_host->pdev->dev;
-}
-
-static inline struct device *sciport_to_dev(struct isci_port *iport)
-{
-
- if (!iport || !iport->isci_host)
- return NULL;
-
- return &iport->isci_host->pdev->dev;
-}
-
static inline struct device *scirdev_to_dev(struct isci_remote_device *idev)
{
if (!idev || !idev->isci_port || !idev->isci_port->isci_host)
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index 17c4c2c89c2e..5137db5a5d85 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -60,6 +60,7 @@
#include <linux/efi.h>
#include <asm/string.h>
#include <scsi/scsi_host.h>
+#include "host.h"
#include "isci.h"
#include "task.h"
#include "probe_roms.h"
@@ -154,7 +155,6 @@ static struct scsi_host_template isci_sht = {
.queuecommand = sas_queuecommand,
.target_alloc = sas_target_alloc,
.slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
.scan_finished = isci_host_scan_finished,
.scan_start = isci_host_scan_start,
.change_queue_depth = sas_change_queue_depth,
@@ -166,9 +166,6 @@ static struct scsi_host_template isci_sht = {
.sg_tablesize = SG_ALL,
.max_sectors = SCSI_DEFAULT_MAX_SECTORS,
.use_clustering = ENABLE_CLUSTERING,
- .eh_device_reset_handler = sas_eh_device_reset_handler,
- .eh_bus_reset_handler = isci_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
.shost_attrs = isci_host_attrs,
@@ -194,6 +191,9 @@ static struct sas_domain_function_template isci_transport_ops = {
.lldd_lu_reset = isci_task_lu_reset,
.lldd_query_task = isci_task_query_task,
+ /* ata recovery called from ata-eh */
+ .lldd_ata_check_ready = isci_ata_check_ready,
+
/* Port and Adapter management */
.lldd_clear_nexus_port = isci_task_clear_nexus_port,
.lldd_clear_nexus_ha = isci_task_clear_nexus_ha,
@@ -242,18 +242,13 @@ static int isci_register_sas_ha(struct isci_host *isci_host)
if (!sas_ports)
return -ENOMEM;
- /*----------------- Libsas Initialization Stuff----------------------
- * Set various fields in the sas_ha struct:
- */
-
sas_ha->sas_ha_name = DRV_NAME;
sas_ha->lldd_module = THIS_MODULE;
sas_ha->sas_addr = &isci_host->phys[0].sas_addr[0];
- /* set the array of phy and port structs. */
for (i = 0; i < SCI_MAX_PHYS; i++) {
sas_phys[i] = &isci_host->phys[i].sas_phy;
- sas_ports[i] = &isci_host->ports[i].sas_port;
+ sas_ports[i] = &isci_host->sas_ports[i];
}
sas_ha->sas_phy = sas_phys;
@@ -528,6 +523,13 @@ static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_devic
goto err_host_alloc;
}
pci_info->hosts[i] = h;
+
+ /* turn on DIF support */
+ scsi_host_set_prot(h->shost,
+ SHOST_DIF_TYPE1_PROTECTION |
+ SHOST_DIF_TYPE2_PROTECTION |
+ SHOST_DIF_TYPE3_PROTECTION);
+ scsi_host_set_guard(h->shost, SHOST_DIX_GUARD_CRC);
}
err = isci_setup_interrupts(pdev);
@@ -551,9 +553,9 @@ static void __devexit isci_pci_remove(struct pci_dev *pdev)
int i;
for_each_isci_host(i, ihost, pdev) {
+ wait_for_start(ihost);
isci_unregister(ihost);
isci_host_deinit(ihost);
- sci_controller_disable_interrupts(ihost);
}
}
diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c
index fe18acfd6eb3..fab3586840b5 100644
--- a/drivers/scsi/isci/phy.c
+++ b/drivers/scsi/isci/phy.c
@@ -59,6 +59,16 @@
#include "scu_event_codes.h"
#include "probe_roms.h"
+#undef C
+#define C(a) (#a)
+static const char *phy_state_name(enum sci_phy_states state)
+{
+ static const char * const strings[] = PHY_STATES;
+
+ return strings[state];
+}
+#undef C
+
/* Maximum arbitration wait time in micro-seconds */
#define SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME (700)
@@ -67,6 +77,19 @@ enum sas_linkrate sci_phy_linkrate(struct isci_phy *iphy)
return iphy->max_negotiated_speed;
}
+static struct isci_host *phy_to_host(struct isci_phy *iphy)
+{
+ struct isci_phy *table = iphy - iphy->phy_index;
+ struct isci_host *ihost = container_of(table, typeof(*ihost), phys[0]);
+
+ return ihost;
+}
+
+static struct device *sciphy_to_dev(struct isci_phy *iphy)
+{
+ return &phy_to_host(iphy)->pdev->dev;
+}
+
static enum sci_status
sci_phy_transport_layer_initialization(struct isci_phy *iphy,
struct scu_transport_layer_registers __iomem *reg)
@@ -446,8 +469,8 @@ enum sci_status sci_phy_start(struct isci_phy *iphy)
enum sci_phy_states state = iphy->sm.current_state_id;
if (state != SCI_PHY_STOPPED) {
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -472,8 +495,8 @@ enum sci_status sci_phy_stop(struct isci_phy *iphy)
case SCI_PHY_READY:
break;
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -486,8 +509,8 @@ enum sci_status sci_phy_reset(struct isci_phy *iphy)
enum sci_phy_states state = iphy->sm.current_state_id;
if (state != SCI_PHY_READY) {
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -536,8 +559,8 @@ enum sci_status sci_phy_consume_power_handler(struct isci_phy *iphy)
return SCI_SUCCESS;
}
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -591,6 +614,60 @@ static void sci_phy_complete_link_training(struct isci_phy *iphy,
sci_change_state(&iphy->sm, next_state);
}
+static const char *phy_event_name(u32 event_code)
+{
+ switch (scu_get_event_code(event_code)) {
+ case SCU_EVENT_PORT_SELECTOR_DETECTED:
+ return "port selector";
+ case SCU_EVENT_SENT_PORT_SELECTION:
+ return "port selection";
+ case SCU_EVENT_HARD_RESET_TRANSMITTED:
+ return "tx hard reset";
+ case SCU_EVENT_HARD_RESET_RECEIVED:
+ return "rx hard reset";
+ case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT:
+ return "identify timeout";
+ case SCU_EVENT_LINK_FAILURE:
+ return "link fail";
+ case SCU_EVENT_SATA_SPINUP_HOLD:
+ return "sata spinup hold";
+ case SCU_EVENT_SAS_15_SSC:
+ case SCU_EVENT_SAS_15:
+ return "sas 1.5";
+ case SCU_EVENT_SAS_30_SSC:
+ case SCU_EVENT_SAS_30:
+ return "sas 3.0";
+ case SCU_EVENT_SAS_60_SSC:
+ case SCU_EVENT_SAS_60:
+ return "sas 6.0";
+ case SCU_EVENT_SATA_15_SSC:
+ case SCU_EVENT_SATA_15:
+ return "sata 1.5";
+ case SCU_EVENT_SATA_30_SSC:
+ case SCU_EVENT_SATA_30:
+ return "sata 3.0";
+ case SCU_EVENT_SATA_60_SSC:
+ case SCU_EVENT_SATA_60:
+ return "sata 6.0";
+ case SCU_EVENT_SAS_PHY_DETECTED:
+ return "sas detect";
+ case SCU_EVENT_SATA_PHY_DETECTED:
+ return "sata detect";
+ default:
+ return "unknown";
+ }
+}
+
+#define phy_event_dbg(iphy, state, code) \
+ dev_dbg(sciphy_to_dev(iphy), "phy-%d:%d: %s event: %s (%x)\n", \
+ phy_to_host(iphy)->id, iphy->phy_index, \
+ phy_state_name(state), phy_event_name(code), code)
+
+#define phy_event_warn(iphy, state, code) \
+ dev_warn(sciphy_to_dev(iphy), "phy-%d:%d: %s event: %s (%x)\n", \
+ phy_to_host(iphy)->id, iphy->phy_index, \
+ phy_state_name(state), phy_event_name(code), code)
+
enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
{
enum sci_phy_states state = iphy->sm.current_state_id;
@@ -607,11 +684,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
iphy->is_in_link_training = true;
break;
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__,
- event_code);
+ phy_event_dbg(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -648,11 +721,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
break;
}
@@ -677,10 +746,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -691,11 +757,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received unexpected "
- "event_code %x\n",
- __func__,
- event_code);
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -719,11 +781,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -751,12 +809,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sas_link_training(iphy);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__,
- event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -793,11 +846,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_phy_start_sas_link_training(iphy);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
@@ -815,12 +864,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: PHY starting substate machine received "
- "unexpected event_code %x\n",
- __func__,
- event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE;
}
return SCI_SUCCESS;
@@ -838,10 +882,7 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
iphy->bcn_received_while_port_unassigned = true;
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%sP SCIC PHY 0x%p ready state machine received "
- "unexpected event_code %x\n",
- __func__, iphy, event_code);
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE_INVALID_STATE;
}
return SCI_SUCCESS;
@@ -852,18 +893,14 @@ enum sci_status sci_phy_event_handler(struct isci_phy *iphy, u32 event_code)
sci_change_state(&iphy->sm, SCI_PHY_STARTING);
break;
default:
- dev_warn(sciphy_to_dev(iphy),
- "%s: SCIC PHY 0x%p resetting state machine received "
- "unexpected event_code %x\n",
- __func__, iphy, event_code);
-
+ phy_event_warn(iphy, state, event_code);
return SCI_FAILURE_INVALID_STATE;
break;
}
return SCI_SUCCESS;
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -956,8 +993,8 @@ enum sci_status sci_phy_frame_handler(struct isci_phy *iphy, u32 frame_index)
return result;
}
default:
- dev_dbg(sciphy_to_dev(iphy),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_dbg(sciphy_to_dev(iphy), "%s: in wrong state: %s\n",
+ __func__, phy_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1299,7 +1336,6 @@ void isci_phy_init(struct isci_phy *iphy, struct isci_host *ihost, int index)
sas_addr = cpu_to_be64(sci_sas_addr);
memcpy(iphy->sas_addr, &sas_addr, sizeof(sas_addr));
- iphy->isci_port = NULL;
iphy->sas_phy.enabled = 0;
iphy->sas_phy.id = index;
iphy->sas_phy.sas_addr = &iphy->sas_addr[0];
@@ -1333,13 +1369,13 @@ int isci_phy_control(struct asd_sas_phy *sas_phy,
{
int ret = 0;
struct isci_phy *iphy = sas_phy->lldd_phy;
- struct isci_port *iport = iphy->isci_port;
+ struct asd_sas_port *port = sas_phy->port;
struct isci_host *ihost = sas_phy->ha->lldd_ha;
unsigned long flags;
dev_dbg(&ihost->pdev->dev,
"%s: phy %p; func %d; buf %p; isci phy %p, port %p\n",
- __func__, sas_phy, func, buf, iphy, iport);
+ __func__, sas_phy, func, buf, iphy, port);
switch (func) {
case PHY_FUNC_DISABLE:
@@ -1356,11 +1392,10 @@ int isci_phy_control(struct asd_sas_phy *sas_phy,
break;
case PHY_FUNC_HARD_RESET:
- if (!iport)
+ if (!port)
return -ENODEV;
- /* Perform the port reset. */
- ret = isci_port_perform_hard_reset(ihost, iport, iphy);
+ ret = isci_port_perform_hard_reset(ihost, port->lldd_port, iphy);
break;
case PHY_FUNC_GET_EVENTS: {
diff --git a/drivers/scsi/isci/phy.h b/drivers/scsi/isci/phy.h
index 67699c8e321c..0e45833ba06d 100644
--- a/drivers/scsi/isci/phy.h
+++ b/drivers/scsi/isci/phy.h
@@ -103,7 +103,6 @@ struct isci_phy {
struct scu_transport_layer_registers __iomem *transport_layer_registers;
struct scu_link_layer_registers __iomem *link_layer_registers;
struct asd_sas_phy sas_phy;
- struct isci_port *isci_port;
u8 sas_addr[SAS_ADDR_SIZE];
union {
struct sas_identify_frame iaf;
@@ -344,101 +343,65 @@ enum sci_phy_counter_id {
SCIC_PHY_COUNTER_SN_DWORD_SYNC_ERROR
};
-enum sci_phy_states {
- /**
- * Simply the initial state for the base domain state machine.
- */
- SCI_PHY_INITIAL,
-
- /**
- * This state indicates that the phy has successfully been stopped.
- * In this state no new IO operations are permitted on this phy.
- * This state is entered from the INITIAL state.
- * This state is entered from the STARTING state.
- * This state is entered from the READY state.
- * This state is entered from the RESETTING state.
- */
- SCI_PHY_STOPPED,
-
- /**
- * This state indicates that the phy is in the process of becomming
- * ready. In this state no new IO operations are permitted on this phy.
- * This state is entered from the STOPPED state.
- * This state is entered from the READY state.
- * This state is entered from the RESETTING state.
- */
- SCI_PHY_STARTING,
-
- /**
- * Initial state
- */
- SCI_PHY_SUB_INITIAL,
-
- /**
- * Wait state for the hardware OSSP event type notification
- */
- SCI_PHY_SUB_AWAIT_OSSP_EN,
-
- /**
- * Wait state for the PHY speed notification
- */
- SCI_PHY_SUB_AWAIT_SAS_SPEED_EN,
-
- /**
- * Wait state for the IAF Unsolicited frame notification
- */
- SCI_PHY_SUB_AWAIT_IAF_UF,
-
- /**
- * Wait state for the request to consume power
- */
- SCI_PHY_SUB_AWAIT_SAS_POWER,
-
- /**
- * Wait state for request to consume power
- */
- SCI_PHY_SUB_AWAIT_SATA_POWER,
-
- /**
- * Wait state for the SATA PHY notification
- */
- SCI_PHY_SUB_AWAIT_SATA_PHY_EN,
-
- /**
- * Wait for the SATA PHY speed notification
- */
- SCI_PHY_SUB_AWAIT_SATA_SPEED_EN,
-
- /**
- * Wait state for the SIGNATURE FIS unsolicited frame notification
- */
- SCI_PHY_SUB_AWAIT_SIG_FIS_UF,
-
- /**
- * Exit state for this state machine
- */
- SCI_PHY_SUB_FINAL,
-
- /**
- * This state indicates the the phy is now ready. Thus, the user
- * is able to perform IO operations utilizing this phy as long as it
- * is currently part of a valid port.
- * This state is entered from the STARTING state.
- */
- SCI_PHY_READY,
-
- /**
- * This state indicates that the phy is in the process of being reset.
- * In this state no new IO operations are permitted on this phy.
- * This state is entered from the READY state.
- */
- SCI_PHY_RESETTING,
-
- /**
- * Simply the final state for the base phy state machine.
- */
- SCI_PHY_FINAL,
-};
+/**
+ * enum sci_phy_states - phy state machine states
+ * @SCI_PHY_INITIAL: Simply the initial state for the base domain state
+ * machine.
+ * @SCI_PHY_STOPPED: phy has successfully been stopped. In this state
+ * no new IO operations are permitted on this phy.
+ * @SCI_PHY_STARTING: the phy is in the process of becomming ready. In
+ * this state no new IO operations are permitted on
+ * this phy.
+ * @SCI_PHY_SUB_INITIAL: Initial state
+ * @SCI_PHY_SUB_AWAIT_OSSP_EN: Wait state for the hardware OSSP event
+ * type notification
+ * @SCI_PHY_SUB_AWAIT_SAS_SPEED_EN: Wait state for the PHY speed
+ * notification
+ * @SCI_PHY_SUB_AWAIT_IAF_UF: Wait state for the IAF Unsolicited frame
+ * notification
+ * @SCI_PHY_SUB_AWAIT_SAS_POWER: Wait state for the request to consume
+ * power
+ * @SCI_PHY_SUB_AWAIT_SATA_POWER: Wait state for request to consume
+ * power
+ * @SCI_PHY_SUB_AWAIT_SATA_PHY_EN: Wait state for the SATA PHY
+ * notification
+ * @SCI_PHY_SUB_AWAIT_SATA_SPEED_EN: Wait for the SATA PHY speed
+ * notification
+ * @SCI_PHY_SUB_AWAIT_SIG_FIS_UF: Wait state for the SIGNATURE FIS
+ * unsolicited frame notification
+ * @SCI_PHY_SUB_FINAL: Exit state for this state machine
+ * @SCI_PHY_READY: phy is now ready. Thus, the user is able to perform
+ * IO operations utilizing this phy as long as it is
+ * currently part of a valid port. This state is
+ * entered from the STARTING state.
+ * @SCI_PHY_RESETTING: phy is in the process of being reset. In this
+ * state no new IO operations are permitted on this
+ * phy. This state is entered from the READY state.
+ * @SCI_PHY_FINAL: Simply the final state for the base phy state
+ * machine.
+ */
+#define PHY_STATES {\
+ C(PHY_INITIAL),\
+ C(PHY_STOPPED),\
+ C(PHY_STARTING),\
+ C(PHY_SUB_INITIAL),\
+ C(PHY_SUB_AWAIT_OSSP_EN),\
+ C(PHY_SUB_AWAIT_SAS_SPEED_EN),\
+ C(PHY_SUB_AWAIT_IAF_UF),\
+ C(PHY_SUB_AWAIT_SAS_POWER),\
+ C(PHY_SUB_AWAIT_SATA_POWER),\
+ C(PHY_SUB_AWAIT_SATA_PHY_EN),\
+ C(PHY_SUB_AWAIT_SATA_SPEED_EN),\
+ C(PHY_SUB_AWAIT_SIG_FIS_UF),\
+ C(PHY_SUB_FINAL),\
+ C(PHY_READY),\
+ C(PHY_RESETTING),\
+ C(PHY_FINAL),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_phy_states PHY_STATES;
+#undef C
void sci_phy_construct(
struct isci_phy *iphy,
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 7c6ac58a5c4c..5fada73b71ff 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -60,18 +60,29 @@
#define SCIC_SDS_PORT_HARD_RESET_TIMEOUT (1000)
#define SCU_DUMMY_INDEX (0xFFFF)
-static void isci_port_change_state(struct isci_port *iport, enum isci_status status)
+#undef C
+#define C(a) (#a)
+const char *port_state_name(enum sci_port_states state)
{
- unsigned long flags;
+ static const char * const strings[] = PORT_STATES;
+
+ return strings[state];
+}
+#undef C
+
+static struct device *sciport_to_dev(struct isci_port *iport)
+{
+ int i = iport->physical_port_index;
+ struct isci_port *table;
+ struct isci_host *ihost;
+
+ if (i == SCIC_SDS_DUMMY_PORT)
+ i = SCI_MAX_PORTS+1;
- dev_dbg(&iport->isci_host->pdev->dev,
- "%s: iport = %p, state = 0x%x\n",
- __func__, iport, status);
+ table = iport - i;
+ ihost = container_of(table, typeof(*ihost), ports[0]);
- /* XXX pointless lock */
- spin_lock_irqsave(&iport->state_lock, flags);
- iport->status = status;
- spin_unlock_irqrestore(&iport->state_lock, flags);
+ return &ihost->pdev->dev;
}
static void sci_port_get_protocols(struct isci_port *iport, struct sci_phy_proto *proto)
@@ -165,18 +176,12 @@ static void isci_port_link_up(struct isci_host *isci_host,
struct sci_port_properties properties;
unsigned long success = true;
- BUG_ON(iphy->isci_port != NULL);
-
- iphy->isci_port = iport;
-
dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p\n",
__func__, iport);
spin_lock_irqsave(&iphy->sas_phy.frame_rcvd_lock, flags);
- isci_port_change_state(iphy->isci_port, isci_starting);
-
sci_port_get_properties(iport, &properties);
if (iphy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) {
@@ -258,7 +263,6 @@ static void isci_port_link_down(struct isci_host *isci_host,
__func__, isci_device);
set_bit(IDEV_GONE, &isci_device->flags);
}
- isci_port_change_state(isci_port, isci_stopping);
}
}
@@ -269,52 +273,10 @@ static void isci_port_link_down(struct isci_host *isci_host,
isci_host->sas_ha.notify_phy_event(&isci_phy->sas_phy,
PHYE_LOSS_OF_SIGNAL);
- isci_phy->isci_port = NULL;
-
dev_dbg(&isci_host->pdev->dev,
"%s: isci_port = %p - Done\n", __func__, isci_port);
}
-
-/**
- * isci_port_ready() - This function is called by the sci core when a link
- * becomes ready.
- * @isci_host: This parameter specifies the isci host object.
- * @port: This parameter specifies the sci port with the active link.
- *
- */
-static void isci_port_ready(struct isci_host *isci_host, struct isci_port *isci_port)
-{
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_port = %p\n", __func__, isci_port);
-
- complete_all(&isci_port->start_complete);
- isci_port_change_state(isci_port, isci_ready);
- return;
-}
-
-/**
- * isci_port_not_ready() - This function is called by the sci core when a link
- * is not ready. All remote devices on this link will be removed if they are
- * in the stopping state.
- * @isci_host: This parameter specifies the isci host object.
- * @port: This parameter specifies the sci port with the active link.
- *
- */
-static void isci_port_not_ready(struct isci_host *isci_host, struct isci_port *isci_port)
-{
- dev_dbg(&isci_host->pdev->dev,
- "%s: isci_port = %p\n", __func__, isci_port);
-}
-
-static void isci_port_stop_complete(struct isci_host *ihost,
- struct isci_port *iport,
- enum sci_status completion_status)
-{
- dev_dbg(&ihost->pdev->dev, "Port stop complete\n");
-}
-
-
static bool is_port_ready_state(enum sci_port_states state)
{
switch (state) {
@@ -353,7 +315,9 @@ static void port_state_machine_change(struct isci_port *iport,
static void isci_port_hard_reset_complete(struct isci_port *isci_port,
enum sci_status completion_status)
{
- dev_dbg(&isci_port->isci_host->pdev->dev,
+ struct isci_host *ihost = isci_port->owning_controller;
+
+ dev_dbg(&ihost->pdev->dev,
"%s: isci_port = %p, completion_status=%x\n",
__func__, isci_port, completion_status);
@@ -364,23 +328,24 @@ static void isci_port_hard_reset_complete(struct isci_port *isci_port,
/* The reset failed. The port state is now SCI_PORT_FAILED. */
if (isci_port->active_phy_mask == 0) {
+ int phy_idx = isci_port->last_active_phy;
+ struct isci_phy *iphy = &ihost->phys[phy_idx];
/* Generate the link down now to the host, since it
* was intercepted by the hard reset state machine when
* it really happened.
*/
- isci_port_link_down(isci_port->isci_host,
- &isci_port->isci_host->phys[
- isci_port->last_active_phy],
- isci_port);
+ isci_port_link_down(ihost, iphy, isci_port);
}
/* Advance the port state so that link state changes will be
- * noticed.
- */
+ * noticed.
+ */
port_state_machine_change(isci_port, SCI_PORT_SUB_WAITING);
}
- complete_all(&isci_port->hard_reset_complete);
+ clear_bit(IPORT_RESET_PENDING, &isci_port->state);
+ wake_up(&ihost->eventq);
+
}
/* This method will return a true value if the specified phy can be assigned to
@@ -835,10 +800,9 @@ static void port_timeout(unsigned long data)
__func__,
iport);
} else if (current_state == SCI_PORT_STOPPING) {
- /* if the port is still stopping then the stop has not completed */
- isci_port_stop_complete(iport->owning_controller,
- iport,
- SCI_FAILURE_TIMEOUT);
+ dev_dbg(sciport_to_dev(iport),
+ "%s: port%d: stop complete timeout\n",
+ __func__, iport->physical_port_index);
} else {
/* The port is in the ready state and we have a timer
* reporting a timeout this should not happen.
@@ -1003,7 +967,8 @@ static void sci_port_ready_substate_operational_enter(struct sci_base_state_mach
struct isci_port *iport = container_of(sm, typeof(*iport), sm);
struct isci_host *ihost = iport->owning_controller;
- isci_port_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d ready\n",
+ __func__, iport->physical_port_index);
for (index = 0; index < SCI_MAX_PHYS; index++) {
if (iport->phy_table[index]) {
@@ -1069,7 +1034,8 @@ static void sci_port_ready_substate_operational_exit(struct sci_base_state_machi
*/
sci_port_abort_dummy_request(iport);
- isci_port_not_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d !ready\n",
+ __func__, iport->physical_port_index);
if (iport->ready_exit)
sci_port_invalidate_dummy_remote_node(iport);
@@ -1081,7 +1047,8 @@ static void sci_port_ready_substate_configuring_enter(struct sci_base_state_mach
struct isci_host *ihost = iport->owning_controller;
if (iport->active_phy_mask == 0) {
- isci_port_not_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d !ready\n",
+ __func__, iport->physical_port_index);
port_state_machine_change(iport, SCI_PORT_SUB_WAITING);
} else
@@ -1097,8 +1064,8 @@ enum sci_status sci_port_start(struct isci_port *iport)
state = iport->sm.current_state_id;
if (state != SCI_PORT_STOPPED) {
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1172,8 +1139,8 @@ enum sci_status sci_port_stop(struct isci_port *iport)
SCI_PORT_STOPPING);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1187,8 +1154,8 @@ static enum sci_status sci_port_hard_reset(struct isci_port *iport, u32 timeout)
state = iport->sm.current_state_id;
if (state != SCI_PORT_SUB_OPERATIONAL) {
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1282,8 +1249,8 @@ enum sci_status sci_port_add_phy(struct isci_port *iport,
SCI_PORT_SUB_CONFIGURING);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1332,8 +1299,8 @@ enum sci_status sci_port_remove_phy(struct isci_port *iport,
SCI_PORT_SUB_CONFIGURING);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1375,8 +1342,8 @@ enum sci_status sci_port_link_up(struct isci_port *iport,
sci_port_general_link_up_handler(iport, iphy, PF_RESUME);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1405,8 +1372,8 @@ enum sci_status sci_port_link_down(struct isci_port *iport,
sci_port_deactivate_phy(iport, iphy, false);
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1425,8 +1392,8 @@ enum sci_status sci_port_start_io(struct isci_port *iport,
iport->started_request_count++;
return SCI_SUCCESS;
default:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -1440,8 +1407,8 @@ enum sci_status sci_port_complete_io(struct isci_port *iport,
state = iport->sm.current_state_id;
switch (state) {
case SCI_PORT_STOPPED:
- dev_warn(sciport_to_dev(iport),
- "%s: in wrong state: %d\n", __func__, state);
+ dev_warn(sciport_to_dev(iport), "%s: in wrong state: %s\n",
+ __func__, port_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_PORT_STOPPING:
sci_port_decrement_request_count(iport);
@@ -1547,7 +1514,8 @@ static void sci_port_ready_state_enter(struct sci_base_state_machine *sm)
if (prev_state == SCI_PORT_RESETTING)
isci_port_hard_reset_complete(iport, SCI_SUCCESS);
else
- isci_port_not_ready(ihost, iport);
+ dev_dbg(&ihost->pdev->dev, "%s: port%d !ready\n",
+ __func__, iport->physical_port_index);
/* Post and suspend the dummy remote node context for this port. */
sci_port_post_dummy_remote_node(iport);
@@ -1644,22 +1612,7 @@ void isci_port_init(struct isci_port *iport, struct isci_host *ihost, int index)
{
INIT_LIST_HEAD(&iport->remote_dev_list);
INIT_LIST_HEAD(&iport->domain_dev_list);
- spin_lock_init(&iport->state_lock);
- init_completion(&iport->start_complete);
iport->isci_host = ihost;
- isci_port_change_state(iport, isci_freed);
-}
-
-/**
- * isci_port_get_state() - This function gets the status of the port object.
- * @isci_port: This parameter points to the isci_port object
- *
- * status of the object as a isci_status enum.
- */
-enum isci_status isci_port_get_state(
- struct isci_port *isci_port)
-{
- return isci_port->status;
}
void sci_port_broadcast_change_received(struct isci_port *iport, struct isci_phy *iphy)
@@ -1670,6 +1623,11 @@ void sci_port_broadcast_change_received(struct isci_port *iport, struct isci_phy
isci_port_bc_change_received(ihost, iport, iphy);
}
+static void wait_port_reset(struct isci_host *ihost, struct isci_port *iport)
+{
+ wait_event(ihost->eventq, !test_bit(IPORT_RESET_PENDING, &iport->state));
+}
+
int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport,
struct isci_phy *iphy)
{
@@ -1680,9 +1638,8 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
dev_dbg(&ihost->pdev->dev, "%s: iport = %p\n",
__func__, iport);
- init_completion(&iport->hard_reset_complete);
-
spin_lock_irqsave(&ihost->scic_lock, flags);
+ set_bit(IPORT_RESET_PENDING, &iport->state);
#define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT
status = sci_port_hard_reset(iport, ISCI_PORT_RESET_TIMEOUT);
@@ -1690,7 +1647,7 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
spin_unlock_irqrestore(&ihost->scic_lock, flags);
if (status == SCI_SUCCESS) {
- wait_for_completion(&iport->hard_reset_complete);
+ wait_port_reset(ihost, iport);
dev_dbg(&ihost->pdev->dev,
"%s: iport = %p; hard reset completion\n",
@@ -1704,6 +1661,8 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
__func__, iport, iport->hard_reset_status);
}
} else {
+ clear_bit(IPORT_RESET_PENDING, &iport->state);
+ wake_up(&ihost->eventq);
ret = TMF_RESP_FUNC_FAILED;
dev_err(&ihost->pdev->dev,
@@ -1726,24 +1685,80 @@ int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *ipor
return ret;
}
-/**
- * isci_port_deformed() - This function is called by libsas when a port becomes
- * inactive.
- * @phy: This parameter specifies the libsas phy with the inactive port.
- *
- */
+int isci_ata_check_ready(struct domain_device *dev)
+{
+ struct isci_port *iport = dev->port->lldd_port;
+ struct isci_host *ihost = dev_to_ihost(dev);
+ struct isci_remote_device *idev;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ idev = isci_lookup_device(dev);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (!idev)
+ goto out;
+
+ if (test_bit(IPORT_RESET_PENDING, &iport->state))
+ goto out;
+
+ rc = !!iport->active_phy_mask;
+ out:
+ isci_put_device(idev);
+
+ return rc;
+}
+
void isci_port_deformed(struct asd_sas_phy *phy)
{
- pr_debug("%s: sas_phy = %p\n", __func__, phy);
+ struct isci_host *ihost = phy->ha->lldd_ha;
+ struct isci_port *iport = phy->port->lldd_port;
+ unsigned long flags;
+ int i;
+
+ /* we got a port notification on a port that was subsequently
+ * torn down and libsas is just now catching up
+ */
+ if (!iport)
+ return;
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ for (i = 0; i < SCI_MAX_PHYS; i++) {
+ if (iport->active_phy_mask & 1 << i)
+ break;
+ }
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (i >= SCI_MAX_PHYS)
+ dev_dbg(&ihost->pdev->dev, "%s: port: %ld\n",
+ __func__, (long) (iport - &ihost->ports[0]));
}
-/**
- * isci_port_formed() - This function is called by libsas when a port becomes
- * active.
- * @phy: This parameter specifies the libsas phy with the active port.
- *
- */
void isci_port_formed(struct asd_sas_phy *phy)
{
- pr_debug("%s: sas_phy = %p, sas_port = %p\n", __func__, phy, phy->port);
+ struct isci_host *ihost = phy->ha->lldd_ha;
+ struct isci_phy *iphy = to_iphy(phy);
+ struct asd_sas_port *port = phy->port;
+ struct isci_port *iport;
+ unsigned long flags;
+ int i;
+
+ /* initial ports are formed as the driver is still initializing,
+ * wait for that process to complete
+ */
+ wait_for_start(ihost);
+
+ spin_lock_irqsave(&ihost->scic_lock, flags);
+ for (i = 0; i < SCI_MAX_PORTS; i++) {
+ iport = &ihost->ports[i];
+ if (iport->active_phy_mask & 1 << iphy->phy_index)
+ break;
+ }
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+
+ if (i >= SCI_MAX_PORTS)
+ iport = NULL;
+
+ port->lldd_port = iport;
}
diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h
index 08116090eb70..6b56240c2051 100644
--- a/drivers/scsi/isci/port.h
+++ b/drivers/scsi/isci/port.h
@@ -95,14 +95,11 @@ enum isci_status {
* @timer: timeout start/stop operations
*/
struct isci_port {
- enum isci_status status;
struct isci_host *isci_host;
- struct asd_sas_port sas_port;
struct list_head remote_dev_list;
- spinlock_t state_lock;
struct list_head domain_dev_list;
- struct completion start_complete;
- struct completion hard_reset_complete;
+ #define IPORT_RESET_PENDING 0
+ unsigned long state;
enum sci_status hard_reset_status;
struct sci_base_state_machine sm;
bool ready_exit;
@@ -147,70 +144,47 @@ struct sci_port_properties {
};
/**
- * enum sci_port_states - This enumeration depicts all the states for the
- * common port state machine.
- *
- *
+ * enum sci_port_states - port state machine states
+ * @SCI_PORT_STOPPED: port has successfully been stopped. In this state
+ * no new IO operations are permitted. This state is
+ * entered from the STOPPING state.
+ * @SCI_PORT_STOPPING: port is in the process of stopping. In this
+ * state no new IO operations are permitted, but
+ * existing IO operations are allowed to complete.
+ * This state is entered from the READY state.
+ * @SCI_PORT_READY: port is now ready. Thus, the user is able to
+ * perform IO operations on this port. This state is
+ * entered from the STARTING state.
+ * @SCI_PORT_SUB_WAITING: port is started and ready but has no active
+ * phys.
+ * @SCI_PORT_SUB_OPERATIONAL: port is started and ready and there is at
+ * least one phy operational.
+ * @SCI_PORT_SUB_CONFIGURING: port is started and there was an
+ * add/remove phy event. This state is only
+ * used in Automatic Port Configuration Mode
+ * (APC)
+ * @SCI_PORT_RESETTING: port is in the process of performing a hard
+ * reset. Thus, the user is unable to perform IO
+ * operations on this port. This state is entered
+ * from the READY state.
+ * @SCI_PORT_FAILED: port has failed a reset request. This state is
+ * entered when a port reset request times out. This
+ * state is entered from the RESETTING state.
*/
-enum sci_port_states {
- /**
- * This state indicates that the port has successfully been stopped.
- * In this state no new IO operations are permitted.
- * This state is entered from the STOPPING state.
- */
- SCI_PORT_STOPPED,
-
- /**
- * This state indicates that the port is in the process of stopping.
- * In this state no new IO operations are permitted, but existing IO
- * operations are allowed to complete.
- * This state is entered from the READY state.
- */
- SCI_PORT_STOPPING,
-
- /**
- * This state indicates the port is now ready. Thus, the user is
- * able to perform IO operations on this port.
- * This state is entered from the STARTING state.
- */
- SCI_PORT_READY,
-
- /**
- * The substate where the port is started and ready but has no
- * active phys.
- */
- SCI_PORT_SUB_WAITING,
-
- /**
- * The substate where the port is started and ready and there is
- * at least one phy operational.
- */
- SCI_PORT_SUB_OPERATIONAL,
-
- /**
- * The substate where the port is started and there was an
- * add/remove phy event. This state is only used in Automatic
- * Port Configuration Mode (APC)
- */
- SCI_PORT_SUB_CONFIGURING,
-
- /**
- * This state indicates the port is in the process of performing a hard
- * reset. Thus, the user is unable to perform IO operations on this
- * port.
- * This state is entered from the READY state.
- */
- SCI_PORT_RESETTING,
-
- /**
- * This state indicates the port has failed a reset request. This state
- * is entered when a port reset request times out.
- * This state is entered from the RESETTING state.
- */
- SCI_PORT_FAILED,
-
-
-};
+#define PORT_STATES {\
+ C(PORT_STOPPED),\
+ C(PORT_STOPPING),\
+ C(PORT_READY),\
+ C(PORT_SUB_WAITING),\
+ C(PORT_SUB_OPERATIONAL),\
+ C(PORT_SUB_CONFIGURING),\
+ C(PORT_RESETTING),\
+ C(PORT_FAILED),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_port_states PORT_STATES;
+#undef C
static inline void sci_port_decrement_request_count(struct isci_port *iport)
{
@@ -296,9 +270,6 @@ void sci_port_get_attached_sas_address(
struct isci_port *iport,
struct sci_sas_address *sas_address);
-enum isci_status isci_port_get_state(
- struct isci_port *isci_port);
-
void isci_port_formed(struct asd_sas_phy *);
void isci_port_deformed(struct asd_sas_phy *);
@@ -309,4 +280,5 @@ void isci_port_init(
int isci_port_perform_hard_reset(struct isci_host *ihost, struct isci_port *iport,
struct isci_phy *iphy);
+int isci_ata_check_ready(struct domain_device *dev);
#endif /* !defined(_ISCI_PORT_H_) */
diff --git a/drivers/scsi/isci/registers.h b/drivers/scsi/isci/registers.h
index eaa541afc755..7eb0ccd45fe6 100644
--- a/drivers/scsi/isci/registers.h
+++ b/drivers/scsi/isci/registers.h
@@ -370,6 +370,27 @@ struct scu_iit_entry {
>> SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT \
)
+/* ***************************************************************************** */
+#define SMU_CLOCK_GATING_CONTROL_IDLE_ENABLE_SHIFT (0)
+#define SMU_CLOCK_GATING_CONTROL_IDLE_ENABLE_MASK (0x00000001)
+#define SMU_CLOCK_GATING_CONTROL_XCLK_ENABLE_SHIFT (1)
+#define SMU_CLOCK_GATING_CONTROL_XCLK_ENABLE_MASK (0x00000002)
+#define SMU_CLOCK_GATING_CONTROL_TXCLK_ENABLE_SHIFT (2)
+#define SMU_CLOCK_GATING_CONTROL_TXCLK_ENABLE_MASK (0x00000004)
+#define SMU_CLOCK_GATING_CONTROL_REGCLK_ENABLE_SHIFT (3)
+#define SMU_CLOCK_GATING_CONTROL_REGCLK_ENABLE_MASK (0x00000008)
+#define SMU_CLOCK_GATING_CONTROL_IDLE_TIMEOUT_SHIFT (16)
+#define SMU_CLOCK_GATING_CONTROL_IDLE_TIMEOUT_MASK (0x000F0000)
+#define SMU_CLOCK_GATING_CONTROL_FORCE_IDLE_SHIFT (31)
+#define SMU_CLOCK_GATING_CONTROL_FORCE_IDLE_MASK (0x80000000)
+#define SMU_CLOCK_GATING_CONTROL_RESERVED_MASK (0x7FF0FFF0)
+
+#define SMU_CGUCR_GEN_VAL(name, value) \
+ SCU_GEN_VALUE(SMU_CLOCK_GATING_CONTROL_##name, value)
+
+#define SMU_CGUCR_GEN_BIT(name) \
+ SCU_GEN_BIT(SMU_CLOCK_GATING_CONTROL_##name)
+
/* -------------------------------------------------------------------------- */
#define SMU_CONTROL_STATUS_TASK_CONTEXT_RANGE_ENABLE_SHIFT (0)
@@ -992,8 +1013,10 @@ struct smu_registers {
u32 mmr_address_window;
/* 0x00A4 SMDW */
u32 mmr_data_window;
- u32 reserved_A8;
- u32 reserved_AC;
+/* 0x00A8 CGUCR */
+ u32 clock_gating_control;
+/* 0x00AC CGUPC */
+ u32 clock_gating_performance;
/* A whole bunch of reserved space */
u32 reserved_Bx[4];
u32 reserved_Cx[4];
diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c
index dd74b6ceeb82..8f501b0a81d6 100644
--- a/drivers/scsi/isci/remote_device.c
+++ b/drivers/scsi/isci/remote_device.c
@@ -62,6 +62,16 @@
#include "scu_event_codes.h"
#include "task.h"
+#undef C
+#define C(a) (#a)
+const char *dev_state_name(enum sci_remote_device_states state)
+{
+ static const char * const strings[] = REMOTE_DEV_STATES;
+
+ return strings[state];
+}
+#undef C
+
/**
* isci_remote_device_not_ready() - This function is called by the ihost when
* the remote device is not ready. We mark the isci device as ready (not
@@ -167,8 +177,8 @@ enum sci_status sci_remote_device_stop(struct isci_remote_device *idev,
case SCI_DEV_FAILED:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_STOPPED:
return SCI_SUCCESS;
@@ -226,8 +236,8 @@ enum sci_status sci_remote_device_reset(struct isci_remote_device *idev)
case SCI_DEV_RESETTING:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_READY:
case SCI_STP_DEV_IDLE:
@@ -246,8 +256,8 @@ enum sci_status sci_remote_device_reset_complete(struct isci_remote_device *idev
enum sci_remote_device_states state = sm->current_state_id;
if (state != SCI_DEV_RESETTING) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -262,8 +272,8 @@ enum sci_status sci_remote_device_suspend(struct isci_remote_device *idev,
enum sci_remote_device_states state = sm->current_state_id;
if (state != SCI_STP_DEV_CMD) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -287,8 +297,8 @@ enum sci_status sci_remote_device_frame_handler(struct isci_remote_device *idev,
case SCI_SMP_DEV_IDLE:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
/* Return the frame back to the controller */
sci_controller_release_frame(ihost, frame_index);
return SCI_FAILURE_INVALID_STATE;
@@ -502,8 +512,8 @@ enum sci_status sci_remote_device_start_io(struct isci_host *ihost,
case SCI_DEV_RESETTING:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_READY:
/* attempt to start an io request for this device object. The remote
@@ -637,8 +647,8 @@ enum sci_status sci_remote_device_complete_io(struct isci_host *ihost,
case SCI_DEV_FAILED:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_DEV_READY:
case SCI_STP_DEV_AWAIT_RESET:
@@ -721,8 +731,8 @@ enum sci_status sci_remote_device_start_task(struct isci_host *ihost,
case SCI_DEV_RESETTING:
case SCI_DEV_FINAL:
default:
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
case SCI_STP_DEV_IDLE:
case SCI_STP_DEV_CMD:
@@ -853,8 +863,8 @@ static enum sci_status sci_remote_device_destruct(struct isci_remote_device *ide
struct isci_host *ihost;
if (state != SCI_DEV_STOPPED) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1204,8 +1214,8 @@ static enum sci_status sci_remote_device_start(struct isci_remote_device *idev,
enum sci_status status;
if (state != SCI_DEV_STOPPED) {
- dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %d\n",
- __func__, state);
+ dev_warn(scirdev_to_dev(idev), "%s: in wrong state: %s\n",
+ __func__, dev_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1308,7 +1318,6 @@ void isci_remote_device_release(struct kref *kref)
clear_bit(IDEV_STOP_PENDING, &idev->flags);
clear_bit(IDEV_IO_READY, &idev->flags);
clear_bit(IDEV_GONE, &idev->flags);
- clear_bit(IDEV_EH, &idev->flags);
smp_mb__before_clear_bit();
clear_bit(IDEV_ALLOCATED, &idev->flags);
wake_up(&ihost->eventq);
@@ -1381,34 +1390,17 @@ void isci_remote_device_gone(struct domain_device *dev)
*
* status, zero indicates success.
*/
-int isci_remote_device_found(struct domain_device *domain_dev)
+int isci_remote_device_found(struct domain_device *dev)
{
- struct isci_host *isci_host = dev_to_ihost(domain_dev);
- struct isci_port *isci_port;
- struct isci_phy *isci_phy;
- struct asd_sas_port *sas_port;
- struct asd_sas_phy *sas_phy;
+ struct isci_host *isci_host = dev_to_ihost(dev);
+ struct isci_port *isci_port = dev->port->lldd_port;
struct isci_remote_device *isci_device;
enum sci_status status;
dev_dbg(&isci_host->pdev->dev,
- "%s: domain_device = %p\n", __func__, domain_dev);
-
- wait_for_start(isci_host);
-
- sas_port = domain_dev->port;
- sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy,
- port_phy_el);
- isci_phy = to_iphy(sas_phy);
- isci_port = isci_phy->isci_port;
-
- /* we are being called for a device on this port,
- * so it has to come up eventually
- */
- wait_for_completion(&isci_port->start_complete);
+ "%s: domain_device = %p\n", __func__, dev);
- if ((isci_stopping == isci_port_get_state(isci_port)) ||
- (isci_stopped == isci_port_get_state(isci_port)))
+ if (!isci_port)
return -ENODEV;
isci_device = isci_remote_device_alloc(isci_host, isci_port);
@@ -1419,7 +1411,7 @@ int isci_remote_device_found(struct domain_device *domain_dev)
INIT_LIST_HEAD(&isci_device->node);
spin_lock_irq(&isci_host->scic_lock);
- isci_device->domain_dev = domain_dev;
+ isci_device->domain_dev = dev;
isci_device->isci_port = isci_port;
list_add_tail(&isci_device->node, &isci_port->remote_dev_list);
@@ -1432,7 +1424,7 @@ int isci_remote_device_found(struct domain_device *domain_dev)
if (status == SCI_SUCCESS) {
/* device came up, advertise it to the world */
- domain_dev->lldd_dev = isci_device;
+ dev->lldd_dev = isci_device;
} else
isci_put_device(isci_device);
spin_unlock_irq(&isci_host->scic_lock);
diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h
index 483ee50152f3..58637ee08f55 100644
--- a/drivers/scsi/isci/remote_device.h
+++ b/drivers/scsi/isci/remote_device.h
@@ -82,10 +82,9 @@ struct isci_remote_device {
#define IDEV_START_PENDING 0
#define IDEV_STOP_PENDING 1
#define IDEV_ALLOCATED 2
- #define IDEV_EH 3
- #define IDEV_GONE 4
- #define IDEV_IO_READY 5
- #define IDEV_IO_NCQERROR 6
+ #define IDEV_GONE 3
+ #define IDEV_IO_READY 4
+ #define IDEV_IO_NCQERROR 5
unsigned long flags;
struct kref kref;
struct isci_port *isci_port;
@@ -180,122 +179,101 @@ enum sci_status sci_remote_device_reset_complete(
/**
* enum sci_remote_device_states - This enumeration depicts all the states
* for the common remote device state machine.
+ * @SCI_DEV_INITIAL: Simply the initial state for the base remote device
+ * state machine.
*
+ * @SCI_DEV_STOPPED: This state indicates that the remote device has
+ * successfully been stopped. In this state no new IO operations are
+ * permitted. This state is entered from the INITIAL state. This state
+ * is entered from the STOPPING state.
*
+ * @SCI_DEV_STARTING: This state indicates the the remote device is in
+ * the process of becoming ready (i.e. starting). In this state no new
+ * IO operations are permitted. This state is entered from the STOPPED
+ * state.
+ *
+ * @SCI_DEV_READY: This state indicates the remote device is now ready.
+ * Thus, the user is able to perform IO operations on the remote device.
+ * This state is entered from the STARTING state.
+ *
+ * @SCI_STP_DEV_IDLE: This is the idle substate for the stp remote
+ * device. When there are no active IO for the device it is is in this
+ * state.
+ *
+ * @SCI_STP_DEV_CMD: This is the command state for for the STP remote
+ * device. This state is entered when the device is processing a
+ * non-NCQ command. The device object will fail any new start IO
+ * requests until this command is complete.
+ *
+ * @SCI_STP_DEV_NCQ: This is the NCQ state for the STP remote device.
+ * This state is entered when the device is processing an NCQ reuqest.
+ * It will remain in this state so long as there is one or more NCQ
+ * requests being processed.
+ *
+ * @SCI_STP_DEV_NCQ_ERROR: This is the NCQ error state for the STP
+ * remote device. This state is entered when an SDB error FIS is
+ * received by the device object while in the NCQ state. The device
+ * object will only accept a READ LOG command while in this state.
+ *
+ * @SCI_STP_DEV_ATAPI_ERROR: This is the ATAPI error state for the STP
+ * ATAPI remote device. This state is entered when ATAPI device sends
+ * error status FIS without data while the device object is in CMD
+ * state. A suspension event is expected in this state. The device
+ * object will resume right away.
+ *
+ * @SCI_STP_DEV_AWAIT_RESET: This is the READY substate indicates the
+ * device is waiting for the RESET task coming to be recovered from
+ * certain hardware specific error.
+ *
+ * @SCI_SMP_DEV_IDLE: This is the ready operational substate for the
+ * remote device. This is the normal operational state for a remote
+ * device.
+ *
+ * @SCI_SMP_DEV_CMD: This is the suspended state for the remote device.
+ * This is the state that the device is placed in when a RNC suspend is
+ * received by the SCU hardware.
+ *
+ * @SCI_DEV_STOPPING: This state indicates that the remote device is in
+ * the process of stopping. In this state no new IO operations are
+ * permitted, but existing IO operations are allowed to complete. This
+ * state is entered from the READY state. This state is entered from
+ * the FAILED state.
+ *
+ * @SCI_DEV_FAILED: This state indicates that the remote device has
+ * failed. In this state no new IO operations are permitted. This
+ * state is entered from the INITIALIZING state. This state is entered
+ * from the READY state.
+ *
+ * @SCI_DEV_RESETTING: This state indicates the device is being reset.
+ * In this state no new IO operations are permitted. This state is
+ * entered from the READY state.
+ *
+ * @SCI_DEV_FINAL: Simply the final state for the base remote device
+ * state machine.
*/
-enum sci_remote_device_states {
- /**
- * Simply the initial state for the base remote device state machine.
- */
- SCI_DEV_INITIAL,
-
- /**
- * This state indicates that the remote device has successfully been
- * stopped. In this state no new IO operations are permitted.
- * This state is entered from the INITIAL state.
- * This state is entered from the STOPPING state.
- */
- SCI_DEV_STOPPED,
-
- /**
- * This state indicates the the remote device is in the process of
- * becoming ready (i.e. starting). In this state no new IO operations
- * are permitted.
- * This state is entered from the STOPPED state.
- */
- SCI_DEV_STARTING,
-
- /**
- * This state indicates the remote device is now ready. Thus, the user
- * is able to perform IO operations on the remote device.
- * This state is entered from the STARTING state.
- */
- SCI_DEV_READY,
-
- /**
- * This is the idle substate for the stp remote device. When there are no
- * active IO for the device it is is in this state.
- */
- SCI_STP_DEV_IDLE,
-
- /**
- * This is the command state for for the STP remote device. This state is
- * entered when the device is processing a non-NCQ command. The device object
- * will fail any new start IO requests until this command is complete.
- */
- SCI_STP_DEV_CMD,
-
- /**
- * This is the NCQ state for the STP remote device. This state is entered
- * when the device is processing an NCQ reuqest. It will remain in this state
- * so long as there is one or more NCQ requests being processed.
- */
- SCI_STP_DEV_NCQ,
-
- /**
- * This is the NCQ error state for the STP remote device. This state is
- * entered when an SDB error FIS is received by the device object while in the
- * NCQ state. The device object will only accept a READ LOG command while in
- * this state.
- */
- SCI_STP_DEV_NCQ_ERROR,
-
- /**
- * This is the ATAPI error state for the STP ATAPI remote device.
- * This state is entered when ATAPI device sends error status FIS
- * without data while the device object is in CMD state.
- * A suspension event is expected in this state.
- * The device object will resume right away.
- */
- SCI_STP_DEV_ATAPI_ERROR,
-
- /**
- * This is the READY substate indicates the device is waiting for the RESET task
- * coming to be recovered from certain hardware specific error.
- */
- SCI_STP_DEV_AWAIT_RESET,
-
- /**
- * This is the ready operational substate for the remote device. This is the
- * normal operational state for a remote device.
- */
- SCI_SMP_DEV_IDLE,
-
- /**
- * This is the suspended state for the remote device. This is the state that
- * the device is placed in when a RNC suspend is received by the SCU hardware.
- */
- SCI_SMP_DEV_CMD,
-
- /**
- * This state indicates that the remote device is in the process of
- * stopping. In this state no new IO operations are permitted, but
- * existing IO operations are allowed to complete.
- * This state is entered from the READY state.
- * This state is entered from the FAILED state.
- */
- SCI_DEV_STOPPING,
-
- /**
- * This state indicates that the remote device has failed.
- * In this state no new IO operations are permitted.
- * This state is entered from the INITIALIZING state.
- * This state is entered from the READY state.
- */
- SCI_DEV_FAILED,
-
- /**
- * This state indicates the device is being reset.
- * In this state no new IO operations are permitted.
- * This state is entered from the READY state.
- */
- SCI_DEV_RESETTING,
-
- /**
- * Simply the final state for the base remote device state machine.
- */
- SCI_DEV_FINAL,
-};
+#define REMOTE_DEV_STATES {\
+ C(DEV_INITIAL),\
+ C(DEV_STOPPED),\
+ C(DEV_STARTING),\
+ C(DEV_READY),\
+ C(STP_DEV_IDLE),\
+ C(STP_DEV_CMD),\
+ C(STP_DEV_NCQ),\
+ C(STP_DEV_NCQ_ERROR),\
+ C(STP_DEV_ATAPI_ERROR),\
+ C(STP_DEV_AWAIT_RESET),\
+ C(SMP_DEV_IDLE),\
+ C(SMP_DEV_CMD),\
+ C(DEV_STOPPING),\
+ C(DEV_FAILED),\
+ C(DEV_RESETTING),\
+ C(DEV_FINAL),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_remote_device_states REMOTE_DEV_STATES;
+#undef C
+const char *dev_state_name(enum sci_remote_device_states state);
static inline struct isci_remote_device *rnc_to_dev(struct sci_remote_node_context *rnc)
{
diff --git a/drivers/scsi/isci/remote_node_context.c b/drivers/scsi/isci/remote_node_context.c
index 748e8339d1ec..3a9463481f38 100644
--- a/drivers/scsi/isci/remote_node_context.c
+++ b/drivers/scsi/isci/remote_node_context.c
@@ -60,18 +60,15 @@
#include "scu_event_codes.h"
#include "scu_task_context.h"
+#undef C
+#define C(a) (#a)
+const char *rnc_state_name(enum scis_sds_remote_node_context_states state)
+{
+ static const char * const strings[] = RNC_STATES;
-/**
- *
- * @sci_rnc: The RNC for which the is posted request is being made.
- *
- * This method will return true if the RNC is not in the initial state. In all
- * other states the RNC is considered active and this will return true. The
- * destroy request of the state machine drives the RNC back to the initial
- * state. If the state machine changes then this routine will also have to be
- * changed. bool true if the state machine is not in the initial state false if
- * the state machine is in the initial state
- */
+ return strings[state];
+}
+#undef C
/**
*
diff --git a/drivers/scsi/isci/remote_node_context.h b/drivers/scsi/isci/remote_node_context.h
index 41580ad12520..a241e0f4c865 100644
--- a/drivers/scsi/isci/remote_node_context.h
+++ b/drivers/scsi/isci/remote_node_context.h
@@ -85,61 +85,50 @@ struct sci_remote_node_context;
typedef void (*scics_sds_remote_node_context_callback)(void *);
/**
- * This is the enumeration of the remote node context states.
+ * enum sci_remote_node_context_states
+ * @SCI_RNC_INITIAL initial state for a remote node context. On a resume
+ * request the remote node context will transition to the posting state.
+ *
+ * @SCI_RNC_POSTING: transition state that posts the RNi to the hardware. Once
+ * the RNC is posted the remote node context will be made ready.
+ *
+ * @SCI_RNC_INVALIDATING: transition state that will post an RNC invalidate to
+ * the hardware. Once the invalidate is complete the remote node context will
+ * transition to the posting state.
+ *
+ * @SCI_RNC_RESUMING: transition state that will post an RNC resume to the
+ * hardare. Once the event notification of resume complete is received the
+ * remote node context will transition to the ready state.
+ *
+ * @SCI_RNC_READY: state that the remote node context must be in to accept io
+ * request operations.
+ *
+ * @SCI_RNC_TX_SUSPENDED: state that the remote node context transitions to when
+ * it gets a TX suspend notification from the hardware.
+ *
+ * @SCI_RNC_TX_RX_SUSPENDED: state that the remote node context transitions to
+ * when it gets a TX RX suspend notification from the hardware.
+ *
+ * @SCI_RNC_AWAIT_SUSPENSION: wait state for the remote node context that waits
+ * for a suspend notification from the hardware. This state is entered when
+ * either there is a request to supend the remote node context or when there is
+ * a TC completion where the remote node will be suspended by the hardware.
*/
-enum scis_sds_remote_node_context_states {
- /**
- * This state is the initial state for a remote node context. On a resume
- * request the remote node context will transition to the posting state.
- */
- SCI_RNC_INITIAL,
-
- /**
- * This is a transition state that posts the RNi to the hardware. Once the RNC
- * is posted the remote node context will be made ready.
- */
- SCI_RNC_POSTING,
-
- /**
- * This is a transition state that will post an RNC invalidate to the
- * hardware. Once the invalidate is complete the remote node context will
- * transition to the posting state.
- */
- SCI_RNC_INVALIDATING,
-
- /**
- * This is a transition state that will post an RNC resume to the hardare.
- * Once the event notification of resume complete is received the remote node
- * context will transition to the ready state.
- */
- SCI_RNC_RESUMING,
-
- /**
- * This is the state that the remote node context must be in to accept io
- * request operations.
- */
- SCI_RNC_READY,
-
- /**
- * This is the state that the remote node context transitions to when it gets
- * a TX suspend notification from the hardware.
- */
- SCI_RNC_TX_SUSPENDED,
-
- /**
- * This is the state that the remote node context transitions to when it gets
- * a TX RX suspend notification from the hardware.
- */
- SCI_RNC_TX_RX_SUSPENDED,
-
- /**
- * This state is a wait state for the remote node context that waits for a
- * suspend notification from the hardware. This state is entered when either
- * there is a request to supend the remote node context or when there is a TC
- * completion where the remote node will be suspended by the hardware.
- */
- SCI_RNC_AWAIT_SUSPENSION
-};
+#define RNC_STATES {\
+ C(RNC_INITIAL),\
+ C(RNC_POSTING),\
+ C(RNC_INVALIDATING),\
+ C(RNC_RESUMING),\
+ C(RNC_READY),\
+ C(RNC_TX_SUSPENDED),\
+ C(RNC_TX_RX_SUSPENDED),\
+ C(RNC_AWAIT_SUSPENSION),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum scis_sds_remote_node_context_states RNC_STATES;
+#undef C
+const char *rnc_state_name(enum scis_sds_remote_node_context_states state);
/**
*
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index ee0dc05c6269..2def1e3960f6 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -53,6 +53,7 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include <scsi/scsi_cmnd.h>
#include "isci.h"
#include "task.h"
#include "request.h"
@@ -60,6 +61,16 @@
#include "scu_event_codes.h"
#include "sas.h"
+#undef C
+#define C(a) (#a)
+const char *req_state_name(enum sci_base_request_states state)
+{
+ static const char * const strings[] = REQUEST_STATES;
+
+ return strings[state];
+}
+#undef C
+
static struct scu_sgl_element_pair *to_sgl_element_pair(struct isci_request *ireq,
int idx)
{
@@ -264,6 +275,141 @@ static void scu_ssp_reqeust_construct_task_context(
task_context->response_iu_lower = lower_32_bits(dma_addr);
}
+static u8 scu_bg_blk_size(struct scsi_device *sdp)
+{
+ switch (sdp->sector_size) {
+ case 512:
+ return 0;
+ case 1024:
+ return 1;
+ case 4096:
+ return 3;
+ default:
+ return 0xff;
+ }
+}
+
+static u32 scu_dif_bytes(u32 len, u32 sector_size)
+{
+ return (len >> ilog2(sector_size)) * 8;
+}
+
+static void scu_ssp_ireq_dif_insert(struct isci_request *ireq, u8 type, u8 op)
+{
+ struct scu_task_context *tc = ireq->tc;
+ struct scsi_cmnd *scmd = ireq->ttype_ptr.io_task_ptr->uldd_task;
+ u8 blk_sz = scu_bg_blk_size(scmd->device);
+
+ tc->block_guard_enable = 1;
+ tc->blk_prot_en = 1;
+ tc->blk_sz = blk_sz;
+ /* DIF write insert */
+ tc->blk_prot_func = 0x2;
+
+ tc->transfer_length_bytes += scu_dif_bytes(tc->transfer_length_bytes,
+ scmd->device->sector_size);
+
+ /* always init to 0, used by hw */
+ tc->interm_crc_val = 0;
+
+ tc->init_crc_seed = 0;
+ tc->app_tag_verify = 0;
+ tc->app_tag_gen = 0;
+ tc->ref_tag_seed_verify = 0;
+
+ /* always init to same as bg_blk_sz */
+ tc->UD_bytes_immed_val = scmd->device->sector_size;
+
+ tc->reserved_DC_0 = 0;
+
+ /* always init to 8 */
+ tc->DIF_bytes_immed_val = 8;
+
+ tc->reserved_DC_1 = 0;
+ tc->bgc_blk_sz = scmd->device->sector_size;
+ tc->reserved_E0_0 = 0;
+ tc->app_tag_gen_mask = 0;
+
+ /** setup block guard control **/
+ tc->bgctl = 0;
+
+ /* DIF write insert */
+ tc->bgctl_f.op = 0x2;
+
+ tc->app_tag_verify_mask = 0;
+
+ /* must init to 0 for hw */
+ tc->blk_guard_err = 0;
+
+ tc->reserved_E8_0 = 0;
+
+ if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2))
+ tc->ref_tag_seed_gen = scsi_get_lba(scmd) & 0xffffffff;
+ else if (type & SCSI_PROT_DIF_TYPE3)
+ tc->ref_tag_seed_gen = 0;
+}
+
+static void scu_ssp_ireq_dif_strip(struct isci_request *ireq, u8 type, u8 op)
+{
+ struct scu_task_context *tc = ireq->tc;
+ struct scsi_cmnd *scmd = ireq->ttype_ptr.io_task_ptr->uldd_task;
+ u8 blk_sz = scu_bg_blk_size(scmd->device);
+
+ tc->block_guard_enable = 1;
+ tc->blk_prot_en = 1;
+ tc->blk_sz = blk_sz;
+ /* DIF read strip */
+ tc->blk_prot_func = 0x1;
+
+ tc->transfer_length_bytes += scu_dif_bytes(tc->transfer_length_bytes,
+ scmd->device->sector_size);
+
+ /* always init to 0, used by hw */
+ tc->interm_crc_val = 0;
+
+ tc->init_crc_seed = 0;
+ tc->app_tag_verify = 0;
+ tc->app_tag_gen = 0;
+
+ if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2))
+ tc->ref_tag_seed_verify = scsi_get_lba(scmd) & 0xffffffff;
+ else if (type & SCSI_PROT_DIF_TYPE3)
+ tc->ref_tag_seed_verify = 0;
+
+ /* always init to same as bg_blk_sz */
+ tc->UD_bytes_immed_val = scmd->device->sector_size;
+
+ tc->reserved_DC_0 = 0;
+
+ /* always init to 8 */
+ tc->DIF_bytes_immed_val = 8;
+
+ tc->reserved_DC_1 = 0;
+ tc->bgc_blk_sz = scmd->device->sector_size;
+ tc->reserved_E0_0 = 0;
+ tc->app_tag_gen_mask = 0;
+
+ /** setup block guard control **/
+ tc->bgctl = 0;
+
+ /* DIF read strip */
+ tc->bgctl_f.crc_verify = 1;
+ tc->bgctl_f.op = 0x1;
+ if ((type & SCSI_PROT_DIF_TYPE1) || (type & SCSI_PROT_DIF_TYPE2)) {
+ tc->bgctl_f.ref_tag_chk = 1;
+ tc->bgctl_f.app_f_detect = 1;
+ } else if (type & SCSI_PROT_DIF_TYPE3)
+ tc->bgctl_f.app_ref_f_detect = 1;
+
+ tc->app_tag_verify_mask = 0;
+
+ /* must init to 0 for hw */
+ tc->blk_guard_err = 0;
+
+ tc->reserved_E8_0 = 0;
+ tc->ref_tag_seed_gen = 0;
+}
+
/**
* This method is will fill in the SCU Task Context for a SSP IO request.
* @sci_req:
@@ -274,6 +420,10 @@ static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq,
u32 len)
{
struct scu_task_context *task_context = ireq->tc;
+ struct sas_task *sas_task = ireq->ttype_ptr.io_task_ptr;
+ struct scsi_cmnd *scmd = sas_task->uldd_task;
+ u8 prot_type = scsi_get_prot_type(scmd);
+ u8 prot_op = scsi_get_prot_op(scmd);
scu_ssp_reqeust_construct_task_context(ireq, task_context);
@@ -296,6 +446,13 @@ static void scu_ssp_io_request_construct_task_context(struct isci_request *ireq,
if (task_context->transfer_length_bytes > 0)
sci_request_build_sgl(ireq);
+
+ if (prot_type != SCSI_PROT_DIF_TYPE0) {
+ if (prot_op == SCSI_PROT_READ_STRIP)
+ scu_ssp_ireq_dif_strip(ireq, prot_type, prot_op);
+ else if (prot_op == SCSI_PROT_WRITE_INSERT)
+ scu_ssp_ireq_dif_insert(ireq, prot_type, prot_op);
+ }
}
/**
@@ -519,18 +676,12 @@ sci_io_request_construct_sata(struct isci_request *ireq,
if (test_bit(IREQ_TMF, &ireq->flags)) {
struct isci_tmf *tmf = isci_request_access_tmf(ireq);
- if (tmf->tmf_code == isci_tmf_sata_srst_high ||
- tmf->tmf_code == isci_tmf_sata_srst_low) {
- scu_stp_raw_request_construct_task_context(ireq);
- return SCI_SUCCESS;
- } else {
- dev_err(&ireq->owning_controller->pdev->dev,
- "%s: Request 0x%p received un-handled SAT "
- "management protocol 0x%x.\n",
- __func__, ireq, tmf->tmf_code);
+ dev_err(&ireq->owning_controller->pdev->dev,
+ "%s: Request 0x%p received un-handled SAT "
+ "management protocol 0x%x.\n",
+ __func__, ireq, tmf->tmf_code);
- return SCI_FAILURE;
- }
+ return SCI_FAILURE;
}
if (!sas_protocol_ata(task->task_proto)) {
@@ -627,34 +778,6 @@ static enum sci_status sci_io_request_construct_basic_sata(struct isci_request *
return status;
}
-enum sci_status sci_task_request_construct_sata(struct isci_request *ireq)
-{
- enum sci_status status = SCI_SUCCESS;
-
- /* check for management protocols */
- if (test_bit(IREQ_TMF, &ireq->flags)) {
- struct isci_tmf *tmf = isci_request_access_tmf(ireq);
-
- if (tmf->tmf_code == isci_tmf_sata_srst_high ||
- tmf->tmf_code == isci_tmf_sata_srst_low) {
- scu_stp_raw_request_construct_task_context(ireq);
- } else {
- dev_err(&ireq->owning_controller->pdev->dev,
- "%s: Request 0x%p received un-handled SAT "
- "Protocol 0x%x.\n",
- __func__, ireq, tmf->tmf_code);
-
- return SCI_FAILURE;
- }
- }
-
- if (status != SCI_SUCCESS)
- return status;
- sci_change_state(&ireq->sm, SCI_REQ_CONSTRUCTED);
-
- return status;
-}
-
/**
* sci_req_tx_bytes - bytes transferred when reply underruns request
* @ireq: request that was terminated early
@@ -756,9 +879,6 @@ sci_io_request_terminate(struct isci_request *ireq)
case SCI_REQ_STP_PIO_WAIT_FRAME:
case SCI_REQ_STP_PIO_DATA_IN:
case SCI_REQ_STP_PIO_DATA_OUT:
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED:
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG:
- case SCI_REQ_STP_SOFT_RESET_WAIT_D2H:
case SCI_REQ_ATAPI_WAIT_H2D:
case SCI_REQ_ATAPI_WAIT_PIO_SETUP:
case SCI_REQ_ATAPI_WAIT_D2H:
@@ -800,7 +920,8 @@ enum sci_status sci_request_complete(struct isci_request *ireq)
state = ireq->sm.current_state_id;
if (WARN_ONCE(state != SCI_REQ_COMPLETED,
- "isci: request completion from wrong state (%d)\n", state))
+ "isci: request completion from wrong state (%s)\n",
+ req_state_name(state)))
return SCI_FAILURE_INVALID_STATE;
if (ireq->saved_rx_frame_index != SCU_INVALID_FRAME_INDEX)
@@ -821,8 +942,8 @@ enum sci_status sci_io_request_event_handler(struct isci_request *ireq,
state = ireq->sm.current_state_id;
if (state != SCI_REQ_STP_PIO_DATA_IN) {
- dev_warn(&ihost->pdev->dev, "%s: (%x) in wrong state %d\n",
- __func__, event_code, state);
+ dev_warn(&ihost->pdev->dev, "%s: (%x) in wrong state %s\n",
+ __func__, event_code, req_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
@@ -1938,59 +2059,6 @@ sci_io_request_frame_handler(struct isci_request *ireq,
return status;
}
- case SCI_REQ_STP_SOFT_RESET_WAIT_D2H: {
- struct dev_to_host_fis *frame_header;
- u32 *frame_buffer;
-
- status = sci_unsolicited_frame_control_get_header(&ihost->uf_control,
- frame_index,
- (void **)&frame_header);
- if (status != SCI_SUCCESS) {
- dev_err(&ihost->pdev->dev,
- "%s: SCIC IO Request 0x%p could not get frame "
- "header for frame index %d, status %x\n",
- __func__,
- stp_req,
- frame_index,
- status);
- return status;
- }
-
- switch (frame_header->fis_type) {
- case FIS_REGD2H:
- sci_unsolicited_frame_control_get_buffer(&ihost->uf_control,
- frame_index,
- (void **)&frame_buffer);
-
- sci_controller_copy_sata_response(&ireq->stp.rsp,
- frame_header,
- frame_buffer);
-
- /* The command has completed with error */
- ireq->scu_status = SCU_TASK_DONE_CHECK_RESPONSE;
- ireq->sci_status = SCI_FAILURE_IO_RESPONSE_VALID;
- break;
-
- default:
- dev_warn(&ihost->pdev->dev,
- "%s: IO Request:0x%p Frame Id:%d protocol "
- "violation occurred\n",
- __func__,
- stp_req,
- frame_index);
-
- ireq->scu_status = SCU_TASK_DONE_UNEXP_FIS;
- ireq->sci_status = SCI_FAILURE_PROTOCOL_VIOLATION;
- break;
- }
-
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
-
- /* Frame has been decoded return it to the controller */
- sci_controller_release_frame(ihost, frame_index);
-
- return status;
- }
case SCI_REQ_ATAPI_WAIT_PIO_SETUP: {
struct sas_task *task = isci_request_access_task(ireq);
@@ -2088,57 +2156,6 @@ static enum sci_status stp_request_udma_await_tc_event(struct isci_request *ireq
return status;
}
-static enum sci_status
-stp_request_soft_reset_await_h2d_asserted_tc_event(struct isci_request *ireq,
- u32 completion_code)
-{
- switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) {
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD):
- ireq->scu_status = SCU_TASK_DONE_GOOD;
- ireq->sci_status = SCI_SUCCESS;
- sci_change_state(&ireq->sm, SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG);
- break;
-
- default:
- /*
- * All other completion status cause the IO to be complete.
- * If a NAK was received, then it is up to the user to retry
- * the request.
- */
- ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
- ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR;
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
- break;
- }
-
- return SCI_SUCCESS;
-}
-
-static enum sci_status
-stp_request_soft_reset_await_h2d_diagnostic_tc_event(struct isci_request *ireq,
- u32 completion_code)
-{
- switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) {
- case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD):
- ireq->scu_status = SCU_TASK_DONE_GOOD;
- ireq->sci_status = SCI_SUCCESS;
- sci_change_state(&ireq->sm, SCI_REQ_STP_SOFT_RESET_WAIT_D2H);
- break;
-
- default:
- /* All other completion status cause the IO to be complete. If
- * a NAK was received, then it is up to the user to retry the
- * request.
- */
- ireq->scu_status = SCU_NORMALIZE_COMPLETION_STATUS(completion_code);
- ireq->sci_status = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR;
- sci_change_state(&ireq->sm, SCI_REQ_COMPLETED);
- break;
- }
-
- return SCI_SUCCESS;
-}
-
static enum sci_status atapi_raw_completion(struct isci_request *ireq, u32 completion_code,
enum sci_base_request_states next)
{
@@ -2284,14 +2301,6 @@ sci_io_request_tc_completion(struct isci_request *ireq,
case SCI_REQ_STP_PIO_DATA_OUT:
return pio_data_out_tx_done_tc_event(ireq, completion_code);
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED:
- return stp_request_soft_reset_await_h2d_asserted_tc_event(ireq,
- completion_code);
-
- case SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG:
- return stp_request_soft_reset_await_h2d_diagnostic_tc_event(ireq,
- completion_code);
-
case SCI_REQ_ABORTING:
return request_aborting_state_tc_event(ireq,
completion_code);
@@ -2308,12 +2317,8 @@ sci_io_request_tc_completion(struct isci_request *ireq,
return atapi_data_tc_completion_handler(ireq, completion_code);
default:
- dev_warn(&ihost->pdev->dev,
- "%s: SCIC IO Request given task completion "
- "notification %x while in wrong state %d\n",
- __func__,
- completion_code,
- state);
+ dev_warn(&ihost->pdev->dev, "%s: %x in wrong state %s\n",
+ __func__, completion_code, req_state_name(state));
return SCI_FAILURE_INVALID_STATE;
}
}
@@ -3065,10 +3070,6 @@ static void sci_request_started_state_enter(struct sci_base_state_machine *sm)
*/
if (!task && dev->dev_type == SAS_END_DEV) {
state = SCI_REQ_TASK_WAIT_TC_COMP;
- } else if (!task &&
- (isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_high ||
- isci_request_access_tmf(ireq)->tmf_code == isci_tmf_sata_srst_low)) {
- state = SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED;
} else if (task && task->task_proto == SAS_PROTOCOL_SMP) {
state = SCI_REQ_SMP_WAIT_RESP;
} else if (task && sas_protocol_ata(task->task_proto) &&
@@ -3125,31 +3126,6 @@ static void sci_stp_request_started_pio_await_h2d_completion_enter(struct sci_ba
ireq->target_device->working_request = ireq;
}
-static void sci_stp_request_started_soft_reset_await_h2d_asserted_completion_enter(struct sci_base_state_machine *sm)
-{
- struct isci_request *ireq = container_of(sm, typeof(*ireq), sm);
-
- ireq->target_device->working_request = ireq;
-}
-
-static void sci_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter(struct sci_base_state_machine *sm)
-{
- struct isci_request *ireq = container_of(sm, typeof(*ireq), sm);
- struct scu_task_context *tc = ireq->tc;
- struct host_to_dev_fis *h2d_fis;
- enum sci_status status;
-
- /* Clear the SRST bit */
- h2d_fis = &ireq->stp.cmd;
- h2d_fis->control = 0;
-
- /* Clear the TC control bit */
- tc->control_frame = 0;
-
- status = sci_controller_continue_io(ireq);
- WARN_ONCE(status != SCI_SUCCESS, "isci: continue io failure\n");
-}
-
static const struct sci_base_state sci_request_state_table[] = {
[SCI_REQ_INIT] = { },
[SCI_REQ_CONSTRUCTED] = { },
@@ -3168,13 +3144,6 @@ static const struct sci_base_state sci_request_state_table[] = {
[SCI_REQ_STP_PIO_DATA_OUT] = { },
[SCI_REQ_STP_UDMA_WAIT_TC_COMP] = { },
[SCI_REQ_STP_UDMA_WAIT_D2H] = { },
- [SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED] = {
- .enter_state = sci_stp_request_started_soft_reset_await_h2d_asserted_completion_enter,
- },
- [SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG] = {
- .enter_state = sci_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter,
- },
- [SCI_REQ_STP_SOFT_RESET_WAIT_D2H] = { },
[SCI_REQ_TASK_WAIT_TC_COMP] = { },
[SCI_REQ_TASK_WAIT_TC_RESP] = { },
[SCI_REQ_SMP_WAIT_RESP] = { },
@@ -3649,8 +3618,7 @@ int isci_request_execute(struct isci_host *ihost, struct isci_remote_device *ide
/* Cause this task to be scheduled in the SCSI error
* handler thread.
*/
- isci_execpath_callback(ihost, task,
- sas_task_abort);
+ sas_task_abort(task);
/* Change the status, since we are holding
* the I/O until it is managed by the SCSI
diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h
index be38933dd6df..057f2378452d 100644
--- a/drivers/scsi/isci/request.h
+++ b/drivers/scsi/isci/request.h
@@ -182,138 +182,103 @@ static inline struct isci_request *to_ireq(struct isci_stp_request *stp_req)
}
/**
- * enum sci_base_request_states - This enumeration depicts all the states for
- * the common request state machine.
+ * enum sci_base_request_states - request state machine states
*
+ * @SCI_REQ_INIT: Simply the initial state for the base request state machine.
*
+ * @SCI_REQ_CONSTRUCTED: This state indicates that the request has been
+ * constructed. This state is entered from the INITIAL state.
+ *
+ * @SCI_REQ_STARTED: This state indicates that the request has been started.
+ * This state is entered from the CONSTRUCTED state.
+ *
+ * @SCI_REQ_STP_UDMA_WAIT_TC_COMP:
+ * @SCI_REQ_STP_UDMA_WAIT_D2H:
+ * @SCI_REQ_STP_NON_DATA_WAIT_H2D:
+ * @SCI_REQ_STP_NON_DATA_WAIT_D2H:
+ *
+ * @SCI_REQ_STP_PIO_WAIT_H2D: While in this state the IO request object is
+ * waiting for the TC completion notification for the H2D Register FIS
+ *
+ * @SCI_REQ_STP_PIO_WAIT_FRAME: While in this state the IO request object is
+ * waiting for either a PIO Setup FIS or a D2H register FIS. The type of frame
+ * received is based on the result of the prior frame and line conditions.
+ *
+ * @SCI_REQ_STP_PIO_DATA_IN: While in this state the IO request object is
+ * waiting for a DATA frame from the device.
+ *
+ * @SCI_REQ_STP_PIO_DATA_OUT: While in this state the IO request object is
+ * waiting to transmit the next data frame to the device.
+ *
+ * @SCI_REQ_ATAPI_WAIT_H2D: While in this state the IO request object is
+ * waiting for the TC completion notification for the H2D Register FIS
+ *
+ * @SCI_REQ_ATAPI_WAIT_PIO_SETUP: While in this state the IO request object is
+ * waiting for either a PIO Setup.
+ *
+ * @SCI_REQ_ATAPI_WAIT_D2H: The non-data IO transit to this state in this state
+ * after receiving TC completion. While in this state IO request object is
+ * waiting for D2H status frame as UF.
+ *
+ * @SCI_REQ_ATAPI_WAIT_TC_COMP: When transmitting raw frames hardware reports
+ * task context completion after every frame submission, so in the
+ * non-accelerated case we need to expect the completion for the "cdb" frame.
+ *
+ * @SCI_REQ_TASK_WAIT_TC_COMP: The AWAIT_TC_COMPLETION sub-state indicates that
+ * the started raw task management request is waiting for the transmission of
+ * the initial frame (i.e. command, task, etc.).
+ *
+ * @SCI_REQ_TASK_WAIT_TC_RESP: This sub-state indicates that the started task
+ * management request is waiting for the reception of an unsolicited frame
+ * (i.e. response IU).
+ *
+ * @SCI_REQ_SMP_WAIT_RESP: This sub-state indicates that the started task
+ * management request is waiting for the reception of an unsolicited frame
+ * (i.e. response IU).
+ *
+ * @SCI_REQ_SMP_WAIT_TC_COMP: The AWAIT_TC_COMPLETION sub-state indicates that
+ * the started SMP request is waiting for the transmission of the initial frame
+ * (i.e. command, task, etc.).
+ *
+ * @SCI_REQ_COMPLETED: This state indicates that the request has completed.
+ * This state is entered from the STARTED state. This state is entered from the
+ * ABORTING state.
+ *
+ * @SCI_REQ_ABORTING: This state indicates that the request is in the process
+ * of being terminated/aborted. This state is entered from the CONSTRUCTED
+ * state. This state is entered from the STARTED state.
+ *
+ * @SCI_REQ_FINAL: Simply the final state for the base request state machine.
*/
-enum sci_base_request_states {
- /*
- * Simply the initial state for the base request state machine.
- */
- SCI_REQ_INIT,
-
- /*
- * This state indicates that the request has been constructed.
- * This state is entered from the INITIAL state.
- */
- SCI_REQ_CONSTRUCTED,
-
- /*
- * This state indicates that the request has been started. This state
- * is entered from the CONSTRUCTED state.
- */
- SCI_REQ_STARTED,
-
- SCI_REQ_STP_UDMA_WAIT_TC_COMP,
- SCI_REQ_STP_UDMA_WAIT_D2H,
-
- SCI_REQ_STP_NON_DATA_WAIT_H2D,
- SCI_REQ_STP_NON_DATA_WAIT_D2H,
-
- SCI_REQ_STP_SOFT_RESET_WAIT_H2D_ASSERTED,
- SCI_REQ_STP_SOFT_RESET_WAIT_H2D_DIAG,
- SCI_REQ_STP_SOFT_RESET_WAIT_D2H,
-
- /*
- * While in this state the IO request object is waiting for the TC
- * completion notification for the H2D Register FIS
- */
- SCI_REQ_STP_PIO_WAIT_H2D,
-
- /*
- * While in this state the IO request object is waiting for either a
- * PIO Setup FIS or a D2H register FIS. The type of frame received is
- * based on the result of the prior frame and line conditions.
- */
- SCI_REQ_STP_PIO_WAIT_FRAME,
-
- /*
- * While in this state the IO request object is waiting for a DATA
- * frame from the device.
- */
- SCI_REQ_STP_PIO_DATA_IN,
-
- /*
- * While in this state the IO request object is waiting to transmit
- * the next data frame to the device.
- */
- SCI_REQ_STP_PIO_DATA_OUT,
-
- /*
- * While in this state the IO request object is waiting for the TC
- * completion notification for the H2D Register FIS
- */
- SCI_REQ_ATAPI_WAIT_H2D,
-
- /*
- * While in this state the IO request object is waiting for either a
- * PIO Setup.
- */
- SCI_REQ_ATAPI_WAIT_PIO_SETUP,
-
- /*
- * The non-data IO transit to this state in this state after receiving
- * TC completion. While in this state IO request object is waiting for
- * D2H status frame as UF.
- */
- SCI_REQ_ATAPI_WAIT_D2H,
-
- /*
- * When transmitting raw frames hardware reports task context completion
- * after every frame submission, so in the non-accelerated case we need
- * to expect the completion for the "cdb" frame.
- */
- SCI_REQ_ATAPI_WAIT_TC_COMP,
-
- /*
- * The AWAIT_TC_COMPLETION sub-state indicates that the started raw
- * task management request is waiting for the transmission of the
- * initial frame (i.e. command, task, etc.).
- */
- SCI_REQ_TASK_WAIT_TC_COMP,
-
- /*
- * This sub-state indicates that the started task management request
- * is waiting for the reception of an unsolicited frame
- * (i.e. response IU).
- */
- SCI_REQ_TASK_WAIT_TC_RESP,
-
- /*
- * This sub-state indicates that the started task management request
- * is waiting for the reception of an unsolicited frame
- * (i.e. response IU).
- */
- SCI_REQ_SMP_WAIT_RESP,
-
- /*
- * The AWAIT_TC_COMPLETION sub-state indicates that the started SMP
- * request is waiting for the transmission of the initial frame
- * (i.e. command, task, etc.).
- */
- SCI_REQ_SMP_WAIT_TC_COMP,
-
- /*
- * This state indicates that the request has completed.
- * This state is entered from the STARTED state. This state is entered
- * from the ABORTING state.
- */
- SCI_REQ_COMPLETED,
-
- /*
- * This state indicates that the request is in the process of being
- * terminated/aborted.
- * This state is entered from the CONSTRUCTED state.
- * This state is entered from the STARTED state.
- */
- SCI_REQ_ABORTING,
-
- /*
- * Simply the final state for the base request state machine.
- */
- SCI_REQ_FINAL,
-};
+#define REQUEST_STATES {\
+ C(REQ_INIT),\
+ C(REQ_CONSTRUCTED),\
+ C(REQ_STARTED),\
+ C(REQ_STP_UDMA_WAIT_TC_COMP),\
+ C(REQ_STP_UDMA_WAIT_D2H),\
+ C(REQ_STP_NON_DATA_WAIT_H2D),\
+ C(REQ_STP_NON_DATA_WAIT_D2H),\
+ C(REQ_STP_PIO_WAIT_H2D),\
+ C(REQ_STP_PIO_WAIT_FRAME),\
+ C(REQ_STP_PIO_DATA_IN),\
+ C(REQ_STP_PIO_DATA_OUT),\
+ C(REQ_ATAPI_WAIT_H2D),\
+ C(REQ_ATAPI_WAIT_PIO_SETUP),\
+ C(REQ_ATAPI_WAIT_D2H),\
+ C(REQ_ATAPI_WAIT_TC_COMP),\
+ C(REQ_TASK_WAIT_TC_COMP),\
+ C(REQ_TASK_WAIT_TC_RESP),\
+ C(REQ_SMP_WAIT_RESP),\
+ C(REQ_SMP_WAIT_TC_COMP),\
+ C(REQ_COMPLETED),\
+ C(REQ_ABORTING),\
+ C(REQ_FINAL),\
+ }
+#undef C
+#define C(a) SCI_##a
+enum sci_base_request_states REQUEST_STATES;
+#undef C
+const char *req_state_name(enum sci_base_request_states state);
enum sci_status sci_request_start(struct isci_request *ireq);
enum sci_status sci_io_request_terminate(struct isci_request *ireq);
@@ -446,10 +411,7 @@ sci_task_request_construct(struct isci_host *ihost,
struct isci_remote_device *idev,
u16 io_tag,
struct isci_request *ireq);
-enum sci_status
-sci_task_request_construct_ssp(struct isci_request *ireq);
-enum sci_status
-sci_task_request_construct_sata(struct isci_request *ireq);
+enum sci_status sci_task_request_construct_ssp(struct isci_request *ireq);
void sci_smp_request_copy_response(struct isci_request *ireq);
static inline int isci_task_is_ncq_recovery(struct sas_task *task)
diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h
index 7df87d923285..869a979eb5b2 100644
--- a/drivers/scsi/isci/scu_task_context.h
+++ b/drivers/scsi/isci/scu_task_context.h
@@ -866,9 +866,9 @@ struct scu_task_context {
struct transport_snapshot snapshot; /* read only set to 0 */
/* OFFSET 0x5C */
- u32 block_protection_enable:1;
- u32 block_size:2;
- u32 block_protection_function:2;
+ u32 blk_prot_en:1;
+ u32 blk_sz:2;
+ u32 blk_prot_func:2;
u32 reserved_5C_0:9;
u32 active_sgl_element:2; /* read only set to 0 */
u32 sgl_exhausted:1; /* read only set to 0 */
@@ -896,33 +896,56 @@ struct scu_task_context {
u32 reserved_C4_CC[3];
/* OFFSET 0xD0 */
- u32 intermediate_crc_value:16;
- u32 initial_crc_seed:16;
+ u32 interm_crc_val:16;
+ u32 init_crc_seed:16;
/* OFFSET 0xD4 */
- u32 application_tag_for_verify:16;
- u32 application_tag_for_generate:16;
+ u32 app_tag_verify:16;
+ u32 app_tag_gen:16;
/* OFFSET 0xD8 */
- u32 reference_tag_seed_for_verify_function;
+ u32 ref_tag_seed_verify;
/* OFFSET 0xDC */
- u32 reserved_DC;
+ u32 UD_bytes_immed_val:13;
+ u32 reserved_DC_0:3;
+ u32 DIF_bytes_immed_val:4;
+ u32 reserved_DC_1:12;
/* OFFSET 0xE0 */
- u32 reserved_E0_0:16;
- u32 application_tag_mask_for_generate:16;
+ u32 bgc_blk_sz:13;
+ u32 reserved_E0_0:3;
+ u32 app_tag_gen_mask:16;
/* OFFSET 0xE4 */
- u32 block_protection_control:16;
- u32 application_tag_mask_for_verify:16;
+ union {
+ u16 bgctl;
+ struct {
+ u16 crc_verify:1;
+ u16 app_tag_chk:1;
+ u16 ref_tag_chk:1;
+ u16 op:2;
+ u16 legacy:1;
+ u16 invert_crc_seed:1;
+ u16 ref_tag_gen:1;
+ u16 fixed_ref_tag:1;
+ u16 invert_crc:1;
+ u16 app_ref_f_detect:1;
+ u16 uninit_dif_check_err:1;
+ u16 uninit_dif_bypass:1;
+ u16 app_f_detect:1;
+ u16 reserved_0:2;
+ } bgctl_f;
+ };
+
+ u16 app_tag_verify_mask;
/* OFFSET 0xE8 */
- u32 block_protection_error:8;
+ u32 blk_guard_err:8;
u32 reserved_E8_0:24;
/* OFFSET 0xEC */
- u32 reference_tag_seed_for_verify;
+ u32 ref_tag_seed_gen;
/* OFFSET 0xF0 */
u32 intermediate_crc_valid_snapshot:16;
@@ -937,6 +960,6 @@ struct scu_task_context {
/* OFFSET 0xFC */
u32 reference_tag_seed_for_generate_function_snapshot;
-};
+} __packed;
#endif /* _SCU_TASK_CONTEXT_H_ */
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index f5a3f7d2bdab..374254ede9d4 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -96,8 +96,7 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task,
__func__, task, response, status);
task->lldd_task = NULL;
-
- isci_execpath_callback(ihost, task, task->task_done);
+ task->task_done(task);
break;
case isci_perform_aborted_io_completion:
@@ -117,8 +116,7 @@ static void isci_task_refuse(struct isci_host *ihost, struct sas_task *task,
"%s: Error - task = %p, response=%d, "
"status=%d\n",
__func__, task, response, status);
-
- isci_execpath_callback(ihost, task, sas_task_abort);
+ sas_task_abort(task);
break;
default:
@@ -249,46 +247,6 @@ int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags)
return 0;
}
-static enum sci_status isci_sata_management_task_request_build(struct isci_request *ireq)
-{
- struct isci_tmf *isci_tmf;
- enum sci_status status;
-
- if (!test_bit(IREQ_TMF, &ireq->flags))
- return SCI_FAILURE;
-
- isci_tmf = isci_request_access_tmf(ireq);
-
- switch (isci_tmf->tmf_code) {
-
- case isci_tmf_sata_srst_high:
- case isci_tmf_sata_srst_low: {
- struct host_to_dev_fis *fis = &ireq->stp.cmd;
-
- memset(fis, 0, sizeof(*fis));
-
- fis->fis_type = 0x27;
- fis->flags &= ~0x80;
- fis->flags &= 0xF0;
- if (isci_tmf->tmf_code == isci_tmf_sata_srst_high)
- fis->control |= ATA_SRST;
- else
- fis->control &= ~ATA_SRST;
- break;
- }
- /* other management commnd go here... */
- default:
- return SCI_FAILURE;
- }
-
- /* core builds the protocol specific request
- * based on the h2d fis.
- */
- status = sci_task_request_construct_sata(ireq);
-
- return status;
-}
-
static struct isci_request *isci_task_request_build(struct isci_host *ihost,
struct isci_remote_device *idev,
u16 tag, struct isci_tmf *isci_tmf)
@@ -328,13 +286,6 @@ static struct isci_request *isci_task_request_build(struct isci_host *ihost,
return NULL;
}
- if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTOCOL_STP)) {
- isci_tmf->proto = SAS_PROTOCOL_SATA;
- status = isci_sata_management_task_request_build(ireq);
-
- if (status != SCI_SUCCESS)
- return NULL;
- }
return ireq;
}
@@ -873,53 +824,20 @@ static int isci_task_send_lu_reset_sas(
return ret;
}
-static int isci_task_send_lu_reset_sata(struct isci_host *ihost,
- struct isci_remote_device *idev, u8 *lun)
-{
- int ret = TMF_RESP_FUNC_FAILED;
- struct isci_tmf tmf;
-
- /* Send the soft reset to the target */
- #define ISCI_SRST_TIMEOUT_MS 25000 /* 25 second timeout. */
- isci_task_build_tmf(&tmf, isci_tmf_sata_srst_high, NULL, NULL);
-
- ret = isci_task_execute_tmf(ihost, idev, &tmf, ISCI_SRST_TIMEOUT_MS);
-
- if (ret != TMF_RESP_FUNC_COMPLETE) {
- dev_dbg(&ihost->pdev->dev,
- "%s: Assert SRST failed (%p) = %x",
- __func__, idev, ret);
-
- /* Return the failure so that the LUN reset is escalated
- * to a target reset.
- */
- }
- return ret;
-}
-
-/**
- * isci_task_lu_reset() - This function is one of the SAS Domain Template
- * functions. This is one of the Task Management functoins called by libsas,
- * to reset the given lun. Note the assumption that while this call is
- * executing, no I/O will be sent by the host to the device.
- * @lun: This parameter specifies the lun to be reset.
- *
- * status, zero indicates success.
- */
-int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun)
+int isci_task_lu_reset(struct domain_device *dev, u8 *lun)
{
- struct isci_host *isci_host = dev_to_ihost(domain_device);
+ struct isci_host *isci_host = dev_to_ihost(dev);
struct isci_remote_device *isci_device;
unsigned long flags;
int ret;
spin_lock_irqsave(&isci_host->scic_lock, flags);
- isci_device = isci_lookup_device(domain_device);
+ isci_device = isci_lookup_device(dev);
spin_unlock_irqrestore(&isci_host->scic_lock, flags);
dev_dbg(&isci_host->pdev->dev,
"%s: domain_device=%p, isci_host=%p; isci_device=%p\n",
- __func__, domain_device, isci_host, isci_device);
+ __func__, dev, isci_host, isci_device);
if (!isci_device) {
/* If the device is gone, stop the escalations. */
@@ -928,11 +846,11 @@ int isci_task_lu_reset(struct domain_device *domain_device, u8 *lun)
ret = TMF_RESP_FUNC_COMPLETE;
goto out;
}
- set_bit(IDEV_EH, &isci_device->flags);
/* Send the task management part of the reset. */
- if (sas_protocol_ata(domain_device->tproto)) {
- ret = isci_task_send_lu_reset_sata(isci_host, isci_device, lun);
+ if (dev_is_sata(dev)) {
+ sas_ata_schedule_reset(dev);
+ ret = TMF_RESP_FUNC_COMPLETE;
} else
ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun);
@@ -1062,9 +980,6 @@ int isci_task_abort_task(struct sas_task *task)
"%s: dev = %p, task = %p, old_request == %p\n",
__func__, isci_device, task, old_request);
- if (isci_device)
- set_bit(IDEV_EH, &isci_device->flags);
-
/* Device reset conditions signalled in task_state_flags are the
* responsbility of libsas to observe at the start of the error
* handler thread.
@@ -1332,29 +1247,35 @@ isci_task_request_complete(struct isci_host *ihost,
}
static int isci_reset_device(struct isci_host *ihost,
+ struct domain_device *dev,
struct isci_remote_device *idev)
{
- struct sas_phy *phy = sas_find_local_phy(idev->domain_dev);
- enum sci_status status;
- unsigned long flags;
int rc;
+ unsigned long flags;
+ enum sci_status status;
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ struct isci_port *iport = dev->port->lldd_port;
dev_dbg(&ihost->pdev->dev, "%s: idev %p\n", __func__, idev);
spin_lock_irqsave(&ihost->scic_lock, flags);
status = sci_remote_device_reset(idev);
- if (status != SCI_SUCCESS) {
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ spin_unlock_irqrestore(&ihost->scic_lock, flags);
+ if (status != SCI_SUCCESS) {
dev_dbg(&ihost->pdev->dev,
"%s: sci_remote_device_reset(%p) returned %d!\n",
__func__, idev, status);
-
- return TMF_RESP_FUNC_FAILED;
+ rc = TMF_RESP_FUNC_FAILED;
+ goto out;
}
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
- rc = sas_phy_reset(phy, true);
+ if (scsi_is_sas_phy_local(phy)) {
+ struct isci_phy *iphy = &ihost->phys[phy->number];
+
+ rc = isci_port_perform_hard_reset(ihost, iport, iphy);
+ } else
+ rc = sas_phy_reset(phy, !dev_is_sata(dev));
/* Terminate in-progress I/O now. */
isci_remote_device_nuke_requests(ihost, idev);
@@ -1371,7 +1292,8 @@ static int isci_reset_device(struct isci_host *ihost,
}
dev_dbg(&ihost->pdev->dev, "%s: idev %p complete.\n", __func__, idev);
-
+ out:
+ sas_put_local_phy(phy);
return rc;
}
@@ -1386,35 +1308,15 @@ int isci_task_I_T_nexus_reset(struct domain_device *dev)
idev = isci_lookup_device(dev);
spin_unlock_irqrestore(&ihost->scic_lock, flags);
- if (!idev || !test_bit(IDEV_EH, &idev->flags)) {
- ret = TMF_RESP_FUNC_COMPLETE;
- goto out;
- }
-
- ret = isci_reset_device(ihost, idev);
- out:
- isci_put_device(idev);
- return ret;
-}
-
-int isci_bus_reset_handler(struct scsi_cmnd *cmd)
-{
- struct domain_device *dev = sdev_to_domain_dev(cmd->device);
- struct isci_host *ihost = dev_to_ihost(dev);
- struct isci_remote_device *idev;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&ihost->scic_lock, flags);
- idev = isci_lookup_device(dev);
- spin_unlock_irqrestore(&ihost->scic_lock, flags);
-
if (!idev) {
+ /* XXX: need to cleanup any ireqs targeting this
+ * domain_device
+ */
ret = TMF_RESP_FUNC_COMPLETE;
goto out;
}
- ret = isci_reset_device(ihost, idev);
+ ret = isci_reset_device(ihost, dev, idev);
out:
isci_put_device(idev);
return ret;
diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h
index 1b27b3797c6c..7b6d0e32fd9b 100644
--- a/drivers/scsi/isci/task.h
+++ b/drivers/scsi/isci/task.h
@@ -86,8 +86,6 @@ enum isci_tmf_function_codes {
isci_tmf_func_none = 0,
isci_tmf_ssp_task_abort = TMF_ABORT_TASK,
isci_tmf_ssp_lun_reset = TMF_LU_RESET,
- isci_tmf_sata_srst_high = TMF_LU_RESET + 0x100, /* Non SCSI */
- isci_tmf_sata_srst_low = TMF_LU_RESET + 0x101 /* Non SCSI */
};
/**
* struct isci_tmf - This class represents the task management object which
@@ -210,8 +208,6 @@ int isci_queuecommand(
struct scsi_cmnd *scsi_cmd,
void (*donefunc)(struct scsi_cmnd *));
-int isci_bus_reset_handler(struct scsi_cmnd *cmd);
-
/**
* enum isci_completion_selection - This enum defines the possible actions to
* take with respect to a given request's notification back to libsas.
@@ -321,40 +317,4 @@ isci_task_set_completion_status(
return task_notification_selection;
}
-/**
-* isci_execpath_callback() - This function is called from the task
-* execute path when the task needs to callback libsas about the submit-time
-* task failure. The callback occurs either through the task's done function
-* or through sas_task_abort. In the case of regular non-discovery SATA/STP I/O
-* requests, libsas takes the host lock before calling execute task. Therefore
-* in this situation the host lock must be managed before calling the func.
-*
-* @ihost: This parameter is the controller to which the I/O request was sent.
-* @task: This parameter is the I/O request.
-* @func: This parameter is the function to call in the correct context.
-* @status: This parameter is the status code for the completed task.
-*
-*/
-static inline void isci_execpath_callback(struct isci_host *ihost,
- struct sas_task *task,
- void (*func)(struct sas_task *))
-{
- struct domain_device *dev = task->dev;
-
- if (dev_is_sata(dev) && task->uldd_task) {
- unsigned long flags;
-
- /* Since we are still in the submit path, and since
- * libsas takes the host lock on behalf of SATA
- * devices before I/O starts (in the non-discovery case),
- * we need to unlock before we can call the callback function.
- */
- raw_local_irq_save(flags);
- spin_unlock(dev->sata_dev.ap->lock);
- func(task);
- spin_lock(dev->sata_dev.ap->lock);
- raw_local_irq_restore(flags);
- } else
- func(task);
-}
#endif /* !defined(_SCI_TASK_H_) */
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
index db47158e0dde..453a740fa68e 100644
--- a/drivers/scsi/iscsi_tcp.c
+++ b/drivers/scsi/iscsi_tcp.c
@@ -684,10 +684,8 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn,
int buflen)
{
struct iscsi_conn *conn = cls_conn->dd_data;
- struct iscsi_session *session = conn->session;
struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data;
- int value;
switch(param) {
case ISCSI_PARAM_HDRDGST_EN:
@@ -699,16 +697,7 @@ static int iscsi_sw_tcp_conn_set_param(struct iscsi_cls_conn *cls_conn,
sock_no_sendpage : tcp_sw_conn->sock->ops->sendpage;
break;
case ISCSI_PARAM_MAX_R2T:
- sscanf(buf, "%d", &value);
- if (value <= 0 || !is_power_of_2(value))
- return -EINVAL;
- if (session->max_r2t == value)
- break;
- iscsi_tcp_r2tpool_free(session);
- iscsi_set_param(cls_conn, param, buf, buflen);
- if (iscsi_tcp_r2tpool_alloc(session))
- return -ENOMEM;
- break;
+ return iscsi_tcp_set_max_r2t(conn, buf);
default:
return iscsi_set_param(cls_conn, param, buf, buflen);
}
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c
index 1d1b0c9da29b..8e561e6a557c 100644
--- a/drivers/scsi/libfc/fc_disc.c
+++ b/drivers/scsi/libfc/fc_disc.c
@@ -337,6 +337,13 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)
schedule_delayed_work(&disc->disc_work, delay);
} else
fc_disc_done(disc, DISC_EV_FAILED);
+ } else if (PTR_ERR(fp) == -FC_EX_CLOSED) {
+ /*
+ * if discovery fails due to lport reset, clear
+ * pending flag so that subsequent discovery can
+ * continue
+ */
+ disc->pending = 0;
}
}
diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c
index e17a28d324d0..c2384d501470 100644
--- a/drivers/scsi/libfc/fc_elsct.c
+++ b/drivers/scsi/libfc/fc_elsct.c
@@ -56,8 +56,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,
rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);
else {
/* CT requests */
- rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type);
- did = FC_FID_DIR_SERV;
+ rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type, &did);
}
if (rc) {
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c
index 4d70d96fa5dc..630291f01826 100644
--- a/drivers/scsi/libfc/fc_exch.c
+++ b/drivers/scsi/libfc/fc_exch.c
@@ -1642,9 +1642,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)
case FC_RCTL_ACK_0:
break;
default:
- FC_EXCH_DBG(ep, "BLS rctl %x - %s received",
- fh->fh_r_ctl,
- fc_exch_rctl_name(fh->fh_r_ctl));
+ if (ep)
+ FC_EXCH_DBG(ep, "BLS rctl %x - %s received",
+ fh->fh_r_ctl,
+ fc_exch_rctl_name(fh->fh_r_ctl));
break;
}
fc_frame_free(fp);
diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c
index b577c907b318..f7357308655a 100644
--- a/drivers/scsi/libfc/fc_fcp.c
+++ b/drivers/scsi/libfc/fc_fcp.c
@@ -1074,8 +1074,7 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)
fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK;
- int_to_scsilun(fsp->cmd->device->lun,
- (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+ int_to_scsilun(fsp->cmd->device->lun, &fsp->cdb_cmd.fc_lun);
memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);
spin_lock_irqsave(&si->scsi_queue_lock, flags);
@@ -1257,7 +1256,7 @@ static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,
fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);
fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET;
- int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun);
+ int_to_scsilun(lun, &fsp->cdb_cmd.fc_lun);
fsp->wait_for_comp = 1;
init_completion(&fsp->tm_done);
diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c
index c1a808cc5920..bd5d31d022d9 100644
--- a/drivers/scsi/libfc/fc_lport.c
+++ b/drivers/scsi/libfc/fc_lport.c
@@ -116,6 +116,8 @@ static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);
static void fc_lport_enter_scr(struct fc_lport *);
static void fc_lport_enter_ready(struct fc_lport *);
static void fc_lport_enter_logo(struct fc_lport *);
+static void fc_lport_enter_fdmi(struct fc_lport *lport);
+static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state);
static const char *fc_lport_state_names[] = {
[LPORT_ST_DISABLED] = "disabled",
@@ -126,6 +128,11 @@ static const char *fc_lport_state_names[] = {
[LPORT_ST_RSPN_ID] = "RSPN_ID",
[LPORT_ST_RFT_ID] = "RFT_ID",
[LPORT_ST_RFF_ID] = "RFF_ID",
+ [LPORT_ST_FDMI] = "FDMI",
+ [LPORT_ST_RHBA] = "RHBA",
+ [LPORT_ST_RPA] = "RPA",
+ [LPORT_ST_DHBA] = "DHBA",
+ [LPORT_ST_DPRT] = "DPRT",
[LPORT_ST_SCR] = "SCR",
[LPORT_ST_READY] = "Ready",
[LPORT_ST_LOGO] = "LOGO",
@@ -183,11 +190,14 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
if (lport->state == LPORT_ST_DNS) {
lport->dns_rdata = rdata;
fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
+ } else if (lport->state == LPORT_ST_FDMI) {
+ lport->ms_rdata = rdata;
+ fc_lport_enter_ms(lport, LPORT_ST_DHBA);
} else {
FC_LPORT_DBG(lport, "Received an READY event "
"on port (%6.6x) for the directory "
"server, but the lport is not "
- "in the DNS state, it's in the "
+ "in the DNS or FDMI state, it's in the "
"%d state", rdata->ids.port_id,
lport->state);
lport->tt.rport_logoff(rdata);
@@ -196,7 +206,10 @@ static void fc_lport_rport_callback(struct fc_lport *lport,
case RPORT_EV_LOGO:
case RPORT_EV_FAILED:
case RPORT_EV_STOP:
- lport->dns_rdata = NULL;
+ if (rdata->ids.port_id == FC_FID_DIR_SERV)
+ lport->dns_rdata = NULL;
+ else if (rdata->ids.port_id == FC_FID_MGMT_SERV)
+ lport->ms_rdata = NULL;
break;
case RPORT_EV_NONE:
break;
@@ -1148,7 +1161,10 @@ static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);
break;
case LPORT_ST_RFF_ID:
- fc_lport_enter_scr(lport);
+ if (lport->fdmi_enabled)
+ fc_lport_enter_fdmi(lport);
+ else
+ fc_lport_enter_scr(lport);
break;
default:
/* should have already been caught by state checks */
@@ -1163,6 +1179,85 @@ err:
}
/**
+ * fc_lport_ms_resp() - Handle response to a management server
+ * exchange
+ * @sp: current sequence in exchange
+ * @fp: response frame
+ * @lp_arg: Fibre Channel host port instance
+ *
+ * Locking Note: This function will be called without the lport lock
+ * held, but it will lock, call an _enter_* function or fc_lport_error()
+ * and then unlock the lport.
+ */
+static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp,
+ void *lp_arg)
+{
+ struct fc_lport *lport = lp_arg;
+ struct fc_frame_header *fh;
+ struct fc_ct_hdr *ct;
+
+ FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp));
+
+ if (fp == ERR_PTR(-FC_EX_CLOSED))
+ return;
+
+ mutex_lock(&lport->lp_mutex);
+
+ if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) {
+ FC_LPORT_DBG(lport, "Received a management server response, "
+ "but in state %s\n", fc_lport_state(lport));
+ if (IS_ERR(fp))
+ goto err;
+ goto out;
+ }
+
+ if (IS_ERR(fp)) {
+ fc_lport_error(lport, fp);
+ goto err;
+ }
+
+ fh = fc_frame_header_get(fp);
+ ct = fc_frame_payload_get(fp, sizeof(*ct));
+
+ if (fh && ct && fh->fh_type == FC_TYPE_CT &&
+ ct->ct_fs_type == FC_FST_MGMT &&
+ ct->ct_fs_subtype == FC_FDMI_SUBTYPE) {
+ FC_LPORT_DBG(lport, "Received a management server response, "
+ "reason=%d explain=%d\n",
+ ct->ct_reason,
+ ct->ct_explan);
+
+ switch (lport->state) {
+ case LPORT_ST_RHBA:
+ if (ntohs(ct->ct_cmd) == FC_FS_ACC)
+ fc_lport_enter_ms(lport, LPORT_ST_RPA);
+ else /* Error Skip RPA */
+ fc_lport_enter_scr(lport);
+ break;
+ case LPORT_ST_RPA:
+ fc_lport_enter_scr(lport);
+ break;
+ case LPORT_ST_DPRT:
+ fc_lport_enter_ms(lport, LPORT_ST_RHBA);
+ break;
+ case LPORT_ST_DHBA:
+ fc_lport_enter_ms(lport, LPORT_ST_DPRT);
+ break;
+ default:
+ /* should have already been caught by state checks */
+ break;
+ }
+ } else {
+ /* Invalid Frame? */
+ fc_lport_error(lport, fp);
+ }
+out:
+ fc_frame_free(fp);
+err:
+ mutex_unlock(&lport->lp_mutex);
+}
+
+/**
* fc_lport_scr_resp() - Handle response to State Change Register (SCR) request
* @sp: current sequence in SCR exchange
* @fp: response frame
@@ -1339,6 +1434,123 @@ err:
}
/**
+ * fc_lport_enter_ms() - management server commands
+ * @lport: Fibre Channel local port to register
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state)
+{
+ struct fc_frame *fp;
+ enum fc_fdmi_req cmd;
+ int size = sizeof(struct fc_ct_hdr);
+ size_t len;
+ int numattrs;
+
+ FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
+ fc_lport_state_names[state],
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, state);
+
+ switch (state) {
+ case LPORT_ST_RHBA:
+ cmd = FC_FDMI_RHBA;
+ /* Number of HBA Attributes */
+ numattrs = 10;
+ len = sizeof(struct fc_fdmi_rhba);
+ len -= sizeof(struct fc_fdmi_attr_entry);
+ len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+ len += FC_FDMI_HBA_ATTR_NODENAME_LEN;
+ len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN;
+ len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN;
+ len += FC_FDMI_HBA_ATTR_MODEL_LEN;
+ len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN;
+ len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN;
+ len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN;
+
+ size += len;
+ break;
+ case LPORT_ST_RPA:
+ cmd = FC_FDMI_RPA;
+ /* Number of Port Attributes */
+ numattrs = 6;
+ len = sizeof(struct fc_fdmi_rpa);
+ len -= sizeof(struct fc_fdmi_attr_entry);
+ len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
+ len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN;
+ len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN;
+ len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN;
+ len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN;
+ len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN;
+ len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN;
+
+ size += len;
+ break;
+ case LPORT_ST_DPRT:
+ cmd = FC_FDMI_DPRT;
+ len = sizeof(struct fc_fdmi_dprt);
+ size += len;
+ break;
+ case LPORT_ST_DHBA:
+ cmd = FC_FDMI_DHBA;
+ len = sizeof(struct fc_fdmi_dhba);
+ size += len;
+ break;
+ default:
+ fc_lport_error(lport, NULL);
+ return;
+ }
+
+ FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n",
+ cmd, (int)len, size);
+ fp = fc_frame_alloc(lport, size);
+ if (!fp) {
+ fc_lport_error(lport, fp);
+ return;
+ }
+
+ if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd,
+ fc_lport_ms_resp,
+ lport, 3 * lport->r_a_tov))
+ fc_lport_error(lport, fp);
+}
+
+/**
+ * fc_rport_enter_fdmi() - Create a fc_rport for the management server
+ * @lport: The local port requesting a remote port for the management server
+ *
+ * Locking Note: The lport lock is expected to be held before calling
+ * this routine.
+ */
+static void fc_lport_enter_fdmi(struct fc_lport *lport)
+{
+ struct fc_rport_priv *rdata;
+
+ FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n",
+ fc_lport_state(lport));
+
+ fc_lport_state_enter(lport, LPORT_ST_FDMI);
+
+ mutex_lock(&lport->disc.disc_mutex);
+ rdata = lport->tt.rport_create(lport, FC_FID_MGMT_SERV);
+ mutex_unlock(&lport->disc.disc_mutex);
+ if (!rdata)
+ goto err;
+
+ rdata->ops = &fc_lport_rport_ops;
+ lport->tt.rport_login(rdata);
+ return;
+
+err:
+ fc_lport_error(lport, NULL);
+}
+
+/**
* fc_lport_timeout() - Handler for the retry_work timer
* @work: The work struct of the local port
*/
@@ -1371,6 +1583,15 @@ static void fc_lport_timeout(struct work_struct *work)
case LPORT_ST_RFF_ID:
fc_lport_enter_ns(lport, lport->state);
break;
+ case LPORT_ST_FDMI:
+ fc_lport_enter_fdmi(lport);
+ break;
+ case LPORT_ST_RHBA:
+ case LPORT_ST_RPA:
+ case LPORT_ST_DHBA:
+ case LPORT_ST_DPRT:
+ fc_lport_enter_ms(lport, lport->state);
+ break;
case LPORT_ST_SCR:
fc_lport_enter_scr(lport);
break;
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c
index 143bbe448bec..82c3fd4bc938 100644
--- a/drivers/scsi/libiscsi.c
+++ b/drivers/scsi/libiscsi.c
@@ -1909,6 +1909,16 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc);
spin_lock(&session->lock);
+ task = (struct iscsi_task *)sc->SCp.ptr;
+ if (!task) {
+ /*
+ * Raced with completion. Blk layer has taken ownership
+ * so let timeout code complete it now.
+ */
+ rc = BLK_EH_HANDLED;
+ goto done;
+ }
+
if (session->state != ISCSI_STATE_LOGGED_IN) {
/*
* We are probably in the middle of iscsi recovery so let
@@ -1925,16 +1935,6 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc)
goto done;
}
- task = (struct iscsi_task *)sc->SCp.ptr;
- if (!task) {
- /*
- * Raced with completion. Just reset timer, and let it
- * complete normally
- */
- rc = BLK_EH_RESET_TIMER;
- goto done;
- }
-
/*
* If we have sent (at least queued to the network layer) a pdu or
* recvd one for the task since the last timeout ask for
@@ -2807,6 +2807,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session)
kfree(session->username);
kfree(session->username_in);
kfree(session->targetname);
+ kfree(session->targetalias);
kfree(session->initiatorname);
kfree(session->ifacename);
@@ -3200,7 +3201,7 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
sscanf(buf, "%d", &session->initial_r2t_en);
break;
case ISCSI_PARAM_MAX_R2T:
- sscanf(buf, "%d", &session->max_r2t);
+ sscanf(buf, "%hu", &session->max_r2t);
break;
case ISCSI_PARAM_IMM_DATA_EN:
sscanf(buf, "%d", &session->imm_data_en);
@@ -3233,6 +3234,8 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn,
return iscsi_switch_str_param(&session->password_in, buf);
case ISCSI_PARAM_TARGET_NAME:
return iscsi_switch_str_param(&session->targetname, buf);
+ case ISCSI_PARAM_TARGET_ALIAS:
+ return iscsi_switch_str_param(&session->targetalias, buf);
case ISCSI_PARAM_TPGT:
sscanf(buf, "%d", &session->tpgt);
break;
@@ -3299,6 +3302,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session,
case ISCSI_PARAM_TARGET_NAME:
len = sprintf(buf, "%s\n", session->targetname);
break;
+ case ISCSI_PARAM_TARGET_ALIAS:
+ len = sprintf(buf, "%s\n", session->targetalias);
+ break;
case ISCSI_PARAM_TPGT:
len = sprintf(buf, "%d\n", session->tpgt);
break;
diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c
index 7f0465b9623e..552e8a2b6f5f 100644
--- a/drivers/scsi/libiscsi_tcp.c
+++ b/drivers/scsi/libiscsi_tcp.c
@@ -1170,6 +1170,24 @@ void iscsi_tcp_r2tpool_free(struct iscsi_session *session)
}
EXPORT_SYMBOL_GPL(iscsi_tcp_r2tpool_free);
+int iscsi_tcp_set_max_r2t(struct iscsi_conn *conn, char *buf)
+{
+ struct iscsi_session *session = conn->session;
+ unsigned short r2ts = 0;
+
+ sscanf(buf, "%hu", &r2ts);
+ if (session->max_r2t == r2ts)
+ return 0;
+
+ if (!r2ts || !is_power_of_2(r2ts))
+ return -EINVAL;
+
+ session->max_r2t = r2ts;
+ iscsi_tcp_r2tpool_free(session);
+ return iscsi_tcp_r2tpool_alloc(session);
+}
+EXPORT_SYMBOL_GPL(iscsi_tcp_set_max_r2t);
+
void iscsi_tcp_conn_get_stats(struct iscsi_cls_conn *cls_conn,
struct iscsi_stats *stats)
{
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index db9238f2ecb8..bc0cecc6ad62 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -23,6 +23,8 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
+#include <linux/async.h>
+#include <linux/export.h>
#include <scsi/sas_ata.h>
#include "sas_internal.h"
@@ -93,22 +95,47 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)
static void sas_ata_task_done(struct sas_task *task)
{
struct ata_queued_cmd *qc = task->uldd_task;
- struct domain_device *dev;
+ struct domain_device *dev = task->dev;
struct task_status_struct *stat = &task->task_status;
struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf;
- struct sas_ha_struct *sas_ha;
+ struct sas_ha_struct *sas_ha = dev->port->ha;
enum ata_completion_errors ac;
unsigned long flags;
struct ata_link *link;
+ struct ata_port *ap;
+
+ spin_lock_irqsave(&dev->done_lock, flags);
+ if (test_bit(SAS_HA_FROZEN, &sas_ha->state))
+ task = NULL;
+ else if (qc && qc->scsicmd)
+ ASSIGN_SAS_TASK(qc->scsicmd, NULL);
+ spin_unlock_irqrestore(&dev->done_lock, flags);
+
+ /* check if libsas-eh got to the task before us */
+ if (unlikely(!task))
+ return;
if (!qc)
goto qc_already_gone;
- dev = qc->ap->private_data;
- sas_ha = dev->port->ha;
- link = &dev->sata_dev.ap->link;
+ ap = qc->ap;
+ link = &ap->link;
+
+ spin_lock_irqsave(ap->lock, flags);
+ /* check if we lost the race with libata/sas_ata_post_internal() */
+ if (unlikely(ap->pflags & ATA_PFLAG_FROZEN)) {
+ spin_unlock_irqrestore(ap->lock, flags);
+ if (qc->scsicmd)
+ goto qc_already_gone;
+ else {
+ /* if eh is not involved and the port is frozen then the
+ * ata internal abort process has taken responsibility
+ * for this sas_task
+ */
+ return;
+ }
+ }
- spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_STAT_GOOD ||
((stat->stat == SAM_STAT_CHECK_CONDITION &&
dev->sata_dev.command_set == ATAPI_COMMAND_SET))) {
@@ -121,10 +148,6 @@ static void sas_ata_task_done(struct sas_task *task)
if (unlikely(link->eh_info.err_mask))
qc->flags |= ATA_QCFLAG_FAILED;
}
-
- dev->sata_dev.sstatus = resp->sstatus;
- dev->sata_dev.serror = resp->serror;
- dev->sata_dev.scontrol = resp->scontrol;
} else {
ac = sas_to_ata_err(stat);
if (ac) {
@@ -144,24 +167,8 @@ static void sas_ata_task_done(struct sas_task *task)
}
qc->lldd_task = NULL;
- if (qc->scsicmd)
- ASSIGN_SAS_TASK(qc->scsicmd, NULL);
ata_qc_complete(qc);
- spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
-
- /*
- * If the sas_task has an ata qc, a scsi_cmnd and the aborted
- * flag is set, then we must have come in via the libsas EH
- * functions. When we exit this function, we need to put the
- * scsi_cmnd on the list of finished errors. The ata_qc_complete
- * call cleans up the libata side of things but we're protected
- * from the scsi_cmnd going away because the scsi_cmnd is owned
- * by the EH, making libata's call to scsi_done a NOP.
- */
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED)
- scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q);
- spin_unlock_irqrestore(&task->task_state_lock, flags);
+ spin_unlock_irqrestore(ap->lock, flags);
qc_already_gone:
list_del_init(&task->list);
@@ -170,23 +177,30 @@ qc_already_gone:
static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
{
- int res;
+ unsigned long flags;
struct sas_task *task;
- struct domain_device *dev = qc->ap->private_data;
+ struct scatterlist *sg;
+ int ret = AC_ERR_SYSTEM;
+ unsigned int si, xfer = 0;
+ struct ata_port *ap = qc->ap;
+ struct domain_device *dev = ap->private_data;
struct sas_ha_struct *sas_ha = dev->port->ha;
struct Scsi_Host *host = sas_ha->core.shost;
struct sas_internal *i = to_sas_internal(host->transportt);
- struct scatterlist *sg;
- unsigned int xfer = 0;
- unsigned int si;
+
+ /* TODO: audit callers to ensure they are ready for qc_issue to
+ * unconditionally re-enable interrupts
+ */
+ local_irq_save(flags);
+ spin_unlock(ap->lock);
/* If the device fell off, no sense in issuing commands */
- if (dev->gone)
- return AC_ERR_SYSTEM;
+ if (test_bit(SAS_DEV_GONE, &dev->state))
+ goto out;
task = sas_alloc_task(GFP_ATOMIC);
if (!task)
- return AC_ERR_SYSTEM;
+ goto out;
task->dev = dev;
task->task_proto = SAS_PROTOCOL_STP;
task->task_done = sas_ata_task_done;
@@ -231,21 +245,24 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
ASSIGN_SAS_TASK(qc->scsicmd, task);
if (sas_ha->lldd_max_execute_num < 2)
- res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
+ ret = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
else
- res = sas_queue_up(task);
+ ret = sas_queue_up(task);
/* Examine */
- if (res) {
- SAS_DPRINTK("lldd_execute_task returned: %d\n", res);
+ if (ret) {
+ SAS_DPRINTK("lldd_execute_task returned: %d\n", ret);
if (qc->scsicmd)
ASSIGN_SAS_TASK(qc->scsicmd, NULL);
sas_free_task(task);
- return AC_ERR_SYSTEM;
+ ret = AC_ERR_SYSTEM;
}
- return 0;
+ out:
+ spin_lock(ap->lock);
+ local_irq_restore(flags);
+ return ret;
}
static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
@@ -256,83 +273,222 @@ static bool sas_ata_qc_fill_rtf(struct ata_queued_cmd *qc)
return true;
}
-static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
+static struct sas_internal *dev_to_sas_internal(struct domain_device *dev)
+{
+ return to_sas_internal(dev->port->ha->core.shost->transportt);
+}
+
+static void sas_get_ata_command_set(struct domain_device *dev);
+
+int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy)
{
+ if (phy->attached_tproto & SAS_PROTOCOL_STP)
+ dev->tproto = phy->attached_tproto;
+ if (phy->attached_sata_dev)
+ dev->tproto |= SATA_DEV;
+
+ if (phy->attached_dev_type == SATA_PENDING)
+ dev->dev_type = SATA_PENDING;
+ else {
+ int res;
+
+ dev->dev_type = SATA_DEV;
+ res = sas_get_report_phy_sata(dev->parent, phy->phy_id,
+ &dev->sata_dev.rps_resp);
+ if (res) {
+ SAS_DPRINTK("report phy sata to %016llx:0x%x returned "
+ "0x%x\n", SAS_ADDR(dev->parent->sas_addr),
+ phy->phy_id, res);
+ return res;
+ }
+ memcpy(dev->frame_rcvd, &dev->sata_dev.rps_resp.rps.fis,
+ sizeof(struct dev_to_host_fis));
+ /* TODO switch to ata_dev_classify() */
+ sas_get_ata_command_set(dev);
+ }
+ return 0;
+}
+
+static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy)
+{
+ int res;
+
+ /* we weren't pending, so successfully end the reset sequence now */
+ if (dev->dev_type != SATA_PENDING)
+ return 1;
+
+ /* hmmm, if this succeeds do we need to repost the domain_device to the
+ * lldd so it can pick up new parameters?
+ */
+ res = sas_get_ata_info(dev, phy);
+ if (res)
+ return 0; /* retry */
+ else
+ return 1;
+}
+
+static int smp_ata_check_ready(struct ata_link *link)
+{
+ int res;
struct ata_port *ap = link->ap;
struct domain_device *dev = ap->private_data;
- struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
- int res = TMF_RESP_FUNC_FAILED;
- int ret = 0;
+ struct domain_device *ex_dev = dev->parent;
+ struct sas_phy *phy = sas_get_local_phy(dev);
+ struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy->number];
- if (i->dft->lldd_I_T_nexus_reset)
- res = i->dft->lldd_I_T_nexus_reset(dev);
+ res = sas_ex_phy_discover(ex_dev, phy->number);
+ sas_put_local_phy(phy);
- if (res != TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __func__);
- ret = -EAGAIN;
+ /* break the wait early if the expander is unreachable,
+ * otherwise keep polling
+ */
+ if (res == -ECOMM)
+ return res;
+ if (res != SMP_RESP_FUNC_ACC)
+ return 0;
+
+ switch (ex_phy->attached_dev_type) {
+ case SATA_PENDING:
+ return 0;
+ case SAS_END_DEV:
+ if (ex_phy->attached_sata_dev)
+ return sas_ata_clear_pending(dev, ex_phy);
+ default:
+ return -ENODEV;
}
+}
- switch (dev->sata_dev.command_set) {
- case ATA_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATA device.\n", __func__);
- *class = ATA_DEV_ATA;
- break;
- case ATAPI_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATAPI device.\n", __func__);
- *class = ATA_DEV_ATAPI;
- break;
- default:
- SAS_DPRINTK("%s: Unknown SATA command set: %d.\n",
- __func__,
- dev->sata_dev.command_set);
- *class = ATA_DEV_UNKNOWN;
- break;
+static int local_ata_check_ready(struct ata_link *link)
+{
+ struct ata_port *ap = link->ap;
+ struct domain_device *dev = ap->private_data;
+ struct sas_internal *i = dev_to_sas_internal(dev);
+
+ if (i->dft->lldd_ata_check_ready)
+ return i->dft->lldd_ata_check_ready(dev);
+ else {
+ /* lldd's that don't implement 'ready' checking get the
+ * old default behavior of not coordinating reset
+ * recovery with libata
+ */
+ return 1;
}
+}
- ap->cbl = ATA_CBL_SATA;
- return ret;
+static int sas_ata_printk(const char *level, const struct domain_device *ddev,
+ const char *fmt, ...)
+{
+ struct ata_port *ap = ddev->sata_dev.ap;
+ struct device *dev = &ddev->rphy->dev;
+ struct va_format vaf;
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ r = printk("%ssas: ata%u: %s: %pV",
+ level, ap->print_id, dev_name(dev), &vaf);
+
+ va_end(args);
+
+ return r;
}
-static int sas_ata_soft_reset(struct ata_link *link, unsigned int *class,
- unsigned long deadline)
+static int sas_ata_hard_reset(struct ata_link *link, unsigned int *class,
+ unsigned long deadline)
{
+ int ret = 0, res;
+ struct sas_phy *phy;
struct ata_port *ap = link->ap;
+ int (*check_ready)(struct ata_link *link);
struct domain_device *dev = ap->private_data;
- struct sas_internal *i =
- to_sas_internal(dev->port->ha->core.shost->transportt);
- int res = TMF_RESP_FUNC_FAILED;
- int ret = 0;
+ struct sas_internal *i = dev_to_sas_internal(dev);
- if (i->dft->lldd_ata_soft_reset)
- res = i->dft->lldd_ata_soft_reset(dev);
+ res = i->dft->lldd_I_T_nexus_reset(dev);
+ if (res == -ENODEV)
+ return res;
- if (res != TMF_RESP_FUNC_COMPLETE) {
- SAS_DPRINTK("%s: Unable to soft reset\n", __func__);
- ret = -EAGAIN;
- }
+ if (res != TMF_RESP_FUNC_COMPLETE)
+ sas_ata_printk(KERN_DEBUG, dev, "Unable to reset ata device?\n");
+
+ phy = sas_get_local_phy(dev);
+ if (scsi_is_sas_phy_local(phy))
+ check_ready = local_ata_check_ready;
+ else
+ check_ready = smp_ata_check_ready;
+ sas_put_local_phy(phy);
+
+ ret = ata_wait_after_reset(link, deadline, check_ready);
+ if (ret && ret != -EAGAIN)
+ sas_ata_printk(KERN_ERR, dev, "reset failed (errno=%d)\n", ret);
+ /* XXX: if the class changes during the reset the upper layer
+ * should be informed, if the device has gone away we assume
+ * libsas will eventually delete it
+ */
switch (dev->sata_dev.command_set) {
case ATA_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATA device.\n", __func__);
*class = ATA_DEV_ATA;
break;
case ATAPI_COMMAND_SET:
- SAS_DPRINTK("%s: Found ATAPI device.\n", __func__);
*class = ATA_DEV_ATAPI;
break;
- default:
- SAS_DPRINTK("%s: Unknown SATA command set: %d.\n",
- __func__, dev->sata_dev.command_set);
- *class = ATA_DEV_UNKNOWN;
- break;
}
ap->cbl = ATA_CBL_SATA;
return ret;
}
+/*
+ * notify the lldd to forget the sas_task for this internal ata command
+ * that bypasses scsi-eh
+ */
+static void sas_ata_internal_abort(struct sas_task *task)
+{
+ struct sas_internal *si = dev_to_sas_internal(task->dev);
+ unsigned long flags;
+ int res;
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_ABORTED ||
+ task->task_state_flags & SAS_TASK_STATE_DONE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ SAS_DPRINTK("%s: Task %p already finished.\n", __func__,
+ task);
+ goto out;
+ }
+ task->task_state_flags |= SAS_TASK_STATE_ABORTED;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ res = si->dft->lldd_abort_task(task);
+
+ spin_lock_irqsave(&task->task_state_lock, flags);
+ if (task->task_state_flags & SAS_TASK_STATE_DONE ||
+ res == TMF_RESP_FUNC_COMPLETE) {
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+ goto out;
+ }
+
+ /* XXX we are not prepared to deal with ->lldd_abort_task()
+ * failures. TODO: lldds need to unconditionally forget about
+ * aborted ata tasks, otherwise we (likely) leak the sas task
+ * here
+ */
+ SAS_DPRINTK("%s: Task %p leaked.\n", __func__, task);
+
+ if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
+ task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
+ spin_unlock_irqrestore(&task->task_state_lock, flags);
+
+ return;
+ out:
+ list_del_init(&task->list);
+ sas_free_task(task);
+}
+
static void sas_ata_post_internal(struct ata_queued_cmd *qc)
{
if (qc->flags & ATA_QCFLAG_FAILED)
@@ -340,30 +496,35 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc)
if (qc->err_mask) {
/*
- * Find the sas_task and kill it. By this point,
- * libata has decided to kill the qc, so we needn't
- * bother with sas_ata_task_done. But we still
- * ought to abort the task.
+ * Find the sas_task and kill it. By this point, libata
+ * has decided to kill the qc and has frozen the port.
+ * In this state sas_ata_task_done() will no longer free
+ * the sas_task, so we need to notify the lldd (via
+ * ->lldd_abort_task) that the task is dead and free it
+ * ourselves.
*/
struct sas_task *task = qc->lldd_task;
- unsigned long flags;
qc->lldd_task = NULL;
- if (task) {
- /* Should this be a AT(API) device reset? */
- spin_lock_irqsave(&task->task_state_lock, flags);
- task->task_state_flags |= SAS_TASK_NEED_DEV_RESET;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- task->uldd_task = NULL;
- __sas_task_abort(task);
- }
+ if (!task)
+ return;
+ task->uldd_task = NULL;
+ sas_ata_internal_abort(task);
}
}
+
+static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev)
+{
+ struct domain_device *dev = ap->private_data;
+ struct sas_internal *i = dev_to_sas_internal(dev);
+
+ if (i->dft->lldd_ata_set_dmamode)
+ i->dft->lldd_ata_set_dmamode(dev);
+}
+
static struct ata_port_operations sas_sata_ops = {
.prereset = ata_std_prereset,
- .softreset = sas_ata_soft_reset,
.hardreset = sas_ata_hard_reset,
.postreset = ata_std_postreset,
.error_handler = ata_std_error_handler,
@@ -374,6 +535,7 @@ static struct ata_port_operations sas_sata_ops = {
.qc_fill_rtf = sas_ata_qc_fill_rtf,
.port_start = ata_sas_port_start,
.port_stop = ata_sas_port_stop,
+ .set_dmamode = sas_ata_set_dmamode,
};
static struct ata_port_info sata_port_info = {
@@ -384,11 +546,10 @@ static struct ata_port_info sata_port_info = {
.port_ops = &sas_sata_ops
};
-int sas_ata_init_host_and_port(struct domain_device *found_dev,
- struct scsi_target *starget)
+int sas_ata_init_host_and_port(struct domain_device *found_dev)
{
- struct Scsi_Host *shost = dev_to_shost(&starget->dev);
- struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_ha_struct *ha = found_dev->port->ha;
+ struct Scsi_Host *shost = ha->core.shost;
struct ata_port *ap;
ata_host_init(&found_dev->sata_dev.ata_host,
@@ -406,6 +567,8 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev,
ap->private_data = found_dev;
ap->cbl = ATA_CBL_SATA;
ap->scsi_host = shost;
+ /* publish initialized ata port */
+ smp_wmb();
found_dev->sata_dev.ap = ap;
return 0;
@@ -436,168 +599,14 @@ void sas_ata_task_abort(struct sas_task *task)
complete(waiting);
}
-static void sas_task_timedout(unsigned long _task)
-{
- struct sas_task *task = (void *) _task;
- unsigned long flags;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- complete(&task->completion);
-}
-
-static void sas_disc_task_done(struct sas_task *task)
-{
- if (!del_timer(&task->timer))
- return;
- complete(&task->completion);
-}
-
-#define SAS_DEV_TIMEOUT 10
-
-/**
- * sas_execute_task -- Basic task processing for discovery
- * @task: the task to be executed
- * @buffer: pointer to buffer to do I/O
- * @size: size of @buffer
- * @dma_dir: DMA direction. DMA_xxx
- */
-static int sas_execute_task(struct sas_task *task, void *buffer, int size,
- enum dma_data_direction dma_dir)
-{
- int res = 0;
- struct scatterlist *scatter = NULL;
- struct task_status_struct *ts = &task->task_status;
- int num_scatter = 0;
- int retries = 0;
- struct sas_internal *i =
- to_sas_internal(task->dev->port->ha->core.shost->transportt);
-
- if (dma_dir != DMA_NONE) {
- scatter = kzalloc(sizeof(*scatter), GFP_KERNEL);
- if (!scatter)
- goto out;
-
- sg_init_one(scatter, buffer, size);
- num_scatter = 1;
- }
-
- task->task_proto = task->dev->tproto;
- task->scatter = scatter;
- task->num_scatter = num_scatter;
- task->total_xfer_len = size;
- task->data_dir = dma_dir;
- task->task_done = sas_disc_task_done;
- if (dma_dir != DMA_NONE &&
- sas_protocol_ata(task->task_proto)) {
- task->num_scatter = dma_map_sg(task->dev->port->ha->dev,
- task->scatter,
- task->num_scatter,
- task->data_dir);
- }
-
- for (retries = 0; retries < 5; retries++) {
- task->task_state_flags = SAS_TASK_STATE_PENDING;
- init_completion(&task->completion);
-
- task->timer.data = (unsigned long) task;
- task->timer.function = sas_task_timedout;
- task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ;
- add_timer(&task->timer);
-
- res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL);
- if (res) {
- del_timer(&task->timer);
- SAS_DPRINTK("executing SAS discovery task failed:%d\n",
- res);
- goto ex_err;
- }
- wait_for_completion(&task->completion);
- res = -ECOMM;
- if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
- int res2;
- SAS_DPRINTK("task aborted, flags:0x%x\n",
- task->task_state_flags);
- res2 = i->dft->lldd_abort_task(task);
- SAS_DPRINTK("came back from abort task\n");
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
- if (res2 == TMF_RESP_FUNC_COMPLETE)
- continue; /* Retry the task */
- else
- goto ex_err;
- }
- }
- if (task->task_status.stat == SAM_STAT_BUSY ||
- task->task_status.stat == SAM_STAT_TASK_SET_FULL ||
- task->task_status.stat == SAS_QUEUE_FULL) {
- SAS_DPRINTK("task: q busy, sleeping...\n");
- schedule_timeout_interruptible(HZ);
- } else if (task->task_status.stat == SAM_STAT_CHECK_CONDITION) {
- struct scsi_sense_hdr shdr;
-
- if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size,
- &shdr)) {
- SAS_DPRINTK("couldn't normalize sense\n");
- continue;
- }
- if ((shdr.sense_key == 6 && shdr.asc == 0x29) ||
- (shdr.sense_key == 2 && shdr.asc == 4 &&
- shdr.ascq == 1)) {
- SAS_DPRINTK("device %016llx LUN: %016llx "
- "powering up or not ready yet, "
- "sleeping...\n",
- SAS_ADDR(task->dev->sas_addr),
- SAS_ADDR(task->ssp_task.LUN));
-
- schedule_timeout_interruptible(5*HZ);
- } else if (shdr.sense_key == 1) {
- res = 0;
- break;
- } else if (shdr.sense_key == 5) {
- break;
- } else {
- SAS_DPRINTK("dev %016llx LUN: %016llx "
- "sense key:0x%x ASC:0x%x ASCQ:0x%x"
- "\n",
- SAS_ADDR(task->dev->sas_addr),
- SAS_ADDR(task->ssp_task.LUN),
- shdr.sense_key,
- shdr.asc, shdr.ascq);
- }
- } else if (task->task_status.resp != SAS_TASK_COMPLETE ||
- task->task_status.stat != SAM_STAT_GOOD) {
- SAS_DPRINTK("task finished with resp:0x%x, "
- "stat:0x%x\n",
- task->task_status.resp,
- task->task_status.stat);
- goto ex_err;
- } else {
- res = 0;
- break;
- }
- }
-ex_err:
- if (dma_dir != DMA_NONE) {
- if (sas_protocol_ata(task->task_proto))
- dma_unmap_sg(task->dev->port->ha->dev,
- task->scatter, task->num_scatter,
- task->data_dir);
- kfree(scatter);
- }
-out:
- return res;
-}
-
-/* ---------- SATA ---------- */
-
static void sas_get_ata_command_set(struct domain_device *dev)
{
struct dev_to_host_fis *fis =
(struct dev_to_host_fis *) dev->frame_rcvd;
+ if (dev->dev_type == SATA_PENDING)
+ return;
+
if ((fis->sector_count == 1 && /* ATA */
fis->lbal == 1 &&
fis->lbam == 0 &&
@@ -636,224 +645,152 @@ static void sas_get_ata_command_set(struct domain_device *dev)
dev->sata_dev.command_set = ATAPI_COMMAND_SET;
}
-/**
- * sas_issue_ata_cmd -- Basic SATA command processing for discovery
- * @dev: the device to send the command to
- * @command: the command register
- * @features: the features register
- * @buffer: pointer to buffer to do I/O
- * @size: size of @buffer
- * @dma_dir: DMA direction. DMA_xxx
- */
-static int sas_issue_ata_cmd(struct domain_device *dev, u8 command,
- u8 features, void *buffer, int size,
- enum dma_data_direction dma_dir)
-{
- int res = 0;
- struct sas_task *task;
- struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *)
- &dev->frame_rcvd[0];
-
- res = -ENOMEM;
- task = sas_alloc_task(GFP_KERNEL);
- if (!task)
- goto out;
-
- task->dev = dev;
-
- task->ata_task.fis.fis_type = 0x27;
- task->ata_task.fis.command = command;
- task->ata_task.fis.features = features;
- task->ata_task.fis.device = d2h_fis->device;
- task->ata_task.retry_count = 1;
-
- res = sas_execute_task(task, buffer, size, dma_dir);
-
- sas_free_task(task);
-out:
- return res;
-}
-
-#define ATA_IDENTIFY_DEV 0xEC
-#define ATA_IDENTIFY_PACKET_DEV 0xA1
-#define ATA_SET_FEATURES 0xEF
-#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07
-
-/**
- * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV)
- * @dev: STP/SATA device of interest (ATA/ATAPI)
- *
- * The LLDD has already been notified of this device, so that we can
- * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY
- * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its
- * performance for this device.
- */
-static int sas_discover_sata_dev(struct domain_device *dev)
+void sas_probe_sata(struct asd_sas_port *port)
{
- int res;
- __le16 *identify_x;
- u8 command;
+ struct domain_device *dev, *n;
+ int err;
- identify_x = kzalloc(512, GFP_KERNEL);
- if (!identify_x)
- return -ENOMEM;
-
- if (dev->sata_dev.command_set == ATA_COMMAND_SET) {
- dev->sata_dev.identify_device = identify_x;
- command = ATA_IDENTIFY_DEV;
- } else {
- dev->sata_dev.identify_packet_device = identify_x;
- command = ATA_IDENTIFY_PACKET_DEV;
- }
+ mutex_lock(&port->ha->disco_mutex);
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
- res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
- DMA_FROM_DEVICE);
- if (res)
- goto out_err;
-
- /* lives on the media? */
- if (le16_to_cpu(identify_x[0]) & 4) {
- /* incomplete response */
- SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to "
- "dev %llx\n", SAS_ADDR(dev->sas_addr));
- if (!(identify_x[83] & cpu_to_le16(1<<6)))
- goto cont1;
- res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES,
- ATA_FEATURE_PUP_STBY_SPIN_UP,
- NULL, 0, DMA_NONE);
- if (res)
- goto cont1;
-
- schedule_timeout_interruptible(5*HZ); /* More time? */
- res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
- DMA_FROM_DEVICE);
- if (res)
- goto out_err;
+ err = sas_ata_init_host_and_port(dev);
+ if (err)
+ sas_fail_probe(dev, __func__, err);
+ else
+ ata_sas_async_port_init(dev->sata_dev.ap);
}
-cont1:
- /* XXX Hint: register this SATA device with SATL.
- When this returns, dev->sata_dev->lu is alive and
- present.
- sas_satl_register_dev(dev);
- */
+ mutex_unlock(&port->ha->disco_mutex);
- sas_fill_in_rphy(dev, dev->rphy);
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
- return 0;
-out_err:
- dev->sata_dev.identify_packet_device = NULL;
- dev->sata_dev.identify_device = NULL;
- kfree(identify_x);
- return res;
-}
+ sas_ata_wait_eh(dev);
-static int sas_discover_sata_pm(struct domain_device *dev)
-{
- return -ENODEV;
+ /* if libata could not bring the link up, don't surface
+ * the device
+ */
+ if (ata_dev_disabled(sas_to_ata_dev(dev)))
+ sas_fail_probe(dev, __func__, -ENODEV);
+ }
}
/**
* sas_discover_sata -- discover an STP/SATA domain device
* @dev: pointer to struct domain_device of interest
*
- * First we notify the LLDD of this device, so we can send frames to
- * it. Then depending on the type of device we call the appropriate
- * discover functions. Once device discover is done, we notify the
- * LLDD so that it can fine-tune its parameters for the device, by
- * removing it and then adding it. That is, the second time around,
- * the driver would have certain fields, that it is looking at, set.
- * Finally we initialize the kobj so that the device can be added to
- * the system at registration time. Devices directly attached to a HA
- * port, have no parents. All other devices do, and should have their
- * "parent" pointer set appropriately before calling this function.
+ * Devices directly attached to a HA port, have no parents. All other
+ * devices do, and should have their "parent" pointer set appropriately
+ * before calling this function.
*/
int sas_discover_sata(struct domain_device *dev)
{
int res;
+ if (dev->dev_type == SATA_PM)
+ return -ENODEV;
+
sas_get_ata_command_set(dev);
+ sas_fill_in_rphy(dev, dev->rphy);
res = sas_notify_lldd_dev_found(dev);
if (res)
return res;
- switch (dev->dev_type) {
- case SATA_DEV:
- res = sas_discover_sata_dev(dev);
- break;
- case SATA_PM:
- res = sas_discover_sata_pm(dev);
- break;
- default:
- break;
- }
- sas_notify_lldd_dev_gone(dev);
- if (!res) {
- sas_notify_lldd_dev_found(dev);
- res = sas_rphy_add(dev->rphy);
- }
-
- return res;
+ sas_discover_event(dev->port, DISCE_PROBE);
+ return 0;
}
-void sas_ata_strategy_handler(struct Scsi_Host *shost)
+static void async_sas_ata_eh(void *data, async_cookie_t cookie)
{
- struct scsi_device *sdev;
+ struct domain_device *dev = data;
+ struct ata_port *ap = dev->sata_dev.ap;
+ struct sas_ha_struct *ha = dev->port->ha;
- shost_for_each_device(sdev, shost) {
- struct domain_device *ddev = sdev_to_domain_dev(sdev);
- struct ata_port *ap = ddev->sata_dev.ap;
+ /* hold a reference over eh since we may be racing with final
+ * remove once all commands are completed
+ */
+ kref_get(&dev->kref);
+ sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n");
+ ata_scsi_port_error_handler(ha->core.shost, ap);
+ sas_put_device(dev);
+}
- if (!dev_is_sata(ddev))
- continue;
+static bool sas_ata_dev_eh_valid(struct domain_device *dev)
+{
+ struct ata_port *ap;
- ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata port error handler");
- ata_scsi_port_error_handler(shost, ap);
- }
+ if (!dev_is_sata(dev))
+ return false;
+ ap = dev->sata_dev.ap;
+ /* consume fully initialized ata ports */
+ smp_rmb();
+ return !!ap;
}
-int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task,
- enum blk_eh_timer_return *rtn)
+void sas_ata_strategy_handler(struct Scsi_Host *shost)
{
- struct domain_device *ddev = cmd_to_domain_dev(cmd);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ LIST_HEAD(async);
+ int i;
+
+ /* it's ok to defer revalidation events during ata eh, these
+ * disks are in one of three states:
+ * 1/ present for initial domain discovery, and these
+ * resets will cause bcn flutters
+ * 2/ hot removed, we'll discover that after eh fails
+ * 3/ hot added after initial discovery, lost the race, and need
+ * to catch the next train.
+ */
+ sas_disable_revalidation(sas_ha);
- if (!dev_is_sata(ddev) || task)
- return 0;
+ spin_lock_irq(&sas_ha->phy_port_lock);
+ for (i = 0; i < sas_ha->num_phys; i++) {
+ struct asd_sas_port *port = sas_ha->sas_port[i];
+ struct domain_device *dev;
- /* we're a sata device with no task, so this must be a libata
- * eh timeout. Ideally should hook into libata timeout
- * handling, but there's no point, it just wants to activate
- * the eh thread */
- *rtn = BLK_EH_NOT_HANDLED;
- return 1;
+ spin_lock(&port->dev_list_lock);
+ list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+ if (!sas_ata_dev_eh_valid(dev))
+ continue;
+ async_schedule_domain(async_sas_ata_eh, dev, &async);
+ }
+ spin_unlock(&port->dev_list_lock);
+ }
+ spin_unlock_irq(&sas_ha->phy_port_lock);
+
+ async_synchronize_full_domain(&async);
+
+ sas_enable_revalidation(sas_ha);
}
-int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
- struct list_head *done_q)
+void sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
+ struct list_head *done_q)
{
- int rtn = 0;
struct scsi_cmnd *cmd, *n;
- struct ata_port *ap;
+ struct domain_device *eh_dev;
do {
LIST_HEAD(sata_q);
-
- ap = NULL;
+ eh_dev = NULL;
list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
struct domain_device *ddev = cmd_to_domain_dev(cmd);
if (!dev_is_sata(ddev) || TO_SAS_TASK(cmd))
continue;
- if (ap && ap != ddev->sata_dev.ap)
+ if (eh_dev && eh_dev != ddev)
continue;
- ap = ddev->sata_dev.ap;
- rtn = 1;
+ eh_dev = ddev;
list_move(&cmd->eh_entry, &sata_q);
}
if (!list_empty(&sata_q)) {
- ata_port_printk(ap, KERN_DEBUG, "sas eh calling libata cmd error handler\n");
+ struct ata_port *ap = eh_dev->sata_dev.ap;
+
+ sas_ata_printk(KERN_DEBUG, eh_dev, "cmd error handler\n");
ata_scsi_cmd_error_handler(shost, ap, &sata_q);
/*
* ata's error handler may leave the cmd on the list
@@ -869,7 +806,36 @@ int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
while (!list_empty(&sata_q))
list_del_init(sata_q.next);
}
- } while (ap);
+ } while (eh_dev);
+}
+
+void sas_ata_schedule_reset(struct domain_device *dev)
+{
+ struct ata_eh_info *ehi;
+ struct ata_port *ap;
+ unsigned long flags;
+
+ if (!dev_is_sata(dev))
+ return;
+
+ ap = dev->sata_dev.ap;
+ ehi = &ap->link.eh_info;
+
+ spin_lock_irqsave(ap->lock, flags);
+ ehi->err_mask |= AC_ERR_TIMEOUT;
+ ehi->action |= ATA_EH_RESET;
+ ata_port_schedule_eh(ap);
+ spin_unlock_irqrestore(ap->lock, flags);
+}
+EXPORT_SYMBOL_GPL(sas_ata_schedule_reset);
+
+void sas_ata_wait_eh(struct domain_device *dev)
+{
+ struct ata_port *ap;
+
+ if (!dev_is_sata(dev))
+ return;
- return rtn;
+ ap = dev->sata_dev.ap;
+ ata_port_wait_eh(ap);
}
diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c
index 54a5199ceb56..364679675602 100644
--- a/drivers/scsi/libsas/sas_discover.c
+++ b/drivers/scsi/libsas/sas_discover.c
@@ -30,29 +30,30 @@
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
+#include <scsi/sas_ata.h>
#include "../scsi_sas_internal.h"
/* ---------- Basic task processing for discovery purposes ---------- */
void sas_init_dev(struct domain_device *dev)
{
- INIT_LIST_HEAD(&dev->siblings);
- INIT_LIST_HEAD(&dev->dev_list_node);
- switch (dev->dev_type) {
- case SAS_END_DEV:
- break;
- case EDGE_DEV:
- case FANOUT_DEV:
- INIT_LIST_HEAD(&dev->ex_dev.children);
- break;
- case SATA_DEV:
- case SATA_PM:
- case SATA_PM_PORT:
- INIT_LIST_HEAD(&dev->sata_dev.children);
- break;
- default:
- break;
- }
+ switch (dev->dev_type) {
+ case SAS_END_DEV:
+ break;
+ case EDGE_DEV:
+ case FANOUT_DEV:
+ INIT_LIST_HEAD(&dev->ex_dev.children);
+ mutex_init(&dev->ex_dev.cmd_mutex);
+ break;
+ case SATA_DEV:
+ case SATA_PM:
+ case SATA_PM_PORT:
+ case SATA_PENDING:
+ INIT_LIST_HEAD(&dev->sata_dev.children);
+ break;
+ default:
+ break;
+ }
}
/* ---------- Domain device discovery ---------- */
@@ -68,19 +69,18 @@ void sas_init_dev(struct domain_device *dev)
*/
static int sas_get_port_device(struct asd_sas_port *port)
{
- unsigned long flags;
struct asd_sas_phy *phy;
struct sas_rphy *rphy;
struct domain_device *dev;
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ dev = sas_alloc_device();
if (!dev)
return -ENOMEM;
- spin_lock_irqsave(&port->phy_list_lock, flags);
+ spin_lock_irq(&port->phy_list_lock);
if (list_empty(&port->phy_list)) {
- spin_unlock_irqrestore(&port->phy_list_lock, flags);
- kfree(dev);
+ spin_unlock_irq(&port->phy_list_lock);
+ sas_put_device(dev);
return -ENODEV;
}
phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el);
@@ -88,7 +88,7 @@ static int sas_get_port_device(struct asd_sas_port *port)
memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),
(size_t)phy->frame_rcvd_size));
spin_unlock(&phy->frame_rcvd_lock);
- spin_unlock_irqrestore(&port->phy_list_lock, flags);
+ spin_unlock_irq(&port->phy_list_lock);
if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {
struct dev_to_host_fis *fis =
@@ -130,9 +130,14 @@ static int sas_get_port_device(struct asd_sas_port *port)
}
if (!rphy) {
- kfree(dev);
+ sas_put_device(dev);
return -ENODEV;
}
+
+ spin_lock_irq(&port->phy_list_lock);
+ list_for_each_entry(phy, &port->phy_list, port_phy_el)
+ sas_phy_set_target(phy, dev);
+ spin_unlock_irq(&port->phy_list_lock);
rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;
memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);
sas_fill_in_rphy(dev, rphy);
@@ -147,11 +152,17 @@ static int sas_get_port_device(struct asd_sas_port *port)
memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE);
memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE);
port->disc.max_level = 0;
+ sas_device_set_phy(dev, port->port);
dev->rphy = rphy;
- spin_lock_irq(&port->dev_list_lock);
- list_add_tail(&dev->dev_list_node, &port->dev_list);
- spin_unlock_irq(&port->dev_list_lock);
+
+ if (dev_is_sata(dev) || dev->dev_type == SAS_END_DEV)
+ list_add_tail(&dev->disco_list_node, &port->disco_list);
+ else {
+ spin_lock_irq(&port->dev_list_lock);
+ list_add_tail(&dev->dev_list_node, &port->dev_list);
+ spin_unlock_irq(&port->dev_list_lock);
+ }
return 0;
}
@@ -173,6 +184,7 @@ int sas_notify_lldd_dev_found(struct domain_device *dev)
dev_name(sas_ha->dev),
SAS_ADDR(dev->sas_addr), res);
}
+ kref_get(&dev->kref);
}
return res;
}
@@ -184,12 +196,40 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev)
struct Scsi_Host *shost = sas_ha->core.shost;
struct sas_internal *i = to_sas_internal(shost->transportt);
- if (i->dft->lldd_dev_gone)
+ if (i->dft->lldd_dev_gone) {
i->dft->lldd_dev_gone(dev);
+ sas_put_device(dev);
+ }
}
-/* ---------- Common/dispatchers ---------- */
+static void sas_probe_devices(struct work_struct *work)
+{
+ struct domain_device *dev, *n;
+ struct sas_discovery_event *ev =
+ container_of(work, struct sas_discovery_event, work);
+ struct asd_sas_port *port = ev->port;
+
+ clear_bit(DISCE_PROBE, &port->disc.pending);
+ /* devices must be domain members before link recovery and probe */
+ list_for_each_entry(dev, &port->disco_list, disco_list_node) {
+ spin_lock_irq(&port->dev_list_lock);
+ list_add_tail(&dev->dev_list_node, &port->dev_list);
+ spin_unlock_irq(&port->dev_list_lock);
+ }
+
+ sas_probe_sata(port);
+
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node) {
+ int err;
+
+ err = sas_rphy_add(dev->rphy);
+ if (err)
+ sas_fail_probe(dev, __func__, err);
+ else
+ list_del_init(&dev->disco_list_node);
+ }
+}
/**
* sas_discover_end_dev -- discover an end device (SSP, etc)
@@ -203,22 +243,36 @@ int sas_discover_end_dev(struct domain_device *dev)
res = sas_notify_lldd_dev_found(dev);
if (res)
- goto out_err2;
-
- res = sas_rphy_add(dev->rphy);
- if (res)
- goto out_err;
+ return res;
+ sas_discover_event(dev->port, DISCE_PROBE);
return 0;
-
-out_err:
- sas_notify_lldd_dev_gone(dev);
-out_err2:
- return res;
}
/* ---------- Device registration and unregistration ---------- */
+void sas_free_device(struct kref *kref)
+{
+ struct domain_device *dev = container_of(kref, typeof(*dev), kref);
+
+ if (dev->parent)
+ sas_put_device(dev->parent);
+
+ sas_port_put_phy(dev->phy);
+ dev->phy = NULL;
+
+ /* remove the phys and ports, everything else should be gone */
+ if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV)
+ kfree(dev->ex_dev.ex_phy);
+
+ if (dev_is_sata(dev) && dev->sata_dev.ap) {
+ ata_sas_port_destroy(dev->sata_dev.ap);
+ dev->sata_dev.ap = NULL;
+ }
+
+ kfree(dev);
+}
+
static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_device *dev)
{
sas_notify_lldd_dev_gone(dev);
@@ -230,34 +284,84 @@ static void sas_unregister_common_dev(struct asd_sas_port *port, struct domain_d
spin_lock_irq(&port->dev_list_lock);
list_del_init(&dev->dev_list_node);
spin_unlock_irq(&port->dev_list_lock);
+
+ sas_put_device(dev);
}
-void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
+static void sas_destruct_devices(struct work_struct *work)
{
- if (dev->rphy) {
+ struct domain_device *dev, *n;
+ struct sas_discovery_event *ev =
+ container_of(work, struct sas_discovery_event, work);
+ struct asd_sas_port *port = ev->port;
+
+ clear_bit(DISCE_DESTRUCT, &port->disc.pending);
+
+ list_for_each_entry_safe(dev, n, &port->destroy_list, disco_list_node) {
+ list_del_init(&dev->disco_list_node);
+
sas_remove_children(&dev->rphy->dev);
sas_rphy_delete(dev->rphy);
dev->rphy = NULL;
+ sas_unregister_common_dev(port, dev);
}
- if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) {
- /* remove the phys and ports, everything else should be gone */
- kfree(dev->ex_dev.ex_phy);
- dev->ex_dev.ex_phy = NULL;
+}
+
+void sas_unregister_dev(struct asd_sas_port *port, struct domain_device *dev)
+{
+ if (!test_bit(SAS_DEV_DESTROY, &dev->state) &&
+ !list_empty(&dev->disco_list_node)) {
+ /* this rphy never saw sas_rphy_add */
+ list_del_init(&dev->disco_list_node);
+ sas_rphy_free(dev->rphy);
+ dev->rphy = NULL;
+ sas_unregister_common_dev(port, dev);
+ }
+
+ if (dev->rphy && !test_and_set_bit(SAS_DEV_DESTROY, &dev->state)) {
+ sas_rphy_unlink(dev->rphy);
+ list_move_tail(&dev->disco_list_node, &port->destroy_list);
+ sas_discover_event(dev->port, DISCE_DESTRUCT);
}
- sas_unregister_common_dev(port, dev);
}
-void sas_unregister_domain_devices(struct asd_sas_port *port)
+void sas_unregister_domain_devices(struct asd_sas_port *port, int gone)
{
struct domain_device *dev, *n;
- list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node)
+ list_for_each_entry_safe_reverse(dev, n, &port->dev_list, dev_list_node) {
+ if (gone)
+ set_bit(SAS_DEV_GONE, &dev->state);
+ sas_unregister_dev(port, dev);
+ }
+
+ list_for_each_entry_safe(dev, n, &port->disco_list, disco_list_node)
sas_unregister_dev(port, dev);
port->port->rphy = NULL;
}
+void sas_device_set_phy(struct domain_device *dev, struct sas_port *port)
+{
+ struct sas_ha_struct *ha;
+ struct sas_phy *new_phy;
+
+ if (!dev)
+ return;
+
+ ha = dev->port->ha;
+ new_phy = sas_port_get_phy(port);
+
+ /* pin and record last seen phy */
+ spin_lock_irq(&ha->phy_port_lock);
+ if (new_phy) {
+ sas_port_put_phy(dev->phy);
+ dev->phy = new_phy;
+ }
+ spin_unlock_irq(&ha->phy_port_lock);
+}
+
/* ---------- Discovery and Revalidation ---------- */
/**
@@ -277,8 +381,7 @@ static void sas_discover_domain(struct work_struct *work)
container_of(work, struct sas_discovery_event, work);
struct asd_sas_port *port = ev->port;
- sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock,
- &port->disc.pending);
+ clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending);
if (port->port_dev)
return;
@@ -318,11 +421,12 @@ static void sas_discover_domain(struct work_struct *work)
sas_rphy_free(dev->rphy);
dev->rphy = NULL;
+ list_del_init(&dev->disco_list_node);
spin_lock_irq(&port->dev_list_lock);
list_del_init(&dev->dev_list_node);
spin_unlock_irq(&port->dev_list_lock);
- kfree(dev); /* not kobject_register-ed yet */
+ sas_put_device(dev);
port->port_dev = NULL;
}
@@ -336,21 +440,51 @@ static void sas_revalidate_domain(struct work_struct *work)
struct sas_discovery_event *ev =
container_of(work, struct sas_discovery_event, work);
struct asd_sas_port *port = ev->port;
+ struct sas_ha_struct *ha = port->ha;
+
+ /* prevent revalidation from finding sata links in recovery */
+ mutex_lock(&ha->disco_mutex);
+ if (test_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state)) {
+ SAS_DPRINTK("REVALIDATION DEFERRED on port %d, pid:%d\n",
+ port->id, task_pid_nr(current));
+ goto out;
+ }
- sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock,
- &port->disc.pending);
+ clear_bit(DISCE_REVALIDATE_DOMAIN, &port->disc.pending);
SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
task_pid_nr(current));
+
if (port->port_dev)
res = sas_ex_revalidate_domain(port->port_dev);
SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
port->id, task_pid_nr(current), res);
+ out:
+ mutex_unlock(&ha->disco_mutex);
}
/* ---------- Events ---------- */
+static void sas_chain_work(struct sas_ha_struct *ha, struct work_struct *work)
+{
+ /* chained work is not subject to SA_HA_DRAINING or SAS_HA_REGISTERED */
+ scsi_queue_work(ha->core.shost, work);
+}
+
+static void sas_chain_event(int event, unsigned long *pending,
+ struct work_struct *work,
+ struct sas_ha_struct *ha)
+{
+ if (!test_and_set_bit(event, pending)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->state_lock, flags);
+ sas_chain_work(ha, work);
+ spin_unlock_irqrestore(&ha->state_lock, flags);
+ }
+}
+
int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)
{
struct sas_discovery *disc;
@@ -361,8 +495,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)
BUG_ON(ev >= DISC_NUM_EVENTS);
- sas_queue_event(ev, &disc->disc_event_lock, &disc->pending,
- &disc->disc_work[ev].work, port->ha);
+ sas_chain_event(ev, &disc->pending, &disc->disc_work[ev].work, port->ha);
return 0;
}
@@ -380,9 +513,10 @@ void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
static const work_func_t sas_event_fns[DISC_NUM_EVENTS] = {
[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
+ [DISCE_PROBE] = sas_probe_devices,
+ [DISCE_DESTRUCT] = sas_destruct_devices,
};
- spin_lock_init(&disc->disc_event_lock);
disc->pending = 0;
for (i = 0; i < DISC_NUM_EVENTS; i++) {
INIT_WORK(&disc->disc_work[i].work, sas_event_fns[i]);
diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c
index 9db30fb5caf2..16639bbae629 100644
--- a/drivers/scsi/libsas/sas_event.c
+++ b/drivers/scsi/libsas/sas_event.c
@@ -22,15 +22,103 @@
*
*/
+#include <linux/export.h>
#include <scsi/scsi_host.h>
#include "sas_internal.h"
#include "sas_dump.h"
+void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work)
+{
+ if (!test_bit(SAS_HA_REGISTERED, &ha->state))
+ return;
+
+ if (test_bit(SAS_HA_DRAINING, &ha->state))
+ list_add(&work->entry, &ha->defer_q);
+ else
+ scsi_queue_work(ha->core.shost, work);
+}
+
+static void sas_queue_event(int event, unsigned long *pending,
+ struct work_struct *work,
+ struct sas_ha_struct *ha)
+{
+ if (!test_and_set_bit(event, pending)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->state_lock, flags);
+ sas_queue_work(ha, work);
+ spin_unlock_irqrestore(&ha->state_lock, flags);
+ }
+}
+
+
+void __sas_drain_work(struct sas_ha_struct *ha)
+{
+ struct workqueue_struct *wq = ha->core.shost->work_q;
+ struct work_struct *w, *_w;
+
+ set_bit(SAS_HA_DRAINING, &ha->state);
+ /* flush submitters */
+ spin_lock_irq(&ha->state_lock);
+ spin_unlock_irq(&ha->state_lock);
+
+ drain_workqueue(wq);
+
+ spin_lock_irq(&ha->state_lock);
+ clear_bit(SAS_HA_DRAINING, &ha->state);
+ list_for_each_entry_safe(w, _w, &ha->defer_q, entry) {
+ list_del_init(&w->entry);
+ sas_queue_work(ha, w);
+ }
+ spin_unlock_irq(&ha->state_lock);
+}
+
+int sas_drain_work(struct sas_ha_struct *ha)
+{
+ int err;
+
+ err = mutex_lock_interruptible(&ha->drain_mutex);
+ if (err)
+ return err;
+ if (test_bit(SAS_HA_REGISTERED, &ha->state))
+ __sas_drain_work(ha);
+ mutex_unlock(&ha->drain_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sas_drain_work);
+
+void sas_disable_revalidation(struct sas_ha_struct *ha)
+{
+ mutex_lock(&ha->disco_mutex);
+ set_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
+ mutex_unlock(&ha->disco_mutex);
+}
+
+void sas_enable_revalidation(struct sas_ha_struct *ha)
+{
+ int i;
+
+ mutex_lock(&ha->disco_mutex);
+ clear_bit(SAS_HA_ATA_EH_ACTIVE, &ha->state);
+ for (i = 0; i < ha->num_phys; i++) {
+ struct asd_sas_port *port = ha->sas_port[i];
+ const int ev = DISCE_REVALIDATE_DOMAIN;
+ struct sas_discovery *d = &port->disc;
+
+ if (!test_and_clear_bit(ev, &d->pending))
+ continue;
+
+ sas_queue_event(ev, &d->pending, &d->disc_work[ev].work, ha);
+ }
+ mutex_unlock(&ha->disco_mutex);
+}
+
static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)
{
BUG_ON(event >= HA_NUM_EVENTS);
- sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending,
+ sas_queue_event(event, &sas_ha->pending,
&sas_ha->ha_events[event].work, sas_ha);
}
@@ -40,7 +128,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)
BUG_ON(event >= PORT_NUM_EVENTS);
- sas_queue_event(event, &ha->event_lock, &phy->port_events_pending,
+ sas_queue_event(event, &phy->port_events_pending,
&phy->port_events[event].work, ha);
}
@@ -50,7 +138,7 @@ static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
BUG_ON(event >= PHY_NUM_EVENTS);
- sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending,
+ sas_queue_event(event, &phy->phy_events_pending,
&phy->phy_events[event].work, ha);
}
@@ -62,8 +150,6 @@ int sas_init_events(struct sas_ha_struct *sas_ha)
int i;
- spin_lock_init(&sas_ha->event_lock);
-
for (i = 0; i < HA_NUM_EVENTS; i++) {
INIT_WORK(&sas_ha->ha_events[i].work, sas_ha_event_fns[i]);
sas_ha->ha_events[i].ha = sas_ha;
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index 1b831c55ec6e..05acd9e35fc4 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -28,6 +28,7 @@
#include "sas_internal.h"
+#include <scsi/sas_ata.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_sas.h>
#include "../scsi_sas_internal.h"
@@ -71,11 +72,18 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
struct sas_internal *i =
to_sas_internal(dev->port->ha->core.shost->transportt);
+ mutex_lock(&dev->ex_dev.cmd_mutex);
for (retry = 0; retry < 3; retry++) {
- task = sas_alloc_task(GFP_KERNEL);
- if (!task)
- return -ENOMEM;
+ if (test_bit(SAS_DEV_GONE, &dev->state)) {
+ res = -ECOMM;
+ break;
+ }
+ task = sas_alloc_task(GFP_KERNEL);
+ if (!task) {
+ res = -ENOMEM;
+ break;
+ }
task->dev = dev;
task->task_proto = dev->tproto;
sg_init_one(&task->smp_task.smp_req, req, req_size);
@@ -93,7 +101,7 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
if (res) {
del_timer(&task->timer);
SAS_DPRINTK("executing SMP task failed:%d\n", res);
- goto ex_err;
+ break;
}
wait_for_completion(&task->completion);
@@ -103,24 +111,30 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
i->dft->lldd_abort_task(task);
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
SAS_DPRINTK("SMP task aborted and not done\n");
- goto ex_err;
+ break;
}
}
if (task->task_status.resp == SAS_TASK_COMPLETE &&
task->task_status.stat == SAM_STAT_GOOD) {
res = 0;
break;
- } if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_UNDERRUN) {
+ }
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_UNDERRUN) {
/* no error, but return the number of bytes of
* underrun */
res = task->task_status.residual;
break;
- } if (task->task_status.resp == SAS_TASK_COMPLETE &&
- task->task_status.stat == SAS_DATA_OVERRUN) {
+ }
+ if (task->task_status.resp == SAS_TASK_COMPLETE &&
+ task->task_status.stat == SAS_DATA_OVERRUN) {
res = -EMSGSIZE;
break;
- } else {
+ }
+ if (task->task_status.resp == SAS_TASK_UNDELIVERED &&
+ task->task_status.stat == SAS_DEVICE_UNKNOWN)
+ break;
+ else {
SAS_DPRINTK("%s: task to dev %016llx response: 0x%x "
"status 0x%x\n", __func__,
SAS_ADDR(dev->sas_addr),
@@ -130,11 +144,10 @@ static int smp_execute_task(struct domain_device *dev, void *req, int req_size,
task = NULL;
}
}
-ex_err:
+ mutex_unlock(&dev->ex_dev.cmd_mutex);
+
BUG_ON(retry == 3 && task != NULL);
- if (task != NULL) {
- sas_free_task(task);
- }
+ sas_free_task(task);
return res;
}
@@ -153,19 +166,49 @@ static inline void *alloc_smp_resp(int size)
return kzalloc(size, GFP_KERNEL);
}
-/* ---------- Expander configuration ---------- */
+static char sas_route_char(struct domain_device *dev, struct ex_phy *phy)
+{
+ switch (phy->routing_attr) {
+ case TABLE_ROUTING:
+ if (dev->ex_dev.t2t_supp)
+ return 'U';
+ else
+ return 'T';
+ case DIRECT_ROUTING:
+ return 'D';
+ case SUBTRACTIVE_ROUTING:
+ return 'S';
+ default:
+ return '?';
+ }
+}
+
+static enum sas_dev_type to_dev_type(struct discover_resp *dr)
+{
+ /* This is detecting a failure to transmit initial dev to host
+ * FIS as described in section J.5 of sas-2 r16
+ */
+ if (dr->attached_dev_type == NO_DEVICE && dr->attached_sata_dev &&
+ dr->linkrate >= SAS_LINK_RATE_1_5_GBPS)
+ return SATA_PENDING;
+ else
+ return dr->attached_dev_type;
+}
-static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
- void *disc_resp)
+static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp)
{
+ enum sas_dev_type dev_type;
+ enum sas_linkrate linkrate;
+ u8 sas_addr[SAS_ADDR_SIZE];
+ struct smp_resp *resp = rsp;
+ struct discover_resp *dr = &resp->disc;
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
- struct smp_resp *resp = disc_resp;
- struct discover_resp *dr = &resp->disc;
struct sas_rphy *rphy = dev->rphy;
- int rediscover = (phy->phy != NULL);
+ bool new_phy = !phy->phy;
+ char *type;
- if (!rediscover) {
+ if (new_phy) {
phy->phy = sas_phy_alloc(&rphy->dev, phy_id);
/* FIXME: error_handling */
@@ -184,8 +227,13 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
break;
}
+ /* check if anything important changed to squelch debug */
+ dev_type = phy->attached_dev_type;
+ linkrate = phy->linkrate;
+ memcpy(sas_addr, phy->attached_sas_addr, SAS_ADDR_SIZE);
+
+ phy->attached_dev_type = to_dev_type(dr);
phy->phy_id = phy_id;
- phy->attached_dev_type = dr->attached_dev_type;
phy->linkrate = dr->linkrate;
phy->attached_sata_host = dr->attached_sata_host;
phy->attached_sata_dev = dr->attached_sata_dev;
@@ -200,9 +248,11 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
phy->last_da_index = -1;
phy->phy->identify.sas_address = SAS_ADDR(phy->attached_sas_addr);
- phy->phy->identify.device_type = phy->attached_dev_type;
+ phy->phy->identify.device_type = dr->attached_dev_type;
phy->phy->identify.initiator_port_protocols = phy->attached_iproto;
phy->phy->identify.target_port_protocols = phy->attached_tproto;
+ if (!phy->attached_tproto && dr->attached_sata_dev)
+ phy->phy->identify.target_port_protocols = SAS_PROTOCOL_SATA;
phy->phy->identify.phy_identifier = phy_id;
phy->phy->minimum_linkrate_hw = dr->hmin_linkrate;
phy->phy->maximum_linkrate_hw = dr->hmax_linkrate;
@@ -210,20 +260,76 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
phy->phy->maximum_linkrate = dr->pmax_linkrate;
phy->phy->negotiated_linkrate = phy->linkrate;
- if (!rediscover)
+ if (new_phy)
if (sas_phy_add(phy->phy)) {
sas_phy_free(phy->phy);
return;
}
- SAS_DPRINTK("ex %016llx phy%02d:%c attached: %016llx\n",
+ switch (phy->attached_dev_type) {
+ case SATA_PENDING:
+ type = "stp pending";
+ break;
+ case NO_DEVICE:
+ type = "no device";
+ break;
+ case SAS_END_DEV:
+ if (phy->attached_iproto) {
+ if (phy->attached_tproto)
+ type = "host+target";
+ else
+ type = "host";
+ } else {
+ if (dr->attached_sata_dev)
+ type = "stp";
+ else
+ type = "ssp";
+ }
+ break;
+ case EDGE_DEV:
+ case FANOUT_DEV:
+ type = "smp";
+ break;
+ default:
+ type = "unknown";
+ }
+
+ /* this routine is polled by libata error recovery so filter
+ * unimportant messages
+ */
+ if (new_phy || phy->attached_dev_type != dev_type ||
+ phy->linkrate != linkrate ||
+ SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr))
+ /* pass */;
+ else
+ return;
+
+ SAS_DPRINTK("ex %016llx phy%02d:%c:%X attached: %016llx (%s)\n",
SAS_ADDR(dev->sas_addr), phy->phy_id,
- phy->routing_attr == TABLE_ROUTING ? 'T' :
- phy->routing_attr == DIRECT_ROUTING ? 'D' :
- phy->routing_attr == SUBTRACTIVE_ROUTING ? 'S' : '?',
- SAS_ADDR(phy->attached_sas_addr));
+ sas_route_char(dev, phy), phy->linkrate,
+ SAS_ADDR(phy->attached_sas_addr), type);
+}
+
+/* check if we have an existing attached ata device on this expander phy */
+struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
+{
+ struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id];
+ struct domain_device *dev;
+ struct sas_rphy *rphy;
+
+ if (!ex_phy->port)
+ return NULL;
- return;
+ rphy = ex_phy->port->rphy;
+ if (!rphy)
+ return NULL;
+
+ dev = sas_find_dev_by_rphy(rphy);
+
+ if (dev && dev_is_sata(dev))
+ return dev;
+
+ return NULL;
}
#define DISCOVER_REQ_SIZE 16
@@ -232,39 +338,25 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req,
u8 *disc_resp, int single)
{
- int i, res;
+ struct discover_resp *dr;
+ int res;
disc_req[9] = single;
- for (i = 1 ; i < 3; i++) {
- struct discover_resp *dr;
- res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
- disc_resp, DISCOVER_RESP_SIZE);
- if (res)
- return res;
- /* This is detecting a failure to transmit initial
- * dev to host FIS as described in section G.5 of
- * sas-2 r 04b */
- dr = &((struct smp_resp *)disc_resp)->disc;
- if (memcmp(dev->sas_addr, dr->attached_sas_addr,
- SAS_ADDR_SIZE) == 0) {
- sas_printk("Found loopback topology, just ignore it!\n");
- return 0;
- }
- if (!(dr->attached_dev_type == 0 &&
- dr->attached_sata_dev))
- break;
- /* In order to generate the dev to host FIS, we
- * send a link reset to the expander port */
- sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL);
- /* Wait for the reset to trigger the negotiation */
- msleep(500);
+ res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE,
+ disc_resp, DISCOVER_RESP_SIZE);
+ if (res)
+ return res;
+ dr = &((struct smp_resp *)disc_resp)->disc;
+ if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) {
+ sas_printk("Found loopback topology, just ignore it!\n");
+ return 0;
}
sas_set_ex_phy(dev, single, disc_resp);
return 0;
}
-static int sas_ex_phy_discover(struct domain_device *dev, int single)
+int sas_ex_phy_discover(struct domain_device *dev, int single)
{
struct expander_device *ex = &dev->ex_dev;
int res = 0;
@@ -569,9 +661,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy)
#define RPS_REQ_SIZE 16
#define RPS_RESP_SIZE 60
-static int sas_get_report_phy_sata(struct domain_device *dev,
- int phy_id,
- struct smp_resp *rps_resp)
+int sas_get_report_phy_sata(struct domain_device *dev, int phy_id,
+ struct smp_resp *rps_resp)
{
int res;
u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE);
@@ -657,10 +748,11 @@ static struct domain_device *sas_ex_discover_end_dev(
if (phy->attached_sata_host || phy->attached_sata_ps)
return NULL;
- child = kzalloc(sizeof(*child), GFP_KERNEL);
+ child = sas_alloc_device();
if (!child)
return NULL;
+ kref_get(&parent->kref);
child->parent = parent;
child->port = parent->port;
child->iproto = phy->attached_iproto;
@@ -676,24 +768,13 @@ static struct domain_device *sas_ex_discover_end_dev(
}
}
sas_ex_get_linkrate(parent, child, phy);
+ sas_device_set_phy(child, phy->port);
#ifdef CONFIG_SCSI_SAS_ATA
if ((phy->attached_tproto & SAS_PROTOCOL_STP) || phy->attached_sata_dev) {
- child->dev_type = SATA_DEV;
- if (phy->attached_tproto & SAS_PROTOCOL_STP)
- child->tproto = phy->attached_tproto;
- if (phy->attached_sata_dev)
- child->tproto |= SATA_DEV;
- res = sas_get_report_phy_sata(parent, phy_id,
- &child->sata_dev.rps_resp);
- if (res) {
- SAS_DPRINTK("report phy sata to %016llx:0x%x returned "
- "0x%x\n", SAS_ADDR(parent->sas_addr),
- phy_id, res);
+ res = sas_get_ata_info(child, phy);
+ if (res)
goto out_free;
- }
- memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis,
- sizeof(struct dev_to_host_fis));
rphy = sas_end_device_alloc(phy->port);
if (unlikely(!rphy))
@@ -703,9 +784,7 @@ static struct domain_device *sas_ex_discover_end_dev(
child->rphy = rphy;
- spin_lock_irq(&parent->port->dev_list_lock);
- list_add_tail(&child->dev_list_node, &parent->port->dev_list);
- spin_unlock_irq(&parent->port->dev_list_lock);
+ list_add_tail(&child->disco_list_node, &parent->port->disco_list);
res = sas_discover_sata(child);
if (res) {
@@ -729,9 +808,7 @@ static struct domain_device *sas_ex_discover_end_dev(
child->rphy = rphy;
sas_fill_in_rphy(child, rphy);
- spin_lock_irq(&parent->port->dev_list_lock);
- list_add_tail(&child->dev_list_node, &parent->port->dev_list);
- spin_unlock_irq(&parent->port->dev_list_lock);
+ list_add_tail(&child->disco_list_node, &parent->port->disco_list);
res = sas_discover_end_dev(child);
if (res) {
@@ -755,6 +832,7 @@ static struct domain_device *sas_ex_discover_end_dev(
sas_rphy_free(child->rphy);
child->rphy = NULL;
+ list_del(&child->disco_list_node);
spin_lock_irq(&parent->port->dev_list_lock);
list_del(&child->dev_list_node);
spin_unlock_irq(&parent->port->dev_list_lock);
@@ -762,7 +840,7 @@ static struct domain_device *sas_ex_discover_end_dev(
sas_port_delete(phy->port);
out_err:
phy->port = NULL;
- kfree(child);
+ sas_put_device(child);
return NULL;
}
@@ -809,7 +887,7 @@ static struct domain_device *sas_ex_discover_expander(
phy->attached_phy_id);
return NULL;
}
- child = kzalloc(sizeof(*child), GFP_KERNEL);
+ child = sas_alloc_device();
if (!child)
return NULL;
@@ -835,6 +913,7 @@ static struct domain_device *sas_ex_discover_expander(
child->rphy = rphy;
edev = rphy_to_expander_device(rphy);
child->dev_type = phy->attached_dev_type;
+ kref_get(&parent->kref);
child->parent = parent;
child->port = port;
child->iproto = phy->attached_iproto;
@@ -858,7 +937,7 @@ static struct domain_device *sas_ex_discover_expander(
spin_lock_irq(&parent->port->dev_list_lock);
list_del(&child->dev_list_node);
spin_unlock_irq(&parent->port->dev_list_lock);
- kfree(child);
+ sas_put_device(child);
return NULL;
}
list_add_tail(&child->siblings, &parent->ex_dev.children);
@@ -908,7 +987,8 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
if (ex_phy->attached_dev_type != SAS_END_DEV &&
ex_phy->attached_dev_type != FANOUT_DEV &&
- ex_phy->attached_dev_type != EDGE_DEV) {
+ ex_phy->attached_dev_type != EDGE_DEV &&
+ ex_phy->attached_dev_type != SATA_PENDING) {
SAS_DPRINTK("unknown device type(0x%x) attached to ex %016llx "
"phy 0x%x\n", ex_phy->attached_dev_type,
SAS_ADDR(dev->sas_addr),
@@ -934,6 +1014,7 @@ static int sas_ex_discover_dev(struct domain_device *dev, int phy_id)
switch (ex_phy->attached_dev_type) {
case SAS_END_DEV:
+ case SATA_PENDING:
child = sas_ex_discover_end_dev(dev, phy_id);
break;
case FANOUT_DEV:
@@ -1128,32 +1209,25 @@ static void sas_print_parent_topology_bug(struct domain_device *child,
struct ex_phy *parent_phy,
struct ex_phy *child_phy)
{
- static const char ra_char[] = {
- [DIRECT_ROUTING] = 'D',
- [SUBTRACTIVE_ROUTING] = 'S',
- [TABLE_ROUTING] = 'T',
- };
static const char *ex_type[] = {
[EDGE_DEV] = "edge",
[FANOUT_DEV] = "fanout",
};
struct domain_device *parent = child->parent;
- sas_printk("%s ex %016llx (T2T supp:%d) phy 0x%x <--> %s ex %016llx "
- "(T2T supp:%d) phy 0x%x has %c:%c routing link!\n",
+ sas_printk("%s ex %016llx phy 0x%x <--> %s ex %016llx "
+ "phy 0x%x has %c:%c routing link!\n",
ex_type[parent->dev_type],
SAS_ADDR(parent->sas_addr),
- parent->ex_dev.t2t_supp,
parent_phy->phy_id,
ex_type[child->dev_type],
SAS_ADDR(child->sas_addr),
- child->ex_dev.t2t_supp,
child_phy->phy_id,
- ra_char[parent_phy->routing_attr],
- ra_char[child_phy->routing_attr]);
+ sas_route_char(parent, parent_phy),
+ sas_route_char(child, child_phy));
}
static int sas_check_eeds(struct domain_device *child,
@@ -1610,8 +1684,8 @@ static int sas_get_phy_change_count(struct domain_device *dev,
return res;
}
-static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
- int phy_id, u8 *attached_sas_addr)
+static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id,
+ u8 *sas_addr, enum sas_dev_type *type)
{
int res;
struct smp_resp *disc_resp;
@@ -1623,10 +1697,11 @@ static int sas_get_phy_attached_sas_addr(struct domain_device *dev,
dr = &disc_resp->disc;
res = sas_get_phy_discover(dev, phy_id, disc_resp);
- if (!res) {
- memcpy(attached_sas_addr,disc_resp->disc.attached_sas_addr,8);
- if (dr->attached_dev_type == 0)
- memset(attached_sas_addr, 0, 8);
+ if (res == 0) {
+ memcpy(sas_addr, disc_resp->disc.attached_sas_addr, 8);
+ *type = to_dev_type(dr);
+ if (*type == 0)
+ memset(sas_addr, 0, 8);
}
kfree(disc_resp);
return res;
@@ -1748,7 +1823,7 @@ static void sas_unregister_ex_tree(struct asd_sas_port *port, struct domain_devi
struct domain_device *child, *n;
list_for_each_entry_safe(child, n, &ex->children, siblings) {
- child->gone = 1;
+ set_bit(SAS_DEV_GONE, &child->state);
if (child->dev_type == EDGE_DEV ||
child->dev_type == FANOUT_DEV)
sas_unregister_ex_tree(port, child);
@@ -1763,27 +1838,28 @@ static void sas_unregister_devs_sas_addr(struct domain_device *parent,
{
struct expander_device *ex_dev = &parent->ex_dev;
struct ex_phy *phy = &ex_dev->ex_phy[phy_id];
- struct domain_device *child, *n;
+ struct domain_device *child, *n, *found = NULL;
if (last) {
list_for_each_entry_safe(child, n,
&ex_dev->children, siblings) {
if (SAS_ADDR(child->sas_addr) ==
SAS_ADDR(phy->attached_sas_addr)) {
- child->gone = 1;
+ set_bit(SAS_DEV_GONE, &child->state);
if (child->dev_type == EDGE_DEV ||
child->dev_type == FANOUT_DEV)
sas_unregister_ex_tree(parent->port, child);
else
sas_unregister_dev(parent->port, child);
+ found = child;
break;
}
}
- parent->gone = 1;
sas_disable_routing(parent, phy->attached_sas_addr);
}
memset(phy->attached_sas_addr, 0, SAS_ADDR_SIZE);
if (phy->port) {
sas_port_delete_phy(phy->port, phy->phy);
+ sas_device_set_phy(found, phy->port);
if (phy->port->num_phys == 0)
sas_port_delete(phy->port);
phy->port = NULL;
@@ -1874,39 +1950,71 @@ out:
return res;
}
+static bool dev_type_flutter(enum sas_dev_type new, enum sas_dev_type old)
+{
+ if (old == new)
+ return true;
+
+ /* treat device directed resets as flutter, if we went
+ * SAS_END_DEV to SATA_PENDING the link needs recovery
+ */
+ if ((old == SATA_PENDING && new == SAS_END_DEV) ||
+ (old == SAS_END_DEV && new == SATA_PENDING))
+ return true;
+
+ return false;
+}
+
static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last)
{
struct expander_device *ex = &dev->ex_dev;
struct ex_phy *phy = &ex->ex_phy[phy_id];
- u8 attached_sas_addr[8];
+ enum sas_dev_type type = NO_DEVICE;
+ u8 sas_addr[8];
int res;
- res = sas_get_phy_attached_sas_addr(dev, phy_id, attached_sas_addr);
+ res = sas_get_phy_attached_dev(dev, phy_id, sas_addr, &type);
switch (res) {
case SMP_RESP_NO_PHY:
phy->phy_state = PHY_NOT_PRESENT;
sas_unregister_devs_sas_addr(dev, phy_id, last);
- goto out; break;
+ return res;
case SMP_RESP_PHY_VACANT:
phy->phy_state = PHY_VACANT;
sas_unregister_devs_sas_addr(dev, phy_id, last);
- goto out; break;
+ return res;
case SMP_RESP_FUNC_ACC:
break;
}
- if (SAS_ADDR(attached_sas_addr) == 0) {
+ if (SAS_ADDR(sas_addr) == 0) {
phy->phy_state = PHY_EMPTY;
sas_unregister_devs_sas_addr(dev, phy_id, last);
- } else if (SAS_ADDR(attached_sas_addr) ==
- SAS_ADDR(phy->attached_sas_addr)) {
- SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter\n",
- SAS_ADDR(dev->sas_addr), phy_id);
+ return res;
+ } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) &&
+ dev_type_flutter(type, phy->attached_dev_type)) {
+ struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id);
+ char *action = "";
+
sas_ex_phy_discover(dev, phy_id);
- } else
- res = sas_discover_new(dev, phy_id);
-out:
- return res;
+
+ if (ata_dev && phy->attached_dev_type == SATA_PENDING)
+ action = ", needs recovery";
+ SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n",
+ SAS_ADDR(dev->sas_addr), phy_id, action);
+ return res;
+ }
+
+ /* delete the old link */
+ if (SAS_ADDR(phy->attached_sas_addr) &&
+ SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr)) {
+ SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n",
+ SAS_ADDR(dev->sas_addr), phy_id,
+ SAS_ADDR(phy->attached_sas_addr));
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
+ }
+
+ return sas_discover_new(dev, phy_id);
}
/**
diff --git a/drivers/scsi/libsas/sas_host_smp.c b/drivers/scsi/libsas/sas_host_smp.c
index 3814d3eed401..d24792575169 100644
--- a/drivers/scsi/libsas/sas_host_smp.c
+++ b/drivers/scsi/libsas/sas_host_smp.c
@@ -187,11 +187,14 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
struct sas_internal *i =
to_sas_internal(sas_ha->core.shost->transportt);
struct sas_phy_linkrates rates;
+ struct asd_sas_phy *asd_phy;
if (phy_id >= sas_ha->num_phys) {
resp_data[2] = SMP_RESP_NO_PHY;
return;
}
+
+ asd_phy = sas_ha->sas_phy[phy_id];
switch (phy_op) {
case PHY_FUNC_NOP:
case PHY_FUNC_LINK_RESET:
@@ -210,7 +213,13 @@ static void sas_phy_control(struct sas_ha_struct *sas_ha, u8 phy_id,
rates.minimum_linkrate = min;
rates.maximum_linkrate = max;
- if (i->dft->lldd_control_phy(sas_ha->sas_phy[phy_id], phy_op, &rates))
+ /* filter reset requests through libata eh */
+ if (phy_op == PHY_FUNC_LINK_RESET && sas_try_ata_reset(asd_phy) == 0) {
+ resp_data[2] = SMP_RESP_FUNC_ACC;
+ return;
+ }
+
+ if (i->dft->lldd_control_phy(asd_phy, phy_op, &rates))
resp_data[2] = SMP_RESP_FUNC_FAILED;
else
resp_data[2] = SMP_RESP_FUNC_ACC;
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index d81c3b1989f7..120bff64be30 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -28,6 +28,7 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/spinlock.h>
+#include <scsi/sas_ata.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
@@ -97,14 +98,14 @@ void sas_hae_reset(struct work_struct *work)
container_of(work, struct sas_ha_event, work);
struct sas_ha_struct *ha = ev->ha;
- sas_begin_event(HAE_RESET, &ha->event_lock,
- &ha->pending);
+ clear_bit(HAE_RESET, &ha->pending);
}
int sas_register_ha(struct sas_ha_struct *sas_ha)
{
int error = 0;
+ mutex_init(&sas_ha->disco_mutex);
spin_lock_init(&sas_ha->phy_port_lock);
sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
@@ -113,8 +114,10 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
else if (sas_ha->lldd_queue_size == -1)
sas_ha->lldd_queue_size = 128; /* Sanity */
- sas_ha->state = SAS_HA_REGISTERED;
+ set_bit(SAS_HA_REGISTERED, &sas_ha->state);
spin_lock_init(&sas_ha->state_lock);
+ mutex_init(&sas_ha->drain_mutex);
+ INIT_LIST_HEAD(&sas_ha->defer_q);
error = sas_register_phys(sas_ha);
if (error) {
@@ -144,6 +147,7 @@ int sas_register_ha(struct sas_ha_struct *sas_ha)
}
INIT_LIST_HEAD(&sas_ha->eh_done_q);
+ INIT_LIST_HEAD(&sas_ha->eh_ata_q);
return 0;
@@ -156,17 +160,23 @@ Undo_phys:
int sas_unregister_ha(struct sas_ha_struct *sas_ha)
{
- unsigned long flags;
-
- /* Set the state to unregistered to avoid further
- * events to be queued */
- spin_lock_irqsave(&sas_ha->state_lock, flags);
- sas_ha->state = SAS_HA_UNREGISTERED;
- spin_unlock_irqrestore(&sas_ha->state_lock, flags);
- scsi_flush_work(sas_ha->core.shost);
+ /* Set the state to unregistered to avoid further unchained
+ * events to be queued, and flush any in-progress drainers
+ */
+ mutex_lock(&sas_ha->drain_mutex);
+ spin_lock_irq(&sas_ha->state_lock);
+ clear_bit(SAS_HA_REGISTERED, &sas_ha->state);
+ spin_unlock_irq(&sas_ha->state_lock);
+ __sas_drain_work(sas_ha);
+ mutex_unlock(&sas_ha->drain_mutex);
sas_unregister_ports(sas_ha);
+ /* flush unregistration work */
+ mutex_lock(&sas_ha->drain_mutex);
+ __sas_drain_work(sas_ha);
+ mutex_unlock(&sas_ha->drain_mutex);
+
if (sas_ha->lldd_max_execute_num > 1) {
sas_shutdown_queue(sas_ha);
sas_ha->lldd_max_execute_num = 1;
@@ -190,15 +200,75 @@ static int sas_get_linkerrors(struct sas_phy *phy)
return sas_smp_get_phy_events(phy);
}
-int sas_phy_enable(struct sas_phy *phy, int enable)
+int sas_try_ata_reset(struct asd_sas_phy *asd_phy)
+{
+ struct domain_device *dev = NULL;
+
+ /* try to route user requested link resets through libata */
+ if (asd_phy->port)
+ dev = asd_phy->port->port_dev;
+
+ /* validate that dev has been probed */
+ if (dev)
+ dev = sas_find_dev_by_rphy(dev->rphy);
+
+ if (dev && dev_is_sata(dev)) {
+ sas_ata_schedule_reset(dev);
+ sas_ata_wait_eh(dev);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+/**
+ * transport_sas_phy_reset - reset a phy and permit libata to manage the link
+ *
+ * phy reset request via sysfs in host workqueue context so we know we
+ * can block on eh and safely traverse the domain_device topology
+ */
+static int transport_sas_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ enum phy_func reset_type;
+
+ if (hard_reset)
+ reset_type = PHY_FUNC_HARD_RESET;
+ else
+ reset_type = PHY_FUNC_LINK_RESET;
+
+ if (scsi_is_sas_phy_local(phy)) {
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
+ struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
+ struct sas_internal *i =
+ to_sas_internal(sas_ha->core.shost->transportt);
+
+ if (!hard_reset && sas_try_ata_reset(asd_phy) == 0)
+ return 0;
+ return i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
+ } else {
+ struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
+ struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
+ struct domain_device *ata_dev = sas_ex_to_ata(ddev, phy->number);
+
+ if (ata_dev && !hard_reset) {
+ sas_ata_schedule_reset(ata_dev);
+ sas_ata_wait_eh(ata_dev);
+ return 0;
+ } else
+ return sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
+ }
+}
+
+static int sas_phy_enable(struct sas_phy *phy, int enable)
{
int ret;
- enum phy_func command;
+ enum phy_func cmd;
if (enable)
- command = PHY_FUNC_LINK_RESET;
+ cmd = PHY_FUNC_LINK_RESET;
else
- command = PHY_FUNC_DISABLE;
+ cmd = PHY_FUNC_DISABLE;
if (scsi_is_sas_phy_local(phy)) {
struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
@@ -207,15 +277,18 @@ int sas_phy_enable(struct sas_phy *phy, int enable)
struct sas_internal *i =
to_sas_internal(sas_ha->core.shost->transportt);
- if (!enable) {
- sas_phy_disconnected(asd_phy);
- sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL);
- }
- ret = i->dft->lldd_control_phy(asd_phy, command, NULL);
+ if (enable)
+ ret = transport_sas_phy_reset(phy, 0);
+ else
+ ret = i->dft->lldd_control_phy(asd_phy, cmd, NULL);
} else {
struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
- ret = sas_smp_phy_control(ddev, phy->number, command, NULL);
+
+ if (enable)
+ ret = transport_sas_phy_reset(phy, 0);
+ else
+ ret = sas_smp_phy_control(ddev, phy->number, cmd, NULL);
}
return ret;
}
@@ -225,6 +298,9 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset)
int ret;
enum phy_func reset_type;
+ if (!phy->enabled)
+ return -ENODEV;
+
if (hard_reset)
reset_type = PHY_FUNC_HARD_RESET;
else
@@ -285,9 +361,101 @@ int sas_set_phy_speed(struct sas_phy *phy,
return ret;
}
+static void sas_phy_release(struct sas_phy *phy)
+{
+ kfree(phy->hostdata);
+ phy->hostdata = NULL;
+}
+
+static void phy_reset_work(struct work_struct *work)
+{
+ struct sas_phy_data *d = container_of(work, typeof(*d), reset_work);
+
+ d->reset_result = transport_sas_phy_reset(d->phy, d->hard_reset);
+}
+
+static void phy_enable_work(struct work_struct *work)
+{
+ struct sas_phy_data *d = container_of(work, typeof(*d), enable_work);
+
+ d->enable_result = sas_phy_enable(d->phy, d->enable);
+}
+
+static int sas_phy_setup(struct sas_phy *phy)
+{
+ struct sas_phy_data *d = kzalloc(sizeof(*d), GFP_KERNEL);
+
+ if (!d)
+ return -ENOMEM;
+
+ mutex_init(&d->event_lock);
+ INIT_WORK(&d->reset_work, phy_reset_work);
+ INIT_WORK(&d->enable_work, phy_enable_work);
+ d->phy = phy;
+ phy->hostdata = d;
+
+ return 0;
+}
+
+static int queue_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_phy_data *d = phy->hostdata;
+ int rc;
+
+ if (!d)
+ return -ENOMEM;
+
+ /* libsas workqueue coordinates ata-eh reset with discovery */
+ mutex_lock(&d->event_lock);
+ d->reset_result = 0;
+ d->hard_reset = hard_reset;
+
+ spin_lock_irq(&ha->state_lock);
+ sas_queue_work(ha, &d->reset_work);
+ spin_unlock_irq(&ha->state_lock);
+
+ rc = sas_drain_work(ha);
+ if (rc == 0)
+ rc = d->reset_result;
+ mutex_unlock(&d->event_lock);
+
+ return rc;
+}
+
+static int queue_phy_enable(struct sas_phy *phy, int enable)
+{
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ struct sas_phy_data *d = phy->hostdata;
+ int rc;
+
+ if (!d)
+ return -ENOMEM;
+
+ /* libsas workqueue coordinates ata-eh reset with discovery */
+ mutex_lock(&d->event_lock);
+ d->enable_result = 0;
+ d->enable = enable;
+
+ spin_lock_irq(&ha->state_lock);
+ sas_queue_work(ha, &d->enable_work);
+ spin_unlock_irq(&ha->state_lock);
+
+ rc = sas_drain_work(ha);
+ if (rc == 0)
+ rc = d->enable_result;
+ mutex_unlock(&d->event_lock);
+
+ return rc;
+}
+
static struct sas_function_template sft = {
- .phy_enable = sas_phy_enable,
- .phy_reset = sas_phy_reset,
+ .phy_enable = queue_phy_enable,
+ .phy_reset = queue_phy_reset,
+ .phy_setup = sas_phy_setup,
+ .phy_release = sas_phy_release,
.set_phy_speed = sas_set_phy_speed,
.get_linkerrors = sas_get_linkerrors,
.smp_handler = sas_smp_handler,
diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h
index 14e21b5fb8ba..f05c63879949 100644
--- a/drivers/scsi/libsas/sas_internal.h
+++ b/drivers/scsi/libsas/sas_internal.h
@@ -30,6 +30,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport_sas.h>
#include <scsi/libsas.h>
+#include <scsi/sas_ata.h>
#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
@@ -38,6 +39,18 @@
#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble)
#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0)
+struct sas_phy_data {
+ /* let reset be performed in sas_queue_work() context */
+ struct sas_phy *phy;
+ struct mutex event_lock;
+ int hard_reset;
+ int reset_result;
+ struct work_struct reset_work;
+ int enable;
+ int enable_result;
+ struct work_struct enable_work;
+};
+
void sas_scsi_recover_host(struct Scsi_Host *shost);
int sas_show_class(enum sas_class class, char *buf);
@@ -56,6 +69,9 @@ enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
int sas_init_queue(struct sas_ha_struct *sas_ha);
int sas_init_events(struct sas_ha_struct *sas_ha);
void sas_shutdown_queue(struct sas_ha_struct *sas_ha);
+void sas_disable_revalidation(struct sas_ha_struct *ha);
+void sas_enable_revalidation(struct sas_ha_struct *ha);
+void __sas_drain_work(struct sas_ha_struct *ha);
void sas_deform_port(struct asd_sas_phy *phy, int gone);
@@ -64,6 +80,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work);
void sas_porte_link_reset_err(struct work_struct *work);
void sas_porte_timer_event(struct work_struct *work);
void sas_porte_hard_reset(struct work_struct *work);
+void sas_queue_work(struct sas_ha_struct *ha, struct work_struct *work);
int sas_notify_lldd_dev_found(struct domain_device *);
void sas_notify_lldd_dev_gone(struct domain_device *);
@@ -72,10 +89,17 @@ int sas_smp_phy_control(struct domain_device *dev, int phy_id,
enum phy_func phy_func, struct sas_phy_linkrates *);
int sas_smp_get_phy_events(struct sas_phy *phy);
+void sas_device_set_phy(struct domain_device *dev, struct sas_port *port);
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
-
+struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id);
+int sas_ex_phy_discover(struct domain_device *dev, int single);
+int sas_get_report_phy_sata(struct domain_device *dev, int phy_id,
+ struct smp_resp *rps_resp);
+int sas_try_ata_reset(struct asd_sas_phy *phy);
void sas_hae_reset(struct work_struct *work);
+void sas_free_device(struct kref *kref);
+
#ifdef CONFIG_SCSI_SAS_HOST_SMP
extern int sas_smp_host_handler(struct Scsi_Host *shost, struct request *req,
struct request *rsp);
@@ -90,36 +114,13 @@ static inline int sas_smp_host_handler(struct Scsi_Host *shost,
}
#endif
-static inline void sas_queue_event(int event, spinlock_t *lock,
- unsigned long *pending,
- struct work_struct *work,
- struct sas_ha_struct *sas_ha)
+static inline void sas_fail_probe(struct domain_device *dev, const char *func, int err)
{
- unsigned long flags;
-
- spin_lock_irqsave(lock, flags);
- if (test_bit(event, pending)) {
- spin_unlock_irqrestore(lock, flags);
- return;
- }
- __set_bit(event, pending);
- spin_unlock_irqrestore(lock, flags);
-
- spin_lock_irqsave(&sas_ha->state_lock, flags);
- if (sas_ha->state != SAS_HA_UNREGISTERED) {
- scsi_queue_work(sas_ha->core.shost, work);
- }
- spin_unlock_irqrestore(&sas_ha->state_lock, flags);
-}
-
-static inline void sas_begin_event(int event, spinlock_t *lock,
- unsigned long *pending)
-{
- unsigned long flags;
-
- spin_lock_irqsave(lock, flags);
- __clear_bit(event, pending);
- spin_unlock_irqrestore(lock, flags);
+ SAS_DPRINTK("%s: for %s device %16llx returned %d\n",
+ func, dev->parent ? "exp-attached" :
+ "direct-attached",
+ SAS_ADDR(dev->sas_addr), err);
+ sas_unregister_dev(dev->port, dev);
}
static inline void sas_fill_in_rphy(struct domain_device *dev,
@@ -132,6 +133,7 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,
case SATA_DEV:
/* FIXME: need sata device type */
case SAS_END_DEV:
+ case SATA_PENDING:
rphy->identify.device_type = SAS_END_DEVICE;
break;
case EDGE_DEV:
@@ -146,6 +148,22 @@ static inline void sas_fill_in_rphy(struct domain_device *dev,
}
}
+static inline void sas_phy_set_target(struct asd_sas_phy *p, struct domain_device *dev)
+{
+ struct sas_phy *phy = p->phy;
+
+ if (dev) {
+ if (dev_is_sata(dev))
+ phy->identify.device_type = SAS_END_DEVICE;
+ else
+ phy->identify.device_type = dev->dev_type;
+ phy->identify.target_port_protocols = dev->tproto;
+ } else {
+ phy->identify.device_type = SAS_PHY_UNUSED;
+ phy->identify.target_port_protocols = 0;
+ }
+}
+
static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)
{
struct expander_device *ex = &dev->ex_dev;
@@ -161,4 +179,23 @@ static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)
sas_port_add_phy(ex->parent_port, ex_phy->phy);
}
+static inline struct domain_device *sas_alloc_device(void)
+{
+ struct domain_device *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+
+ if (dev) {
+ INIT_LIST_HEAD(&dev->siblings);
+ INIT_LIST_HEAD(&dev->dev_list_node);
+ INIT_LIST_HEAD(&dev->disco_list_node);
+ kref_init(&dev->kref);
+ spin_lock_init(&dev->done_lock);
+ }
+ return dev;
+}
+
+static inline void sas_put_device(struct domain_device *dev)
+{
+ kref_put(&dev->kref, sas_free_device);
+}
+
#endif /* _SAS_INTERNAL_H_ */
diff --git a/drivers/scsi/libsas/sas_phy.c b/drivers/scsi/libsas/sas_phy.c
index e0f5018e9071..dcfd4a9105c5 100644
--- a/drivers/scsi/libsas/sas_phy.c
+++ b/drivers/scsi/libsas/sas_phy.c
@@ -36,8 +36,7 @@ static void sas_phye_loss_of_signal(struct work_struct *work)
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock,
- &phy->phy_events_pending);
+ clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending);
phy->error = 0;
sas_deform_port(phy, 1);
}
@@ -48,8 +47,7 @@ static void sas_phye_oob_done(struct work_struct *work)
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock,
- &phy->phy_events_pending);
+ clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending);
phy->error = 0;
}
@@ -63,8 +61,7 @@ static void sas_phye_oob_error(struct work_struct *work)
struct sas_internal *i =
to_sas_internal(sas_ha->core.shost->transportt);
- sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock,
- &phy->phy_events_pending);
+ clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending);
sas_deform_port(phy, 1);
@@ -95,8 +92,7 @@ static void sas_phye_spinup_hold(struct work_struct *work)
struct sas_internal *i =
to_sas_internal(sas_ha->core.shost->transportt);
- sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock,
- &phy->phy_events_pending);
+ clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending);
phy->error = 0;
i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
diff --git a/drivers/scsi/libsas/sas_port.c b/drivers/scsi/libsas/sas_port.c
index 42fd1f25b664..eb19c016d500 100644
--- a/drivers/scsi/libsas/sas_port.c
+++ b/drivers/scsi/libsas/sas_port.c
@@ -104,13 +104,11 @@ static void sas_form_port(struct asd_sas_phy *phy)
/* add the phy to the port */
list_add_tail(&phy->port_phy_el, &port->phy_list);
+ sas_phy_set_target(phy, port->port_dev);
phy->port = port;
port->num_phys++;
port->phy_mask |= (1U << phy->id);
- if (!port->phy)
- port->phy = phy->phy;
-
if (*(u64 *)port->attached_sas_addr == 0) {
port->class = phy->class;
memcpy(port->attached_sas_addr, phy->attached_sas_addr,
@@ -125,7 +123,7 @@ static void sas_form_port(struct asd_sas_phy *phy)
spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
if (!port->port) {
- port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
+ port->port = sas_port_alloc(phy->phy->dev.parent, phy->id);
BUG_ON(!port->port);
sas_port_add(port->port);
}
@@ -170,13 +168,13 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
dev->pathways--;
if (port->num_phys == 1) {
- if (dev && gone)
- dev->gone = 1;
- sas_unregister_domain_devices(port);
+ sas_unregister_domain_devices(port, gone);
sas_port_delete(port->port);
port->port = NULL;
- } else
+ } else {
sas_port_delete_phy(port->port, phy->phy);
+ sas_device_set_phy(dev, port->port);
+ }
if (si->dft->lldd_port_deformed)
si->dft->lldd_port_deformed(phy);
@@ -185,6 +183,7 @@ void sas_deform_port(struct asd_sas_phy *phy, int gone)
spin_lock(&port->phy_list_lock);
list_del_init(&phy->port_phy_el);
+ sas_phy_set_target(phy, NULL);
phy->port = NULL;
port->num_phys--;
port->phy_mask &= ~(1U << phy->id);
@@ -213,8 +212,7 @@ void sas_porte_bytes_dmaed(struct work_struct *work)
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock,
- &phy->port_events_pending);
+ clear_bit(PORTE_BYTES_DMAED, &phy->port_events_pending);
sas_form_port(phy);
}
@@ -227,8 +225,7 @@ void sas_porte_broadcast_rcvd(struct work_struct *work)
unsigned long flags;
u32 prim;
- sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock,
- &phy->port_events_pending);
+ clear_bit(PORTE_BROADCAST_RCVD, &phy->port_events_pending);
spin_lock_irqsave(&phy->sas_prim_lock, flags);
prim = phy->sas_prim;
@@ -244,8 +241,7 @@ void sas_porte_link_reset_err(struct work_struct *work)
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock,
- &phy->port_events_pending);
+ clear_bit(PORTE_LINK_RESET_ERR, &phy->port_events_pending);
sas_deform_port(phy, 1);
}
@@ -256,8 +252,7 @@ void sas_porte_timer_event(struct work_struct *work)
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock,
- &phy->port_events_pending);
+ clear_bit(PORTE_TIMER_EVENT, &phy->port_events_pending);
sas_deform_port(phy, 1);
}
@@ -268,8 +263,7 @@ void sas_porte_hard_reset(struct work_struct *work)
container_of(work, struct asd_sas_event, work);
struct asd_sas_phy *phy = ev->phy;
- sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock,
- &phy->port_events_pending);
+ clear_bit(PORTE_HARD_RESET, &phy->port_events_pending);
sas_deform_port(phy, 1);
}
@@ -282,6 +276,8 @@ static void sas_init_port(struct asd_sas_port *port,
memset(port, 0, sizeof(*port));
port->id = i;
INIT_LIST_HEAD(&port->dev_list);
+ INIT_LIST_HEAD(&port->disco_list);
+ INIT_LIST_HEAD(&port->destroy_list);
spin_lock_init(&port->phy_list_lock);
INIT_LIST_HEAD(&port->phy_list);
port->ha = sas_ha;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index b6e233d9a0a1..f0b9b7bf1882 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -49,27 +49,12 @@
#include <linux/scatterlist.h>
#include <linux/libata.h>
-/* ---------- SCSI Host glue ---------- */
-
-static void sas_scsi_task_done(struct sas_task *task)
+/* record final status and free the task */
+static void sas_end_task(struct scsi_cmnd *sc, struct sas_task *task)
{
struct task_status_struct *ts = &task->task_status;
- struct scsi_cmnd *sc = task->uldd_task;
int hs = 0, stat = 0;
- if (unlikely(task->task_state_flags & SAS_TASK_STATE_ABORTED)) {
- /* Aborted tasks will be completed by the error handler */
- SAS_DPRINTK("task done but aborted\n");
- return;
- }
-
- if (unlikely(!sc)) {
- SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n");
- list_del_init(&task->list);
- sas_free_task(task);
- return;
- }
-
if (ts->resp == SAS_TASK_UNDELIVERED) {
/* transport error */
hs = DID_NO_CONNECT;
@@ -124,10 +109,41 @@ static void sas_scsi_task_done(struct sas_task *task)
break;
}
}
- ASSIGN_SAS_TASK(sc, NULL);
+
sc->result = (hs << 16) | stat;
+ ASSIGN_SAS_TASK(sc, NULL);
list_del_init(&task->list);
sas_free_task(task);
+}
+
+static void sas_scsi_task_done(struct sas_task *task)
+{
+ struct scsi_cmnd *sc = task->uldd_task;
+ struct domain_device *dev = task->dev;
+ struct sas_ha_struct *ha = dev->port->ha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->done_lock, flags);
+ if (test_bit(SAS_HA_FROZEN, &ha->state))
+ task = NULL;
+ else
+ ASSIGN_SAS_TASK(sc, NULL);
+ spin_unlock_irqrestore(&dev->done_lock, flags);
+
+ if (unlikely(!task)) {
+ /* task will be completed by the error handler */
+ SAS_DPRINTK("task done but aborted\n");
+ return;
+ }
+
+ if (unlikely(!sc)) {
+ SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n");
+ list_del_init(&task->list);
+ sas_free_task(task);
+ return;
+ }
+
+ sas_end_task(sc, task);
sc->scsi_done(sc);
}
@@ -192,17 +208,15 @@ int sas_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
int res = 0;
/* If the device fell off, no sense in issuing commands */
- if (dev->gone) {
+ if (test_bit(SAS_DEV_GONE, &dev->state)) {
cmd->result = DID_BAD_TARGET << 16;
goto out_done;
}
if (dev_is_sata(dev)) {
- unsigned long flags;
-
- spin_lock_irqsave(dev->sata_dev.ap->lock, flags);
+ spin_lock_irq(dev->sata_dev.ap->lock);
res = ata_sas_queuecmd(cmd, dev->sata_dev.ap);
- spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags);
+ spin_unlock_irq(dev->sata_dev.ap->lock);
return res;
}
@@ -235,24 +249,38 @@ out_done:
static void sas_eh_finish_cmd(struct scsi_cmnd *cmd)
{
- struct sas_task *task = TO_SAS_TASK(cmd);
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(cmd->device->host);
+ struct sas_task *task = TO_SAS_TASK(cmd);
+
+ /* At this point, we only get called following an actual abort
+ * of the task, so we should be guaranteed not to be racing with
+ * any completions from the LLD. Task is freed after this.
+ */
+ sas_end_task(cmd, task);
- /* remove the aborted task flag to allow the task to be
- * completed now. At this point, we only get called following
- * an actual abort of the task, so we should be guaranteed not
- * to be racing with any completions from the LLD (hence we
- * don't need the task state lock to clear the flag) */
- task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
- /* Now call task_done. However, task will be free'd after
- * this */
- task->task_done(task);
/* now finish the command and move it on to the error
* handler done list, this also takes it off the
- * error handler pending list */
+ * error handler pending list.
+ */
scsi_eh_finish_cmd(cmd, &sas_ha->eh_done_q);
}
+static void sas_eh_defer_cmd(struct scsi_cmnd *cmd)
+{
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_ha_struct *ha = dev->port->ha;
+ struct sas_task *task = TO_SAS_TASK(cmd);
+
+ if (!dev_is_sata(dev)) {
+ sas_eh_finish_cmd(cmd);
+ return;
+ }
+
+ /* report the timeout to libata */
+ sas_end_task(cmd, task);
+ list_move_tail(&cmd->eh_entry, &ha->eh_ata_q);
+}
+
static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd)
{
struct scsi_cmnd *cmd, *n;
@@ -260,7 +288,7 @@ static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd
list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
if (cmd->device->sdev_target == my_cmd->device->sdev_target &&
cmd->device->lun == my_cmd->device->lun)
- sas_eh_finish_cmd(cmd);
+ sas_eh_defer_cmd(cmd);
}
}
@@ -295,6 +323,7 @@ enum task_disposition {
TASK_IS_DONE,
TASK_IS_ABORTED,
TASK_IS_AT_LU,
+ TASK_IS_NOT_AT_HA,
TASK_IS_NOT_AT_LU,
TASK_ABORT_FAILED,
};
@@ -311,19 +340,18 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task)
struct scsi_core *core = &ha->core;
struct sas_task *t, *n;
+ mutex_lock(&core->task_queue_flush);
spin_lock_irqsave(&core->task_queue_lock, flags);
- list_for_each_entry_safe(t, n, &core->task_queue, list) {
+ list_for_each_entry_safe(t, n, &core->task_queue, list)
if (task == t) {
list_del_init(&t->list);
- spin_unlock_irqrestore(&core->task_queue_lock,
- flags);
- SAS_DPRINTK("%s: task 0x%p aborted from "
- "task_queue\n",
- __func__, task);
- return TASK_IS_ABORTED;
+ break;
}
- }
spin_unlock_irqrestore(&core->task_queue_lock, flags);
+ mutex_unlock(&core->task_queue_flush);
+
+ if (task == t)
+ return TASK_IS_NOT_AT_HA;
}
for (i = 0; i < 5; i++) {
@@ -411,30 +439,26 @@ static int sas_recover_I_T(struct domain_device *dev)
return res;
}
-/* Find the sas_phy that's attached to this device */
-struct sas_phy *sas_find_local_phy(struct domain_device *dev)
+/* take a reference on the last known good phy for this device */
+struct sas_phy *sas_get_local_phy(struct domain_device *dev)
{
- struct domain_device *pdev = dev->parent;
- struct ex_phy *exphy = NULL;
- int i;
+ struct sas_ha_struct *ha = dev->port->ha;
+ struct sas_phy *phy;
+ unsigned long flags;
- /* Directly attached device */
- if (!pdev)
- return dev->port->phy;
+ /* a published domain device always has a valid phy, it may be
+ * stale, but it is never NULL
+ */
+ BUG_ON(!dev->phy);
- /* Otherwise look in the expander */
- for (i = 0; i < pdev->ex_dev.num_phys; i++)
- if (!memcmp(dev->sas_addr,
- pdev->ex_dev.ex_phy[i].attached_sas_addr,
- SAS_ADDR_SIZE)) {
- exphy = &pdev->ex_dev.ex_phy[i];
- break;
- }
+ spin_lock_irqsave(&ha->phy_port_lock, flags);
+ phy = dev->phy;
+ get_device(&phy->dev);
+ spin_unlock_irqrestore(&ha->phy_port_lock, flags);
- BUG_ON(!exphy);
- return exphy->phy;
+ return phy;
}
-EXPORT_SYMBOL_GPL(sas_find_local_phy);
+EXPORT_SYMBOL_GPL(sas_get_local_phy);
/* Attempt to send a LUN reset message to a device */
int sas_eh_device_reset_handler(struct scsi_cmnd *cmd)
@@ -461,7 +485,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd)
int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd)
{
struct domain_device *dev = cmd_to_domain_dev(cmd);
- struct sas_phy *phy = sas_find_local_phy(dev);
+ struct sas_phy *phy = sas_get_local_phy(dev);
int res;
res = sas_phy_reset(phy, 1);
@@ -469,6 +493,8 @@ int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd)
SAS_DPRINTK("Bus reset of %s failed 0x%x\n",
kobject_name(&phy->dev.kobj),
res);
+ sas_put_local_phy(phy);
+
if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE)
return SUCCESS;
@@ -495,9 +521,7 @@ try_bus_reset:
return FAILED;
}
-static int sas_eh_handle_sas_errors(struct Scsi_Host *shost,
- struct list_head *work_q,
- struct list_head *done_q)
+static void sas_eh_handle_sas_errors(struct Scsi_Host *shost, struct list_head *work_q)
{
struct scsi_cmnd *cmd, *n;
enum task_disposition res = TASK_IS_DONE;
@@ -505,13 +529,28 @@ static int sas_eh_handle_sas_errors(struct Scsi_Host *shost,
struct sas_internal *i = to_sas_internal(shost->transportt);
unsigned long flags;
struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+ LIST_HEAD(done);
-Again:
+ /* clean out any commands that won the completion vs eh race */
list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
- struct sas_task *task = TO_SAS_TASK(cmd);
+ struct domain_device *dev = cmd_to_domain_dev(cmd);
+ struct sas_task *task;
+
+ spin_lock_irqsave(&dev->done_lock, flags);
+ /* by this point the lldd has either observed
+ * SAS_HA_FROZEN and is leaving the task alone, or has
+ * won the race with eh and decided to complete it
+ */
+ task = TO_SAS_TASK(cmd);
+ spin_unlock_irqrestore(&dev->done_lock, flags);
if (!task)
- continue;
+ list_move_tail(&cmd->eh_entry, &done);
+ }
+
+ Again:
+ list_for_each_entry_safe(cmd, n, work_q, eh_entry) {
+ struct sas_task *task = TO_SAS_TASK(cmd);
list_del_init(&cmd->eh_entry);
@@ -531,15 +570,23 @@ Again:
cmd->eh_eflags = 0;
switch (res) {
+ case TASK_IS_NOT_AT_HA:
+ SAS_DPRINTK("%s: task 0x%p is not at ha: %s\n",
+ __func__, task,
+ cmd->retries ? "retry" : "aborted");
+ if (cmd->retries)
+ cmd->retries--;
+ sas_eh_finish_cmd(cmd);
+ continue;
case TASK_IS_DONE:
SAS_DPRINTK("%s: task 0x%p is done\n", __func__,
task);
- sas_eh_finish_cmd(cmd);
+ sas_eh_defer_cmd(cmd);
continue;
case TASK_IS_ABORTED:
SAS_DPRINTK("%s: task 0x%p is aborted\n",
__func__, task);
- sas_eh_finish_cmd(cmd);
+ sas_eh_defer_cmd(cmd);
continue;
case TASK_IS_AT_LU:
SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task);
@@ -550,7 +597,7 @@ Again:
"recovered\n",
SAS_ADDR(task->dev),
cmd->device->lun);
- sas_eh_finish_cmd(cmd);
+ sas_eh_defer_cmd(cmd);
sas_scsi_clear_queue_lu(work_q, cmd);
goto Again;
}
@@ -560,7 +607,8 @@ Again:
SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n",
task);
tmf_resp = sas_recover_I_T(task->dev);
- if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
+ if (tmf_resp == TMF_RESP_FUNC_COMPLETE ||
+ tmf_resp == -ENODEV) {
struct domain_device *dev = task->dev;
SAS_DPRINTK("I_T %016llx recovered\n",
SAS_ADDR(task->dev->sas_addr));
@@ -607,13 +655,16 @@ Again:
goto clear_q;
}
}
- return list_empty(work_q);
-clear_q:
+ out:
+ list_splice_tail(&done, work_q);
+ list_splice_tail_init(&ha->eh_ata_q, work_q);
+ return;
+
+ clear_q:
SAS_DPRINTK("--- Exit %s -- clear_q\n", __func__);
list_for_each_entry_safe(cmd, n, work_q, eh_entry)
sas_eh_finish_cmd(cmd);
-
- return list_empty(work_q);
+ goto out;
}
void sas_scsi_recover_host(struct Scsi_Host *shost)
@@ -627,12 +678,17 @@ void sas_scsi_recover_host(struct Scsi_Host *shost)
shost->host_eh_scheduled = 0;
spin_unlock_irqrestore(shost->host_lock, flags);
- SAS_DPRINTK("Enter %s\n", __func__);
+ SAS_DPRINTK("Enter %s busy: %d failed: %d\n",
+ __func__, shost->host_busy, shost->host_failed);
/*
* Deal with commands that still have SAS tasks (i.e. they didn't
- * complete via the normal sas_task completion mechanism)
+ * complete via the normal sas_task completion mechanism),
+ * SAS_HA_FROZEN gives eh dominion over all sas_task completion.
*/
- if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q))
+ set_bit(SAS_HA_FROZEN, &ha->state);
+ sas_eh_handle_sas_errors(shost, &eh_work_q);
+ clear_bit(SAS_HA_FROZEN, &ha->state);
+ if (list_empty(&eh_work_q))
goto out;
/*
@@ -641,59 +697,26 @@ void sas_scsi_recover_host(struct Scsi_Host *shost)
* scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any
* command we see here has no sas_task and is thus unknown to the HA.
*/
- if (!sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q))
- if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q))
- scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
+ sas_ata_eh(shost, &eh_work_q, &ha->eh_done_q);
+ if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q))
+ scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q);
out:
+ if (ha->lldd_max_execute_num > 1)
+ wake_up_process(ha->core.queue_thread);
+
/* now link into libata eh --- if we have any ata devices */
sas_ata_strategy_handler(shost);
scsi_eh_flush_done_q(&ha->eh_done_q);
- SAS_DPRINTK("--- Exit %s\n", __func__);
- return;
+ SAS_DPRINTK("--- Exit %s: busy: %d failed: %d\n",
+ __func__, shost->host_busy, shost->host_failed);
}
enum blk_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
{
- struct sas_task *task = TO_SAS_TASK(cmd);
- unsigned long flags;
- enum blk_eh_timer_return rtn;
-
- if (sas_ata_timed_out(cmd, task, &rtn))
- return rtn;
-
- if (!task) {
- cmd->request->timeout /= 2;
- SAS_DPRINTK("command 0x%p, task 0x%p, gone: %s\n",
- cmd, task, (cmd->request->timeout ?
- "BLK_EH_RESET_TIMER" : "BLK_EH_NOT_HANDLED"));
- if (!cmd->request->timeout)
- return BLK_EH_NOT_HANDLED;
- return BLK_EH_RESET_TIMER;
- }
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED);
- if (task->task_state_flags & SAS_TASK_STATE_DONE) {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("command 0x%p, task 0x%p, timed out: "
- "BLK_EH_HANDLED\n", cmd, task);
- return BLK_EH_HANDLED;
- }
- if (!(task->task_state_flags & SAS_TASK_AT_INITIATOR)) {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("command 0x%p, task 0x%p, not at initiator: "
- "BLK_EH_RESET_TIMER\n",
- cmd, task);
- return BLK_EH_RESET_TIMER;
- }
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- SAS_DPRINTK("command 0x%p, task 0x%p, timed out: BLK_EH_NOT_HANDLED\n",
- cmd, task);
+ scmd_printk(KERN_DEBUG, cmd, "command %p timed out\n", cmd);
return BLK_EH_NOT_HANDLED;
}
@@ -737,27 +760,15 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
return found_dev;
}
-static inline struct domain_device *sas_find_target(struct scsi_target *starget)
-{
- struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent);
-
- return sas_find_dev_by_rphy(rphy);
-}
-
int sas_target_alloc(struct scsi_target *starget)
{
- struct domain_device *found_dev = sas_find_target(starget);
- int res;
+ struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent);
+ struct domain_device *found_dev = sas_find_dev_by_rphy(rphy);
if (!found_dev)
return -ENODEV;
- if (dev_is_sata(found_dev)) {
- res = sas_ata_init_host_and_port(found_dev, starget);
- if (res)
- return res;
- }
-
+ kref_get(&found_dev->kref);
starget->hostdata = found_dev;
return 0;
}
@@ -797,14 +808,6 @@ int sas_slave_configure(struct scsi_device *scsi_dev)
return 0;
}
-void sas_slave_destroy(struct scsi_device *scsi_dev)
-{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
-
- if (dev_is_sata(dev))
- sas_to_ata_dev(dev)->class = ATA_DEV_NONE;
-}
-
int sas_change_queue_depth(struct scsi_device *sdev, int depth, int reason)
{
struct domain_device *dev = sdev_to_domain_dev(sdev);
@@ -871,9 +874,11 @@ static void sas_queue(struct sas_ha_struct *sas_ha)
int res;
struct sas_internal *i = to_sas_internal(core->shost->transportt);
+ mutex_lock(&core->task_queue_flush);
spin_lock_irqsave(&core->task_queue_lock, flags);
while (!kthread_should_stop() &&
- !list_empty(&core->task_queue)) {
+ !list_empty(&core->task_queue) &&
+ !test_bit(SAS_HA_FROZEN, &sas_ha->state)) {
can_queue = sas_ha->lldd_queue_size - core->task_queue_size;
if (can_queue >= 0) {
@@ -909,6 +914,7 @@ static void sas_queue(struct sas_ha_struct *sas_ha)
}
}
spin_unlock_irqrestore(&core->task_queue_lock, flags);
+ mutex_unlock(&core->task_queue_flush);
}
/**
@@ -935,6 +941,7 @@ int sas_init_queue(struct sas_ha_struct *sas_ha)
struct scsi_core *core = &sas_ha->core;
spin_lock_init(&core->task_queue_lock);
+ mutex_init(&core->task_queue_flush);
core->task_queue_size = 0;
INIT_LIST_HEAD(&core->task_queue);
@@ -972,49 +979,6 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha)
}
/*
- * Call the LLDD task abort routine directly. This function is intended for
- * use by upper layers that need to tell the LLDD to abort a task.
- */
-int __sas_task_abort(struct sas_task *task)
-{
- struct sas_internal *si =
- to_sas_internal(task->dev->port->ha->core.shost->transportt);
- unsigned long flags;
- int res;
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if (task->task_state_flags & SAS_TASK_STATE_ABORTED ||
- task->task_state_flags & SAS_TASK_STATE_DONE) {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- SAS_DPRINTK("%s: Task %p already finished.\n", __func__,
- task);
- return 0;
- }
- task->task_state_flags |= SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- if (!si->dft->lldd_abort_task)
- return -ENODEV;
-
- res = si->dft->lldd_abort_task(task);
-
- spin_lock_irqsave(&task->task_state_lock, flags);
- if ((task->task_state_flags & SAS_TASK_STATE_DONE) ||
- (res == TMF_RESP_FUNC_COMPLETE))
- {
- spin_unlock_irqrestore(&task->task_state_lock, flags);
- task->task_done(task);
- return 0;
- }
-
- if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
- task->task_state_flags &= ~SAS_TASK_STATE_ABORTED;
- spin_unlock_irqrestore(&task->task_state_lock, flags);
-
- return -EAGAIN;
-}
-
-/*
* Tell an upper layer that it needs to initiate an abort for a given task.
* This should only ever be called by an LLDD.
*/
@@ -1043,27 +1007,15 @@ void sas_task_abort(struct sas_task *task)
}
}
-int sas_slave_alloc(struct scsi_device *scsi_dev)
-{
- struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
-
- if (dev_is_sata(dev))
- return ata_sas_port_init(dev->sata_dev.ap);
-
- return 0;
-}
-
void sas_target_destroy(struct scsi_target *starget)
{
- struct domain_device *found_dev = sas_find_target(starget);
+ struct domain_device *found_dev = starget->hostdata;
if (!found_dev)
return;
- if (dev_is_sata(found_dev))
- ata_sas_port_destroy(found_dev->sata_dev.ap);
-
- return;
+ starget->hostdata = NULL;
+ sas_put_device(found_dev);
}
static void sas_parse_addr(u8 *sas_addr, const char *p)
@@ -1108,16 +1060,12 @@ EXPORT_SYMBOL_GPL(sas_request_addr);
EXPORT_SYMBOL_GPL(sas_queuecommand);
EXPORT_SYMBOL_GPL(sas_target_alloc);
EXPORT_SYMBOL_GPL(sas_slave_configure);
-EXPORT_SYMBOL_GPL(sas_slave_destroy);
EXPORT_SYMBOL_GPL(sas_change_queue_depth);
EXPORT_SYMBOL_GPL(sas_change_queue_type);
EXPORT_SYMBOL_GPL(sas_bios_param);
-EXPORT_SYMBOL_GPL(__sas_task_abort);
EXPORT_SYMBOL_GPL(sas_task_abort);
EXPORT_SYMBOL_GPL(sas_phy_reset);
-EXPORT_SYMBOL_GPL(sas_phy_enable);
EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler);
EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler);
-EXPORT_SYMBOL_GPL(sas_slave_alloc);
EXPORT_SYMBOL_GPL(sas_target_destroy);
EXPORT_SYMBOL_GPL(sas_ioctl);
diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h
index 825f9307417a..5fc044ff656e 100644
--- a/drivers/scsi/lpfc/lpfc.h
+++ b/drivers/scsi/lpfc/lpfc.h
@@ -534,6 +534,7 @@ struct lpfc_hba {
void (*lpfc_scsi_prep_cmnd)
(struct lpfc_vport *, struct lpfc_scsi_buf *,
struct lpfc_nodelist *);
+
/* IOCB interface function jump table entries */
int (*__lpfc_sli_issue_iocb)
(struct lpfc_hba *, uint32_t,
@@ -541,8 +542,6 @@ struct lpfc_hba {
void (*__lpfc_sli_release_iocbq)(struct lpfc_hba *,
struct lpfc_iocbq *);
int (*lpfc_hba_down_post)(struct lpfc_hba *phba);
-
-
IOCB_t * (*lpfc_get_iocb_from_iocbq)
(struct lpfc_iocbq *);
void (*lpfc_scsi_cmd_iocb_cmpl)
@@ -551,10 +550,12 @@ struct lpfc_hba {
/* MBOX interface function jump table entries */
int (*lpfc_sli_issue_mbox)
(struct lpfc_hba *, LPFC_MBOXQ_t *, uint32_t);
+
/* Slow-path IOCB process function jump table entries */
void (*lpfc_sli_handle_slow_ring_event)
(struct lpfc_hba *phba, struct lpfc_sli_ring *pring,
uint32_t mask);
+
/* INIT device interface function jump table entries */
int (*lpfc_sli_hbq_to_firmware)
(struct lpfc_hba *, uint32_t, struct hbq_dmabuf *);
@@ -573,6 +574,10 @@ struct lpfc_hba {
int (*lpfc_selective_reset)
(struct lpfc_hba *);
+ int (*lpfc_bg_scsi_prep_dma_buf)
+ (struct lpfc_hba *, struct lpfc_scsi_buf *);
+ /* Add new entries here */
+
/* SLI4 specific HBA data structure */
struct lpfc_sli4_hba sli4_hba;
@@ -838,6 +843,7 @@ struct lpfc_hba {
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 */
+ struct dentry *debug_readGuard; /* inject read guard_tag errors */
struct dentry *debug_readApp; /* inject read app_tag errors */
struct dentry *debug_readRef; /* inject read ref_tag errors */
@@ -845,10 +851,11 @@ struct lpfc_hba {
uint32_t lpfc_injerr_wgrd_cnt;
uint32_t lpfc_injerr_wapp_cnt;
uint32_t lpfc_injerr_wref_cnt;
+ uint32_t lpfc_injerr_rgrd_cnt;
uint32_t lpfc_injerr_rapp_cnt;
uint32_t lpfc_injerr_rref_cnt;
sector_t lpfc_injerr_lba;
-#define LPFC_INJERR_LBA_OFF (sector_t)0xffffffffffffffff
+#define LPFC_INJERR_LBA_OFF (sector_t)(-1)
struct dentry *debug_slow_ring_trc;
struct lpfc_debugfs_trc *slow_ring_trc;
diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c
index f6697cb0e216..296ad5bc4240 100644
--- a/drivers/scsi/lpfc/lpfc_attr.c
+++ b/drivers/scsi/lpfc/lpfc_attr.c
@@ -353,7 +353,7 @@ lpfc_fwrev_show(struct device *dev, struct device_attribute *attr,
struct lpfc_hba *phba = vport->phba;
uint32_t if_type;
uint8_t sli_family;
- char fwrev[32];
+ char fwrev[FW_REV_STR_SIZE];
int len;
lpfc_decode_firmware_rev(phba, fwrev, 1);
@@ -922,11 +922,15 @@ lpfc_sli4_pdev_reg_request(struct lpfc_hba *phba, uint32_t opcode)
rc = lpfc_sli4_pdev_status_reg_wait(phba);
if (rc == -EPERM) {
- /* no privilage for reset, restore if needed */
- if (before_fc_flag & FC_OFFLINE_MODE)
- goto out;
+ /* no privilage for reset */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3150 No privilage to perform the requested "
+ "access: x%x\n", reg_val);
} else if (rc == -EIO) {
/* reset failed, there is nothing more we can do */
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3153 Fail to perform the requested "
+ "access: x%x\n", reg_val);
return rc;
}
diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c
index 56a86baece5b..141e4b40bb55 100644
--- a/drivers/scsi/lpfc/lpfc_bsg.c
+++ b/drivers/scsi/lpfc/lpfc_bsg.c
@@ -589,7 +589,10 @@ lpfc_bsg_rport_els(struct fc_bsg_job *job)
}
cmdiocbq->iocb.un.elsreq64.bdl.bdeSize =
(request_nseg + reply_nseg) * sizeof(struct ulp_bde64);
- cmdiocbq->iocb.ulpContext = rpi;
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ cmdiocbq->iocb.ulpContext = phba->sli4_hba.rpi_ids[rpi];
+ else
+ cmdiocbq->iocb.ulpContext = rpi;
cmdiocbq->iocb_flag |= LPFC_IO_LIBDFC;
cmdiocbq->context1 = NULL;
cmdiocbq->context2 = NULL;
@@ -1768,7 +1771,7 @@ lpfc_sli4_bsg_set_internal_loopback(struct lpfc_hba *phba)
bf_set(lpfc_mbx_set_diag_state_link_type,
&link_diag_loopback->u.req, phba->sli4_hba.lnk_info.lnk_tp);
bf_set(lpfc_mbx_set_diag_lpbk_type, &link_diag_loopback->u.req,
- LPFC_DIAG_LOOPBACK_TYPE_SERDES);
+ LPFC_DIAG_LOOPBACK_TYPE_INTERNAL);
mbxstatus = lpfc_sli_issue_mbox_wait(phba, pmboxq, LPFC_MBOX_TMO);
if ((mbxstatus != MBX_SUCCESS) || (pmboxq->u.mb.mbxStatus)) {
@@ -3977,7 +3980,7 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
case COMN_OPCODE_GET_CNTL_ADDL_ATTRIBUTES:
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"3106 Handled SLI_CONFIG "
- "subsys_fcoe, opcode:x%x\n",
+ "subsys_comn, opcode:x%x\n",
opcode);
rc = lpfc_bsg_sli_cfg_read_cmd_ext(phba, job,
nemb_mse, dmabuf);
@@ -3985,7 +3988,7 @@ lpfc_bsg_handle_sli_cfg_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
default:
lpfc_printf_log(phba, KERN_INFO, LOG_LIBDFC,
"3107 Reject SLI_CONFIG "
- "subsys_fcoe, opcode:x%x\n",
+ "subsys_comn, opcode:x%x\n",
opcode);
rc = -EPERM;
break;
@@ -4556,7 +4559,12 @@ lpfc_bsg_issue_mbox(struct lpfc_hba *phba, struct fc_bsg_job *job,
+ sizeof(MAILBOX_t));
}
} else if (phba->sli_rev == LPFC_SLI_REV4) {
- if (pmb->mbxCommand == MBX_DUMP_MEMORY) {
+ /* Let type 4 (well known data) through because the data is
+ * returned in varwords[4-8]
+ * otherwise check the recieve length and fetch the buffer addr
+ */
+ if ((pmb->mbxCommand == MBX_DUMP_MEMORY) &&
+ (pmb->un.varDmp.type != DMP_WELL_KNOWN)) {
/* rebuild the command for sli4 using our own buffers
* like we do for biu diags
*/
diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h
index 26924b7a6cde..330dd7192a7f 100644
--- a/drivers/scsi/lpfc/lpfc_crtn.h
+++ b/drivers/scsi/lpfc/lpfc_crtn.h
@@ -462,3 +462,4 @@ int lpfc_issue_unreg_vfi(struct lpfc_vport *);
int lpfc_selective_reset(struct lpfc_hba *);
int lpfc_sli4_read_config(struct lpfc_hba *phba);
int lpfc_scsi_buf_update(struct lpfc_hba *phba);
+void lpfc_sli4_node_prep(struct lpfc_hba *phba);
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index 707081d0a226..93e96b3c9097 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -1076,7 +1076,7 @@ int
lpfc_vport_symbolic_node_name(struct lpfc_vport *vport, char *symbol,
size_t size)
{
- char fwrev[16];
+ char fwrev[FW_REV_STR_SIZE];
int n;
lpfc_decode_firmware_rev(vport->phba, fwrev, 0);
@@ -1834,7 +1834,7 @@ lpfc_decode_firmware_rev(struct lpfc_hba *phba, char *fwrevision, int flag)
uint8_t *fwname;
if (phba->sli_rev == LPFC_SLI_REV4)
- sprintf(fwrevision, "%s", vp->rev.opFwName);
+ snprintf(fwrevision, FW_REV_STR_SIZE, "%s", vp->rev.opFwName);
else if (vp->rev.rBit) {
if (psli->sli_flag & LPFC_SLI_ACTIVE)
rev = vp->rev.sli2FwRev;
diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c
index 3587a3fe8fcb..22e17be04d8a 100644
--- a/drivers/scsi/lpfc/lpfc_debugfs.c
+++ b/drivers/scsi/lpfc/lpfc_debugfs.c
@@ -1019,6 +1019,8 @@ lpfc_debugfs_dif_err_read(struct file *file, char __user *buf,
cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wapp_cnt);
else if (dent == phba->debug_writeRef)
cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_wref_cnt);
+ else if (dent == phba->debug_readGuard)
+ cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rgrd_cnt);
else if (dent == phba->debug_readApp)
cnt = snprintf(cbuf, 16, "%u\n", phba->lpfc_injerr_rapp_cnt);
else if (dent == phba->debug_readRef)
@@ -1057,6 +1059,8 @@ lpfc_debugfs_dif_err_write(struct file *file, const char __user *buf,
phba->lpfc_injerr_wapp_cnt = (uint32_t)tmp;
else if (dent == phba->debug_writeRef)
phba->lpfc_injerr_wref_cnt = (uint32_t)tmp;
+ else if (dent == phba->debug_readGuard)
+ phba->lpfc_injerr_rgrd_cnt = (uint32_t)tmp;
else if (dent == phba->debug_readApp)
phba->lpfc_injerr_rapp_cnt = (uint32_t)tmp;
else if (dent == phba->debug_readRef)
@@ -3978,6 +3982,17 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport)
goto debug_failed;
}
+ snprintf(name, sizeof(name), "readGuardInjErr");
+ phba->debug_readGuard =
+ debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
+ phba->hba_debugfs_root,
+ phba, &lpfc_debugfs_op_dif_err);
+ if (!phba->debug_readGuard) {
+ lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT,
+ "0808 Cannot create debugfs readGuard\n");
+ goto debug_failed;
+ }
+
snprintf(name, sizeof(name), "readAppInjErr");
phba->debug_readApp =
debugfs_create_file(name, S_IFREG|S_IRUGO|S_IWUSR,
@@ -4318,6 +4333,10 @@ lpfc_debugfs_terminate(struct lpfc_vport *vport)
debugfs_remove(phba->debug_writeRef); /* writeRef */
phba->debug_writeRef = NULL;
}
+ if (phba->debug_readGuard) {
+ debugfs_remove(phba->debug_readGuard); /* readGuard */
+ phba->debug_readGuard = NULL;
+ }
if (phba->debug_readApp) {
debugfs_remove(phba->debug_readApp); /* readApp */
phba->debug_readApp = NULL;
diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c
index 7afc757338de..8db2fb3b45ec 100644
--- a/drivers/scsi/lpfc/lpfc_els.c
+++ b/drivers/scsi/lpfc/lpfc_els.c
@@ -1526,7 +1526,6 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
memcpy(&ndlp->active_rrqs.xri_bitmap,
&rrq.xri_bitmap,
sizeof(ndlp->active_rrqs.xri_bitmap));
- lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
/* Since we are swapping the ndlp passed in with the new one
* and the did has already been swapped, copy over the
* state and names.
@@ -1536,6 +1535,7 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp,
memcpy(&new_ndlp->nlp_nodename, &ndlp->nlp_nodename,
sizeof(struct lpfc_name));
new_ndlp->nlp_state = ndlp->nlp_state;
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_NPR_NODE);
/* Fix up the rport accordingly */
rport = ndlp->rport;
if (rport) {
@@ -7172,7 +7172,7 @@ lpfc_cmpl_els_fdisc(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb,
goto out;
/* FDISC failed */
lpfc_printf_vlog(vport, KERN_ERR, LOG_ELS,
- "0126 FDISC failed. (%d/%d)\n",
+ "0126 FDISC failed. (x%x/x%x)\n",
irsp->ulpStatus, irsp->un.ulpWord[4]);
goto fdisc_failed;
}
@@ -7283,6 +7283,7 @@ lpfc_issue_els_fdisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
int rc;
vport->port_state = LPFC_FDISC;
+ vport->fc_myDID = 0;
cmdsize = (sizeof(uint32_t) + sizeof(struct serv_parm));
elsiocb = lpfc_prep_els_iocb(vport, 1, cmdsize, retry, ndlp, did,
ELS_CMD_FDISC);
diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c
index 678a4b11059c..343d87ba4df8 100644
--- a/drivers/scsi/lpfc/lpfc_hbadisc.c
+++ b/drivers/scsi/lpfc/lpfc_hbadisc.c
@@ -2977,9 +2977,9 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
"topology\n");
/* Get Loop Map information */
if (bf_get(lpfc_mbx_read_top_il, la)) {
- spin_lock_irq(shost->host_lock);
+ spin_lock(shost->host_lock);
vport->fc_flag |= FC_LBIT;
- spin_unlock_irq(shost->host_lock);
+ spin_unlock(shost->host_lock);
}
vport->fc_myDID = bf_get(lpfc_mbx_read_top_alpa_granted, la);
@@ -3029,9 +3029,9 @@ lpfc_mbx_process_link_up(struct lpfc_hba *phba, struct lpfc_mbx_read_top *la)
phba->sli3_options |= LPFC_SLI3_NPIV_ENABLED;
}
vport->fc_myDID = phba->fc_pref_DID;
- spin_lock_irq(shost->host_lock);
+ spin_lock(shost->host_lock);
vport->fc_flag |= FC_LBIT;
- spin_unlock_irq(shost->host_lock);
+ spin_unlock(shost->host_lock);
}
spin_unlock_irq(&phba->hbalock);
@@ -5332,6 +5332,10 @@ lpfc_filter_by_rpi(struct lpfc_nodelist *ndlp, void *param)
{
uint16_t *rpi = param;
+ /* check for active node */
+ if (!NLP_CHK_NODE_ACT(ndlp))
+ return 0;
+
return ndlp->nlp_rpi == *rpi;
}
diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h
index 7245bead3755..5f280b5ae3db 100644
--- a/drivers/scsi/lpfc/lpfc_hw.h
+++ b/drivers/scsi/lpfc/lpfc_hw.h
@@ -1,7 +1,7 @@
/*******************************************************************
* This file is part of the Emulex Linux Device Driver for *
* Fibre Channel Host Bus Adapters. *
- * Copyright (C) 2004-2010 Emulex. All rights reserved. *
+ * Copyright (C) 2004-2011 Emulex. All rights reserved. *
* EMULEX and SLI are trademarks of Emulex. *
* www.emulex.com *
* *
@@ -70,6 +70,7 @@
/* vendor ID used in SCSI netlink calls */
#define LPFC_NL_VENDOR_ID (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX)
+#define FW_REV_STR_SIZE 32
/* Common Transport structures and definitions */
union CtRevisionId {
@@ -2567,6 +2568,8 @@ typedef struct {
#define DMP_MEM_REG 0x1
#define DMP_NV_PARAMS 0x2
+#define DMP_LMSD 0x3 /* Link Module Serial Data */
+#define DMP_WELL_KNOWN 0x4
#define DMP_REGION_VPD 0xe
#define DMP_VPD_SIZE 0x400 /* maximum amount of VPD */
diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h
index e5bfa7f334e3..9e2b9b227e1a 100644
--- a/drivers/scsi/lpfc/lpfc_hw4.h
+++ b/drivers/scsi/lpfc/lpfc_hw4.h
@@ -321,6 +321,10 @@ struct lpfc_cqe {
#define CQE_STATUS_CMD_REJECT 0xb
#define CQE_STATUS_FCP_TGT_LENCHECK 0xc
#define CQE_STATUS_NEED_BUFF_ENTRY 0xf
+#define CQE_STATUS_DI_ERROR 0x16
+
+/* Used when mapping CQE status to IOCB */
+#define LPFC_IOCB_STATUS_MASK 0xf
/* Status returned by hardware (valid only if status = CQE_STATUS_SUCCESS). */
#define CQE_HW_STATUS_NO_ERR 0x0
@@ -348,6 +352,21 @@ struct lpfc_wcqe_complete {
#define lpfc_wcqe_c_hw_status_WORD word0
uint32_t total_data_placed;
uint32_t parameter;
+#define lpfc_wcqe_c_bg_edir_SHIFT 5
+#define lpfc_wcqe_c_bg_edir_MASK 0x00000001
+#define lpfc_wcqe_c_bg_edir_WORD parameter
+#define lpfc_wcqe_c_bg_tdpv_SHIFT 3
+#define lpfc_wcqe_c_bg_tdpv_MASK 0x00000001
+#define lpfc_wcqe_c_bg_tdpv_WORD parameter
+#define lpfc_wcqe_c_bg_re_SHIFT 2
+#define lpfc_wcqe_c_bg_re_MASK 0x00000001
+#define lpfc_wcqe_c_bg_re_WORD parameter
+#define lpfc_wcqe_c_bg_ae_SHIFT 1
+#define lpfc_wcqe_c_bg_ae_MASK 0x00000001
+#define lpfc_wcqe_c_bg_ae_WORD parameter
+#define lpfc_wcqe_c_bg_ge_SHIFT 0
+#define lpfc_wcqe_c_bg_ge_MASK 0x00000001
+#define lpfc_wcqe_c_bg_ge_WORD parameter
uint32_t word3;
#define lpfc_wcqe_c_valid_SHIFT lpfc_cqe_valid_SHIFT
#define lpfc_wcqe_c_valid_MASK lpfc_cqe_valid_MASK
@@ -359,8 +378,8 @@ struct lpfc_wcqe_complete {
#define lpfc_wcqe_c_pv_MASK 0x00000001
#define lpfc_wcqe_c_pv_WORD word3
#define lpfc_wcqe_c_priority_SHIFT 24
-#define lpfc_wcqe_c_priority_MASK 0x00000007
-#define lpfc_wcqe_c_priority_WORD word3
+#define lpfc_wcqe_c_priority_MASK 0x00000007
+#define lpfc_wcqe_c_priority_WORD word3
#define lpfc_wcqe_c_code_SHIFT lpfc_cqe_code_SHIFT
#define lpfc_wcqe_c_code_MASK lpfc_cqe_code_MASK
#define lpfc_wcqe_c_code_WORD lpfc_cqe_code_WORD
@@ -715,12 +734,20 @@ struct lpfc_register {
#define lpfc_eqcq_doorbell_eqci_SHIFT 9
#define lpfc_eqcq_doorbell_eqci_MASK 0x0001
#define lpfc_eqcq_doorbell_eqci_WORD word0
-#define lpfc_eqcq_doorbell_cqid_SHIFT 0
-#define lpfc_eqcq_doorbell_cqid_MASK 0x03FF
-#define lpfc_eqcq_doorbell_cqid_WORD word0
-#define lpfc_eqcq_doorbell_eqid_SHIFT 0
-#define lpfc_eqcq_doorbell_eqid_MASK 0x01FF
-#define lpfc_eqcq_doorbell_eqid_WORD word0
+#define lpfc_eqcq_doorbell_cqid_lo_SHIFT 0
+#define lpfc_eqcq_doorbell_cqid_lo_MASK 0x03FF
+#define lpfc_eqcq_doorbell_cqid_lo_WORD word0
+#define lpfc_eqcq_doorbell_cqid_hi_SHIFT 11
+#define lpfc_eqcq_doorbell_cqid_hi_MASK 0x001F
+#define lpfc_eqcq_doorbell_cqid_hi_WORD word0
+#define lpfc_eqcq_doorbell_eqid_lo_SHIFT 0
+#define lpfc_eqcq_doorbell_eqid_lo_MASK 0x01FF
+#define lpfc_eqcq_doorbell_eqid_lo_WORD word0
+#define lpfc_eqcq_doorbell_eqid_hi_SHIFT 11
+#define lpfc_eqcq_doorbell_eqid_hi_MASK 0x001F
+#define lpfc_eqcq_doorbell_eqid_hi_WORD word0
+#define LPFC_CQID_HI_FIELD_SHIFT 10
+#define LPFC_EQID_HI_FIELD_SHIFT 9
#define LPFC_BMBX 0x0160
#define lpfc_bmbx_addr_SHIFT 2
@@ -3313,7 +3340,11 @@ struct xmit_bls_rsp64_wqe {
uint32_t rsrvd4;
struct wqe_did wqe_dest;
struct wqe_common wqe_com; /* words 6-11 */
- uint32_t rsvd_12_15[4];
+ uint32_t word12;
+#define xmit_bls_rsp64_temprpi_SHIFT 0
+#define xmit_bls_rsp64_temprpi_MASK 0x0000ffff
+#define xmit_bls_rsp64_temprpi_WORD word12
+ uint32_t rsvd_13_15[3];
};
struct wqe_rctl_dfctl {
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index dfea2dada02c..b38f99f3be32 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -32,6 +32,7 @@
#include <linux/aer.h>
#include <linux/slab.h>
#include <linux/firmware.h>
+#include <linux/miscdevice.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
@@ -1474,8 +1475,12 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba)
phba->sli4_hba.u.if_type2.STATUSregaddr,
&portstat_reg.word0);
/* consider PCI bus read error as pci_channel_offline */
- if (pci_rd_rc1 == -EIO)
+ if (pci_rd_rc1 == -EIO) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3151 PCI bus read access failure: x%x\n",
+ readl(phba->sli4_hba.u.if_type2.STATUSregaddr));
return;
+ }
reg_err1 = readl(phba->sli4_hba.u.if_type2.ERR1regaddr);
reg_err2 = readl(phba->sli4_hba.u.if_type2.ERR2regaddr);
if (bf_get(lpfc_sliport_status_oti, &portstat_reg)) {
@@ -1525,6 +1530,9 @@ lpfc_handle_eratt_s4(struct lpfc_hba *phba)
}
/* fall through for not able to recover */
}
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "3152 Unrecoverable error, bring the port "
+ "offline\n");
lpfc_sli4_offline_eratt(phba);
break;
case LPFC_SLI_INTF_IF_TYPE_1:
@@ -2333,13 +2341,20 @@ lpfc_cleanup(struct lpfc_vport *vport)
continue;
}
+ /* take care of nodes in unused state before the state
+ * machine taking action.
+ */
+ if (ndlp->nlp_state == NLP_STE_UNUSED_NODE) {
+ lpfc_nlp_put(ndlp);
+ continue;
+ }
+
if (ndlp->nlp_type & NLP_FABRIC)
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RECOVERY);
lpfc_disc_state_machine(vport, ndlp, NULL,
NLP_EVT_DEVICE_RM);
-
}
/* At this point, ALL ndlp's should be gone
@@ -2513,6 +2528,42 @@ lpfc_block_mgmt_io(struct lpfc_hba * phba)
}
/**
+ * lpfc_sli4_node_prep - Assign RPIs for active nodes.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * Allocate RPIs for all active remote nodes. This is needed whenever
+ * an SLI4 adapter is reset and the driver is not unloading. Its purpose
+ * is to fixup the temporary rpi assignments.
+ **/
+void
+lpfc_sli4_node_prep(struct lpfc_hba *phba)
+{
+ struct lpfc_nodelist *ndlp, *next_ndlp;
+ struct lpfc_vport **vports;
+ int i;
+
+ if (phba->sli_rev != LPFC_SLI_REV4)
+ return;
+
+ vports = lpfc_create_vport_work_array(phba);
+ if (vports != NULL) {
+ for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) {
+ if (vports[i]->load_flag & FC_UNLOADING)
+ continue;
+
+ list_for_each_entry_safe(ndlp, next_ndlp,
+ &vports[i]->fc_nodes,
+ nlp_listp) {
+ if (NLP_CHK_NODE_ACT(ndlp))
+ ndlp->nlp_rpi =
+ lpfc_sli4_alloc_rpi(phba);
+ }
+ }
+ }
+ lpfc_destroy_vport_work_array(phba, vports);
+}
+
+/**
* lpfc_online - Initialize and bring a HBA online
* @phba: pointer to lpfc hba data structure.
*
@@ -2653,6 +2704,15 @@ lpfc_offline_prep(struct lpfc_hba * phba)
}
spin_lock_irq(shost->host_lock);
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
+
+ /*
+ * Whenever an SLI4 port goes offline, free the
+ * RPI. 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);
}
@@ -4327,6 +4387,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
uint8_t pn_page[LPFC_MAX_SUPPORTED_PAGES] = {0};
struct lpfc_mqe *mqe;
int longs, sli_family;
+ int sges_per_segment;
/* Before proceed, wait for POST done and device ready */
rc = lpfc_sli4_post_status_check(phba);
@@ -4390,6 +4451,11 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
phba->fc_map[1] = LPFC_FCOE_FCF_MAP1;
phba->fc_map[2] = LPFC_FCOE_FCF_MAP2;
+ /* With BlockGuard we can have multiple SGEs per Data Segemnt */
+ sges_per_segment = 1;
+ if (phba->cfg_enable_bg)
+ sges_per_segment = 2;
+
/*
* Since the sg_tablesize is module parameter, the sg_dma_buf_size
* used to create the sg_dma_buf_pool must be dynamically calculated.
@@ -4398,7 +4464,8 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
* sgl sizes of must be a power of 2.
*/
buf_size = (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) +
- ((phba->cfg_sg_seg_cnt + 2) * sizeof(struct sli4_sge)));
+ (((phba->cfg_sg_seg_cnt * sges_per_segment) + 2) *
+ sizeof(struct sli4_sge)));
sli_family = bf_get(lpfc_sli_intf_sli_family, &phba->sli4_hba.sli_intf);
max_buf_size = LPFC_SLI4_MAX_BUF_SIZE;
@@ -4415,6 +4482,7 @@ lpfc_sli4_driver_resource_setup(struct lpfc_hba *phba)
default:
break;
}
+
for (dma_buf_size = LPFC_SLI4_MIN_BUF_SIZE;
dma_buf_size < max_buf_size && buf_size > dma_buf_size;
dma_buf_size = dma_buf_size << 1)
@@ -7223,19 +7291,17 @@ lpfc_pci_function_reset(struct lpfc_hba *phba)
rc = -ENODEV;
goto out;
}
- if (bf_get(lpfc_sliport_status_rdy, &reg_data))
- break;
- if (bf_get(lpfc_sliport_status_rn, &reg_data)) {
+ if (bf_get(lpfc_sliport_status_rn, &reg_data))
reset_again++;
+ if (bf_get(lpfc_sliport_status_rdy, &reg_data))
break;
- }
}
/*
* If the port responds to the init request with
* reset needed, delay for a bit and restart the loop.
*/
- if (reset_again) {
+ if (reset_again && (rdy_chk < 1000)) {
msleep(10);
reset_again = 0;
continue;
@@ -8112,6 +8178,9 @@ lpfc_unset_hba(struct lpfc_hba *phba)
vport->load_flag |= FC_UNLOADING;
spin_unlock_irq(shost->host_lock);
+ kfree(phba->vpi_bmask);
+ kfree(phba->vpi_ids);
+
lpfc_stop_hba_timers(phba);
phba->pport->work_port_events = 0;
@@ -8644,6 +8713,9 @@ lpfc_pci_remove_one_s3(struct pci_dev *pdev)
/* Final cleanup of txcmplq and reset the HBA */
lpfc_sli_brdrestart(phba);
+ kfree(phba->vpi_bmask);
+ kfree(phba->vpi_ids);
+
lpfc_stop_hba_timers(phba);
spin_lock_irq(&phba->hbalock);
list_del_init(&vport->listentry);
@@ -9058,7 +9130,7 @@ lpfc_sli4_get_els_iocb_cnt(struct lpfc_hba *phba)
int
lpfc_write_firmware(struct lpfc_hba *phba, const struct firmware *fw)
{
- char fwrev[32];
+ char fwrev[FW_REV_STR_SIZE];
struct lpfc_grp_hdr *image = (struct lpfc_grp_hdr *)fw->data;
struct list_head dma_buffer_list;
int i, rc = 0;
@@ -10012,6 +10084,36 @@ lpfc_io_resume(struct pci_dev *pdev)
return;
}
+/**
+ * lpfc_mgmt_open - method called when 'lpfcmgmt' is opened from userspace
+ * @inode: pointer to the inode representing the lpfcmgmt device
+ * @filep: pointer to the file representing the open lpfcmgmt device
+ *
+ * This routine puts a reference count on the lpfc module whenever the
+ * character device is opened
+ **/
+static int
+lpfc_mgmt_open(struct inode *inode, struct file *filep)
+{
+ try_module_get(THIS_MODULE);
+ return 0;
+}
+
+/**
+ * lpfc_mgmt_release - method called when 'lpfcmgmt' is closed in userspace
+ * @inode: pointer to the inode representing the lpfcmgmt device
+ * @filep: pointer to the file representing the open lpfcmgmt device
+ *
+ * This routine removes a reference count from the lpfc module when the
+ * character device is closed
+ **/
+static int
+lpfc_mgmt_release(struct inode *inode, struct file *filep)
+{
+ module_put(THIS_MODULE);
+ return 0;
+}
+
static struct pci_device_id lpfc_id_table[] = {
{PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER,
PCI_ANY_ID, PCI_ANY_ID, },
@@ -10124,6 +10226,17 @@ static struct pci_driver lpfc_driver = {
.err_handler = &lpfc_err_handler,
};
+static const struct file_operations lpfc_mgmt_fop = {
+ .open = lpfc_mgmt_open,
+ .release = lpfc_mgmt_release,
+};
+
+static struct miscdevice lpfc_mgmt_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "lpfcmgmt",
+ .fops = &lpfc_mgmt_fop,
+};
+
/**
* lpfc_init - lpfc module initialization routine
*
@@ -10144,6 +10257,11 @@ lpfc_init(void)
printk(LPFC_MODULE_DESC "\n");
printk(LPFC_COPYRIGHT "\n");
+ error = misc_register(&lpfc_mgmt_dev);
+ if (error)
+ printk(KERN_ERR "Could not register lpfcmgmt device, "
+ "misc_register returned with status %d", error);
+
if (lpfc_enable_npiv) {
lpfc_transport_functions.vport_create = lpfc_vport_create;
lpfc_transport_functions.vport_delete = lpfc_vport_delete;
@@ -10180,6 +10298,7 @@ lpfc_init(void)
static void __exit
lpfc_exit(void)
{
+ misc_deregister(&lpfc_mgmt_dev);
pci_unregister_driver(&lpfc_driver);
fc_release_transport(lpfc_transport_template);
if (lpfc_enable_npiv)
diff --git a/drivers/scsi/lpfc/lpfc_nportdisc.c b/drivers/scsi/lpfc/lpfc_nportdisc.c
index e8bb00559943..7b6b2aa5795a 100644
--- a/drivers/scsi/lpfc/lpfc_nportdisc.c
+++ b/drivers/scsi/lpfc/lpfc_nportdisc.c
@@ -48,6 +48,10 @@ static int
lpfc_check_adisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_name *nn, struct lpfc_name *pn)
{
+ /* First, we MUST have a RPI registered */
+ if (!(ndlp->nlp_flag & NLP_RPI_REGISTERED))
+ return 0;
+
/* Compare the ADISC rsp WWNN / WWPN matches our internal node
* table entry for that node.
*/
@@ -385,6 +389,10 @@ lpfc_rcv_plogi(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
if (!mbox)
goto out;
+ /* Registering an existing RPI behaves differently for SLI3 vs SLI4 */
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ lpfc_unreg_rpi(vport, ndlp);
+
rc = lpfc_reg_rpi(phba, vport->vpi, icmd->un.rcvels.remoteID,
(uint8_t *) sp, mbox, ndlp->nlp_rpi);
if (rc) {
@@ -445,11 +453,43 @@ out:
return 0;
}
+/**
+ * lpfc_mbx_cmpl_resume_rpi - Resume RPI completion routine
+ * @phba: pointer to lpfc hba data structure.
+ * @mboxq: pointer to mailbox object
+ *
+ * This routine is invoked to issue a completion to a rcv'ed
+ * ADISC or PDISC after the paused RPI has been resumed.
+ **/
+static void
+lpfc_mbx_cmpl_resume_rpi(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq)
+{
+ struct lpfc_vport *vport;
+ struct lpfc_iocbq *elsiocb;
+ struct lpfc_nodelist *ndlp;
+ uint32_t cmd;
+
+ elsiocb = (struct lpfc_iocbq *)mboxq->context1;
+ ndlp = (struct lpfc_nodelist *) mboxq->context2;
+ vport = mboxq->vport;
+ cmd = elsiocb->drvrTimeout;
+
+ if (cmd == ELS_CMD_ADISC) {
+ lpfc_els_rsp_adisc_acc(vport, elsiocb, ndlp);
+ } else {
+ lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, elsiocb,
+ ndlp, NULL);
+ }
+ kfree(elsiocb);
+ mempool_free(mboxq, phba->mbox_mem_pool);
+}
+
static int
lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
struct lpfc_iocbq *cmdiocb)
{
struct Scsi_Host *shost = lpfc_shost_from_vport(vport);
+ struct lpfc_iocbq *elsiocb;
struct lpfc_dmabuf *pcmd;
struct serv_parm *sp;
struct lpfc_name *pnn, *ppn;
@@ -475,12 +515,43 @@ lpfc_rcv_padisc(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp,
icmd = &cmdiocb->iocb;
if (icmd->ulpStatus == 0 && lpfc_check_adisc(vport, ndlp, pnn, ppn)) {
+
+ /*
+ * As soon as we send ACC, the remote NPort can
+ * start sending us data. Thus, for SLI4 we must
+ * resume the RPI before the ACC goes out.
+ */
+ if (vport->phba->sli_rev == LPFC_SLI_REV4) {
+ elsiocb = kmalloc(sizeof(struct lpfc_iocbq),
+ GFP_KERNEL);
+ if (elsiocb) {
+
+ /* Save info from cmd IOCB used in rsp */
+ memcpy((uint8_t *)elsiocb, (uint8_t *)cmdiocb,
+ sizeof(struct lpfc_iocbq));
+
+ /* Save the ELS cmd */
+ elsiocb->drvrTimeout = cmd;
+
+ lpfc_sli4_resume_rpi(ndlp,
+ lpfc_mbx_cmpl_resume_rpi, elsiocb);
+ goto out;
+ }
+ }
+
if (cmd == ELS_CMD_ADISC) {
lpfc_els_rsp_adisc_acc(vport, cmdiocb, ndlp);
} else {
- lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb, ndlp,
- NULL);
+ lpfc_els_rsp_acc(vport, ELS_CMD_PLOGI, cmdiocb,
+ ndlp, NULL);
}
+out:
+ /* If we are authenticated, move to the proper state */
+ if (ndlp->nlp_type & NLP_FCP_TARGET)
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_MAPPED_NODE);
+ else
+ lpfc_nlp_set_state(vport, ndlp, NLP_STE_UNMAPPED_NODE);
+
return 1;
}
/* Reject this request because invalid parameters */
@@ -1229,7 +1300,7 @@ lpfc_cmpl_adisc_adisc_issue(struct lpfc_vport *vport,
}
if (phba->sli_rev == LPFC_SLI_REV4) {
- rc = lpfc_sli4_resume_rpi(ndlp);
+ rc = lpfc_sli4_resume_rpi(ndlp, NULL, NULL);
if (rc) {
/* Stay in state and retry. */
ndlp->nlp_prev_state = NLP_STE_ADISC_ISSUE;
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c
index c60f5d0b3869..efc055b6bac4 100644
--- a/drivers/scsi/lpfc/lpfc_scsi.c
+++ b/drivers/scsi/lpfc/lpfc_scsi.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 *
@@ -1280,31 +1280,45 @@ lpfc_cmd_blksize(struct scsi_cmnd *sc)
}
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
-/*
- * Given a scsi cmnd, determine the BlockGuard tags to be used with it
+
+#define BG_ERR_INIT 1
+#define BG_ERR_TGT 2
+#define BG_ERR_SWAP 3
+#define BG_ERR_CHECK 4
+
+/**
+ * lpfc_bg_err_inject - Determine if we should inject an error
+ * @phba: The Hba for which this call is being executed.
* @sc: The SCSI command to examine
* @reftag: (out) BlockGuard reference tag for transmitted data
* @apptag: (out) BlockGuard application tag for transmitted data
* @new_guard (in) Value to replace CRC with if needed
*
- * Returns (1) if error injection was performed, (0) otherwise
- */
+ * 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
+ **/
static int
lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint32_t *reftag, uint16_t *apptag, uint32_t new_guard)
{
struct scatterlist *sgpe; /* s/g prot entry */
struct scatterlist *sgde; /* s/g data entry */
- struct scsi_dif_tuple *src;
+ struct scsi_dif_tuple *src = NULL;
uint32_t op = scsi_get_prot_op(sc);
uint32_t blksize;
uint32_t numblks;
sector_t lba;
int rc = 0;
+ int blockoff = 0;
if (op == SCSI_PROT_NORMAL)
return 0;
+ sgpe = scsi_prot_sglist(sc);
+ sgde = scsi_sglist(sc);
+
lba = scsi_get_lba(sc);
if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) {
blksize = lpfc_cmd_blksize(sc);
@@ -1314,142 +1328,296 @@ lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc,
if ((phba->lpfc_injerr_lba < lba) ||
(phba->lpfc_injerr_lba >= (lba + numblks)))
return 0;
+ if (sgpe) {
+ blockoff = phba->lpfc_injerr_lba - lba;
+ numblks = sg_dma_len(sgpe) /
+ sizeof(struct scsi_dif_tuple);
+ if (numblks < blockoff)
+ blockoff = numblks;
+ src = (struct scsi_dif_tuple *)sg_virt(sgpe);
+ src += blockoff;
+ }
}
- sgpe = scsi_prot_sglist(sc);
- sgde = scsi_sglist(sc);
-
/* Should we change the Reference Tag */
if (reftag) {
- /*
- * If we are SCSI_PROT_WRITE_STRIP, the protection data is
- * being stripped from the wire, thus it doesn't matter.
- */
- if ((op == SCSI_PROT_WRITE_PASS) ||
- (op == SCSI_PROT_WRITE_INSERT)) {
- if (phba->lpfc_injerr_wref_cnt) {
+ if (phba->lpfc_injerr_wref_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,
+ "9076 BLKGRD: Injecting reftag error: "
+ "write lba x%lx + x%x oldrefTag x%x\n",
+ (unsigned long)lba, blockoff,
+ src->ref_tag);
+
+ /*
+ * NOTE, this will change ref tag in
+ * the memory location forever!
+ */
+ src->ref_tag = 0xDEADBEEF;
+ phba->lpfc_injerr_wref_cnt--;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ rc = BG_ERR_CHECK;
+ break;
+ }
+ /* Drop thru */
+ case SCSI_PROT_WRITE_STRIP:
+ /*
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
+ */
+ *reftag = 0xDEADBEEF;
+ phba->lpfc_injerr_wref_cnt--;
+ phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ rc = BG_ERR_INIT;
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9077 BLKGRD: Injecting reftag error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ /*
+ * 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 = 1;
+ rc = BG_ERR_TGT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9081 BLKGRD: Injecting reftag error: "
+ "9078 BLKGRD: Injecting reftag error: "
"write lba x%lx\n", (unsigned long)lba);
+ break;
}
- } else {
- if (phba->lpfc_injerr_rref_cnt) {
+ }
+ 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:
+ /*
+ * For READ_STRIP and READ_PASS, force the
+ * error on data being read off the wire. It
+ * should force an IO error to the driver.
+ */
*reftag = 0xDEADBEEF;
phba->lpfc_injerr_rref_cnt--;
phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = 1;
+ rc = BG_ERR_INIT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9076 BLKGRD: Injecting reftag error: "
+ "9079 BLKGRD: Injecting reftag error: "
"read lba x%lx\n", (unsigned long)lba);
+ break;
}
}
}
/* Should we change the Application Tag */
if (apptag) {
- /*
- * If we are SCSI_PROT_WRITE_STRIP, the protection data is
- * being stripped from the wire, thus it doesn't matter.
- */
- if ((op == SCSI_PROT_WRITE_PASS) ||
- (op == SCSI_PROT_WRITE_INSERT)) {
- if (phba->lpfc_injerr_wapp_cnt) {
+ if (phba->lpfc_injerr_wapp_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,
+ "9080 BLKGRD: Injecting apptag error: "
+ "write lba x%lx + x%x oldappTag x%x\n",
+ (unsigned long)lba, blockoff,
+ src->app_tag);
+ /*
+ * NOTE, this will change app tag in
+ * the memory location forever!
+ */
+ src->app_tag = 0xDEAD;
+ phba->lpfc_injerr_wapp_cnt--;
+ phba->lpfc_injerr_lba =
+ LPFC_INJERR_LBA_OFF;
+ rc = BG_ERR_CHECK;
+ break;
+ }
+ /* Drop thru */
+ case SCSI_PROT_WRITE_STRIP:
+ /*
+ * For WRITE_STRIP and WRITE_PASS,
+ * force the error on data
+ * being copied from SLI-Host to SLI-Port.
+ */
+ *apptag = 0xDEAD;
+ phba->lpfc_injerr_wapp_cnt--;
+ phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
+ rc = BG_ERR_INIT;
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "0812 BLKGRD: Injecting apptag error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ /*
+ * 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 = 1;
+ rc = BG_ERR_TGT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9077 BLKGRD: Injecting apptag error: "
+ "0813 BLKGRD: Injecting apptag error: "
"write lba x%lx\n", (unsigned long)lba);
+ break;
}
- } else {
- if (phba->lpfc_injerr_rapp_cnt) {
+ }
+ 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:
+ /*
+ * For READ_STRIP and READ_PASS, force the
+ * error on data being read off the wire. It
+ * should force an IO error to the driver.
+ */
*apptag = 0xDEAD;
phba->lpfc_injerr_rapp_cnt--;
phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = 1;
+ rc = BG_ERR_INIT;
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9078 BLKGRD: Injecting apptag error: "
+ "0814 BLKGRD: Injecting apptag error: "
"read lba x%lx\n", (unsigned long)lba);
+ break;
}
}
}
+
/* Should we change the Guard Tag */
+ if (new_guard) {
+ 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);
- /*
- * If we are SCSI_PROT_WRITE_INSERT, the protection data is
- * being on the wire is being fully generated on the HBA.
- * The host cannot change it or force an error.
- */
- if (((op == SCSI_PROT_WRITE_STRIP) ||
- (op == SCSI_PROT_WRITE_PASS)) &&
- phba->lpfc_injerr_wgrd_cnt) {
- if (sgpe) {
- src = (struct scsi_dif_tuple *)sg_virt(sgpe);
- /*
- * Just inject an error in the first
- * prot block.
- */
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9079 BLKGRD: Injecting guard error: "
- "write lba x%lx oldGuard x%x refTag x%x\n",
- (unsigned long)lba, src->guard_tag,
- src->ref_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;
+ }
+ /* Drop thru */
+ case SCSI_PROT_WRITE_STRIP:
+ /*
+ * 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;
- src->guard_tag = (uint16_t)new_guard;
- phba->lpfc_injerr_wgrd_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = 1;
+ rc = BG_ERR_SWAP;
+ /* Signals the caller to swap CRC->CSUM */
- } else {
- blksize = lpfc_cmd_blksize(sc);
- /*
- * Jump past the first data block
- * and inject an error in the
- * prot data. The prot data is already
- * embedded after the regular data.
- */
- src = (struct scsi_dif_tuple *)
- (sg_virt(sgde) + blksize);
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "0816 BLKGRD: Injecting guard error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ /*
+ * 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;
- lpfc_printf_log(phba, KERN_ERR, LOG_BG,
- "9080 BLKGRD: Injecting guard error: "
- "write lba x%lx oldGuard x%x refTag x%x\n",
- (unsigned long)lba, src->guard_tag,
- src->ref_tag);
-
- src->guard_tag = (uint16_t)new_guard;
- phba->lpfc_injerr_wgrd_cnt--;
- phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF;
- rc = 1;
+ rc = BG_ERR_SWAP;
+ /* Signals the caller to swap CRC->CSUM */
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "0817 BLKGRD: Injecting guard error: "
+ "write lba x%lx\n", (unsigned long)lba);
+ break;
+ }
+ }
+ 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:
+ /*
+ * For READ_STRIP and READ_PASS, force the
+ * 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;
+
+ rc = BG_ERR_SWAP;
+ /* Signals the caller to swap CRC->CSUM */
+
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "0818 BLKGRD: Injecting guard error: "
+ "read lba x%lx\n", (unsigned long)lba);
+ }
}
}
+
return rc;
}
#endif
-/*
- * Given a scsi cmnd, determine the BlockGuard opcodes to be used with it
+/**
+ * lpfc_sc_to_bg_opcodes - Determine the BlockGuard opcodes to be used with
+ * the specified SCSI command.
+ * @phba: The Hba for which this call is being executed.
* @sc: The SCSI command to examine
* @txopt: (out) BlockGuard operation for transmitted data
* @rxopt: (out) BlockGuard operation for received data
*
* Returns: zero on success; non-zero if tx and/or rx op cannot be determined
*
- */
+ **/
static int
lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
uint8_t *txop, uint8_t *rxop)
@@ -1519,8 +1687,88 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
return ret;
}
-/*
- * This function sets up buffer list for protection groups of
+#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
+/**
+ * lpfc_bg_err_opcodes - reDetermine the BlockGuard opcodes to be used with
+ * the specified SCSI command in order to force a guard tag error.
+ * @phba: The Hba for which this call is being executed.
+ * @sc: The SCSI command to examine
+ * @txopt: (out) BlockGuard operation for transmitted data
+ * @rxopt: (out) BlockGuard operation for received data
+ *
+ * Returns: zero on success; non-zero if tx and/or rx op cannot be determined
+ *
+ **/
+static int
+lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ uint8_t *txop, uint8_t *rxop)
+{
+ uint8_t guard_type = scsi_host_get_guard(sc->device->host);
+ uint8_t ret = 0;
+
+ if (guard_type == SHOST_DIX_GUARD_IP) {
+ 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;
+ 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;
+ 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;
+ break;
+
+ case SCSI_PROT_NORMAL:
+ default:
+ break;
+
+ }
+ } else {
+ 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;
+ 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;
+ 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;
+ break;
+
+ case SCSI_PROT_NORMAL:
+ default:
+ break;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+/**
+ * lpfc_bg_setup_bpl - Setup BlockGuard BPL with no protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * This function sets up BPL buffer list for protection groups of
* type LPFC_PG_TYPE_NO_DIF
*
* This is usually used when the HBA is instructed to generate
@@ -1539,12 +1787,11 @@ lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc,
* |more Data BDE's ... (opt)|
* +-------------------------+
*
- * @sc: pointer to scsi command we're working on
- * @bpl: pointer to buffer list for protection groups
- * @datacnt: number of segments of data that have been dma mapped
*
* Note: Data s/g buffers have been dma mapped
- */
+ *
+ * Returns the number of BDEs added to the BPL.
+ **/
static int
lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct ulp_bde64 *bpl, int datasegcnt)
@@ -1555,6 +1802,8 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
dma_addr_t physaddr;
int i = 0, num_bde = 0, status;
int datadir = sc->sc_data_direction;
+ uint32_t rc;
+ uint32_t checking = 1;
uint32_t reftag;
unsigned blksize;
uint8_t txop, rxop;
@@ -1565,11 +1814,16 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
/* extract some info from the scsi command for pde*/
blksize = lpfc_cmd_blksize(sc);
- reftag = scsi_get_lba(sc) & 0xffffffff;
+ reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- /* reftag is the only error we can inject here */
- lpfc_bg_err_inject(phba, sc, &reftag, 0, 0);
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+ if (rc) {
+ if (rc == BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc == BG_ERR_CHECK)
+ checking = 0;
+ }
#endif
/* setup PDE5 with what we have */
@@ -1592,8 +1846,8 @@ lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde6_optx, pde6, txop);
bf_set(pde6_oprx, pde6, rxop);
if (datadir == DMA_FROM_DEVICE) {
- bf_set(pde6_ce, pde6, 1);
- bf_set(pde6_re, pde6, 1);
+ bf_set(pde6_ce, pde6, checking);
+ bf_set(pde6_re, pde6, checking);
}
bf_set(pde6_ai, pde6, 1);
bf_set(pde6_ae, pde6, 0);
@@ -1627,9 +1881,16 @@ out:
return num_bde;
}
-/*
- * This function sets up buffer list for protection groups of
- * type LPFC_PG_TYPE_DIF_BUF
+/**
+ * lpfc_bg_setup_bpl_prot - Setup BlockGuard BPL with protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @bpl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * This function sets up BPL buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF
*
* This is usually used when DIFs are in their own buffers,
* separate from the data. The HBA can then by instructed
@@ -1654,14 +1915,11 @@ out:
* | ... |
* +-------------------------+
*
- * @sc: pointer to scsi command we're working on
- * @bpl: pointer to buffer list for protection groups
- * @datacnt: number of segments of data that have been dma mapped
- * @protcnt: number of segment of protection data that have been dma mapped
- *
* Note: It is assumed that both data and protection s/g buffers have been
* mapped for DMA
- */
+ *
+ * Returns the number of BDEs added to the BPL.
+ **/
static int
lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
struct ulp_bde64 *bpl, int datacnt, int protcnt)
@@ -1681,6 +1939,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
int datadir = sc->sc_data_direction;
unsigned char pgdone = 0, alldone = 0;
unsigned blksize;
+ uint32_t rc;
+ uint32_t checking = 1;
uint32_t reftag;
uint8_t txop, rxop;
int num_bde = 0;
@@ -1701,11 +1961,16 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
/* extract some info from the scsi command */
blksize = lpfc_cmd_blksize(sc);
- reftag = scsi_get_lba(sc) & 0xffffffff;
+ reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */
#ifdef CONFIG_SCSI_LPFC_DEBUG_FS
- /* reftag / guard tag are the only errors we can inject here */
- lpfc_bg_err_inject(phba, sc, &reftag, 0, 0xDEAD);
+ rc = lpfc_bg_err_inject(phba, sc, &reftag, 0, 1);
+ if (rc) {
+ if (rc == BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc == BG_ERR_CHECK)
+ checking = 0;
+ }
#endif
split_offset = 0;
@@ -1729,8 +1994,8 @@ lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR);
bf_set(pde6_optx, pde6, txop);
bf_set(pde6_oprx, pde6, rxop);
- bf_set(pde6_ce, pde6, 1);
- bf_set(pde6_re, pde6, 1);
+ bf_set(pde6_ce, pde6, checking);
+ bf_set(pde6_re, pde6, checking);
bf_set(pde6_ai, pde6, 1);
bf_set(pde6_ae, pde6, 0);
bf_set(pde6_apptagval, pde6, 0);
@@ -1852,13 +2117,358 @@ out:
return num_bde;
}
-/*
+/**
+ * lpfc_bg_setup_sgl - Setup BlockGuard SGL with no protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @sgl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ *
+ * This function sets up SGL buffer list for protection groups of
+ * type LPFC_PG_TYPE_NO_DIF
+ *
+ * This is usually used when the HBA is instructed to generate
+ * DIFs and insert them into data stream (or strip DIF from
+ * incoming data stream)
+ *
+ * The buffer list consists of just one protection group described
+ * below:
+ * +-------------------------+
+ * start of prot group --> | DI_SEED |
+ * +-------------------------+
+ * | Data SGE |
+ * +-------------------------+
+ * |more Data SGE's ... (opt)|
+ * +-------------------------+
+ *
+ *
+ * Note: Data s/g buffers have been dma mapped
+ *
+ * Returns the number of SGEs added to the SGL.
+ **/
+static int
+lpfc_bg_setup_sgl(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct sli4_sge *sgl, int datasegcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct sli4_sge_diseed *diseed = NULL;
+ dma_addr_t physaddr;
+ int i = 0, num_sge = 0, status;
+ int datadir = sc->sc_data_direction;
+ uint32_t reftag;
+ unsigned blksize;
+ uint8_t txop, rxop;
+ uint32_t rc;
+ uint32_t checking = 1;
+ uint32_t dma_len;
+ uint32_t dma_offset = 0;
+
+ status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
+ if (status)
+ goto out;
+
+ /* extract some info from the scsi command for pde*/
+ blksize = lpfc_cmd_blksize(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);
+ if (rc) {
+ if (rc == BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc == BG_ERR_CHECK)
+ checking = 0;
+ }
+#endif
+
+ /* setup DISEED with what we have */
+ diseed = (struct sli4_sge_diseed *) sgl;
+ memset(diseed, 0, sizeof(struct sli4_sge_diseed));
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DISEED);
+
+ /* Endianness conversion if necessary */
+ diseed->ref_tag = cpu_to_le32(reftag);
+ diseed->ref_tag_tran = diseed->ref_tag;
+
+ /* setup DISEED with the rest of the info */
+ bf_set(lpfc_sli4_sge_dif_optx, diseed, txop);
+ bf_set(lpfc_sli4_sge_dif_oprx, diseed, rxop);
+ if (datadir == DMA_FROM_DEVICE) {
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+ bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+ }
+ bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
+ bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
+
+ /* Endianness conversion if necessary for DISEED */
+ diseed->word2 = cpu_to_le32(diseed->word2);
+ diseed->word3 = cpu_to_le32(diseed->word3);
+
+ /* advance bpl and increment sge count */
+ num_sge++;
+ sgl++;
+
+ /* assumption: caller has already run dma_map_sg on command data */
+ scsi_for_each_sg(sc, sgde, datasegcnt, i) {
+ physaddr = sg_dma_address(sgde);
+ dma_len = sg_dma_len(sgde);
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(physaddr));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(physaddr));
+ if ((i + 1) == datasegcnt)
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ else
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
+
+ sgl->sge_len = cpu_to_le32(dma_len);
+ dma_offset += dma_len;
+
+ sgl++;
+ num_sge++;
+ }
+
+out:
+ return num_sge;
+}
+
+/**
+ * lpfc_bg_setup_sgl_prot - Setup BlockGuard SGL with protection data
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ * @sgl: pointer to buffer list for protection groups
+ * @datacnt: number of segments of data that have been dma mapped
+ * @protcnt: number of segment of protection data that have been dma mapped
+ *
+ * This function sets up SGL buffer list for protection groups of
+ * type LPFC_PG_TYPE_DIF
+ *
+ * This is usually used when DIFs are in their own buffers,
+ * separate from the data. The HBA can then by instructed
+ * to place the DIFs in the outgoing stream. For read operations,
+ * The HBA could extract the DIFs and place it in DIF buffers.
+ *
+ * The buffer list for this type consists of one or more of the
+ * protection groups described below:
+ * +-------------------------+
+ * start of first prot group --> | DISEED |
+ * +-------------------------+
+ * | DIF (Prot SGE) |
+ * +-------------------------+
+ * | Data SGE |
+ * +-------------------------+
+ * |more Data SGE's ... (opt)|
+ * +-------------------------+
+ * start of new prot group --> | DISEED |
+ * +-------------------------+
+ * | ... |
+ * +-------------------------+
+ *
+ * Note: It is assumed that both data and protection s/g buffers have been
+ * mapped for DMA
+ *
+ * Returns the number of SGEs added to the SGL.
+ **/
+static int
+lpfc_bg_setup_sgl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc,
+ struct sli4_sge *sgl, int datacnt, int protcnt)
+{
+ struct scatterlist *sgde = NULL; /* s/g data entry */
+ struct scatterlist *sgpe = NULL; /* s/g prot entry */
+ struct sli4_sge_diseed *diseed = NULL;
+ dma_addr_t dataphysaddr, protphysaddr;
+ unsigned short curr_data = 0, curr_prot = 0;
+ unsigned int split_offset;
+ unsigned int protgroup_len, protgroup_offset = 0, protgroup_remainder;
+ unsigned int protgrp_blks, protgrp_bytes;
+ unsigned int remainder, subtotal;
+ int status;
+ unsigned char pgdone = 0, alldone = 0;
+ unsigned blksize;
+ uint32_t reftag;
+ uint8_t txop, rxop;
+ uint32_t dma_len;
+ uint32_t rc;
+ uint32_t checking = 1;
+ uint32_t dma_offset = 0;
+ int num_sge = 0;
+
+ sgpe = scsi_prot_sglist(sc);
+ sgde = scsi_sglist(sc);
+
+ if (!sgpe || !sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9082 Invalid s/g entry: data=0x%p prot=0x%p\n",
+ sgpe, sgde);
+ return 0;
+ }
+
+ status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop);
+ if (status)
+ goto out;
+
+ /* extract some info from the scsi command */
+ blksize = lpfc_cmd_blksize(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);
+ if (rc) {
+ if (rc == BG_ERR_SWAP)
+ lpfc_bg_err_opcodes(phba, sc, &txop, &rxop);
+ if (rc == BG_ERR_CHECK)
+ checking = 0;
+ }
+#endif
+
+ split_offset = 0;
+ do {
+ /* setup DISEED with what we have */
+ diseed = (struct sli4_sge_diseed *) sgl;
+ memset(diseed, 0, sizeof(struct sli4_sge_diseed));
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DISEED);
+
+ /* Endianness conversion if necessary */
+ diseed->ref_tag = cpu_to_le32(reftag);
+ diseed->ref_tag_tran = diseed->ref_tag;
+
+ /* setup DISEED with the rest of the info */
+ bf_set(lpfc_sli4_sge_dif_optx, diseed, txop);
+ bf_set(lpfc_sli4_sge_dif_oprx, diseed, rxop);
+ bf_set(lpfc_sli4_sge_dif_ce, diseed, checking);
+ bf_set(lpfc_sli4_sge_dif_re, diseed, checking);
+ bf_set(lpfc_sli4_sge_dif_ai, diseed, 1);
+ bf_set(lpfc_sli4_sge_dif_me, diseed, 0);
+
+ /* Endianness conversion if necessary for DISEED */
+ diseed->word2 = cpu_to_le32(diseed->word2);
+ diseed->word3 = cpu_to_le32(diseed->word3);
+
+ /* advance sgl and increment bde count */
+ num_sge++;
+ sgl++;
+
+ /* setup the first BDE that points to protection buffer */
+ protphysaddr = sg_dma_address(sgpe) + protgroup_offset;
+ protgroup_len = sg_dma_len(sgpe) - protgroup_offset;
+
+ /* must be integer multiple of the DIF block length */
+ BUG_ON(protgroup_len % 8);
+
+ /* Now setup DIF SGE */
+ sgl->word2 = 0;
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DIF);
+ sgl->addr_hi = le32_to_cpu(putPaddrHigh(protphysaddr));
+ sgl->addr_lo = le32_to_cpu(putPaddrLow(protphysaddr));
+ sgl->word2 = cpu_to_le32(sgl->word2);
+
+ protgrp_blks = protgroup_len / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+
+ /* check if DIF SGE is crossing the 4K boundary; if so split */
+ if ((sgl->addr_lo & 0xfff) + protgroup_len > 0x1000) {
+ protgroup_remainder = 0x1000 - (sgl->addr_lo & 0xfff);
+ protgroup_offset += protgroup_remainder;
+ protgrp_blks = protgroup_remainder / 8;
+ protgrp_bytes = protgrp_blks * blksize;
+ } else {
+ protgroup_offset = 0;
+ curr_prot++;
+ }
+
+ num_sge++;
+
+ /* setup SGE's for data blocks associated with DIF data */
+ pgdone = 0;
+ subtotal = 0; /* total bytes processed for current prot grp */
+ while (!pgdone) {
+ if (!sgde) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9086 BLKGRD:%s Invalid data segment\n",
+ __func__);
+ return 0;
+ }
+ sgl++;
+ dataphysaddr = sg_dma_address(sgde) + split_offset;
+
+ remainder = sg_dma_len(sgde) - split_offset;
+
+ if ((subtotal + remainder) <= protgrp_bytes) {
+ /* we can use this whole buffer */
+ dma_len = remainder;
+ split_offset = 0;
+
+ if ((subtotal + remainder) == protgrp_bytes)
+ pgdone = 1;
+ } else {
+ /* must split this buffer with next prot grp */
+ dma_len = protgrp_bytes - subtotal;
+ split_offset += dma_len;
+ }
+
+ subtotal += dma_len;
+
+ sgl->addr_lo = cpu_to_le32(putPaddrLow(dataphysaddr));
+ sgl->addr_hi = cpu_to_le32(putPaddrHigh(dataphysaddr));
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ bf_set(lpfc_sli4_sge_offset, sgl, dma_offset);
+ bf_set(lpfc_sli4_sge_type, sgl, LPFC_SGE_TYPE_DATA);
+
+ sgl->sge_len = cpu_to_le32(dma_len);
+ dma_offset += dma_len;
+
+ num_sge++;
+ curr_data++;
+
+ if (split_offset)
+ break;
+
+ /* Move to the next s/g segment if possible */
+ sgde = sg_next(sgde);
+ }
+
+ if (protgroup_offset) {
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ sgl++;
+ continue;
+ }
+
+ /* are we done ? */
+ if (curr_prot == protcnt) {
+ bf_set(lpfc_sli4_sge_last, sgl, 1);
+ alldone = 1;
+ } else if (curr_prot < protcnt) {
+ /* advance to next prot buffer */
+ sgpe = sg_next(sgpe);
+ sgl++;
+
+ /* update the reference tag */
+ reftag += protgrp_blks;
+ } else {
+ /* if we're here, we have a bug */
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9085 BLKGRD: bug in %s\n", __func__);
+ }
+
+ } while (!alldone);
+
+out:
+
+ return num_sge;
+}
+
+/**
+ * lpfc_prot_group_type - Get prtotection group type of SCSI command
+ * @phba: The Hba for which this call is being executed.
+ * @sc: pointer to scsi command we're working on
+ *
* Given a SCSI command that supports DIF, determine composition of protection
* groups involved in setting up buffer lists
*
- * Returns:
- * for DIF (for both read and write)
- * */
+ * Returns: Protection group type (with or without DIF)
+ *
+ **/
static int
lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
{
@@ -1885,13 +2495,17 @@ lpfc_prot_group_type(struct lpfc_hba *phba, struct scsi_cmnd *sc)
return ret;
}
-/*
+/**
+ * lpfc_bg_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be prep'ed.
+ *
* This is the protection/DIF aware version of
* lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
* two functions eventually, but for now, it's here
- */
+ **/
static int
-lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba,
+lpfc_bg_scsi_prep_dma_buf_s3(struct lpfc_hba *phba,
struct lpfc_scsi_buf *lpfc_cmd)
{
struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
@@ -2147,7 +2761,21 @@ lpfc_parse_bg_err(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd,
cmd->sense_buffer[8] = 0; /* Information descriptor type */
cmd->sense_buffer[9] = 0xa; /* Additional descriptor length */
cmd->sense_buffer[10] = 0x80; /* Validity bit */
- bghm /= cmd->device->sector_size;
+
+ /* bghm is a "on the wire" FC frame based count */
+ switch (scsi_get_prot_op(cmd)) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ bghm /= cmd->device->sector_size;
+ break;
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ bghm /= (cmd->device->sector_size +
+ sizeof(struct scsi_dif_tuple));
+ break;
+ }
failing_sector = scsi_get_lba(cmd);
failing_sector += bghm;
@@ -2292,6 +2920,180 @@ lpfc_scsi_prep_dma_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
}
/**
+ * lpfc_bg_scsi_adjust_dl - Adjust SCSI data length for BlockGuard
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be adjusted.
+ *
+ * Adjust the data length to account for how much data
+ * is actually on the wire.
+ *
+ * returns the adjusted data length
+ **/
+static int
+lpfc_bg_scsi_adjust_dl(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *sc = lpfc_cmd->pCmd;
+ int diflen, fcpdl;
+ unsigned blksize;
+
+ fcpdl = scsi_bufflen(sc);
+
+ /* Check if there is protection data on the wire */
+ if (sc->sc_data_direction == DMA_FROM_DEVICE) {
+ /* Read */
+ if (scsi_get_prot_op(sc) == SCSI_PROT_READ_INSERT)
+ return fcpdl;
+
+ } else {
+ /* Write */
+ if (scsi_get_prot_op(sc) == SCSI_PROT_WRITE_STRIP)
+ return fcpdl;
+ }
+
+ /* If protection data on the wire, adjust the count accordingly */
+ blksize = lpfc_cmd_blksize(sc);
+ diflen = (fcpdl / blksize) * 8;
+ fcpdl += diflen;
+ return fcpdl;
+}
+
+/**
+ * lpfc_bg_scsi_prep_dma_buf_s4 - DMA mapping for scsi buffer to SLI4 IF spec
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This is the protection/DIF aware version of
+ * lpfc_scsi_prep_dma_buf(). It may be a good idea to combine the
+ * two functions eventually, but for now, it's here
+ **/
+static int
+lpfc_bg_scsi_prep_dma_buf_s4(struct lpfc_hba *phba,
+ struct lpfc_scsi_buf *lpfc_cmd)
+{
+ struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd;
+ struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd;
+ struct sli4_sge *sgl = (struct sli4_sge *)(lpfc_cmd->fcp_bpl);
+ IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb;
+ uint32_t num_bde = 0;
+ int datasegcnt, protsegcnt, datadir = scsi_cmnd->sc_data_direction;
+ int prot_group_type = 0;
+ int fcpdl;
+
+ /*
+ * Start the lpfc command prep by bumping the sgl beyond fcp_cmnd
+ * fcp_rsp regions to the first data bde entry
+ */
+ if (scsi_sg_count(scsi_cmnd)) {
+ /*
+ * The driver stores the segment count returned from pci_map_sg
+ * because this a count of dma-mappings used to map the use_sg
+ * pages. They are not guaranteed to be the same for those
+ * architectures that implement an IOMMU.
+ */
+ datasegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_sglist(scsi_cmnd),
+ scsi_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!datasegcnt))
+ return 1;
+
+ sgl += 1;
+ /* clear the last flag in the fcp_rsp map entry */
+ sgl->word2 = le32_to_cpu(sgl->word2);
+ bf_set(lpfc_sli4_sge_last, sgl, 0);
+ sgl->word2 = cpu_to_le32(sgl->word2);
+
+ sgl += 1;
+ lpfc_cmd->seg_cnt = datasegcnt;
+ if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9087 BLKGRD: %s: Too many sg segments"
+ " from dma_map_sg. Config %d, seg_cnt"
+ " %d\n",
+ __func__, phba->cfg_sg_seg_cnt,
+ lpfc_cmd->seg_cnt);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ prot_group_type = lpfc_prot_group_type(phba, scsi_cmnd);
+
+ switch (prot_group_type) {
+ case LPFC_PG_TYPE_NO_DIF:
+ num_bde = lpfc_bg_setup_sgl(phba, scsi_cmnd, sgl,
+ datasegcnt);
+ /* we should have 2 or more entries in buffer list */
+ if (num_bde < 2)
+ goto err;
+ break;
+ case LPFC_PG_TYPE_DIF_BUF:{
+ /*
+ * This type indicates that protection buffers are
+ * passed to the driver, so that needs to be prepared
+ * for DMA
+ */
+ protsegcnt = dma_map_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd), datadir);
+ if (unlikely(!protsegcnt)) {
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ lpfc_cmd->prot_seg_cnt = protsegcnt;
+ if (lpfc_cmd->prot_seg_cnt
+ > phba->cfg_prot_sg_seg_cnt) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_BG,
+ "9088 BLKGRD: %s: Too many prot sg "
+ "segments from dma_map_sg. Config %d,"
+ "prot_seg_cnt %d\n", __func__,
+ phba->cfg_prot_sg_seg_cnt,
+ lpfc_cmd->prot_seg_cnt);
+ dma_unmap_sg(&phba->pcidev->dev,
+ scsi_prot_sglist(scsi_cmnd),
+ scsi_prot_sg_count(scsi_cmnd),
+ datadir);
+ scsi_dma_unmap(scsi_cmnd);
+ return 1;
+ }
+
+ num_bde = lpfc_bg_setup_sgl_prot(phba, scsi_cmnd, sgl,
+ datasegcnt, protsegcnt);
+ /* we should have 3 or more entries in buffer list */
+ if (num_bde < 3)
+ goto err;
+ break;
+ }
+ case LPFC_PG_TYPE_INVALID:
+ default:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9083 Unexpected protection group %i\n",
+ prot_group_type);
+ return 1;
+ }
+ }
+
+ fcpdl = lpfc_bg_scsi_adjust_dl(phba, lpfc_cmd);
+
+ fcp_cmnd->fcpDl = be32_to_cpu(fcpdl);
+
+ /*
+ * Due to difference in data length between DIF/non-DIF paths,
+ * we need to set word 4 of IOCB here
+ */
+ iocb_cmd->un.fcpi.fcpi_parm = fcpdl;
+ lpfc_cmd->cur_iocbq.iocb_flag |= LPFC_IO_DIF;
+
+ return 0;
+err:
+ lpfc_printf_log(phba, KERN_ERR, LOG_FCP,
+ "9084 Could not setup all needed BDE's"
+ "prot_group_type=%d, num_bde=%d\n",
+ prot_group_type, num_bde);
+ return 1;
+}
+
+/**
* lpfc_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
* @phba: The Hba for which this call is being executed.
* @lpfc_cmd: The scsi buffer which is going to be mapped.
@@ -2310,6 +3112,25 @@ lpfc_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
}
/**
+ * lpfc_bg_scsi_prep_dma_buf - Wrapper function for DMA mapping of scsi buffer
+ * using BlockGuard.
+ * @phba: The Hba for which this call is being executed.
+ * @lpfc_cmd: The scsi buffer which is going to be mapped.
+ *
+ * This routine wraps the actual DMA mapping function pointer from the
+ * lpfc_hba struct.
+ *
+ * Return codes:
+ * 1 - Error
+ * 0 - Success
+ **/
+static inline int
+lpfc_bg_scsi_prep_dma_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd)
+{
+ return phba->lpfc_bg_scsi_prep_dma_buf(phba, lpfc_cmd);
+}
+
+/**
* lpfc_send_scsi_error_event - Posts an event when there is SCSI error
* @phba: Pointer to hba context object.
* @vport: Pointer to vport object.
@@ -3072,12 +3893,14 @@ lpfc_scsi_api_table_setup(struct lpfc_hba *phba, uint8_t dev_grp)
case LPFC_PCI_DEV_LP:
phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s3;
phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s3;
+ phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s3;
phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s3;
phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s3;
break;
case LPFC_PCI_DEV_OC:
phba->lpfc_new_scsi_buf = lpfc_new_scsi_buf_s4;
phba->lpfc_scsi_prep_dma_buf = lpfc_scsi_prep_dma_buf_s4;
+ phba->lpfc_bg_scsi_prep_dma_buf = lpfc_bg_scsi_prep_dma_buf_s4;
phba->lpfc_release_scsi_buf = lpfc_release_scsi_buf_s4;
phba->lpfc_get_scsi_buf = lpfc_get_scsi_buf_s4;
break;
@@ -3250,8 +4073,7 @@ lpfc_queuecommand_lck(struct scsi_cmnd *cmnd, void (*done) (struct scsi_cmnd *))
ndlp = rdata->pnode;
if ((scsi_get_prot_op(cmnd) != SCSI_PROT_NORMAL) &&
- (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED) ||
- (phba->sli_rev == LPFC_SLI_REV4))) {
+ (!(phba->sli3_options & LPFC_SLI3_BG_ENABLED))) {
lpfc_printf_log(phba, KERN_ERR, LOG_BG,
"9058 BLKGRD: ERROR: rcvd protected cmd:%02x"
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 23a27592388c..e0e4d8d18244 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -293,7 +293,9 @@ lpfc_sli4_eq_release(struct lpfc_queue *q, bool arm)
}
bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
- bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
+ bf_set(lpfc_eqcq_doorbell_eqid_hi, &doorbell,
+ (q->queue_id >> LPFC_EQID_HI_FIELD_SHIFT));
+ bf_set(lpfc_eqcq_doorbell_eqid_lo, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
/* PCI read to flush PCI pipeline on re-arming for INTx mode */
if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
@@ -372,7 +374,9 @@ lpfc_sli4_cq_release(struct lpfc_queue *q, bool arm)
bf_set(lpfc_eqcq_doorbell_arm, &doorbell, 1);
bf_set(lpfc_eqcq_doorbell_num_released, &doorbell, released);
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_COMPLETION);
- bf_set(lpfc_eqcq_doorbell_cqid, &doorbell, q->queue_id);
+ bf_set(lpfc_eqcq_doorbell_cqid_hi, &doorbell,
+ (q->queue_id >> LPFC_CQID_HI_FIELD_SHIFT));
+ bf_set(lpfc_eqcq_doorbell_cqid_lo, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
return released;
}
@@ -554,81 +558,6 @@ __lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
}
/**
- * __lpfc_set_rrq_active - set RRQ active bit in the ndlp's xri_bitmap.
- * @phba: Pointer to HBA context object.
- * @ndlp: nodelist pointer for this target.
- * @xritag: xri used in this exchange.
- * @rxid: Remote Exchange ID.
- * @send_rrq: Flag used to determine if we should send rrq els cmd.
- *
- * This function is called with hbalock held.
- * The active bit is set in the ndlp's active rrq xri_bitmap. Allocates an
- * rrq struct and adds it to the active_rrq_list.
- *
- * returns 0 for rrq slot for this xri
- * < 0 Were not able to get rrq mem or invalid parameter.
- **/
-static int
-__lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
- uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
-{
- struct lpfc_node_rrq *rrq;
- int empty;
- uint32_t did = 0;
-
-
- if (!ndlp)
- return -EINVAL;
-
- if (!phba->cfg_enable_rrq)
- return -EINVAL;
-
- if (phba->pport->load_flag & FC_UNLOADING) {
- phba->hba_flag &= ~HBA_RRQ_ACTIVE;
- goto out;
- }
- did = ndlp->nlp_DID;
-
- /*
- * set the active bit even if there is no mem available.
- */
- if (NLP_CHK_FREE_REQ(ndlp))
- goto out;
-
- if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING))
- goto out;
-
- if (test_and_set_bit(xritag, ndlp->active_rrqs.xri_bitmap))
- goto out;
-
- rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL);
- if (rrq) {
- rrq->send_rrq = send_rrq;
- rrq->xritag = xritag;
- rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1);
- rrq->ndlp = ndlp;
- rrq->nlp_DID = ndlp->nlp_DID;
- rrq->vport = ndlp->vport;
- rrq->rxid = rxid;
- empty = list_empty(&phba->active_rrq_list);
- rrq->send_rrq = send_rrq;
- list_add_tail(&rrq->list, &phba->active_rrq_list);
- if (!(phba->hba_flag & HBA_RRQ_ACTIVE)) {
- phba->hba_flag |= HBA_RRQ_ACTIVE;
- if (empty)
- lpfc_worker_wake_up(phba);
- }
- return 0;
- }
-out:
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "2921 Can't set rrq active xri:0x%x rxid:0x%x"
- " DID:0x%x Send:%d\n",
- xritag, rxid, did, send_rrq);
- return -EINVAL;
-}
-
-/**
* lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
* @phba: Pointer to HBA context object.
* @xritag: xri used in this exchange.
@@ -856,15 +785,68 @@ lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
**/
int
lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
- uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
+ uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
{
- int ret;
unsigned long iflags;
+ struct lpfc_node_rrq *rrq;
+ int empty;
+
+ if (!ndlp)
+ return -EINVAL;
+
+ if (!phba->cfg_enable_rrq)
+ return -EINVAL;
spin_lock_irqsave(&phba->hbalock, iflags);
- ret = __lpfc_set_rrq_active(phba, ndlp, xritag, rxid, send_rrq);
+ if (phba->pport->load_flag & FC_UNLOADING) {
+ phba->hba_flag &= ~HBA_RRQ_ACTIVE;
+ goto out;
+ }
+
+ /*
+ * set the active bit even if there is no mem available.
+ */
+ if (NLP_CHK_FREE_REQ(ndlp))
+ goto out;
+
+ if (ndlp->vport && (ndlp->vport->load_flag & FC_UNLOADING))
+ goto out;
+
+ if (test_and_set_bit(xritag, ndlp->active_rrqs.xri_bitmap))
+ goto out;
+
spin_unlock_irqrestore(&phba->hbalock, iflags);
- return ret;
+ rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL);
+ if (!rrq) {
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "3155 Unable to allocate RRQ xri:0x%x rxid:0x%x"
+ " DID:0x%x Send:%d\n",
+ xritag, rxid, ndlp->nlp_DID, send_rrq);
+ return -EINVAL;
+ }
+ rrq->send_rrq = send_rrq;
+ rrq->xritag = xritag;
+ rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1);
+ rrq->ndlp = ndlp;
+ rrq->nlp_DID = ndlp->nlp_DID;
+ rrq->vport = ndlp->vport;
+ rrq->rxid = rxid;
+ rrq->send_rrq = send_rrq;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ empty = list_empty(&phba->active_rrq_list);
+ list_add_tail(&rrq->list, &phba->active_rrq_list);
+ phba->hba_flag |= HBA_RRQ_ACTIVE;
+ if (empty)
+ lpfc_worker_wake_up(phba);
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ return 0;
+out:
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
+ "2921 Can't set rrq active xri:0x%x rxid:0x%x"
+ " DID:0x%x Send:%d\n",
+ xritag, rxid, ndlp->nlp_DID, send_rrq);
+ return -EINVAL;
}
/**
@@ -5596,6 +5578,8 @@ 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;
@@ -7555,6 +7539,8 @@ lpfc_sli4_bpl2sgl(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq,
sgl = (struct sli4_sge *)sglq->sgl;
icmd = &piocbq->iocb;
+ if (icmd->ulpCommand == CMD_XMIT_BLS_RSP64_CX)
+ return sglq->sli4_xritag;
if (icmd->un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
numBdes = icmd->un.genreq64.bdl.bdeSize /
sizeof(struct ulp_bde64);
@@ -7756,6 +7742,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
if (if_type == LPFC_SLI_INTF_IF_TYPE_2) {
if (pcmd && (*pcmd == ELS_CMD_FLOGI ||
*pcmd == ELS_CMD_SCR ||
+ *pcmd == ELS_CMD_FDISC ||
*pcmd == ELS_CMD_PLOGI)) {
bf_set(els_req64_sp, &wqe->els_req, 1);
bf_set(els_req64_sid, &wqe->els_req,
@@ -7763,7 +7750,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(wqe_ct, &wqe->els_req.wqe_com, 1);
bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
phba->vpi_ids[phba->pport->vpi]);
- } else if (iocbq->context1) {
+ } else if (pcmd && iocbq->context1) {
bf_set(wqe_ct, &wqe->els_req.wqe_com, 0);
bf_set(wqe_ctxt_tag, &wqe->els_req.wqe_com,
phba->sli4_hba.rpi_ids[ndlp->nlp_rpi]);
@@ -7830,12 +7817,16 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(wqe_lnk, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpXS);
/* Always open the exchange */
bf_set(wqe_xc, &wqe->fcp_iwrite.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
bf_set(wqe_iod, &wqe->fcp_iwrite.wqe_com, LPFC_WQE_IOD_WRITE);
bf_set(wqe_lenloc, &wqe->fcp_iwrite.wqe_com,
LPFC_WQE_LENLOC_WORD4);
bf_set(wqe_ebde_cnt, &wqe->fcp_iwrite.wqe_com, 0);
bf_set(wqe_pu, &wqe->fcp_iwrite.wqe_com, iocbq->iocb.ulpPU);
+ if (iocbq->iocb_flag & LPFC_IO_DIF) {
+ iocbq->iocb_flag &= ~LPFC_IO_DIF;
+ bf_set(wqe_dif, &wqe->generic.wqe_com, 1);
+ }
+ bf_set(wqe_dbde, &wqe->fcp_iwrite.wqe_com, 1);
break;
case CMD_FCP_IREAD64_CR:
/* word3 iocb=iotag wqe=payload_offset_len */
@@ -7849,12 +7840,16 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
bf_set(wqe_lnk, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpXS);
/* Always open the exchange */
bf_set(wqe_xc, &wqe->fcp_iread.wqe_com, 0);
- bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
bf_set(wqe_iod, &wqe->fcp_iread.wqe_com, LPFC_WQE_IOD_READ);
bf_set(wqe_lenloc, &wqe->fcp_iread.wqe_com,
LPFC_WQE_LENLOC_WORD4);
bf_set(wqe_ebde_cnt, &wqe->fcp_iread.wqe_com, 0);
bf_set(wqe_pu, &wqe->fcp_iread.wqe_com, iocbq->iocb.ulpPU);
+ if (iocbq->iocb_flag & LPFC_IO_DIF) {
+ iocbq->iocb_flag &= ~LPFC_IO_DIF;
+ bf_set(wqe_dif, &wqe->generic.wqe_com, 1);
+ }
+ bf_set(wqe_dbde, &wqe->fcp_iread.wqe_com, 1);
break;
case CMD_FCP_ICMND64_CR:
/* word3 iocb=IO_TAG wqe=reserved */
@@ -7982,6 +7977,7 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
xritag = 0;
break;
case CMD_XMIT_BLS_RSP64_CX:
+ ndlp = (struct lpfc_nodelist *)iocbq->context1;
/* As BLS ABTS RSP WQE is very different from other WQEs,
* we re-construct this WQE here based on information in
* iocbq from scratch.
@@ -8008,8 +8004,15 @@ lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
}
bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
+
+ /* Use CT=VPI */
+ bf_set(wqe_els_did, &wqe->xmit_bls_rsp.wqe_dest,
+ ndlp->nlp_DID);
+ bf_set(xmit_bls_rsp64_temprpi, &wqe->xmit_bls_rsp,
+ iocbq->iocb.ulpContext);
+ bf_set(wqe_ct, &wqe->xmit_bls_rsp.wqe_com, 1);
bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
- iocbq->iocb.ulpContext);
+ phba->vpi_ids[phba->pport->vpi]);
bf_set(wqe_qosd, &wqe->xmit_bls_rsp.wqe_com, 1);
bf_set(wqe_lenloc, &wqe->xmit_bls_rsp.wqe_com,
LPFC_WQE_LENLOC_NONE);
@@ -8073,8 +8076,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
if (piocb->sli4_xritag == NO_XRI) {
if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN ||
- piocb->iocb.ulpCommand == CMD_XMIT_BLS_RSP64_CX)
+ piocb->iocb.ulpCommand == CMD_CLOSE_XRI_CN)
sglq = NULL;
else {
if (pring->txq_cnt) {
@@ -8384,10 +8386,13 @@ lpfc_sli4_abts_err_handler(struct lpfc_hba *phba,
{
struct lpfc_vport *vport;
- if (!ndlp || !NLP_CHK_NODE_ACT(ndlp))
+ if (!ndlp || !NLP_CHK_NODE_ACT(ndlp)) {
lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
"3115 Node Context not found, driver "
"ignoring abts err event\n");
+ return;
+ }
+
vport = ndlp->vport;
lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
"3116 Port generated FCP XRI ABORT event on "
@@ -10653,12 +10658,14 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
struct lpfc_wcqe_complete *wcqe)
{
unsigned long iflags;
+ uint32_t status;
size_t offset = offsetof(struct lpfc_iocbq, iocb);
memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
sizeof(struct lpfc_iocbq) - offset);
/* Map WCQE parameters into irspiocb parameters */
- pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
+ status = bf_get(lpfc_wcqe_c_status, wcqe);
+ pIocbIn->iocb.ulpStatus = (status & LPFC_IOCB_STATUS_MASK);
if (pIocbOut->iocb_flag & LPFC_IO_FCP)
if (pIocbIn->iocb.ulpStatus == IOSTAT_FCP_RSP_ERROR)
pIocbIn->iocb.un.fcpi.fcpi_parm =
@@ -10671,6 +10678,44 @@ lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed;
}
+ /* Convert BG errors for completion status */
+ if (status == CQE_STATUS_DI_ERROR) {
+ pIocbIn->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
+
+ if (bf_get(lpfc_wcqe_c_bg_edir, wcqe))
+ pIocbIn->iocb.un.ulpWord[4] = IOERR_RX_DMA_FAILED;
+ else
+ pIocbIn->iocb.un.ulpWord[4] = IOERR_TX_DMA_FAILED;
+
+ pIocbIn->iocb.unsli3.sli3_bg.bgstat = 0;
+ if (bf_get(lpfc_wcqe_c_bg_ge, wcqe)) /* Guard Check failed */
+ pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+ BGS_GUARD_ERR_MASK;
+ if (bf_get(lpfc_wcqe_c_bg_ae, wcqe)) /* App Tag Check failed */
+ pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+ BGS_APPTAG_ERR_MASK;
+ if (bf_get(lpfc_wcqe_c_bg_re, wcqe)) /* Ref Tag Check failed */
+ pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+ BGS_REFTAG_ERR_MASK;
+
+ /* Check to see if there was any good data before the error */
+ if (bf_get(lpfc_wcqe_c_bg_tdpv, wcqe)) {
+ pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+ BGS_HI_WATER_MARK_PRESENT_MASK;
+ pIocbIn->iocb.unsli3.sli3_bg.bghm =
+ wcqe->total_data_placed;
+ }
+
+ /*
+ * Set ALL the error bits to indicate we don't know what
+ * type of error it is.
+ */
+ if (!pIocbIn->iocb.unsli3.sli3_bg.bgstat)
+ pIocbIn->iocb.unsli3.sli3_bg.bgstat |=
+ (BGS_REFTAG_ERR_MASK | BGS_APPTAG_ERR_MASK |
+ BGS_GUARD_ERR_MASK);
+ }
+
/* Pick up HBA exchange busy condition */
if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
spin_lock_irqsave(&phba->hbalock, iflags);
@@ -14042,6 +14087,13 @@ lpfc_sli4_seq_abort_rsp_cmpl(struct lpfc_hba *phba,
{
if (cmd_iocbq)
lpfc_sli_release_iocbq(phba, cmd_iocbq);
+
+ /* Failure means BLS ABORT RSP did not get delivered to remote node*/
+ if (rsp_iocbq && rsp_iocbq->iocb.ulpStatus)
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "3154 BLS ABORT RSP failed, data: x%x/x%x\n",
+ rsp_iocbq->iocb.ulpStatus,
+ rsp_iocbq->iocb.un.ulpWord[4]);
}
/**
@@ -14748,7 +14800,8 @@ lpfc_sli4_remove_rpis(struct lpfc_hba *phba)
* provided rpi via a bitmask.
**/
int
-lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp)
+lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp,
+ void (*cmpl)(struct lpfc_hba *, LPFC_MBOXQ_t *), void *arg)
{
LPFC_MBOXQ_t *mboxq;
struct lpfc_hba *phba = ndlp->phba;
@@ -14761,6 +14814,13 @@ lpfc_sli4_resume_rpi(struct lpfc_nodelist *ndlp)
/* Post all rpi memory regions to the port. */
lpfc_resume_rpi(mboxq, ndlp);
+ if (cmpl) {
+ mboxq->mbox_cmpl = cmpl;
+ mboxq->context1 = arg;
+ mboxq->context2 = ndlp;
+ } else
+ mboxq->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ mboxq->vport = ndlp->vport;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
if (rc == MBX_NOT_FINISHED) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h
index 29c13b63e323..3290b8e7ab65 100644
--- a/drivers/scsi/lpfc/lpfc_sli.h
+++ b/drivers/scsi/lpfc/lpfc_sli.h
@@ -69,6 +69,7 @@ struct lpfc_iocbq {
#define LPFC_USE_FCPWQIDX 0x80 /* Submit to specified FCPWQ index */
#define DSS_SECURITY_OP 0x100 /* security IO */
#define LPFC_IO_ON_Q 0x200 /* The IO is still on the TXCMPLQ */
+#define LPFC_IO_DIF 0x400 /* T10 DIF IO */
#define LPFC_FIP_ELS_ID_MASK 0xc000 /* ELS_ID range 0-3, non-shifted mask */
#define LPFC_FIP_ELS_ID_SHIFT 14
diff --git a/drivers/scsi/lpfc/lpfc_sli4.h b/drivers/scsi/lpfc/lpfc_sli4.h
index 3f266e2c54e0..c19d139618b7 100644
--- a/drivers/scsi/lpfc/lpfc_sli4.h
+++ b/drivers/scsi/lpfc/lpfc_sli4.h
@@ -633,7 +633,8 @@ void lpfc_sli4_free_rpi(struct lpfc_hba *, int);
void lpfc_sli4_remove_rpis(struct lpfc_hba *);
void lpfc_sli4_async_event_proc(struct lpfc_hba *);
void lpfc_sli4_fcf_redisc_event_proc(struct lpfc_hba *);
-int lpfc_sli4_resume_rpi(struct lpfc_nodelist *);
+int lpfc_sli4_resume_rpi(struct lpfc_nodelist *,
+ void (*)(struct lpfc_hba *, LPFC_MBOXQ_t *), void *);
void lpfc_sli4_fcp_xri_abort_event_proc(struct lpfc_hba *);
void lpfc_sli4_els_xri_abort_event_proc(struct lpfc_hba *);
void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *,
diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h
index dd044d01a07f..f2a2602e5c35 100644
--- a/drivers/scsi/lpfc/lpfc_version.h
+++ b/drivers/scsi/lpfc/lpfc_version.h
@@ -18,7 +18,7 @@
* included with this package. *
*******************************************************************/
-#define LPFC_DRIVER_VERSION "8.3.28"
+#define LPFC_DRIVER_VERSION "8.3.29"
#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 e6173376605d..e5cd8d8d4ce7 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 2bccfbe5661e..24828b54773a 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 494474779532..e8a04ae3276a 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 a78036f5e1a6..5e69f468535f 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -132,7 +132,7 @@ static int mpt2sas_remove_dead_ioc_func(void *arg)
pdev = ioc->pdev;
if ((pdev == NULL))
return -1;
- pci_remove_bus_device(pdev);
+ pci_stop_and_remove_bus_device(pdev);
return 0;
}
@@ -2575,6 +2575,11 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
GFP_KERNEL, ioc->chain_pages);
+ if (!ioc->chain_lookup) {
+ printk(MPT2SAS_ERR_FMT "chain_lookup: get_free_pages failed, "
+ "sz(%d)\n", ioc->name, (int)sz);
+ goto out;
+ }
ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev,
ioc->request_sz, 16, 0);
if (!ioc->chain_dma_pool) {
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 193e33e28e49..d953a57e779d 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -5744,7 +5744,7 @@ _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc,
}
/**
- * _scsih_sas_broadcast_primative_event - handle broadcast events
+ * _scsih_sas_broadcast_primitive_event - handle broadcast events
* @ioc: per adapter object
* @fw_event: The fw_event_work object
* Context: user.
@@ -5752,7 +5752,7 @@ _scsih_sas_enclosure_dev_status_change_event(struct MPT2SAS_ADAPTER *ioc,
* Return nothing.
*/
static void
-_scsih_sas_broadcast_primative_event(struct MPT2SAS_ADAPTER *ioc,
+_scsih_sas_broadcast_primitive_event(struct MPT2SAS_ADAPTER *ioc,
struct fw_event_work *fw_event)
{
struct scsi_cmnd *scmd;
@@ -7263,7 +7263,7 @@ _firmware_event_work(struct work_struct *work)
fw_event);
break;
case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE:
- _scsih_sas_broadcast_primative_event(ioc,
+ _scsih_sas_broadcast_primitive_event(ioc,
fw_event);
break;
case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE:
diff --git a/drivers/scsi/mvsas/mv_init.c b/drivers/scsi/mvsas/mv_init.c
index 6f589195746c..cc59dff3810b 100644
--- a/drivers/scsi/mvsas/mv_init.c
+++ b/drivers/scsi/mvsas/mv_init.c
@@ -60,7 +60,6 @@ static struct scsi_host_template mvs_sht = {
.queuecommand = sas_queuecommand,
.target_alloc = sas_target_alloc,
.slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
.scan_finished = mvs_scan_finished,
.scan_start = mvs_scan_start,
.change_queue_depth = sas_change_queue_depth,
@@ -74,7 +73,6 @@ static struct scsi_host_template mvs_sht = {
.use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = sas_eh_device_reset_handler,
.eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
.shost_attrs = mvst_host_attrs,
diff --git a/drivers/scsi/mvsas/mv_sas.c b/drivers/scsi/mvsas/mv_sas.c
index 01ab9c4d3464..fd3b2839843b 100644
--- a/drivers/scsi/mvsas/mv_sas.c
+++ b/drivers/scsi/mvsas/mv_sas.c
@@ -308,7 +308,7 @@ int mvs_scan_finished(struct Scsi_Host *shost, unsigned long time)
if (mvs_prv->scan_finished == 0)
return 0;
- scsi_flush_work(shost);
+ sas_drain_work(sha);
return 1;
}
@@ -893,9 +893,6 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
mvi = ((struct mvs_device *)task->dev->lldd_dev)->mvi_info;
- if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
- spin_unlock_irq(dev->sata_dev.ap->lock);
-
spin_lock_irqsave(&mvi->lock, flags);
rc = mvs_task_prep(task, mvi, is_tmf, tmf, &pass);
if (rc)
@@ -906,9 +903,6 @@ static int mvs_task_exec(struct sas_task *task, const int num, gfp_t gfp_flags,
(MVS_CHIP_SLOT_SZ - 1));
spin_unlock_irqrestore(&mvi->lock, flags);
- if ((dev->dev_type == SATA_DEV) && (dev->sata_dev.ap != NULL))
- spin_lock_irq(dev->sata_dev.ap->lock);
-
return rc;
}
@@ -1480,10 +1474,11 @@ static int mvs_debug_issue_ssp_tmf(struct domain_device *dev,
static int mvs_debug_I_T_nexus_reset(struct domain_device *dev)
{
int rc;
- struct sas_phy *phy = sas_find_local_phy(dev);
+ struct sas_phy *phy = sas_get_local_phy(dev);
int reset_type = (dev->dev_type == SATA_DEV ||
(dev->tproto & SAS_PROTOCOL_STP)) ? 0 : 1;
rc = sas_phy_reset(phy, reset_type);
+ sas_put_local_phy(phy);
msleep(2000);
return rc;
}
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 4b3b4755945c..5982a587babc 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 002924963cd8..62b616891a33 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 de0b1a704fb5..21883a2d6324 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 f2018b46f494..2f72c9807b12 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_chips.h b/drivers/scsi/pm8001/pm8001_chips.h
index 4efa4d0950e5..9241c7826034 100644
--- a/drivers/scsi/pm8001/pm8001_chips.h
+++ b/drivers/scsi/pm8001/pm8001_chips.h
@@ -46,9 +46,9 @@ static inline u32 pm8001_read_32(void *virt_addr)
return *((u32 *)virt_addr);
}
-static inline void pm8001_write_32(void *addr, u32 offset, u32 val)
+static inline void pm8001_write_32(void *addr, u32 offset, __le32 val)
{
- *((u32 *)(addr + offset)) = val;
+ *((__le32 *)(addr + offset)) = val;
}
static inline u32 pm8001_cr32(struct pm8001_hba_info *pm8001_ha, u32 bar,
diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c
index e12c4f632a63..3619f6eeeeda 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.c
+++ b/drivers/scsi/pm8001/pm8001_hwi.c
@@ -338,26 +338,25 @@ update_outbnd_queue_table(struct pm8001_hba_info *pm8001_ha, int number)
}
/**
- * bar4_shift - function is called to shift BAR base address
- * @pm8001_ha : our hba card information
+ * pm8001_bar4_shift - function is called to shift BAR base address
+ * @pm8001_ha : our hba card infomation
* @shiftValue : shifting value in memory bar.
*/
-static int bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
+int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue)
{
u32 regVal;
- u32 max_wait_count;
+ unsigned long start;
/* program the inbound AXI translation Lower Address */
pm8001_cw32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW, shiftValue);
/* confirm the setting is written */
- max_wait_count = 1 * 1000 * 1000; /* 1 sec */
+ start = jiffies + HZ; /* 1 sec */
do {
- udelay(1);
regVal = pm8001_cr32(pm8001_ha, 1, SPC_IBW_AXI_TRANSLATION_LOW);
- } while ((regVal != shiftValue) && (--max_wait_count));
+ } while ((regVal != shiftValue) && time_before(jiffies, start));
- if (!max_wait_count) {
+ if (regVal != shiftValue) {
PM8001_INIT_DBG(pm8001_ha,
pm8001_printk("TIMEOUT:SPC_IBW_AXI_TRANSLATION_LOW"
" = 0x%x\n", regVal));
@@ -375,6 +374,7 @@ static void __devinit
mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
{
u32 value, offset, i;
+ unsigned long flags;
#define SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR 0x00030000
#define SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR 0x00040000
@@ -388,16 +388,23 @@ mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
* Using shifted destination address 0x3_0000:0x1074 + 0x4000*N (N=0:3)
* Using shifted destination address 0x4_0000:0x1074 + 0x4000*(N-4) (N=4:7)
*/
- if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR))
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ SAS2_SETTINGS_LOCAL_PHY_0_3_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 0; i < 4; i++) {
offset = SAS2_SETTINGS_LOCAL_PHY_0_3_OFFSET + 0x4000 * i;
pm8001_cw32(pm8001_ha, 2, offset, 0x80001501);
}
/* shift membase 3 for SAS2_SETTINGS_LOCAL_PHY 4 - 7 */
- if (-1 == bar4_shift(pm8001_ha, SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR))
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ SAS2_SETTINGS_LOCAL_PHY_4_7_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 4; i < 8; i++) {
offset = SAS2_SETTINGS_LOCAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
pm8001_cw32(pm8001_ha, 2, offset, 0x80001501);
@@ -421,7 +428,8 @@ mpi_set_phys_g3_with_ssc(struct pm8001_hba_info *pm8001_ha, u32 SSCbit)
pm8001_cw32(pm8001_ha, 2, 0xd8, 0x8000C016);
/*set the shifted destination address to 0x0 to avoid error operation */
- bar4_shift(pm8001_ha, 0x0);
+ pm8001_bar4_shift(pm8001_ha, 0x0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
}
@@ -437,6 +445,7 @@ mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
u32 offset;
u32 value;
u32 i;
+ unsigned long flags;
#define OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR 0x00030000
#define OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR 0x00040000
@@ -445,24 +454,30 @@ mpi_set_open_retry_interval_reg(struct pm8001_hba_info *pm8001_ha,
#define OPEN_RETRY_INTERVAL_REG_MASK 0x0000FFFF
value = interval & OPEN_RETRY_INTERVAL_REG_MASK;
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
/* shift bar and set the OPEN_REJECT(RETRY) interval time of PHY 0 -3.*/
- if (-1 == bar4_shift(pm8001_ha,
- OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR))
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_0_3_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 0; i < 4; i++) {
offset = OPEN_RETRY_INTERVAL_PHY_0_3_OFFSET + 0x4000 * i;
pm8001_cw32(pm8001_ha, 2, offset, value);
}
- if (-1 == bar4_shift(pm8001_ha,
- OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR))
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ OPEN_RETRY_INTERVAL_PHY_4_7_SHIFT_ADDR)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
+ }
for (i = 4; i < 8; i++) {
offset = OPEN_RETRY_INTERVAL_PHY_4_7_OFFSET + 0x4000 * (i-4);
pm8001_cw32(pm8001_ha, 2, offset, value);
}
/*set the shifted destination address to 0x0 to avoid error operation */
- bar4_shift(pm8001_ha, 0x0);
+ pm8001_bar4_shift(pm8001_ha, 0x0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return;
}
@@ -607,7 +622,8 @@ static int __devinit pm8001_chip_init(struct pm8001_hba_info *pm8001_ha)
update_inbnd_queue_table(pm8001_ha, 0);
update_outbnd_queue_table(pm8001_ha, 0);
mpi_set_phys_g3_with_ssc(pm8001_ha, 0);
- mpi_set_open_retry_interval_reg(pm8001_ha, 7);
+ /* 7->130ms, 34->500ms, 119->1.5s */
+ mpi_set_open_retry_interval_reg(pm8001_ha, 119);
/* notify firmware update finished and check initialization status */
if (0 == mpi_init_check(pm8001_ha)) {
PM8001_INIT_DBG(pm8001_ha,
@@ -688,8 +704,11 @@ static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
PM8001_INIT_DBG(pm8001_ha,
pm8001_printk("Firmware is ready for reset .\n"));
} else {
- /* Trigger NMI twice via RB6 */
- if (-1 == bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
+ unsigned long flags;
+ /* Trigger NMI twice via RB6 */
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha, RB6_ACCESS_REG)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Shift Bar4 to 0x%x failed\n",
RB6_ACCESS_REG));
@@ -715,8 +734,10 @@ static u32 soft_reset_ready_check(struct pm8001_hba_info *pm8001_ha)
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
pm8001_cr32(pm8001_ha, 0, MSGU_SCRATCH_PAD_3)));
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return -1;
}
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
}
return 0;
}
@@ -733,6 +754,7 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
u32 regVal, toggleVal;
u32 max_wait_count;
u32 regVal1, regVal2, regVal3;
+ unsigned long flags;
/* step1: Check FW is ready for soft reset */
if (soft_reset_ready_check(pm8001_ha) != 0) {
@@ -743,7 +765,9 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* step 2: clear NMI status register on AAP1 and IOP, write the same
value to clear */
/* map 0x60000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha, MBIC_AAP1_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Shift Bar4 to 0x%x failed\n",
MBIC_AAP1_ADDR_BASE));
@@ -754,7 +778,8 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
pm8001_printk("MBIC - NMI Enable VPE0 (IOP)= 0x%x\n", regVal));
pm8001_cw32(pm8001_ha, 2, MBIC_NMI_ENABLE_VPE0_IOP, 0x0);
/* map 0x70000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, MBIC_IOP_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Shift Bar4 to 0x%x failed\n",
MBIC_IOP_ADDR_BASE));
@@ -796,7 +821,8 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* read required registers for confirmming */
/* map 0x0700000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Shift Bar4 to 0x%x failed\n",
GSM_ADDR_BASE));
@@ -862,7 +888,8 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* step 5: delay 10 usec */
udelay(10);
/* step 5-b: set GPIO-0 output control to tristate anyway */
- if (-1 == bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GPIO_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_INIT_DBG(pm8001_ha,
pm8001_printk("Shift Bar4 to 0x%x failed\n",
GPIO_ADDR_BASE));
@@ -878,7 +905,8 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* Step 6: Reset the IOP and AAP1 */
/* map 0x00000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
SPC_TOP_LEVEL_ADDR_BASE));
@@ -915,7 +943,8 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* step 11: reads and sets the GSM Configuration and Reset Register */
/* map 0x0700000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, GSM_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("SPC Shift Bar4 to 0x%x failed\n",
GSM_ADDR_BASE));
@@ -968,7 +997,8 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
/* step 13: bring the IOP and AAP1 out of reset */
/* map 0x00000 to BAR4(0x20), BAR2(win) */
- if (-1 == bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ if (-1 == pm8001_bar4_shift(pm8001_ha, SPC_TOP_LEVEL_ADDR_BASE)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_FAIL_DBG(pm8001_ha,
pm8001_printk("Shift Bar4 to 0x%x failed\n",
SPC_TOP_LEVEL_ADDR_BASE));
@@ -1010,6 +1040,7 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
pm8001_cr32(pm8001_ha, 0,
MSGU_SCRATCH_PAD_3)));
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return -1;
}
@@ -1039,9 +1070,12 @@ pm8001_chip_soft_rst(struct pm8001_hba_info *pm8001_ha, u32 signature)
pm8001_printk("SCRATCH_PAD3 value = 0x%x\n",
pm8001_cr32(pm8001_ha, 0,
MSGU_SCRATCH_PAD_3)));
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return -1;
}
}
+ pm8001_bar4_shift(pm8001_ha, 0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
PM8001_INIT_DBG(pm8001_ha,
pm8001_printk("SPC soft reset Complete\n"));
@@ -1157,8 +1191,8 @@ pm8001_chip_msix_interrupt_disable(struct pm8001_hba_info *pm8001_ha,
msi_index = int_vec_idx * MSIX_TABLE_ELEMENT_SIZE;
msi_index += MSIX_TABLE_BASE;
pm8001_cw32(pm8001_ha, 0, msi_index, MSIX_INTERRUPT_DISABLE);
-
}
+
/**
* pm8001_chip_interrupt_enable - enable PM8001 chip interrupt
* @pm8001_ha: our hba card information
@@ -1212,7 +1246,7 @@ static int mpi_msg_free_get(struct inbound_queue_table *circularQ,
consumer_index = pm8001_read_32(circularQ->ci_virt);
circularQ->consumer_index = cpu_to_le32(consumer_index);
if (((circularQ->producer_idx + bcCount) % 256) ==
- circularQ->consumer_index) {
+ le32_to_cpu(circularQ->consumer_index)) {
*messagePtr = NULL;
return -1;
}
@@ -1321,7 +1355,8 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
u32 header_tmp;
do {
/* If there are not-yet-delivered messages ... */
- if (circularQ->producer_index != circularQ->consumer_idx) {
+ if (le32_to_cpu(circularQ->producer_index)
+ != circularQ->consumer_idx) {
/*Get the pointer to the circular queue buffer element*/
msgHeader = (struct mpi_msg_hdr *)
(circularQ->base_virt +
@@ -1329,14 +1364,14 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
/* read header */
header_tmp = pm8001_read_32(msgHeader);
msgHeader_tmp = cpu_to_le32(header_tmp);
- if (0 != (msgHeader_tmp & 0x80000000)) {
+ if (0 != (le32_to_cpu(msgHeader_tmp) & 0x80000000)) {
if (OPC_OUB_SKIP_ENTRY !=
- (msgHeader_tmp & 0xfff)) {
+ (le32_to_cpu(msgHeader_tmp) & 0xfff)) {
*messagePtr1 =
((u8 *)msgHeader) +
sizeof(struct mpi_msg_hdr);
- *pBC = (u8)((msgHeader_tmp >> 24) &
- 0x1f);
+ *pBC = (u8)((le32_to_cpu(msgHeader_tmp)
+ >> 24) & 0x1f);
PM8001_IO_DBG(pm8001_ha,
pm8001_printk(": CI=%d PI=%d "
"msgHeader=%x\n",
@@ -1347,8 +1382,8 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
} else {
circularQ->consumer_idx =
(circularQ->consumer_idx +
- ((msgHeader_tmp >> 24) & 0x1f))
- % 256;
+ ((le32_to_cpu(msgHeader_tmp)
+ >> 24) & 0x1f)) % 256;
msgHeader_tmp = 0;
pm8001_write_32(msgHeader, 0, 0);
/* update the CI of outbound queue */
@@ -1360,7 +1395,8 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
} else {
circularQ->consumer_idx =
(circularQ->consumer_idx +
- ((msgHeader_tmp >> 24) & 0x1f)) % 256;
+ ((le32_to_cpu(msgHeader_tmp) >> 24) &
+ 0x1f)) % 256;
msgHeader_tmp = 0;
pm8001_write_32(msgHeader, 0, 0);
/* update the CI of outbound queue */
@@ -1376,7 +1412,8 @@ static u32 mpi_msg_consume(struct pm8001_hba_info *pm8001_ha,
producer_index = pm8001_read_32(pi_virt);
circularQ->producer_index = cpu_to_le32(producer_index);
}
- } while (circularQ->producer_index != circularQ->consumer_idx);
+ } while (le32_to_cpu(circularQ->producer_index) !=
+ circularQ->consumer_idx);
/* while we don't have any more not-yet-delivered message */
/* report empty */
return MPI_IO_STATUS_BUSY;
@@ -1388,24 +1425,191 @@ static void pm8001_work_fn(struct work_struct *work)
struct pm8001_device *pm8001_dev;
struct domain_device *dev;
+ /*
+ * So far, all users of this stash an associated structure here.
+ * If we get here, and this pointer is null, then the action
+ * was cancelled. This nullification happens when the device
+ * goes away.
+ */
+ pm8001_dev = pw->data; /* Most stash device structure */
+ if ((pm8001_dev == NULL)
+ || ((pw->handler != IO_XFER_ERROR_BREAK)
+ && (pm8001_dev->dev_type == NO_DEVICE))) {
+ kfree(pw);
+ return;
+ }
+
switch (pw->handler) {
+ case IO_XFER_ERROR_BREAK:
+ { /* This one stashes the sas_task instead */
+ struct sas_task *t = (struct sas_task *)pm8001_dev;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+ unsigned long flags, flags1;
+ struct task_status_struct *ts;
+ int i;
+
+ if (pm8001_query_task(t) == TMF_RESP_FUNC_SUCC)
+ break; /* Task still on lu */
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ spin_lock_irqsave(&t->task_state_lock, flags1);
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ break; /* Task got completed by another */
+ }
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+
+ /* Search for a possible ccb that matches the task */
+ for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+ ccb = &pm8001_ha->ccb_info[i];
+ tag = ccb->ccb_tag;
+ if ((tag != 0xFFFFFFFF) && (ccb->task == t))
+ break;
+ }
+ if (!ccb) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ break; /* Task got freed by another */
+ }
+ ts = &t->task_status;
+ ts->resp = SAS_TASK_COMPLETE;
+ /* Force the midlayer to retry */
+ ts->stat = SAS_QUEUE_FULL;
+ pm8001_dev = ccb->device;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ spin_lock_irqsave(&t->task_state_lock, flags1);
+ t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ t->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ PM8001_FAIL_DBG(pm8001_ha, pm8001_printk("task 0x%p"
+ " done with event 0x%x resp 0x%x stat 0x%x but"
+ " aborted by upper layer!\n",
+ t, pw->handler, ts->resp, ts->stat));
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ t->task_done(t);
+ }
+ } break;
+ case IO_XFER_OPEN_RETRY_TIMEOUT:
+ { /* This one stashes the sas_task instead */
+ struct sas_task *t = (struct sas_task *)pm8001_dev;
+ u32 tag;
+ struct pm8001_ccb_info *ccb;
+ struct pm8001_hba_info *pm8001_ha = pw->pm8001_ha;
+ unsigned long flags, flags1;
+ int i, ret = 0;
+
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
+
+ ret = pm8001_query_task(t);
+
+ PM8001_IO_DBG(pm8001_ha,
+ switch (ret) {
+ case TMF_RESP_FUNC_SUCC:
+ pm8001_printk("...Task on lu\n");
+ break;
+
+ case TMF_RESP_FUNC_COMPLETE:
+ pm8001_printk("...Task NOT on lu\n");
+ break;
+
+ default:
+ pm8001_printk("...query task failed!!!\n");
+ break;
+ });
+
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ spin_lock_irqsave(&t->task_state_lock, flags1);
+
+ if (unlikely((t->task_state_flags & SAS_TASK_STATE_DONE))) {
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
+ (void)pm8001_abort_task(t);
+ break; /* Task got completed by another */
+ }
+
+ spin_unlock_irqrestore(&t->task_state_lock, flags1);
+
+ /* Search for a possible ccb that matches the task */
+ for (i = 0; ccb = NULL, i < PM8001_MAX_CCB; i++) {
+ ccb = &pm8001_ha->ccb_info[i];
+ tag = ccb->ccb_tag;
+ if ((tag != 0xFFFFFFFF) && (ccb->task == t))
+ break;
+ }
+ if (!ccb) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ if (ret == TMF_RESP_FUNC_SUCC) /* task on lu */
+ (void)pm8001_abort_task(t);
+ break; /* Task got freed by another */
+ }
+
+ pm8001_dev = ccb->device;
+ dev = pm8001_dev->sas_device;
+
+ switch (ret) {
+ case TMF_RESP_FUNC_SUCC: /* task on lu */
+ ccb->open_retry = 1; /* Snub completion */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ ret = pm8001_abort_task(t);
+ ccb->open_retry = 0;
+ switch (ret) {
+ case TMF_RESP_FUNC_SUCC:
+ case TMF_RESP_FUNC_COMPLETE:
+ break;
+ default: /* device misbehavior */
+ ret = TMF_RESP_FUNC_FAILED;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("...Reset phy\n"));
+ pm8001_I_T_nexus_reset(dev);
+ break;
+ }
+ break;
+
+ case TMF_RESP_FUNC_COMPLETE: /* task not on lu */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ /* Do we need to abort the task locally? */
+ break;
+
+ default: /* device misbehavior */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ ret = TMF_RESP_FUNC_FAILED;
+ PM8001_IO_DBG(pm8001_ha,
+ pm8001_printk("...Reset phy\n"));
+ pm8001_I_T_nexus_reset(dev);
+ }
+
+ if (ret == TMF_RESP_FUNC_FAILED)
+ t = NULL;
+ pm8001_open_reject_retry(pm8001_ha, t, pm8001_dev);
+ PM8001_IO_DBG(pm8001_ha, pm8001_printk("...Complete\n"));
+ } break;
case IO_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
case IO_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
case IO_DS_IN_ERROR:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
case IO_DS_NON_OPERATIONAL:
- pm8001_dev = pw->data;
dev = pm8001_dev->sas_device;
pm8001_I_T_nexus_reset(dev);
break;
@@ -1460,6 +1664,11 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
status = le32_to_cpu(psspPayload->status);
tag = le32_to_cpu(psspPayload->tag);
ccb = &pm8001_ha->ccb_info[tag];
+ if ((status == IO_ABORTED) && ccb->open_retry) {
+ /* Being completed by another */
+ ccb->open_retry = 0;
+ return;
+ }
pm8001_dev = ccb->device;
param = le32_to_cpu(psspPayload->param);
@@ -1515,6 +1724,8 @@ mpi_ssp_completion(struct pm8001_hba_info *pm8001_ha , void *piomb)
pm8001_printk("IO_XFER_ERROR_BREAK\n"));
ts->resp = SAS_TASK_COMPLETE;
ts->stat = SAS_OPEN_REJECT;
+ /* Force the midlayer to retry */
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
break;
case IO_XFER_ERROR_PHY_NOT_READY:
PM8001_IO_DBG(pm8001_ha,
@@ -1719,9 +1930,8 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
case IO_XFER_ERROR_BREAK:
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("IO_XFER_ERROR_BREAK\n"));
- ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAS_INTERRUPTED;
- break;
+ pm8001_handle_event(pm8001_ha, t, IO_XFER_ERROR_BREAK);
+ return;
case IO_XFER_ERROR_PHY_NOT_READY:
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("IO_XFER_ERROR_PHY_NOT_READY\n"));
@@ -1800,10 +2010,8 @@ static void mpi_ssp_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
case IO_XFER_OPEN_RETRY_TIMEOUT:
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("IO_XFER_OPEN_RETRY_TIMEOUT\n"));
- ts->resp = SAS_TASK_COMPLETE;
- ts->stat = SAS_OPEN_REJECT;
- ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
- break;
+ pm8001_handle_event(pm8001_ha, t, IO_XFER_OPEN_RETRY_TIMEOUT);
+ return;
case IO_XFER_ERROR_UNEXPECTED_PHASE:
PM8001_IO_DBG(pm8001_ha,
pm8001_printk("IO_XFER_ERROR_UNEXPECTED_PHASE\n"));
@@ -1877,7 +2085,6 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
struct sas_task *t;
struct pm8001_ccb_info *ccb;
- unsigned long flags = 0;
u32 param;
u32 status;
u32 tag;
@@ -2016,9 +2223,9 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_QUEUE_FULL;
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*in order to force CPU ordering*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
return;
}
break;
@@ -2036,9 +2243,9 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_QUEUE_FULL;
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
return;
}
break;
@@ -2064,9 +2271,9 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_QUEUE_FULL;
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/* ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
return;
}
break;
@@ -2131,9 +2338,9 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_QUEUE_FULL;
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
return;
}
break;
@@ -2155,9 +2362,9 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_QUEUE_FULL;
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
return;
}
break;
@@ -2175,31 +2382,31 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
ts->stat = SAS_DEV_NO_RESPONSE;
break;
}
- spin_lock_irqsave(&t->task_state_lock, flags);
+ spin_lock_irq(&t->task_state_lock);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irq(&t->task_state_lock);
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_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irq(&t->task_state_lock);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/* ditto */
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
} else if (!t->uldd_task) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irq(&t->task_state_lock);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
}
}
@@ -2207,7 +2414,6 @@ mpi_sata_completion(struct pm8001_hba_info *pm8001_ha, void *piomb)
static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
{
struct sas_task *t;
- unsigned long flags = 0;
struct task_status_struct *ts;
struct pm8001_ccb_info *ccb;
struct pm8001_device *pm8001_dev;
@@ -2287,9 +2493,9 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
ts->stat = SAS_QUEUE_FULL;
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
return;
}
break;
@@ -2387,31 +2593,31 @@ static void mpi_sata_event(struct pm8001_hba_info *pm8001_ha , void *piomb)
ts->stat = SAS_OPEN_TO;
break;
}
- spin_lock_irqsave(&t->task_state_lock, flags);
+ spin_lock_irq(&t->task_state_lock);
t->task_state_flags &= ~SAS_TASK_STATE_PENDING;
t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
if (unlikely((t->task_state_flags & SAS_TASK_STATE_ABORTED))) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irq(&t->task_state_lock);
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_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irq(&t->task_state_lock);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/* ditto */
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
} else if (!t->uldd_task) {
- spin_unlock_irqrestore(&t->task_state_lock, flags);
+ spin_unlock_irq(&t->task_state_lock);
pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();/*ditto*/
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ spin_unlock_irq(&pm8001_ha->lock);
t->task_done(t);
- spin_lock_irqsave(&pm8001_ha->lock, flags);
+ spin_lock_irq(&pm8001_ha->lock);
}
}
@@ -2857,7 +3063,7 @@ static void pm8001_hw_event_ack_req(struct pm8001_hba_info *pm8001_ha,
memset((u8 *)&payload, 0, sizeof(payload));
circularQ = &pm8001_ha->inbnd_q_tbl[Qnum];
- payload.tag = 1;
+ payload.tag = cpu_to_le32(1);
payload.sea_phyid_portid = cpu_to_le32(((SEA & 0xFFFF) << 8) |
((phyId & 0x0F) << 4) | (port_id & 0x0F));
payload.param0 = cpu_to_le32(param0);
@@ -2929,9 +3135,9 @@ hw_event_sas_phy_up(struct pm8001_hba_info *pm8001_ha, void *piomb)
phy->phy_type |= PORT_TYPE_SAS;
phy->identify.device_type = deviceType;
phy->phy_attached = 1;
- if (phy->identify.device_type == SAS_END_DEV)
+ if (phy->identify.device_type == SAS_END_DEVICE)
phy->identify.target_port_protocols = SAS_PROTOCOL_SSP;
- else if (phy->identify.device_type != NO_DEVICE)
+ else if (phy->identify.device_type != SAS_PHY_UNUSED)
phy->identify.target_port_protocols = SAS_PROTOCOL_SMP;
phy->sas_phy.oob_mode = SAS_OOB_MODE;
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
@@ -3075,7 +3281,7 @@ static int mpi_reg_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
(struct dev_reg_resp *)(piomb + 4);
htag = le32_to_cpu(registerRespPayload->tag);
- ccb = &pm8001_ha->ccb_info[registerRespPayload->tag];
+ ccb = &pm8001_ha->ccb_info[htag];
pm8001_dev = ccb->device;
status = le32_to_cpu(registerRespPayload->status);
device_id = le32_to_cpu(registerRespPayload->device_id);
@@ -3149,7 +3355,7 @@ mpi_fw_flash_update_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
struct fw_control_ex fw_control_context;
struct fw_flash_Update_resp *ppayload =
(struct fw_flash_Update_resp *)(piomb + 4);
- u32 tag = le32_to_cpu(ppayload->tag);
+ u32 tag = ppayload->tag;
struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[tag];
status = le32_to_cpu(ppayload->status);
memcpy(&fw_control_context,
@@ -3238,13 +3444,12 @@ mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
struct task_abort_resp *pPayload =
(struct task_abort_resp *)(piomb + 4);
- ccb = &pm8001_ha->ccb_info[pPayload->tag];
- t = ccb->task;
-
status = le32_to_cpu(pPayload->status);
tag = le32_to_cpu(pPayload->tag);
scp = le32_to_cpu(pPayload->scp);
+ ccb = &pm8001_ha->ccb_info[tag];
+ t = ccb->task;
PM8001_IO_DBG(pm8001_ha,
pm8001_printk(" status = 0x%x\n", status));
if (t == NULL)
@@ -3270,7 +3475,7 @@ mpi_task_abort_resp(struct pm8001_hba_info *pm8001_ha, void *piomb)
t->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
t->task_state_flags |= SAS_TASK_STATE_DONE;
spin_unlock_irqrestore(&t->task_state_lock, flags);
- pm8001_ccb_task_free(pm8001_ha, t, ccb, pPayload->tag);
+ pm8001_ccb_task_free(pm8001_ha, t, ccb, tag);
mb();
t->task_done(t);
return 0;
@@ -3497,7 +3702,7 @@ static int mpi_hw_event(struct pm8001_hba_info *pm8001_ha, void* piomb)
static void process_one_iomb(struct pm8001_hba_info *pm8001_ha, void *piomb)
{
u32 pHeader = (u32)*(u32 *)piomb;
- u8 opc = (u8)((le32_to_cpu(pHeader)) & 0xFFF);
+ u8 opc = (u8)(pHeader & 0xFFF);
PM8001_MSG_DBG(pm8001_ha, pm8001_printk("process_one_iomb:"));
@@ -3664,9 +3869,11 @@ static int process_oq(struct pm8001_hba_info *pm8001_ha)
{
struct outbound_queue_table *circularQ;
void *pMsg1 = NULL;
- u8 bc = 0;
+ u8 uninitialized_var(bc);
u32 ret = MPI_IO_STATUS_FAIL;
+ unsigned long flags;
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
circularQ = &pm8001_ha->outbnd_q_tbl[0];
do {
ret = mpi_msg_consume(pm8001_ha, circularQ, &pMsg1, &bc);
@@ -3677,16 +3884,16 @@ static int process_oq(struct pm8001_hba_info *pm8001_ha)
mpi_msg_free_set(pm8001_ha, pMsg1, circularQ, bc);
}
if (MPI_IO_STATUS_BUSY == ret) {
- u32 producer_idx;
/* Update the producer index from SPC */
- producer_idx = pm8001_read_32(circularQ->pi_virt);
- circularQ->producer_index = cpu_to_le32(producer_idx);
- if (circularQ->producer_index ==
+ circularQ->producer_index =
+ cpu_to_le32(pm8001_read_32(circularQ->pi_virt));
+ if (le32_to_cpu(circularQ->producer_index) ==
circularQ->consumer_idx)
/* OQ is empty */
break;
}
} while (1);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return ret;
}
@@ -3712,9 +3919,9 @@ pm8001_chip_make_sg(struct scatterlist *scatter, int nr, void *prd)
}
}
-static void build_smp_cmd(u32 deviceID, u32 hTag, struct smp_req *psmp_cmd)
+static void build_smp_cmd(u32 deviceID, __le32 hTag, struct smp_req *psmp_cmd)
{
- psmp_cmd->tag = cpu_to_le32(hTag);
+ psmp_cmd->tag = hTag;
psmp_cmd->device_id = cpu_to_le32(deviceID);
psmp_cmd->len_ip_ir = cpu_to_le32(1|(1 << 1));
}
@@ -3798,7 +4005,7 @@ static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
struct ssp_ini_io_start_req ssp_cmd;
u32 tag = ccb->ccb_tag;
int ret;
- __le64 phys_addr;
+ u64 phys_addr;
struct inbound_queue_table *circularQ;
u32 opc = OPC_INB_SSPINIIOSTART;
memset(&ssp_cmd, 0, sizeof(ssp_cmd));
@@ -3819,15 +4026,15 @@ static int pm8001_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha,
/* fill in PRD (scatter/gather) table, if any */
if (task->num_scatter > 1) {
pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
- phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
- offsetof(struct pm8001_ccb_info, buf_prd[0]));
- ssp_cmd.addr_low = lower_32_bits(phys_addr);
- ssp_cmd.addr_high = upper_32_bits(phys_addr);
+ phys_addr = ccb->ccb_dma_handle +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]);
+ ssp_cmd.addr_low = cpu_to_le32(lower_32_bits(phys_addr));
+ ssp_cmd.addr_high = cpu_to_le32(upper_32_bits(phys_addr));
ssp_cmd.esgl = cpu_to_le32(1<<31);
} else if (task->num_scatter == 1) {
- __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
- ssp_cmd.addr_low = lower_32_bits(dma_addr);
- ssp_cmd.addr_high = upper_32_bits(dma_addr);
+ u64 dma_addr = sg_dma_address(task->scatter);
+ ssp_cmd.addr_low = cpu_to_le32(lower_32_bits(dma_addr));
+ ssp_cmd.addr_high = cpu_to_le32(upper_32_bits(dma_addr));
ssp_cmd.len = cpu_to_le32(task->total_xfer_len);
ssp_cmd.esgl = 0;
} else if (task->num_scatter == 0) {
@@ -3850,7 +4057,7 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
int ret;
struct sata_start_req sata_cmd;
u32 hdr_tag, ncg_tag = 0;
- __le64 phys_addr;
+ u64 phys_addr;
u32 ATAP = 0x0;
u32 dir;
struct inbound_queue_table *circularQ;
@@ -3889,13 +4096,13 @@ static int pm8001_chip_sata_req(struct pm8001_hba_info *pm8001_ha,
/* fill in PRD (scatter/gather) table, if any */
if (task->num_scatter > 1) {
pm8001_chip_make_sg(task->scatter, ccb->n_elem, ccb->buf_prd);
- phys_addr = cpu_to_le64(ccb->ccb_dma_handle +
- offsetof(struct pm8001_ccb_info, buf_prd[0]));
+ phys_addr = ccb->ccb_dma_handle +
+ offsetof(struct pm8001_ccb_info, buf_prd[0]);
sata_cmd.addr_low = lower_32_bits(phys_addr);
sata_cmd.addr_high = upper_32_bits(phys_addr);
sata_cmd.esgl = cpu_to_le32(1 << 31);
} else if (task->num_scatter == 1) {
- __le64 dma_addr = cpu_to_le64(sg_dma_address(task->scatter));
+ u64 dma_addr = sg_dma_address(task->scatter);
sata_cmd.addr_low = lower_32_bits(dma_addr);
sata_cmd.addr_high = upper_32_bits(dma_addr);
sata_cmd.len = cpu_to_le32(task->total_xfer_len);
@@ -4039,7 +4246,7 @@ static int pm8001_chip_dereg_dev_req(struct pm8001_hba_info *pm8001_ha,
circularQ = &pm8001_ha->inbnd_q_tbl[0];
memset(&payload, 0, sizeof(payload));
- payload.tag = 1;
+ payload.tag = cpu_to_le32(1);
payload.device_id = cpu_to_le32(device_id);
PM8001_MSG_DBG(pm8001_ha,
pm8001_printk("unregister device device_id = %d\n", device_id));
@@ -4063,7 +4270,7 @@ static int pm8001_chip_phy_ctl_req(struct pm8001_hba_info *pm8001_ha,
u32 opc = OPC_INB_LOCAL_PHY_CONTROL;
memset(&payload, 0, sizeof(payload));
circularQ = &pm8001_ha->inbnd_q_tbl[0];
- payload.tag = 1;
+ payload.tag = cpu_to_le32(1);
payload.phyop_phyid =
cpu_to_le32(((phy_op & 0xff) << 8) | (phyId & 0x0F));
ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
@@ -4092,12 +4299,9 @@ static u32 pm8001_chip_is_our_interupt(struct pm8001_hba_info *pm8001_ha)
static irqreturn_t
pm8001_chip_isr(struct pm8001_hba_info *pm8001_ha)
{
- unsigned long flags;
- spin_lock_irqsave(&pm8001_ha->lock, flags);
pm8001_chip_interrupt_disable(pm8001_ha);
process_oq(pm8001_ha);
pm8001_chip_interrupt_enable(pm8001_ha);
- spin_unlock_irqrestore(&pm8001_ha->lock, flags);
return IRQ_HANDLED;
}
@@ -4360,8 +4564,10 @@ pm8001_chip_fw_flash_update_build(struct pm8001_hba_info *pm8001_ha,
payload.cur_image_offset = cpu_to_le32(info->cur_image_offset);
payload.total_image_len = cpu_to_le32(info->total_image_len);
payload.len = info->sgl.im_len.len ;
- payload.sgl_addr_lo = lower_32_bits(info->sgl.addr);
- payload.sgl_addr_hi = upper_32_bits(info->sgl.addr);
+ payload.sgl_addr_lo =
+ cpu_to_le32(lower_32_bits(le64_to_cpu(info->sgl.addr)));
+ payload.sgl_addr_hi =
+ cpu_to_le32(upper_32_bits(le64_to_cpu(info->sgl.addr)));
ret = mpi_build_cmd(pm8001_ha, circularQ, opc, &payload);
return ret;
}
diff --git a/drivers/scsi/pm8001/pm8001_hwi.h b/drivers/scsi/pm8001/pm8001_hwi.h
index 909132041c07..1a4611eb0321 100644
--- a/drivers/scsi/pm8001/pm8001_hwi.h
+++ b/drivers/scsi/pm8001/pm8001_hwi.h
@@ -625,7 +625,7 @@ struct set_nvm_data_req {
__le32 tag;
__le32 len_ir_vpdd;
__le32 vpd_offset;
- u32 reserved[8];
+ __le32 reserved[8];
__le32 resp_addr_lo;
__le32 resp_addr_hi;
__le32 resp_len;
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c
index c21a2163f9f6..36efaa7c3a54 100644
--- a/drivers/scsi/pm8001/pm8001_init.c
+++ b/drivers/scsi/pm8001/pm8001_init.c
@@ -62,7 +62,6 @@ static struct scsi_host_template pm8001_sht = {
.queuecommand = sas_queuecommand,
.target_alloc = sas_target_alloc,
.slave_configure = sas_slave_configure,
- .slave_destroy = sas_slave_destroy,
.scan_finished = pm8001_scan_finished,
.scan_start = pm8001_scan_start,
.change_queue_depth = sas_change_queue_depth,
@@ -76,7 +75,6 @@ static struct scsi_host_template pm8001_sht = {
.use_clustering = ENABLE_CLUSTERING,
.eh_device_reset_handler = sas_eh_device_reset_handler,
.eh_bus_reset_handler = sas_eh_bus_reset_handler,
- .slave_alloc = sas_slave_alloc,
.target_destroy = sas_target_destroy,
.ioctl = sas_ioctl,
.shost_attrs = pm8001_host_attrs,
diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c
index fb3dc9978861..3b11edd4a50c 100644
--- a/drivers/scsi/pm8001/pm8001_sas.c
+++ b/drivers/scsi/pm8001/pm8001_sas.c
@@ -166,6 +166,7 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
struct pm8001_hba_info *pm8001_ha = NULL;
struct sas_phy_linkrates *rates;
DECLARE_COMPLETION_ONSTACK(completion);
+ unsigned long flags;
pm8001_ha = sas_phy->ha->lldd_ha;
pm8001_ha->phy[phy_id].enable_completion = &completion;
switch (func) {
@@ -209,8 +210,29 @@ int pm8001_phy_control(struct asd_sas_phy *sas_phy, enum phy_func func,
case PHY_FUNC_DISABLE:
PM8001_CHIP_DISP->phy_stop_req(pm8001_ha, phy_id);
break;
+ case PHY_FUNC_GET_EVENTS:
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ if (-1 == pm8001_bar4_shift(pm8001_ha,
+ (phy_id < 4) ? 0x30000 : 0x40000)) {
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return -EINVAL;
+ }
+ {
+ struct sas_phy *phy = sas_phy->phy;
+ uint32_t *qp = (uint32_t *)(((char *)
+ pm8001_ha->io_mem[2].memvirtaddr)
+ + 0x1034 + (0x4000 * (phy_id & 3)));
+
+ phy->invalid_dword_count = qp[0];
+ phy->running_disparity_error_count = qp[1];
+ phy->loss_of_dword_sync_count = qp[3];
+ phy->phy_reset_problem_count = qp[4];
+ }
+ pm8001_bar4_shift(pm8001_ha, 0);
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ return 0;
default:
- rc = -ENOSYS;
+ rc = -EOPNOTSUPP;
}
msleep(300);
return rc;
@@ -234,12 +256,14 @@ void pm8001_scan_start(struct Scsi_Host *shost)
int pm8001_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
+ struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
+
/* give the phy enabling interrupt event time to come in (1s
* is empirically about all it takes) */
if (time < HZ)
return 0;
/* Wait for discovery to finish */
- scsi_flush_work(shost);
+ sas_drain_work(ha);
return 1;
}
@@ -340,7 +364,7 @@ static int pm8001_task_exec(struct sas_task *task, const int num,
struct pm8001_ccb_info *ccb;
u32 tag = 0xdeadbeef, rc, n_elem = 0;
u32 n = num;
- unsigned long flags = 0, flags_libsas = 0;
+ unsigned long flags = 0;
if (!dev->port) {
struct task_status_struct *tsm = &t->task_status;
@@ -364,11 +388,7 @@ static int pm8001_task_exec(struct sas_task *task, const int num,
ts->stat = SAS_PHY_DOWN;
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
- spin_unlock_irqrestore(dev->sata_dev.ap->lock,
- flags_libsas);
t->task_done(t);
- spin_lock_irqsave(dev->sata_dev.ap->lock,
- flags_libsas);
spin_lock_irqsave(&pm8001_ha->lock, flags);
if (n > 1)
t = list_entry(t->list.next,
@@ -516,6 +536,7 @@ void pm8001_ccb_task_free(struct pm8001_hba_info *pm8001_ha,
task->lldd_task = NULL;
ccb->task = NULL;
ccb->ccb_tag = 0xFFFFFFFF;
+ ccb->open_retry = 0;
pm8001_ccb_free(pm8001_ha, ccb_idx);
}
@@ -615,7 +636,7 @@ static int pm8001_dev_found_notify(struct domain_device *dev)
wait_for_completion(&completion);
if (dev->dev_type == SAS_END_DEV)
msleep(50);
- pm8001_ha->flags |= PM8001F_RUN_TIME ;
+ pm8001_ha->flags = PM8001F_RUN_TIME;
return 0;
found_out:
spin_unlock_irqrestore(&pm8001_ha->lock, flags);
@@ -860,6 +881,77 @@ static int pm8001_issue_ssp_tmf(struct domain_device *dev,
tmf);
}
+/* retry commands by ha, by task and/or by device */
+void pm8001_open_reject_retry(
+ struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task_to_close,
+ struct pm8001_device *device_to_close)
+{
+ int i;
+ unsigned long flags;
+
+ if (pm8001_ha == NULL)
+ return;
+
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+
+ for (i = 0; i < PM8001_MAX_CCB; i++) {
+ struct sas_task *task;
+ struct task_status_struct *ts;
+ struct pm8001_device *pm8001_dev;
+ unsigned long flags1;
+ u32 tag;
+ struct pm8001_ccb_info *ccb = &pm8001_ha->ccb_info[i];
+
+ pm8001_dev = ccb->device;
+ if (!pm8001_dev || (pm8001_dev->dev_type == NO_DEVICE))
+ continue;
+ if (!device_to_close) {
+ uintptr_t d = (uintptr_t)pm8001_dev
+ - (uintptr_t)&pm8001_ha->devices;
+ if (((d % sizeof(*pm8001_dev)) != 0)
+ || ((d / sizeof(*pm8001_dev)) >= PM8001_MAX_DEVICES))
+ continue;
+ } else if (pm8001_dev != device_to_close)
+ continue;
+ tag = ccb->ccb_tag;
+ if (!tag || (tag == 0xFFFFFFFF))
+ continue;
+ task = ccb->task;
+ if (!task || !task->task_done)
+ continue;
+ if (task_to_close && (task != task_to_close))
+ continue;
+ ts = &task->task_status;
+ ts->resp = SAS_TASK_COMPLETE;
+ /* Force the midlayer to retry */
+ ts->stat = SAS_OPEN_REJECT;
+ ts->open_rej_reason = SAS_OREJ_RSVD_RETRY;
+ if (pm8001_dev)
+ pm8001_dev->running_req--;
+ spin_lock_irqsave(&task->task_state_lock, flags1);
+ task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
+ task->task_state_flags &= ~SAS_TASK_AT_INITIATOR;
+ task->task_state_flags |= SAS_TASK_STATE_DONE;
+ if (unlikely((task->task_state_flags
+ & SAS_TASK_STATE_ABORTED))) {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags1);
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ } else {
+ spin_unlock_irqrestore(&task->task_state_lock,
+ flags1);
+ pm8001_ccb_task_free(pm8001_ha, task, ccb, tag);
+ mb();/* in order to force CPU ordering */
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+ task->task_done(task);
+ spin_lock_irqsave(&pm8001_ha->lock, flags);
+ }
+ }
+
+ spin_unlock_irqrestore(&pm8001_ha->lock, flags);
+}
+
/**
* Standard mandates link reset for ATA (type 0) and hard reset for
* SSP (type 1) , only for RECOVERY
@@ -875,12 +967,14 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev)
pm8001_dev = dev->lldd_dev;
pm8001_ha = pm8001_find_ha_by_dev(dev);
- phy = sas_find_local_phy(dev);
+ phy = sas_get_local_phy(dev);
if (dev_is_sata(dev)) {
DECLARE_COMPLETION_ONSTACK(completion_setstate);
- if (scsi_is_sas_phy_local(phy))
- return 0;
+ if (scsi_is_sas_phy_local(phy)) {
+ rc = 0;
+ goto out;
+ }
rc = sas_phy_reset(phy, 1);
msleep(2000);
rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
@@ -889,12 +983,14 @@ int pm8001_I_T_nexus_reset(struct domain_device *dev)
rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
pm8001_dev, 0x01);
wait_for_completion(&completion_setstate);
- } else{
- rc = sas_phy_reset(phy, 1);
- msleep(2000);
+ } else {
+ rc = sas_phy_reset(phy, 1);
+ msleep(2000);
}
PM8001_EH_DBG(pm8001_ha, pm8001_printk(" for device[%x]:rc=%d\n",
pm8001_dev->device_id, rc));
+ out:
+ sas_put_local_phy(phy);
return rc;
}
@@ -906,10 +1002,11 @@ int pm8001_lu_reset(struct domain_device *dev, u8 *lun)
struct pm8001_device *pm8001_dev = dev->lldd_dev;
struct pm8001_hba_info *pm8001_ha = pm8001_find_ha_by_dev(dev);
if (dev_is_sata(dev)) {
- struct sas_phy *phy = sas_find_local_phy(dev);
+ struct sas_phy *phy = sas_get_local_phy(dev);
rc = pm8001_exec_internal_task_abort(pm8001_ha, pm8001_dev ,
dev, 1, 0);
rc = sas_phy_reset(phy, 1);
+ sas_put_local_phy(phy);
rc = PM8001_CHIP_DISP->set_dev_state_req(pm8001_ha,
pm8001_dev, 0x01);
msleep(2000);
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h
index 93959febe205..11008205aeb3 100644
--- a/drivers/scsi/pm8001/pm8001_sas.h
+++ b/drivers/scsi/pm8001/pm8001_sas.h
@@ -235,6 +235,7 @@ struct pm8001_ccb_info {
struct pm8001_device *device;
struct pm8001_prd buf_prd[PM8001_MAX_DMA_SG];
struct fw_control_ex *fw_control_context;
+ u8 open_retry;
};
struct mpi_mem {
@@ -484,10 +485,15 @@ void pm8001_dev_gone(struct domain_device *dev);
int pm8001_lu_reset(struct domain_device *dev, u8 *lun);
int pm8001_I_T_nexus_reset(struct domain_device *dev);
int pm8001_query_task(struct sas_task *task);
+void pm8001_open_reject_retry(
+ struct pm8001_hba_info *pm8001_ha,
+ struct sas_task *task_to_close,
+ struct pm8001_device *device_to_close);
int pm8001_mem_alloc(struct pci_dev *pdev, void **virt_addr,
dma_addr_t *pphys_addr, u32 *pphys_addr_hi, u32 *pphys_addr_lo,
u32 mem_size, u32 align);
+int pm8001_bar4_shift(struct pm8001_hba_info *pm8001_ha, u32 shiftValue);
/* ctl shared API */
extern struct device_attribute *pm8001_host_attrs[];
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index d838205ab169..6c6486f626ee 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/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
index 9f41b3b4358f..5926f5a87ea8 100644
--- a/drivers/scsi/qla2xxx/qla_attr.c
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -356,7 +356,8 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj,
else if (start == (ha->flt_region_boot * 4) ||
start == (ha->flt_region_fw * 4))
valid = 1;
- else if (IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha))
+ else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)
+ || IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
valid = 1;
if (!valid) {
ql_log(ql_log_warn, vha, 0x7065,
@@ -627,144 +628,6 @@ static struct bin_attribute sysfs_reset_attr = {
};
static ssize_t
-qla2x00_sysfs_write_edc(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
- struct device, kobj)));
- struct qla_hw_data *ha = vha->hw;
- uint16_t dev, adr, opt, len;
- int rval;
-
- ha->edc_data_len = 0;
-
- if (!capable(CAP_SYS_ADMIN) || off != 0 || count < 8)
- return -EINVAL;
-
- if (!ha->edc_data) {
- ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
- &ha->edc_data_dma);
- if (!ha->edc_data) {
- ql_log(ql_log_warn, vha, 0x7073,
- "Unable to allocate memory for EDC write.\n");
- return -ENOMEM;
- }
- }
-
- dev = le16_to_cpup((void *)&buf[0]);
- adr = le16_to_cpup((void *)&buf[2]);
- opt = le16_to_cpup((void *)&buf[4]);
- len = le16_to_cpup((void *)&buf[6]);
-
- if (!(opt & BIT_0))
- if (len == 0 || len > DMA_POOL_SIZE || len > count - 8)
- return -EINVAL;
-
- memcpy(ha->edc_data, &buf[8], len);
-
- rval = qla2x00_write_sfp(vha, ha->edc_data_dma, ha->edc_data,
- dev, adr, len, opt);
- if (rval != QLA_SUCCESS) {
- ql_log(ql_log_warn, vha, 0x7074,
- "Unable to write EDC (%x) %02x:%04x:%02x:%02x:%02hhx\n",
- rval, dev, adr, opt, len, buf[8]);
- return -EIO;
- }
-
- return count;
-}
-
-static struct bin_attribute sysfs_edc_attr = {
- .attr = {
- .name = "edc",
- .mode = S_IWUSR,
- },
- .size = 0,
- .write = qla2x00_sysfs_write_edc,
-};
-
-static ssize_t
-qla2x00_sysfs_write_edc_status(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
- struct device, kobj)));
- struct qla_hw_data *ha = vha->hw;
- uint16_t dev, adr, opt, len;
- int rval;
-
- ha->edc_data_len = 0;
-
- if (!capable(CAP_SYS_ADMIN) || off != 0 || count < 8)
- return -EINVAL;
-
- if (!ha->edc_data) {
- ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
- &ha->edc_data_dma);
- if (!ha->edc_data) {
- ql_log(ql_log_warn, vha, 0x708c,
- "Unable to allocate memory for EDC status.\n");
- return -ENOMEM;
- }
- }
-
- dev = le16_to_cpup((void *)&buf[0]);
- adr = le16_to_cpup((void *)&buf[2]);
- opt = le16_to_cpup((void *)&buf[4]);
- len = le16_to_cpup((void *)&buf[6]);
-
- if (!(opt & BIT_0))
- if (len == 0 || len > DMA_POOL_SIZE)
- return -EINVAL;
-
- memset(ha->edc_data, 0, len);
- rval = qla2x00_read_sfp(vha, ha->edc_data_dma, ha->edc_data,
- dev, adr, len, opt);
- if (rval != QLA_SUCCESS) {
- ql_log(ql_log_info, vha, 0x7075,
- "Unable to write EDC status (%x) %02x:%04x:%02x:%02x.\n",
- rval, dev, adr, opt, len);
- return -EIO;
- }
-
- ha->edc_data_len = len;
-
- return count;
-}
-
-static ssize_t
-qla2x00_sysfs_read_edc_status(struct file *filp, struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
-{
- struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
- struct device, kobj)));
- struct qla_hw_data *ha = vha->hw;
-
- if (!capable(CAP_SYS_ADMIN) || off != 0 || count == 0)
- return 0;
-
- if (!ha->edc_data || ha->edc_data_len == 0 || ha->edc_data_len > count)
- return -EINVAL;
-
- memcpy(buf, ha->edc_data, ha->edc_data_len);
-
- return ha->edc_data_len;
-}
-
-static struct bin_attribute sysfs_edc_status_attr = {
- .attr = {
- .name = "edc_status",
- .mode = S_IRUSR | S_IWUSR,
- },
- .size = 0,
- .write = qla2x00_sysfs_write_edc_status,
- .read = qla2x00_sysfs_read_edc_status,
-};
-
-static ssize_t
qla2x00_sysfs_read_xgmac_stats(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr,
char *buf, loff_t off, size_t count)
@@ -879,8 +742,6 @@ static struct sysfs_entry {
{ "vpd", &sysfs_vpd_attr, 1 },
{ "sfp", &sysfs_sfp_attr, 1 },
{ "reset", &sysfs_reset_attr, },
- { "edc", &sysfs_edc_attr, 2 },
- { "edc_status", &sysfs_edc_status_attr, 2 },
{ "xgmac_stats", &sysfs_xgmac_stats_attr, 3 },
{ "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 },
{ NULL },
@@ -898,7 +759,7 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
continue;
if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw))
continue;
- if (iter->is4GBp_only == 3 && !(IS_QLA8XXX_TYPE(vha->hw)))
+ if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw)))
continue;
ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
@@ -926,7 +787,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
continue;
if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha))
continue;
- if (iter->is4GBp_only == 3 && !!(IS_QLA8XXX_TYPE(vha->hw)))
+ if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw)))
continue;
sysfs_remove_bin_file(&host->shost_gendev.kobj,
@@ -1231,7 +1092,7 @@ qla2x00_optrom_gold_fw_version_show(struct device *dev,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA81XX(ha))
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
return snprintf(buf, PAGE_SIZE, "\n");
return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%d)\n",
@@ -1278,7 +1139,7 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA81XX(ha))
+ if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
return snprintf(buf, PAGE_SIZE, "\n");
return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n",
@@ -1293,7 +1154,7 @@ qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr,
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA81XX(ha))
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
return snprintf(buf, PAGE_SIZE, "\n");
return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
@@ -1316,7 +1177,7 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
{
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
- if (!IS_QLA8XXX_TYPE(vha->hw))
+ if (!IS_CNA_CAPABLE(vha->hw))
return snprintf(buf, PAGE_SIZE, "\n");
return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id);
@@ -1328,7 +1189,7 @@ qla2x00_vn_port_mac_address_show(struct device *dev,
{
scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
- if (!IS_QLA8XXX_TYPE(vha->hw))
+ if (!IS_CNA_CAPABLE(vha->hw))
return snprintf(buf, PAGE_SIZE, "\n");
return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
@@ -1364,7 +1225,7 @@ qla2x00_thermal_temp_show(struct device *dev,
else if (!vha->hw->flags.eeh_busy)
rval = qla2x00_get_thermal_temp(vha, &temp, &frac);
if (rval != QLA_SUCCESS)
- temp = frac = 0;
+ return snprintf(buf, PAGE_SIZE, "\n");
return snprintf(buf, PAGE_SIZE, "%d.%02d\n", temp, frac);
}
@@ -1493,6 +1354,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost)
case PORT_SPEED_10GB:
speed = FC_PORTSPEED_10GBIT;
break;
+ case PORT_SPEED_16GB:
+ speed = FC_PORTSPEED_16GBIT;
+ break;
}
fc_host_speed(shost) = speed;
}
@@ -1643,10 +1507,14 @@ qla2x00_terminate_rport_io(struct fc_rport *rport)
* final cleanup of firmware resources (PCBs and XCBs).
*/
if (fcport->loop_id != FC_NO_LOOP_ID &&
- !test_bit(UNLOADING, &fcport->vha->dpc_flags))
- fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
- fcport->loop_id, fcport->d_id.b.domain,
- fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ !test_bit(UNLOADING, &fcport->vha->dpc_flags)) {
+ if (IS_FWI2_CAPABLE(fcport->vha->hw))
+ fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ else
+ qla2x00_port_logout(fcport->vha, fcport);
+ }
}
static int
@@ -1889,6 +1757,7 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
break;
}
}
+
if (qos) {
ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0,
qos);
@@ -2086,7 +1955,7 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha)
fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
- if (IS_QLA8XXX_TYPE(ha))
+ if (IS_CNA_CAPABLE(ha))
speed = FC_PORTSPEED_10GBIT;
else if (IS_QLA25XX(ha))
speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
index 2c4714279bcc..f74cc0602f3b 100644
--- a/drivers/scsi/qla2xxx/qla_bsg.c
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -11,29 +11,36 @@
#include <linux/delay.h>
/* BSG support for ELS/CT pass through */
-inline srb_t *
-qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size)
+void
+qla2x00_bsg_job_done(void *data, void *ptr, int res)
{
- srb_t *sp;
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
+
+ bsg_job->reply->result = res;
+ bsg_job->job_done(bsg_job);
+ sp->free(vha, sp);
+}
+
+void
+qla2x00_bsg_sp_free(void *data, void *ptr)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
struct qla_hw_data *ha = vha->hw;
- struct srb_ctx *ctx;
- sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL);
- if (!sp)
- goto done;
- ctx = kzalloc(size, GFP_KERNEL);
- if (!ctx) {
- mempool_free(sp, ha->srb_mempool);
- sp = NULL;
- goto done;
- }
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
- memset(sp, 0, sizeof(*sp));
- sp->fcport = fcport;
- sp->ctx = ctx;
- ctx->iocbs = 1;
-done:
- return sp;
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ if (sp->type == SRB_CT_CMD ||
+ sp->type == SRB_ELS_CMD_HST)
+ kfree(sp->fcport);
+ mempool_free(sp, vha->hw->srb_mempool);
}
int
@@ -101,8 +108,6 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job)
uint32_t len;
uint32_t oper;
- bsg_job->reply->reply_payload_rcv_len = 0;
-
if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA82XX(ha))) {
ret = -EINVAL;
goto exit_fcp_prio_cfg;
@@ -217,6 +222,7 @@ exit_fcp_prio_cfg:
bsg_job->job_done(bsg_job);
return ret;
}
+
static int
qla2x00_process_els(struct fc_bsg_job *bsg_job)
{
@@ -230,7 +236,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job)
int req_sg_cnt, rsp_sg_cnt;
int rval = (DRIVER_ERROR << 16);
uint16_t nextlid = 0;
- struct srb_ctx *els;
if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
rport = bsg_job->rport;
@@ -337,20 +342,21 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job)
}
/* Alloc SRB structure */
- sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx));
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp) {
rval = -ENOMEM;
goto done_unmap_sg;
}
- els = sp->ctx;
- els->type =
+ sp->type =
(bsg_job->request->msgcode == FC_BSG_RPT_ELS ?
SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST);
- els->name =
+ sp->name =
(bsg_job->request->msgcode == FC_BSG_RPT_ELS ?
"bsg_els_rpt" : "bsg_els_hst");
- els->u.bsg_job = bsg_job;
+ sp->u.bsg_job = bsg_job;
+ sp->free = qla2x00_bsg_sp_free;
+ sp->done = qla2x00_bsg_job_done;
ql_dbg(ql_dbg_user, vha, 0x700a,
"bsg rqst type: %s els type: %x - loop-id=%x "
@@ -362,7 +368,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job)
if (rval != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x700e,
"qla2x00_start_sp failed = %d\n", rval);
- kfree(sp->ctx);
mempool_free(sp, ha->srb_mempool);
rval = -EIO;
goto done_unmap_sg;
@@ -409,7 +414,6 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job)
uint16_t loop_id;
struct fc_port *fcport;
char *type = "FC_BSG_HST_CT";
- struct srb_ctx *ct;
req_sg_cnt =
dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
@@ -486,19 +490,20 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job)
fcport->loop_id = loop_id;
/* Alloc SRB structure */
- sp = qla2x00_get_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx));
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp) {
ql_log(ql_log_warn, vha, 0x7015,
- "qla2x00_get_ctx_bsg_sp failed.\n");
+ "qla2x00_get_sp failed.\n");
rval = -ENOMEM;
goto done_free_fcport;
}
- ct = sp->ctx;
- ct->type = SRB_CT_CMD;
- ct->name = "bsg_ct";
- ct->iocbs = qla24xx_calc_ct_iocbs(req_sg_cnt + rsp_sg_cnt);
- ct->u.bsg_job = bsg_job;
+ sp->type = SRB_CT_CMD;
+ sp->name = "bsg_ct";
+ sp->iocbs = qla24xx_calc_ct_iocbs(req_sg_cnt + rsp_sg_cnt);
+ sp->u.bsg_job = bsg_job;
+ sp->free = qla2x00_bsg_sp_free;
+ sp->done = qla2x00_bsg_job_done;
ql_dbg(ql_dbg_user, vha, 0x7016,
"bsg rqst type: %s else type: %x - "
@@ -511,7 +516,6 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job)
if (rval != QLA_SUCCESS) {
ql_log(ql_log_warn, vha, 0x7017,
"qla2x00_start_sp failed=%d.\n", rval);
- kfree(sp->ctx);
mempool_free(sp, ha->srb_mempool);
rval = -EIO;
goto done_free_fcport;
@@ -540,7 +544,7 @@ qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
int rval = 0;
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA81XX(ha))
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
goto done_set_internal;
new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1);
@@ -582,7 +586,7 @@ qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
uint16_t new_config[4];
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA81XX(ha))
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
goto done_reset_internal;
memset(new_config, 0 , sizeof(new_config));
@@ -707,7 +711,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
if ((ha->current_topology == ISP_CFG_F ||
(atomic_read(&vha->loop_state) == LOOP_DOWN) ||
- (IS_QLA81XX(ha) &&
+ ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) &&
le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE
&& req_data_len == MAX_ELS_FRAME_PAYLOAD)) &&
elreq.options == EXTERNAL_LOOPBACK) {
@@ -717,13 +721,12 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
command_sent = INT_DEF_LB_ECHO_CMD;
rval = qla2x00_echo_test(vha, &elreq, response);
} else {
- if (IS_QLA81XX(ha)) {
+ if (IS_QLA81XX(ha) || IS_QLA8031(ha)) {
memset(config, 0, sizeof(config));
memset(new_config, 0, sizeof(new_config));
if (qla81xx_get_port_config(vha, config)) {
ql_log(ql_log_warn, vha, 0x701f,
"Get port config failed.\n");
- bsg_job->reply->reply_payload_rcv_len = 0;
bsg_job->reply->result = (DID_ERROR << 16);
rval = -EPERM;
goto done_free_dma_req;
@@ -737,8 +740,6 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
new_config)) {
ql_log(ql_log_warn, vha, 0x7024,
"Internal loopback failed.\n");
- bsg_job->reply->reply_payload_rcv_len =
- 0;
bsg_job->reply->result =
(DID_ERROR << 16);
rval = -EPERM;
@@ -750,8 +751,6 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
*/
if (qla81xx_reset_internal_loopback(vha,
config, 1)) {
- bsg_job->reply->reply_payload_rcv_len =
- 0;
bsg_job->reply->result =
(DID_ERROR << 16);
rval = -EPERM;
@@ -788,7 +787,6 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
"MPI reset failed.\n");
}
- bsg_job->reply->reply_payload_rcv_len = 0;
bsg_job->reply->result = (DID_ERROR << 16);
rval = -EIO;
goto done_free_dma_req;
@@ -813,7 +811,6 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
fw_sts_ptr += sizeof(response);
*fw_sts_ptr = command_sent;
rval = 0;
- bsg_job->reply->reply_payload_rcv_len = 0;
bsg_job->reply->result = (DID_ERROR << 16);
} else {
ql_dbg(ql_dbg_user, vha, 0x702d,
@@ -872,7 +869,7 @@ qla84xx_reset(struct fc_bsg_job *bsg_job)
if (rval) {
ql_log(ql_log_warn, vha, 0x7030,
"Vendor request 84xx reset failed.\n");
- rval = bsg_job->reply->reply_payload_rcv_len = 0;
+ rval = 0;
bsg_job->reply->result = (DID_ERROR << 16);
} else {
@@ -971,9 +968,8 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job)
ql_log(ql_log_warn, vha, 0x7037,
"Vendor request 84xx updatefw failed.\n");
- rval = bsg_job->reply->reply_payload_rcv_len = 0;
+ rval = 0;
bsg_job->reply->result = (DID_ERROR << 16);
-
} else {
ql_dbg(ql_dbg_user, vha, 0x7038,
"Vendor request 84xx updatefw completed.\n");
@@ -1159,7 +1155,7 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job)
ql_log(ql_log_warn, vha, 0x7043,
"Vendor request 84xx mgmt failed.\n");
- rval = bsg_job->reply->reply_payload_rcv_len = 0;
+ rval = 0;
bsg_job->reply->result = (DID_ERROR << 16);
} else {
@@ -1210,8 +1206,6 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job)
uint16_t mb[MAILBOX_REGISTER_COUNT];
uint8_t *rsp_ptr = NULL;
- bsg_job->reply->reply_payload_rcv_len = 0;
-
if (!IS_IIDMA_CAPABLE(vha->hw)) {
ql_log(ql_log_info, vha, 0x7046, "iiDMA not supported.\n");
return -EINVAL;
@@ -1304,8 +1298,6 @@ qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha,
int valid = 0;
struct qla_hw_data *ha = vha->hw;
- bsg_job->reply->reply_payload_rcv_len = 0;
-
if (unlikely(pci_channel_offline(ha->pdev)))
return -EINVAL;
@@ -1331,7 +1323,7 @@ qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha,
start == (ha->flt_region_fw * 4))
valid = 1;
else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
- IS_QLA8XXX_TYPE(ha))
+ IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
valid = 1;
if (!valid) {
ql_log(ql_log_warn, vha, 0x7058,
@@ -1617,6 +1609,9 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
struct Scsi_Host *host;
scsi_qla_host_t *vha;
+ /* In case no data transferred. */
+ bsg_job->reply->reply_payload_rcv_len = 0;
+
if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
rport = bsg_job->rport;
fcport = *(fc_port_t **) rport->dd_data;
@@ -1655,6 +1650,7 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
case FC_BSG_RPT_CT:
default:
ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n");
+ bsg_job->reply->result = ret;
break;
}
return ret;
@@ -1669,7 +1665,6 @@ qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job)
int cnt, que;
unsigned long flags;
struct req_que *req;
- struct srb_ctx *sp_bsg;
/* find the bsg job from the active list of commands */
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -1681,11 +1676,9 @@ qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job)
for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
sp = req->outstanding_cmds[cnt];
if (sp) {
- sp_bsg = sp->ctx;
-
- if (((sp_bsg->type == SRB_CT_CMD) ||
- (sp_bsg->type == SRB_ELS_CMD_HST))
- && (sp_bsg->u.bsg_job == bsg_job)) {
+ if (((sp->type == SRB_CT_CMD) ||
+ (sp->type == SRB_ELS_CMD_HST))
+ && (sp->u.bsg_job == bsg_job)) {
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (ha->isp_ops->abort_command(sp)) {
ql_log(ql_log_warn, vha, 0x7089,
@@ -1715,7 +1708,6 @@ done:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
if (bsg_job->request->msgcode == FC_BSG_HST_CT)
kfree(sp->fcport);
- kfree(sp->ctx);
mempool_free(sp, ha->srb_mempool);
return 0;
}
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
index 45cbf0ba624d..897731b93df2 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.c
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -11,23 +11,27 @@
* ----------------------------------------------------------------------
* | Level | Last Value Used | Holes |
* ----------------------------------------------------------------------
- * | Module Init and Probe | 0x0116 | 0xfa |
- * | Mailbox commands | 0x112b | |
- * | Device Discovery | 0x2084 | |
- * | Queue Command and IO tracing | 0x302f | 0x3008,0x302d, |
- * | | | 0x302e |
+ * | Module Init and Probe | 0x0120 | 0x4b,0xba,0xfa |
+ * | Mailbox commands | 0x113e | 0x112c-0x112e |
+ * | | | 0x113a |
+ * | Device Discovery | 0x2086 | 0x2020-0x2022 |
+ * | Queue Command and IO tracing | 0x302f | 0x3006,0x3008 |
+ * | | | 0x302d-0x302e |
* | DPC Thread | 0x401c | |
- * | Async Events | 0x5057 | 0x5052 |
- * | Timer Routines | 0x6011 | 0x600e,0x600f |
- * | User Space Interactions | 0x709e | 0x7018,0x702e |
- * | | | 0x7039,0x7045 |
+ * | Async Events | 0x505d | 0x502b-0x502f |
+ * | | | 0x5047,0x5052 |
+ * | Timer Routines | 0x6011 | 0x600e-0x600f |
+ * | User Space Interactions | 0x709f | 0x7018,0x702e, |
+ * | | | 0x7039,0x7045, |
+ * | | | 0x7073-0x7075, |
+ * | | | 0x708c |
* | Task Management | 0x803c | 0x8025-0x8026 |
* | | | 0x800b,0x8039 |
* | AER/EEH | 0x900f | |
* | Virtual Port | 0xa007 | |
- * | ISP82XX Specific | 0xb052 | |
- * | MultiQ | 0xc00b | |
- * | Misc | 0xd00b | |
+ * | ISP82XX Specific | 0xb054 | 0xb053 |
+ * | MultiQ | 0xc00c | |
+ * | Misc | 0xd010 | |
* ----------------------------------------------------------------------
*/
@@ -85,7 +89,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
WRT_REG_WORD(&reg->mailbox0, MBC_DUMP_RISC_RAM_EXTENDED);
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
- dwords = GID_LIST_SIZE / 4;
+ dwords = qla2x00_gid_list_size(ha) / 4;
for (cnt = 0; cnt < ram_dwords && rval == QLA_SUCCESS;
cnt += dwords, addr += dwords) {
if (cnt + dwords > ram_dwords)
@@ -260,7 +264,7 @@ qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram,
WRT_MAILBOX_REG(ha, reg, 0, MBC_DUMP_RISC_RAM_EXTENDED);
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
- words = GID_LIST_SIZE / 2;
+ words = qla2x00_gid_list_size(ha) / 2;
for (cnt = 0; cnt < ram_words && rval == QLA_SUCCESS;
cnt += words, addr += words) {
if (cnt + words > ram_words)
@@ -375,6 +379,77 @@ qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
}
static inline void *
+qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
+{
+ struct qla2xxx_mqueue_chain *q;
+ struct qla2xxx_mqueue_header *qh;
+ struct req_que *req;
+ struct rsp_que *rsp;
+ int que;
+
+ if (!ha->mqenable)
+ return ptr;
+
+ /* Request queues */
+ for (que = 1; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ break;
+
+ /* Add chain. */
+ q = ptr;
+ *last_chain = &q->type;
+ q->type = __constant_htonl(DUMP_CHAIN_QUEUE);
+ q->chain_size = htonl(
+ sizeof(struct qla2xxx_mqueue_chain) +
+ sizeof(struct qla2xxx_mqueue_header) +
+ (req->length * sizeof(request_t)));
+ ptr += sizeof(struct qla2xxx_mqueue_chain);
+
+ /* Add header. */
+ qh = ptr;
+ qh->queue = __constant_htonl(TYPE_REQUEST_QUEUE);
+ qh->number = htonl(que);
+ qh->size = htonl(req->length * sizeof(request_t));
+ ptr += sizeof(struct qla2xxx_mqueue_header);
+
+ /* Add data. */
+ memcpy(ptr, req->ring, req->length * sizeof(request_t));
+ ptr += req->length * sizeof(request_t);
+ }
+
+ /* Response queues */
+ for (que = 1; que < ha->max_rsp_queues; que++) {
+ rsp = ha->rsp_q_map[que];
+ if (!rsp)
+ break;
+
+ /* Add chain. */
+ q = ptr;
+ *last_chain = &q->type;
+ q->type = __constant_htonl(DUMP_CHAIN_QUEUE);
+ q->chain_size = htonl(
+ sizeof(struct qla2xxx_mqueue_chain) +
+ sizeof(struct qla2xxx_mqueue_header) +
+ (rsp->length * sizeof(response_t)));
+ ptr += sizeof(struct qla2xxx_mqueue_chain);
+
+ /* Add header. */
+ qh = ptr;
+ qh->queue = __constant_htonl(TYPE_RESPONSE_QUEUE);
+ qh->number = htonl(que);
+ qh->size = htonl(rsp->length * sizeof(response_t));
+ ptr += sizeof(struct qla2xxx_mqueue_header);
+
+ /* Add data. */
+ memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t));
+ ptr += rsp->length * sizeof(response_t);
+ }
+
+ return ptr;
+}
+
+static inline void *
qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
{
uint32_t cnt, que_idx;
@@ -382,7 +457,7 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
struct qla2xxx_mq_chain *mq = ptr;
struct device_reg_25xxmq __iomem *reg;
- if (!ha->mqenable)
+ if (!ha->mqenable || IS_QLA83XX(ha))
return ptr;
mq = ptr;
@@ -1322,12 +1397,16 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
nxt = qla24xx_copy_eft(ha, nxt);
/* Chain entries -- started with MQ. */
- qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
if (last_chain) {
ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
*last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
}
+ /* Adjust valid length. */
+ ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
qla25xx_fw_dump_failed_0:
qla2xxx_dump_post_process(base_vha, rval);
@@ -1636,12 +1715,16 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
nxt = qla24xx_copy_eft(ha, nxt);
/* Chain entries -- started with MQ. */
- qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
if (last_chain) {
ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
*last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
}
+ /* Adjust valid length. */
+ ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
qla81xx_fw_dump_failed_0:
qla2xxx_dump_post_process(base_vha, rval);
@@ -1650,6 +1733,507 @@ qla81xx_fw_dump_failed:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
+void
+qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt, reg_data;
+ uint32_t risc_address;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ uint32_t __iomem *dmp_reg;
+ uint32_t *iter_reg;
+ uint16_t __iomem *mbx_reg;
+ unsigned long flags;
+ struct qla83xx_fw_dump *fw;
+ uint32_t ext_mem_cnt;
+ void *nxt, *nxt_chain;
+ uint32_t *last_chain = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ risc_address = ext_mem_cnt = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd00c,
+ "No buffer available for dump!!!\n");
+ goto qla83xx_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd00d,
+ "Firmware has been previously dumped (%p) -- ignoring "
+ "request...\n", ha->fw_dump);
+ goto qla83xx_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp83;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+
+ fw->host_status = htonl(RD_REG_DWORD(&reg->host_status));
+
+ /* Pause RISC. */
+ rval = qla24xx_pause_risc(reg);
+ if (rval != QLA_SUCCESS)
+ goto qla83xx_fw_dump_failed_0;
+
+ WRT_REG_DWORD(&reg->iobase_addr, 0x6000);
+ dmp_reg = &reg->iobase_window;
+ reg_data = RD_REG_DWORD(dmp_reg);
+ WRT_REG_DWORD(dmp_reg, 0);
+
+ dmp_reg = &reg->unused_4_1[0];
+ reg_data = RD_REG_DWORD(dmp_reg);
+ WRT_REG_DWORD(dmp_reg, 0);
+
+ WRT_REG_DWORD(&reg->iobase_addr, 0x6010);
+ dmp_reg = &reg->unused_4_1[2];
+ reg_data = RD_REG_DWORD(dmp_reg);
+ WRT_REG_DWORD(dmp_reg, 0);
+
+ /* select PCR and disable ecc checking and correction */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0x60000000); /* write to F0h = PCR */
+
+ /* Host/Risc registers. */
+ iter_reg = fw->host_risc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7010, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7040, 16, iter_reg);
+
+ /* PCIe registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x7C00);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_window, 0x01);
+ dmp_reg = &reg->iobase_c4;
+ fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg));
+ fw->pcie_regs[3] = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ WRT_REG_DWORD(&reg->iobase_window, 0x00);
+ RD_REG_DWORD(&reg->iobase_window);
+
+ /* Host interface registers. */
+ dmp_reg = &reg->flash_addr;
+ for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++)
+ fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Disable interrupts. */
+ WRT_REG_DWORD(&reg->ictrl, 0);
+ RD_REG_DWORD(&reg->ictrl);
+
+ /* Shadow registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0000000);
+ fw->shadow_reg[0] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0100000);
+ fw->shadow_reg[1] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0200000);
+ fw->shadow_reg[2] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0300000);
+ fw->shadow_reg[3] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0400000);
+ fw->shadow_reg[4] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0500000);
+ fw->shadow_reg[5] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0600000);
+ fw->shadow_reg[6] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0700000);
+ fw->shadow_reg[7] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0800000);
+ fw->shadow_reg[8] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0900000);
+ fw->shadow_reg[9] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0A00000);
+ fw->shadow_reg[10] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ /* RISC I/O register. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0010);
+ fw->risc_io_reg = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ /* Mailbox registers. */
+ mbx_reg = &reg->mailbox0;
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++));
+
+ /* Transfer sequence registers. */
+ iter_reg = fw->xseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBE00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBF70, 16, iter_reg);
+
+ iter_reg = fw->xseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBFE0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg);
+
+ qla24xx_read_window(reg, 0xBEF0, 16, fw->xseq_2_reg);
+
+ /* Receive sequence registers. */
+ iter_reg = fw->rseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFE00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFF70, 16, iter_reg);
+
+ iter_reg = fw->rseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFFD0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg);
+ qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg);
+ qla24xx_read_window(reg, 0xFEF0, 16, fw->rseq_3_reg);
+
+ /* Auxiliary sequence registers. */
+ iter_reg = fw->aseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB110, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB120, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB140, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB160, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB170, 16, iter_reg);
+
+ iter_reg = fw->aseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB0D0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg);
+ qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg);
+ qla24xx_read_window(reg, 0xB1F0, 16, fw->aseq_3_reg);
+
+ /* Command DMA registers. */
+ iter_reg = fw->cmd_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7120, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7130, 16, iter_reg);
+ qla24xx_read_window(reg, 0x71F0, 16, iter_reg);
+
+ /* Queues. */
+ iter_reg = fw->req0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->resp0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->req1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Transmit DMA registers. */
+ iter_reg = fw->xmt0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7610, 16, iter_reg);
+
+ iter_reg = fw->xmt1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7630, 16, iter_reg);
+
+ iter_reg = fw->xmt2_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7650, 16, iter_reg);
+
+ iter_reg = fw->xmt3_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7670, 16, iter_reg);
+
+ iter_reg = fw->xmt4_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7690, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg);
+
+ /* Receive DMA registers. */
+ iter_reg = fw->rcvt0_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7710, 16, iter_reg);
+
+ iter_reg = fw->rcvt1_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7730, 16, iter_reg);
+
+ /* RISC registers. */
+ iter_reg = fw->risc_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg);
+ qla24xx_read_window(reg, 0x0F70, 16, iter_reg);
+
+ /* Local memory controller registers. */
+ iter_reg = fw->lmc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg);
+ qla24xx_read_window(reg, 0x3070, 16, iter_reg);
+
+ /* Fibre Protocol Module registers. */
+ iter_reg = fw->fpm_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40D0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40E0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x40F0, 16, iter_reg);
+
+ /* RQ0 Array registers. */
+ iter_reg = fw->rq0_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5C00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CD0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5CF0, 16, iter_reg);
+
+ /* RQ1 Array registers. */
+ iter_reg = fw->rq1_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5D00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DD0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5DF0, 16, iter_reg);
+
+ /* RP0 Array registers. */
+ iter_reg = fw->rp0_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5E00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5ED0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5EF0, 16, iter_reg);
+
+ /* RP1 Array registers. */
+ iter_reg = fw->rp1_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FD0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5FF0, 16, iter_reg);
+
+ iter_reg = fw->at0_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70D0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70E0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x70F0, 16, iter_reg);
+
+ /* I/O Queue Control registers. */
+ qla24xx_read_window(reg, 0x7800, 16, fw->queue_control_reg);
+
+ /* Frame Buffer registers. */
+ iter_reg = fw->fb_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6530, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6540, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6550, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6560, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6570, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6580, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6590, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65D0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65E0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x6F00, 16, iter_reg);
+
+ /* Multi queue registers */
+ nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset,
+ &last_chain);
+
+ rval = qla24xx_soft_reset(ha);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xd00e,
+ "SOFT RESET FAILED, forcing continuation of dump!!!\n");
+ rval = QLA_SUCCESS;
+
+ ql_log(ql_log_warn, vha, 0xd00f, "try a bigger hammer!!!\n");
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_REL_RISC_PAUSE);
+ RD_REG_DWORD(&reg->hccr);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+
+ for (cnt = 30000; cnt && (RD_REG_WORD(&reg->mailbox0)); cnt--)
+ udelay(5);
+
+ if (!cnt) {
+ nxt = fw->code_ram;
+ nxt += sizeof(fw->code_ram),
+ nxt += (ha->fw_memory_size - 0x100000 + 1);
+ goto copy_queue;
+ } else
+ ql_log(ql_log_warn, vha, 0xd010,
+ "bigger hammer success?\n");
+ }
+
+ rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram),
+ &nxt);
+ if (rval != QLA_SUCCESS)
+ goto qla83xx_fw_dump_failed_0;
+
+copy_queue:
+ nxt = qla2xxx_copy_queues(ha, nxt);
+
+ nxt = qla24xx_copy_eft(ha, nxt);
+
+ /* Chain entries -- started with MQ. */
+ nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+ if (last_chain) {
+ ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
+ *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
+ }
+
+ /* Adjust valid length. */
+ ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
+qla83xx_fw_dump_failed_0:
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla83xx_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
/****************************************************************************/
/* Driver Debug Functions. */
/****************************************************************************/
@@ -1782,13 +2366,13 @@ ql_log(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...)
vaf.va = &va;
switch (level) {
- case 0: /* FATAL LOG */
+ case ql_log_fatal: /* FATAL LOG */
pr_crit("%s%pV", pbuf, &vaf);
break;
- case 1:
+ case ql_log_warn:
pr_err("%s%pV", pbuf, &vaf);
break;
- case 2:
+ case ql_log_info:
pr_warn("%s%pV", pbuf, &vaf);
break;
default:
@@ -1837,13 +2421,13 @@ ql_log_pci(uint32_t level, struct pci_dev *pdev, int32_t id,
vaf.va = &va;
switch (level) {
- case 0: /* FATAL LOG */
+ case ql_log_fatal: /* FATAL LOG */
pr_crit("%s%pV", pbuf, &vaf);
break;
- case 1:
+ case ql_log_warn:
pr_err("%s%pV", pbuf, &vaf);
break;
- case 2:
+ case ql_log_info:
pr_warn("%s%pV", pbuf, &vaf);
break;
default:
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
index 5f1b6d9c3dcb..2157bdf1569a 100644
--- a/drivers/scsi/qla2xxx/qla_dbg.h
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -165,6 +165,54 @@ struct qla81xx_fw_dump {
uint32_t ext_mem[1];
};
+struct qla83xx_fw_dump {
+ uint32_t host_status;
+ uint32_t host_risc_reg[48];
+ uint32_t pcie_regs[4];
+ uint32_t host_reg[32];
+ uint32_t shadow_reg[11];
+ uint32_t risc_io_reg;
+ uint16_t mailbox_reg[32];
+ uint32_t xseq_gp_reg[256];
+ uint32_t xseq_0_reg[48];
+ uint32_t xseq_1_reg[16];
+ uint32_t xseq_2_reg[16];
+ uint32_t rseq_gp_reg[256];
+ uint32_t rseq_0_reg[32];
+ uint32_t rseq_1_reg[16];
+ uint32_t rseq_2_reg[16];
+ uint32_t rseq_3_reg[16];
+ uint32_t aseq_gp_reg[256];
+ uint32_t aseq_0_reg[32];
+ uint32_t aseq_1_reg[16];
+ uint32_t aseq_2_reg[16];
+ uint32_t aseq_3_reg[16];
+ uint32_t cmd_dma_reg[64];
+ uint32_t req0_dma_reg[15];
+ uint32_t resp0_dma_reg[15];
+ uint32_t req1_dma_reg[15];
+ uint32_t xmt0_dma_reg[32];
+ uint32_t xmt1_dma_reg[32];
+ uint32_t xmt2_dma_reg[32];
+ uint32_t xmt3_dma_reg[32];
+ uint32_t xmt4_dma_reg[32];
+ uint32_t xmt_data_dma_reg[16];
+ uint32_t rcvt0_data_dma_reg[32];
+ uint32_t rcvt1_data_dma_reg[32];
+ uint32_t risc_gp_reg[128];
+ uint32_t lmc_reg[128];
+ uint32_t fpm_hdw_reg[256];
+ uint32_t rq0_array_reg[256];
+ uint32_t rq1_array_reg[256];
+ uint32_t rp0_array_reg[256];
+ uint32_t rp1_array_reg[256];
+ uint32_t queue_control_reg[16];
+ uint32_t fb_hdw_reg[432];
+ uint32_t at0_array_reg[128];
+ uint32_t code_ram[0x2400];
+ uint32_t ext_mem[1];
+};
+
#define EFT_NUM_BUFFERS 4
#define EFT_BYTES_PER_BUFFER 0x4000
#define EFT_SIZE ((EFT_BYTES_PER_BUFFER) * (EFT_NUM_BUFFERS))
@@ -192,9 +240,23 @@ struct qla2xxx_mq_chain {
uint32_t qregs[4 * QLA_MQ_SIZE];
};
+struct qla2xxx_mqueue_header {
+ uint32_t queue;
+#define TYPE_REQUEST_QUEUE 0x1
+#define TYPE_RESPONSE_QUEUE 0x2
+ uint32_t number;
+ uint32_t size;
+};
+
+struct qla2xxx_mqueue_chain {
+ uint32_t type;
+ uint32_t chain_size;
+};
+
#define DUMP_CHAIN_VARIANT 0x80000000
#define DUMP_CHAIN_FCE 0x7FFFFAF0
#define DUMP_CHAIN_MQ 0x7FFFFAF1
+#define DUMP_CHAIN_QUEUE 0x7FFFFAF2
#define DUMP_CHAIN_LAST 0x80000000
struct qla2xxx_fw_dump {
@@ -228,6 +290,7 @@ struct qla2xxx_fw_dump {
struct qla24xx_fw_dump isp24;
struct qla25xx_fw_dump isp25;
struct qla81xx_fw_dump isp81;
+ struct qla83xx_fw_dump isp83;
} isp;
};
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
index af1003f9de1e..a2443031dbe7 100644
--- a/drivers/scsi/qla2xxx/qla_def.h
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -125,17 +125,17 @@
* Fibre Channel device definitions.
*/
#define WWN_SIZE 8 /* Size of WWPN, WWN & WWNN */
-#define MAX_FIBRE_DEVICES 512
+#define MAX_FIBRE_DEVICES_2100 512
+#define MAX_FIBRE_DEVICES_2400 2048
+#define MAX_FIBRE_DEVICES_LOOP 128
+#define MAX_FIBRE_DEVICES_MAX MAX_FIBRE_DEVICES_2400
#define MAX_FIBRE_LUNS 0xFFFF
-#define MAX_RSCN_COUNT 32
#define MAX_HOST_COUNT 16
/*
* Host adapter default definitions.
*/
#define MAX_BUSES 1 /* We only have one bus today */
-#define MAX_TARGETS_2100 MAX_FIBRE_DEVICES
-#define MAX_TARGETS_2200 MAX_FIBRE_DEVICES
#define MIN_LUNS 8
#define MAX_LUNS MAX_FIBRE_LUNS
#define MAX_CMDS_PER_LUN 255
@@ -202,20 +202,12 @@ struct sd_dif_tuple {
/*
* SCSI Request Block
*/
-typedef struct srb {
- atomic_t ref_count;
- struct fc_port *fcport;
- uint32_t handle;
-
+struct srb_cmd {
struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
-
- uint16_t flags;
-
uint32_t request_sense_length;
uint8_t *request_sense_ptr;
-
void *ctx;
-} srb_t;
+};
/*
* SRB flag definitions
@@ -254,10 +246,7 @@ struct srb_iocb {
} u;
struct timer_list timer;
-
- void (*done)(srb_t *);
- void (*free)(srb_t *);
- void (*timeout)(srb_t *);
+ void (*timeout)(void *);
};
/* Values for srb_ctx type */
@@ -268,16 +257,37 @@ struct srb_iocb {
#define SRB_CT_CMD 5
#define SRB_ADISC_CMD 6
#define SRB_TM_CMD 7
+#define SRB_SCSI_CMD 8
-struct srb_ctx {
+typedef struct srb {
+ atomic_t ref_count;
+ struct fc_port *fcport;
+ uint32_t handle;
+ uint16_t flags;
uint16_t type;
char *name;
int iocbs;
union {
- struct srb_iocb *iocb_cmd;
+ struct srb_iocb iocb_cmd;
struct fc_bsg_job *bsg_job;
+ struct srb_cmd scmd;
} u;
-};
+ void (*done)(void *, void *, int);
+ void (*free)(void *, void *);
+} srb_t;
+
+#define GET_CMD_SP(sp) (sp->u.scmd.cmd)
+#define SET_CMD_SP(sp, cmd) (sp->u.scmd.cmd = cmd)
+#define GET_CMD_CTX_SP(sp) (sp->u.scmd.ctx)
+
+#define GET_CMD_SENSE_LEN(sp) \
+ (sp->u.scmd.request_sense_length)
+#define SET_CMD_SENSE_LEN(sp, len) \
+ (sp->u.scmd.request_sense_length = len)
+#define GET_CMD_SENSE_PTR(sp) \
+ (sp->u.scmd.request_sense_ptr)
+#define SET_CMD_SENSE_PTR(sp, ptr) \
+ (sp->u.scmd.request_sense_ptr = ptr)
struct msg_echo_lb {
dma_addr_t send_dma;
@@ -653,8 +663,10 @@ typedef struct {
#define MBC_DIAGNOSTIC_LOOP_BACK 0x45 /* Diagnostic loop back. */
#define MBC_ONLINE_SELF_TEST 0x46 /* Online self-test. */
#define MBC_ENHANCED_GET_PORT_DATABASE 0x47 /* Get port database + login */
+#define MBC_CONFIGURE_VF 0x4b /* Configure VFs */
#define MBC_RESET_LINK_STATUS 0x52 /* Reset Link Error Status */
#define MBC_IOCB_COMMAND_A64 0x54 /* Execute IOCB command (64) */
+#define MBC_PORT_LOGOUT 0x56 /* Port Logout request */
#define MBC_SEND_RNID_ELS 0x57 /* Send RNID ELS request */
#define MBC_SET_RNID_PARAMS 0x59 /* Set RNID parameters */
#define MBC_GET_RNID_PARAMS 0x5a /* Data Rate */
@@ -1709,6 +1721,7 @@ typedef struct fc_port {
uint16_t vp_idx;
uint8_t fc4_type;
+ uint8_t scan_state;
} fc_port_t;
/*
@@ -1761,7 +1774,6 @@ static const char * const port_state_str[] = {
#define GID_PT_CMD 0x1A1
#define GID_PT_REQ_SIZE (16 + 4)
-#define GID_PT_RSP_SIZE (16 + (MAX_FIBRE_DEVICES * 4))
#define GPN_ID_CMD 0x112
#define GPN_ID_REQ_SIZE (16 + 4)
@@ -2051,7 +2063,9 @@ struct ct_sns_rsp {
} ga_nxt;
struct {
- struct ct_sns_gid_pt_data entries[MAX_FIBRE_DEVICES];
+ /* Assume the largest number of targets for the union */
+ struct ct_sns_gid_pt_data
+ entries[MAX_FIBRE_DEVICES_MAX];
} gid_pt;
struct {
@@ -2112,7 +2126,11 @@ struct ct_sns_pkt {
#define GID_PT_SNS_SCMD_LEN 6
#define GID_PT_SNS_CMD_SIZE 28
-#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES * 4 + 16)
+/*
+ * Assume MAX_FIBRE_DEVICES_2100 as these defines are only used with older
+ * adapters.
+ */
+#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES_2100 * 4 + 16)
#define GPN_ID_SNS_SCMD_LEN 6
#define GPN_ID_SNS_CMD_SIZE 28
@@ -2160,7 +2178,6 @@ struct gid_list_info {
uint16_t loop_id; /* ISP23XX -- 6 bytes. */
uint16_t reserved_1; /* ISP24XX -- 8 bytes. */
};
-#define GID_LIST_SIZE (sizeof(struct gid_list_info) * MAX_FIBRE_DEVICES)
/* NPIV */
typedef struct vport_info {
@@ -2261,6 +2278,7 @@ struct isp_operations {
#define QLA_MIDX_DEFAULT 0
#define QLA_MIDX_RSP_Q 1
#define QLA_PCI_MSIX_CONTROL 0xa2
+#define QLA_83XX_PCI_MSIX_CONTROL 0x92
struct scsi_qla_host;
@@ -2341,7 +2359,7 @@ struct qla_statistics {
#define QLA_MQ_SIZE 32
#define QLA_MAX_QUEUES 256
#define ISP_QUE_REG(ha, id) \
- ((ha->mqenable) ? \
+ ((ha->mqenable || IS_QLA83XX(ha)) ? \
((void *)(ha->mqiobase) +\
(QLA_QUE_PAGE * id)) :\
((void *)(ha->iobase)))
@@ -2461,6 +2479,7 @@ struct qla_hw_data {
#define MIN_IOBASE_LEN 0x100
/* Multi queue data structs */
device_reg_t __iomem *mqiobase;
+ device_reg_t __iomem *msixbase;
uint16_t msix_count;
uint8_t mqenable;
struct req_que **req_q_map;
@@ -2485,6 +2504,7 @@ struct qla_hw_data {
atomic_t loop_down_timer; /* loop down timer */
uint8_t link_down_timeout; /* link down timeout */
uint16_t max_loop_id;
+ uint16_t max_fibre_devices; /* Maximum number of targets */
uint16_t fb_rev;
uint16_t min_external_loopid; /* First external loop Id */
@@ -2494,6 +2514,7 @@ struct qla_hw_data {
#define PORT_SPEED_2GB 0x01
#define PORT_SPEED_4GB 0x03
#define PORT_SPEED_8GB 0x04
+#define PORT_SPEED_16GB 0x05
#define PORT_SPEED_10GB 0x13
uint16_t link_data_rate; /* F/W operating speed */
@@ -2515,6 +2536,8 @@ struct qla_hw_data {
#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532
#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432
#define PCI_DEVICE_ID_QLOGIC_ISP8001 0x8001
+#define PCI_DEVICE_ID_QLOGIC_ISP8031 0x8031
+#define PCI_DEVICE_ID_QLOGIC_ISP2031 0x2031
uint32_t device_type;
#define DT_ISP2100 BIT_0
#define DT_ISP2200 BIT_1
@@ -2531,7 +2554,9 @@ struct qla_hw_data {
#define DT_ISP8432 BIT_12
#define DT_ISP8001 BIT_13
#define DT_ISP8021 BIT_14
-#define DT_ISP_LAST (DT_ISP8021 << 1)
+#define DT_ISP2031 BIT_15
+#define DT_ISP8031 BIT_16
+#define DT_ISP_LAST (DT_ISP8031 << 1)
#define DT_T10_PI BIT_25
#define DT_IIDMA BIT_26
@@ -2555,26 +2580,30 @@ struct qla_hw_data {
#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532)
#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432)
#define IS_QLA8001(ha) (DT_MASK(ha) & DT_ISP8001)
+#define IS_QLA81XX(ha) (IS_QLA8001(ha))
#define IS_QLA82XX(ha) (DT_MASK(ha) & DT_ISP8021)
+#define IS_QLA2031(ha) (DT_MASK(ha) & DT_ISP2031)
+#define IS_QLA8031(ha) (DT_MASK(ha) & DT_ISP8031)
#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
IS_QLA6312(ha) || IS_QLA6322(ha))
#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha))
#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha))
#define IS_QLA25XX(ha) (IS_QLA2532(ha))
+#define IS_QLA83XX(ha) (IS_QLA2031(ha) || IS_QLA8031(ha))
#define IS_QLA84XX(ha) (IS_QLA8432(ha))
#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
IS_QLA84XX(ha))
-#define IS_QLA81XX(ha) (IS_QLA8001(ha))
-#define IS_QLA8XXX_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha))
+#define IS_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \
+ IS_QLA8031(ha))
#define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \
IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
- IS_QLA82XX(ha))
-#define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha))
-#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && \
- (ha)->flags.msix_enabled)
-#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha))
-#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha))
+ IS_QLA82XX(ha) || IS_QLA83XX(ha))
+#define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
+ IS_QLA83XX(ha)) && (ha)->flags.msix_enabled)
+#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha))
#define IS_ALOGIO_CAPABLE(ha) (IS_QLA23XX(ha) || IS_FWI2_CAPABLE(ha))
#define IS_T10_PI_CAPABLE(ha) ((ha)->device_type & DT_T10_PI)
@@ -2583,6 +2612,8 @@ struct qla_hw_data {
#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED)
#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001)
#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
+#define IS_CT6_SUPPORTED(ha) ((ha)->device_type & DT_CT6_SUPPORTED)
+#define IS_MQUE_CAPABLE(ha) ((ha)->mqenable || IS_QLA83XX(ha))
/* HBA serial number */
uint8_t serial0;
@@ -2621,10 +2652,6 @@ struct qla_hw_data {
void *sfp_data;
dma_addr_t sfp_data_dma;
- uint8_t *edc_data;
- dma_addr_t edc_data_dma;
- uint16_t edc_data_len;
-
#define XGMAC_DATA_SIZE 4096
void *xgmac_data;
dma_addr_t xgmac_data_dma;
@@ -2653,6 +2680,8 @@ struct qla_hw_data {
void *async_pd;
dma_addr_t async_pd_dma;
+ void *swl;
+
/* These are used by mailbox operations. */
volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT];
@@ -2674,6 +2703,8 @@ struct qla_hw_data {
uint16_t fw_minor_version;
uint16_t fw_subminor_version;
uint16_t fw_attributes;
+ uint16_t fw_attributes_h;
+ uint16_t fw_attributes_ext[2];
uint32_t fw_memory_size;
uint32_t fw_transfer_size;
uint32_t fw_srisc_address;
@@ -2851,7 +2882,6 @@ typedef struct scsi_qla_host {
volatile struct {
uint32_t init_done :1;
uint32_t online :1;
- uint32_t rscn_queue_overflow :1;
uint32_t reset_active :1;
uint32_t management_server_logged_in :1;
@@ -2905,11 +2935,6 @@ typedef struct scsi_qla_host {
- /* RSCN queue. */
- uint32_t rscn_queue[MAX_RSCN_COUNT];
- uint8_t rscn_in_ptr;
- uint8_t rscn_out_ptr;
-
/* Timeout timers. */
uint8_t loop_down_abort_time; /* port down timer */
atomic_t loop_down_timer; /* loop down timer */
@@ -3005,7 +3030,6 @@ typedef struct scsi_qla_host {
#define QLA_ABORTED 0x105
#define QLA_SUSPENDED 0x106
#define QLA_BUSY 0x107
-#define QLA_RSCNS_HANDLED 0x108
#define QLA_ALREADY_REGISTERED 0x109
#define NVRAM_DELAY() udelay(10)
@@ -3021,6 +3045,7 @@ typedef struct scsi_qla_host {
#define OPTROM_SIZE_25XX 0x200000
#define OPTROM_SIZE_81XX 0x400000
#define OPTROM_SIZE_82XX 0x800000
+#define OPTROM_SIZE_83XX 0x1000000
#define OPTROM_BURST_SIZE 0x1000
#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4)
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
index 0b4c2b794c6f..499c74e39ee5 100644
--- a/drivers/scsi/qla2xxx/qla_dfs.c
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -114,7 +114,7 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha)
{
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))
+ if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
goto out;
if (!ha->fce)
goto out;
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
index aa69486dc064..6d7d7758c797 100644
--- a/drivers/scsi/qla2xxx/qla_fw.h
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -1327,6 +1327,11 @@ struct qla_flt_header {
#define FLT_REG_GOLD_FW 0x2f
#define FLT_REG_FCP_PRIO_0 0x87
#define FLT_REG_FCP_PRIO_1 0x88
+#define FLT_REG_FCOE_FW 0xA4
+#define FLT_REG_FCOE_VPD_0 0xA9
+#define FLT_REG_FCOE_NVRAM_0 0xAA
+#define FLT_REG_FCOE_VPD_1 0xAB
+#define FLT_REG_FCOE_NVRAM_1 0xAC
struct qla_flt_region {
uint32_t code;
@@ -1494,6 +1499,11 @@ struct access_chip_rsp_84xx {
#define MBC_GET_XGMAC_STATS 0x7a
#define MBC_GET_DCBX_PARAMS 0x51
+/*
+ * ISP83xx mailbox commands
+ */
+#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */
+
/* Flash access control option field bit definitions */
#define FAC_OPT_FORCE_SEMAPHORE BIT_15
#define FAC_OPT_REQUESTOR_ID BIT_14
@@ -1875,4 +1885,7 @@ struct qla_fcp_prio_cfg {
#define FA_NPIV_CONF0_ADDR_81 0xD1000
#define FA_NPIV_CONF1_ADDR_81 0xD2000
+/* 83XX Flash locations -- occupies second 8MB region. */
+#define FA_FLASH_LAYOUT_ADDR_83 0xFC400
+
#endif
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
index 408679be8fdf..9f065804bd12 100644
--- a/drivers/scsi/qla2xxx/qla_gbl.h
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -71,8 +71,6 @@ extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *,
uint16_t *);
-extern void qla2x00_async_tm_cmd_done(struct scsi_qla_host *, fc_port_t *,
- struct srb_iocb *);
extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *);
extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *);
@@ -156,8 +154,7 @@ extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *);
-extern void qla2x00_sp_compl(struct qla_hw_data *, srb_t *);
-
+extern void qla2x00_sp_free_dma(void *, void *);
extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int);
@@ -205,8 +202,7 @@ extern int
qla2x00_execute_fw(scsi_qla_host_t *, uint32_t);
extern int
-qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *,
- uint16_t *, uint32_t *, uint8_t *, uint32_t *, uint8_t *);
+qla2x00_get_fw_version(scsi_qla_host_t *);
extern int
qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *);
@@ -371,6 +367,9 @@ qla81xx_get_port_config(scsi_qla_host_t *, uint16_t *);
extern int
qla81xx_set_port_config(scsi_qla_host_t *, uint16_t *);
+extern int
+qla2x00_port_logout(scsi_qla_host_t *, struct fc_port *);
+
/*
* Global Function Prototypes in qla_isr.c source file.
*/
@@ -409,8 +408,10 @@ extern void qla2x00_beacon_blink(struct scsi_qla_host *);
extern int qla24xx_beacon_on(struct scsi_qla_host *);
extern int qla24xx_beacon_off(struct scsi_qla_host *);
extern void qla24xx_beacon_blink(struct scsi_qla_host *);
+extern void qla83xx_beacon_blink(struct scsi_qla_host *);
extern int qla82xx_beacon_on(struct scsi_qla_host *);
extern int qla82xx_beacon_off(struct scsi_qla_host *);
+extern int qla83xx_write_remote_reg(struct scsi_qla_host *, uint32_t, uint32_t);
extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *,
uint32_t, uint32_t);
@@ -541,6 +542,10 @@ extern int qla82xx_restart_isp(scsi_qla_host_t *);
/* IOCB related functions */
extern int qla82xx_start_scsi(srb_t *);
+extern void qla2x00_sp_free(void *, void *);
+extern void qla2x00_sp_timeout(unsigned long);
+extern void qla2x00_bsg_job_done(void *, void *, int);
+extern void qla2x00_bsg_sp_free(void *, void *);
/* Interrupt related */
extern irqreturn_t qla82xx_intr_handler(int, void *);
@@ -576,6 +581,8 @@ extern void qla82xx_start_iocbs(scsi_qla_host_t *);
extern int qla82xx_fcoe_ctx_reset(scsi_qla_host_t *);
extern int qla82xx_check_md_needed(scsi_qla_host_t *);
extern void qla82xx_chip_reset_cleanup(scsi_qla_host_t *);
+extern int qla81xx_set_led_config(scsi_qla_host_t *, uint16_t *);
+extern int qla81xx_get_led_config(scsi_qla_host_t *, uint16_t *);
extern int qla82xx_mbx_beacon_ctl(scsi_qla_host_t *, int);
extern char *qdev_state(uint32_t);
extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *);
@@ -589,6 +596,9 @@ extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *,
extern int qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t,
uint16_t *, uint16_t *);
+/* 83xx related functions */
+extern void qla83xx_fw_dump(scsi_qla_host_t *, int);
+
/* Minidump related functions */
extern int qla82xx_md_get_template_size(scsi_qla_host_t *);
extern int qla82xx_md_get_template(scsi_qla_host_t *);
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
index 4aea4ae23300..3128f80441f5 100644
--- a/drivers/scsi/qla2xxx/qla_gs.c
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -240,6 +240,12 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
return (rval);
}
+static inline int
+qla2x00_gid_pt_rsp_size(scsi_qla_host_t *vha)
+{
+ return vha->hw->max_fibre_devices * 4 + 16;
+}
+
/**
* qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command.
* @ha: HA context
@@ -261,20 +267,21 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
struct ct_sns_gid_pt_data *gid_data;
struct qla_hw_data *ha = vha->hw;
+ uint16_t gid_pt_rsp_size;
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_gid_pt(vha, list);
gid_data = NULL;
-
+ gid_pt_rsp_size = qla2x00_gid_pt_rsp_size(vha);
/* Issue GID_PT */
/* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE,
- GID_PT_RSP_SIZE);
+ gid_pt_rsp_size);
/* Prepare CT request */
ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD,
- GID_PT_RSP_SIZE);
+ gid_pt_rsp_size);
ct_rsp = &ha->ct_sns->p.rsp;
/* Prepare CT arguments -- port_type */
@@ -292,7 +299,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
rval = QLA_FUNCTION_FAILED;
} else {
/* Set port IDs in switch info list. */
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
gid_data = &ct_rsp->rsp.gid_pt.entries[i];
list[i].d_id.b.domain = gid_data->port_id[0];
list[i].d_id.b.area = gid_data->port_id[1];
@@ -313,7 +320,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
* single call. Return a failed status, and let GA_NXT handle
* the overload.
*/
- if (i == MAX_FIBRE_DEVICES)
+ if (i == ha->max_fibre_devices)
rval = QLA_FUNCTION_FAILED;
}
@@ -330,7 +337,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
int
qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
- int rval;
+ int rval = QLA_SUCCESS;
uint16_t i;
ms_iocb_entry_t *ms_pkt;
@@ -341,7 +348,7 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_gpn_id(vha, list);
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GPN_ID */
/* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE,
@@ -364,9 +371,11 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
/*EMPTY*/
ql_dbg(ql_dbg_disc, vha, 0x2056,
"GPN_ID issue IOCB failed (%d).\n", rval);
+ break;
} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GPN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
+ break;
} else {
/* Save portname */
memcpy(list[i].port_name,
@@ -391,7 +400,7 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
int
qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
- int rval;
+ int rval = QLA_SUCCESS;
uint16_t i;
struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
@@ -401,7 +410,7 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
if (IS_QLA2100(ha) || IS_QLA2200(ha))
return qla2x00_sns_gnn_id(vha, list);
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GNN_ID */
/* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE,
@@ -424,9 +433,11 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
/*EMPTY*/
ql_dbg(ql_dbg_disc, vha, 0x2057,
"GNN_ID issue IOCB failed (%d).\n", rval);
+ break;
} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GNN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
+ break;
} else {
/* Save nodename */
memcpy(list[i].node_name,
@@ -735,7 +746,7 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len,
static int
qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
{
- int rval;
+ int rval = QLA_SUCCESS;
struct qla_hw_data *ha = vha->hw;
struct sns_cmd_pkt *sns_cmd;
@@ -814,11 +825,14 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
uint16_t i;
uint8_t *entry;
struct sns_cmd_pkt *sns_cmd;
+ uint16_t gid_pt_sns_data_size;
+
+ gid_pt_sns_data_size = qla2x00_gid_pt_rsp_size(vha);
/* Issue GID_PT. */
/* Prepare SNS command request. */
sns_cmd = qla2x00_prep_sns_cmd(vha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
- GID_PT_SNS_DATA_SIZE);
+ gid_pt_sns_data_size);
/* Prepare SNS command arguments -- port_type. */
sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE;
@@ -839,7 +853,7 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
rval = QLA_FUNCTION_FAILED;
} else {
/* Set port IDs in switch info list. */
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
entry = &sns_cmd->p.gid_data[(i * 4) + 16];
list[i].d_id.b.domain = entry[1];
list[i].d_id.b.area = entry[2];
@@ -858,7 +872,7 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
* single call. Return a failed status, and let GA_NXT handle
* the overload.
*/
- if (i == MAX_FIBRE_DEVICES)
+ if (i == ha->max_fibre_devices)
rval = QLA_FUNCTION_FAILED;
}
@@ -877,12 +891,12 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
static int
qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
- int rval;
+ int rval = QLA_SUCCESS;
struct qla_hw_data *ha = vha->hw;
uint16_t i;
struct sns_cmd_pkt *sns_cmd;
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GPN_ID */
/* Prepare SNS command request. */
sns_cmd = qla2x00_prep_sns_cmd(vha, GPN_ID_CMD,
@@ -933,12 +947,12 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
static int
qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
- int rval;
+ int rval = QLA_SUCCESS;
struct qla_hw_data *ha = vha->hw;
uint16_t i;
struct sns_cmd_pkt *sns_cmd;
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GNN_ID */
/* Prepare SNS command request. */
sns_cmd = qla2x00_prep_sns_cmd(vha, GNN_ID_CMD,
@@ -1107,20 +1121,26 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha)
static int
qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
{
- int ret;
+ int ret, rval;
uint16_t mb[MAILBOX_REGISTER_COUNT];
struct qla_hw_data *ha = vha->hw;
ret = QLA_SUCCESS;
if (vha->flags.management_server_logged_in)
return ret;
- ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa,
- mb, BIT_1|BIT_0);
- if (mb[0] != MBS_COMMAND_COMPLETE) {
- ql_dbg(ql_dbg_disc, vha, 0x2024,
- "Failed management_server login: loopid=%x mb[0]=%x "
- "mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x.\n",
- vha->mgmt_svr_loop_id, mb[0], mb[1], mb[2], mb[6], mb[7]);
+ rval = ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff,
+ 0xfa, mb, BIT_1|BIT_0);
+ if (rval != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) {
+ if (rval == QLA_MEMORY_ALLOC_FAILED)
+ ql_dbg(ql_dbg_disc, vha, 0x2085,
+ "Failed management_server login: loopid=%x "
+ "rval=%d\n", vha->mgmt_svr_loop_id, rval);
+ else
+ ql_dbg(ql_dbg_disc, vha, 0x2024,
+ "Failed management_server login: loopid=%x "
+ "mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x.\n",
+ vha->mgmt_svr_loop_id, mb[0], mb[1], mb[2], mb[6],
+ mb[7]);
ret = QLA_FUNCTION_FAILED;
} else
vha->flags.management_server_logged_in = 1;
@@ -1547,7 +1567,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha)
eiter = (struct ct_fdmi_port_attr *) (entries + size);
eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED);
eiter->len = __constant_cpu_to_be16(4 + 4);
- if (IS_QLA8XXX_TYPE(ha))
+ if (IS_CNA_CAPABLE(ha))
eiter->a.sup_speed = __constant_cpu_to_be32(
FDMI_PORT_SPEED_10GB);
else if (IS_QLA25XX(ha))
@@ -1594,6 +1614,10 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha)
eiter->a.cur_speed =
__constant_cpu_to_be32(FDMI_PORT_SPEED_10GB);
break;
+ case PORT_SPEED_16GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_16GB);
+ break;
default:
eiter->a.cur_speed =
__constant_cpu_to_be32(FDMI_PORT_SPEED_UNKNOWN);
@@ -1724,7 +1748,7 @@ qla2x00_fdmi_register(scsi_qla_host_t *vha)
int
qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
{
- int rval;
+ int rval = QLA_SUCCESS;
uint16_t i;
struct qla_hw_data *ha = vha->hw;
ms_iocb_entry_t *ms_pkt;
@@ -1734,7 +1758,7 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
if (!IS_IIDMA_CAPABLE(ha))
return QLA_FUNCTION_FAILED;
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GFPN_ID */
/* Prepare common MS IOCB */
ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE,
@@ -1757,9 +1781,11 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
/*EMPTY*/
ql_dbg(ql_dbg_disc, vha, 0x2023,
"GFPN_ID issue IOCB failed (%d).\n", rval);
+ break;
} else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
"GFPN_ID") != QLA_SUCCESS) {
rval = QLA_FUNCTION_FAILED;
+ break;
} else {
/* Save fabric portname */
memcpy(list[i].fabric_port_name,
@@ -1846,7 +1872,7 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
if (rval)
return rval;
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Issue GFPN_ID */
/* Prepare common MS IOCB */
ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE,
@@ -1947,7 +1973,7 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list)
struct qla_hw_data *ha = vha->hw;
uint8_t fcp_scsi_features = 0;
- for (i = 0; i < MAX_FIBRE_DEVICES; i++) {
+ for (i = 0; i < ha->max_fibre_devices; i++) {
/* Set default FC4 Type as UNKNOWN so the default is to
* Process this port */
list[i].fc4_type = FC4_TYPE_UNKNOWN;
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index 1fa067e053d2..b9465643396b 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -29,7 +29,6 @@ static int qla2x00_configure_loop(scsi_qla_host_t *);
static int qla2x00_configure_local_loop(scsi_qla_host_t *);
static int qla2x00_configure_fabric(scsi_qla_host_t *);
static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *);
-static int qla2x00_device_resync(scsi_qla_host_t *);
static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
uint16_t *);
@@ -41,11 +40,10 @@ static int qla25xx_init_queues(struct qla_hw_data *);
/* SRB Extensions ---------------------------------------------------------- */
-static void
-qla2x00_ctx_sp_timeout(unsigned long __data)
+void
+qla2x00_sp_timeout(unsigned long __data)
{
srb_t *sp = (srb_t *)__data;
- struct srb_ctx *ctx;
struct srb_iocb *iocb;
fc_port_t *fcport = sp->fcport;
struct qla_hw_data *ha = fcport->vha->hw;
@@ -55,79 +53,25 @@ qla2x00_ctx_sp_timeout(unsigned long __data)
spin_lock_irqsave(&ha->hardware_lock, flags);
req = ha->req_q_map[0];
req->outstanding_cmds[sp->handle] = NULL;
- ctx = sp->ctx;
- iocb = ctx->u.iocb_cmd;
+ iocb = &sp->u.iocb_cmd;
iocb->timeout(sp);
- iocb->free(sp);
+ sp->free(fcport->vha, sp);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
-static void
-qla2x00_ctx_sp_free(srb_t *sp)
+void
+qla2x00_sp_free(void *data, void *ptr)
{
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *iocb = ctx->u.iocb_cmd;
- struct scsi_qla_host *vha = sp->fcport->vha;
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *iocb = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
del_timer(&iocb->timer);
- kfree(iocb);
- kfree(ctx);
- mempool_free(sp, sp->fcport->vha->hw->srb_mempool);
+ mempool_free(sp, vha->hw->srb_mempool);
QLA_VHA_MARK_NOT_BUSY(vha);
}
-inline srb_t *
-qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size,
- unsigned long tmo)
-{
- srb_t *sp = NULL;
- struct qla_hw_data *ha = vha->hw;
- struct srb_ctx *ctx;
- struct srb_iocb *iocb;
- uint8_t bail;
-
- QLA_VHA_MARK_BUSY(vha, bail);
- if (bail)
- return NULL;
-
- sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL);
- if (!sp)
- goto done;
- ctx = kzalloc(size, GFP_KERNEL);
- if (!ctx) {
- mempool_free(sp, ha->srb_mempool);
- sp = NULL;
- goto done;
- }
- iocb = kzalloc(sizeof(struct srb_iocb), GFP_KERNEL);
- if (!iocb) {
- mempool_free(sp, ha->srb_mempool);
- sp = NULL;
- kfree(ctx);
- goto done;
- }
-
- memset(sp, 0, sizeof(*sp));
- sp->fcport = fcport;
- sp->ctx = ctx;
- ctx->iocbs = 1;
- ctx->u.iocb_cmd = iocb;
- iocb->free = qla2x00_ctx_sp_free;
-
- init_timer(&iocb->timer);
- if (!tmo)
- goto done;
- iocb->timer.expires = jiffies + tmo * HZ;
- iocb->timer.data = (unsigned long)sp;
- iocb->timer.function = qla2x00_ctx_sp_timeout;
- add_timer(&iocb->timer);
-done:
- if (!sp)
- QLA_VHA_MARK_NOT_BUSY(vha);
- return sp;
-}
-
/* Asynchronous Login/Logout Routines -------------------------------------- */
static inline unsigned long
@@ -149,19 +93,19 @@ qla2x00_get_async_timeout(struct scsi_qla_host *vha)
}
static void
-qla2x00_async_iocb_timeout(srb_t *sp)
+qla2x00_async_iocb_timeout(void *data)
{
+ srb_t *sp = (srb_t *)data;
fc_port_t *fcport = sp->fcport;
- struct srb_ctx *ctx = sp->ctx;
ql_dbg(ql_dbg_disc, fcport->vha, 0x2071,
"Async-%s timeout - hdl=%x portid=%02x%02x%02x.\n",
- ctx->name, sp->handle, fcport->d_id.b.domain, fcport->d_id.b.area,
+ sp->name, sp->handle, fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa);
fcport->flags &= ~FCF_ASYNC_SENT;
- if (ctx->type == SRB_LOGIN_CMD) {
- struct srb_iocb *lio = ctx->u.iocb_cmd;
+ if (sp->type == SRB_LOGIN_CMD) {
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
qla2x00_post_async_logout_work(fcport->vha, fcport, NULL);
/* Retry as needed. */
lio->u.logio.data[0] = MBS_COMMAND_ERROR;
@@ -173,14 +117,16 @@ qla2x00_async_iocb_timeout(srb_t *sp)
}
static void
-qla2x00_async_login_ctx_done(srb_t *sp)
+qla2x00_async_login_sp_done(void *data, void *ptr, int res)
{
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *lio = ctx->u.iocb_cmd;
-
- qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport,
- lio->u.logio.data);
- lio->free(sp);
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags))
+ qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport,
+ lio->u.logio.data);
+ sp->free(sp->fcport->vha, sp);
}
int
@@ -188,22 +134,21 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
uint16_t *data)
{
srb_t *sp;
- struct srb_ctx *ctx;
struct srb_iocb *lio;
int rval;
rval = QLA_FUNCTION_FAILED;
- sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx),
- qla2x00_get_async_timeout(vha) + 2);
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
- ctx = sp->ctx;
- ctx->type = SRB_LOGIN_CMD;
- ctx->name = "login";
- lio = ctx->u.iocb_cmd;
+ sp->type = SRB_LOGIN_CMD;
+ sp->name = "login";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
- lio->done = qla2x00_async_login_ctx_done;
+ sp->done = qla2x00_async_login_sp_done;
lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
lio->u.logio.flags |= SRB_LOGIN_RETRIED;
@@ -219,42 +164,43 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
return rval;
done_free_sp:
- lio->free(sp);
+ sp->free(fcport->vha, sp);
done:
return rval;
}
static void
-qla2x00_async_logout_ctx_done(srb_t *sp)
+qla2x00_async_logout_sp_done(void *data, void *ptr, int res)
{
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *lio = ctx->u.iocb_cmd;
-
- qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport,
- lio->u.logio.data);
- lio->free(sp);
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags))
+ qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport,
+ lio->u.logio.data);
+ sp->free(sp->fcport->vha, sp);
}
int
qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
{
srb_t *sp;
- struct srb_ctx *ctx;
struct srb_iocb *lio;
int rval;
rval = QLA_FUNCTION_FAILED;
- sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx),
- qla2x00_get_async_timeout(vha) + 2);
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
- ctx = sp->ctx;
- ctx->type = SRB_LOGOUT_CMD;
- ctx->name = "logout";
- lio = ctx->u.iocb_cmd;
+ sp->type = SRB_LOGOUT_CMD;
+ sp->name = "logout";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
- lio->done = qla2x00_async_logout_ctx_done;
+ sp->done = qla2x00_async_logout_sp_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
goto done_free_sp;
@@ -266,20 +212,22 @@ qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
return rval;
done_free_sp:
- lio->free(sp);
+ sp->free(fcport->vha, sp);
done:
return rval;
}
static void
-qla2x00_async_adisc_ctx_done(srb_t *sp)
+qla2x00_async_adisc_sp_done(void *data, void *ptr, int res)
{
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *lio = ctx->u.iocb_cmd;
-
- qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport,
- lio->u.logio.data);
- lio->free(sp);
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags))
+ qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport,
+ lio->u.logio.data);
+ sp->free(sp->fcport->vha, sp);
}
int
@@ -287,22 +235,21 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
uint16_t *data)
{
srb_t *sp;
- struct srb_ctx *ctx;
struct srb_iocb *lio;
int rval;
rval = QLA_FUNCTION_FAILED;
- sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx),
- qla2x00_get_async_timeout(vha) + 2);
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
- ctx = sp->ctx;
- ctx->type = SRB_ADISC_CMD;
- ctx->name = "adisc";
- lio = ctx->u.iocb_cmd;
+ sp->type = SRB_ADISC_CMD;
+ sp->name = "adisc";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ lio = &sp->u.iocb_cmd;
lio->timeout = qla2x00_async_iocb_timeout;
- lio->done = qla2x00_async_adisc_ctx_done;
+ sp->done = qla2x00_async_adisc_sp_done;
if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
lio->u.logio.flags |= SRB_LOGIN_RETRIED;
rval = qla2x00_start_sp(sp);
@@ -316,46 +263,62 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
return rval;
done_free_sp:
- lio->free(sp);
+ sp->free(fcport->vha, sp);
done:
return rval;
}
static void
-qla2x00_async_tm_cmd_ctx_done(srb_t *sp)
+qla2x00_async_tm_cmd_done(void *data, void *ptr, int res)
{
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *iocb = (struct srb_iocb *)ctx->u.iocb_cmd;
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *iocb = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+ uint32_t flags;
+ uint16_t lun;
+ int rval;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags)) {
+ flags = iocb->u.tmf.flags;
+ lun = (uint16_t)iocb->u.tmf.lun;
+
+ /* Issue Marker IOCB */
+ rval = qla2x00_marker(vha, vha->hw->req_q_map[0],
+ vha->hw->rsp_q_map[0], sp->fcport->loop_id, lun,
+ flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID);
- qla2x00_async_tm_cmd_done(sp->fcport->vha, sp->fcport, iocb);
- iocb->free(sp);
+ if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8030,
+ "TM IOCB failed (%x).\n", rval);
+ }
+ }
+ sp->free(sp->fcport->vha, sp);
}
int
-qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun,
+qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t tm_flags, uint32_t lun,
uint32_t tag)
{
struct scsi_qla_host *vha = fcport->vha;
srb_t *sp;
- struct srb_ctx *ctx;
struct srb_iocb *tcf;
int rval;
rval = QLA_FUNCTION_FAILED;
- sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx),
- qla2x00_get_async_timeout(vha) + 2);
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
if (!sp)
goto done;
- ctx = sp->ctx;
- ctx->type = SRB_TM_CMD;
- ctx->name = "tmf";
- tcf = ctx->u.iocb_cmd;
- tcf->u.tmf.flags = flags;
+ sp->type = SRB_TM_CMD;
+ sp->name = "tmf";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ tcf = &sp->u.iocb_cmd;
+ tcf->u.tmf.flags = tm_flags;
tcf->u.tmf.lun = lun;
tcf->u.tmf.data = tag;
tcf->timeout = qla2x00_async_iocb_timeout;
- tcf->done = qla2x00_async_tm_cmd_ctx_done;
+ sp->done = qla2x00_async_tm_cmd_done;
rval = qla2x00_start_sp(sp);
if (rval != QLA_SUCCESS)
@@ -368,7 +331,7 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun,
return rval;
done_free_sp:
- tcf->free(sp);
+ sp->free(fcport->vha, sp);
done:
return rval;
}
@@ -387,6 +350,13 @@ qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
* requests.
*/
rval = qla2x00_get_port_database(vha, fcport, 0);
+ if (rval == QLA_NOT_LOGGED_IN) {
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ fcport->flags |= FCF_LOGIN_NEEDED;
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ break;
+ }
+
if (rval != QLA_SUCCESS) {
qla2x00_post_async_logout_work(vha, fcport, NULL);
qla2x00_post_async_login_work(vha, fcport, NULL);
@@ -452,30 +422,6 @@ qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport,
return;
}
-void
-qla2x00_async_tm_cmd_done(struct scsi_qla_host *vha, fc_port_t *fcport,
- struct srb_iocb *iocb)
-{
- int rval;
- uint32_t flags;
- uint16_t lun;
-
- flags = iocb->u.tmf.flags;
- lun = (uint16_t)iocb->u.tmf.lun;
-
- /* Issue Marker IOCB */
- rval = qla2x00_marker(vha, vha->hw->req_q_map[0],
- vha->hw->rsp_q_map[0], fcport->loop_id, lun,
- flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID);
-
- if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) {
- ql_dbg(ql_dbg_taskm, vha, 0x8030,
- "TM IOCB failed (%x).\n", rval);
- }
-
- return;
-}
-
/****************************************************************************/
/* QLogic ISP2x00 Hardware Support Functions. */
/****************************************************************************/
@@ -969,6 +915,9 @@ qla81xx_reset_mpi(scsi_qla_host_t *vha)
{
uint16_t mb[4] = {0x1010, 0, 1, 0};
+ if (!IS_QLA81XX(vha->hw))
+ return QLA_SUCCESS;
+
return qla81xx_write_mpi_register(vha, mb);
}
@@ -1262,7 +1211,9 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
mem_size = (ha->fw_memory_size - 0x11000 + 1) *
sizeof(uint16_t);
} else if (IS_FWI2_CAPABLE(ha)) {
- if (IS_QLA81XX(ha))
+ if (IS_QLA83XX(ha))
+ fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem);
+ else if (IS_QLA81XX(ha))
fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem);
else if (IS_QLA25XX(ha))
fixed_size = offsetof(struct qla25xx_fw_dump, ext_mem);
@@ -1270,10 +1221,20 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
fixed_size = offsetof(struct qla24xx_fw_dump, ext_mem);
mem_size = (ha->fw_memory_size - 0x100000 + 1) *
sizeof(uint32_t);
- if (ha->mqenable)
- mq_size = sizeof(struct qla2xxx_mq_chain);
+ if (ha->mqenable) {
+ if (!IS_QLA83XX(ha))
+ mq_size = sizeof(struct qla2xxx_mq_chain);
+ /*
+ * Allocate maximum buffer size for all queues.
+ * Resizing must be done at end-of-dump processing.
+ */
+ mq_size += ha->max_req_queues *
+ (req->length * sizeof(request_t));
+ mq_size += ha->max_rsp_queues *
+ (rsp->length * sizeof(response_t));
+ }
/* Allocate memory for Fibre Channel Event Buffer. */
- if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))
+ if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
goto try_eft;
tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma,
@@ -1484,17 +1445,8 @@ enable_82xx_npiv:
fw_major_version = ha->fw_major_version;
if (IS_QLA82XX(ha))
qla82xx_check_md_needed(vha);
- else {
- rval = qla2x00_get_fw_version(vha,
- &ha->fw_major_version,
- &ha->fw_minor_version,
- &ha->fw_subminor_version,
- &ha->fw_attributes,
- &ha->fw_memory_size,
- ha->mpi_version,
- &ha->mpi_capabilities,
- ha->phy_version);
- }
+ else
+ rval = qla2x00_get_fw_version(vha);
if (rval != QLA_SUCCESS)
goto failed;
ha->flags.npiv_supported = 0;
@@ -1535,6 +1487,9 @@ enable_82xx_npiv:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
+ if (IS_QLA83XX(ha))
+ goto skip_fac_check;
+
if (rval == QLA_SUCCESS && IS_FAC_REQUIRED(ha)) {
uint32_t size;
@@ -1547,6 +1502,11 @@ enable_82xx_npiv:
"Unsupported FAC firmware (%d.%02d.%02d).\n",
ha->fw_major_version, ha->fw_minor_version,
ha->fw_subminor_version);
+skip_fac_check:
+ if (IS_QLA83XX(ha)) {
+ ha->flags.fac_supported = 0;
+ rval = QLA_SUCCESS;
+ }
}
}
failed:
@@ -1725,7 +1685,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
struct req_que *req = ha->req_q_map[0];
struct rsp_que *rsp = ha->rsp_q_map[0];
-/* Setup ring parameters in initialization control block. */
+ /* Setup ring parameters in initialization control block. */
icb = (struct init_cb_24xx *)ha->init_cb;
icb->request_q_outpointer = __constant_cpu_to_le16(0);
icb->response_q_inpointer = __constant_cpu_to_le16(0);
@@ -1736,7 +1696,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
- if (ha->mqenable) {
+ if (ha->mqenable || IS_QLA83XX(ha)) {
icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS);
icb->rid = __constant_cpu_to_le16(rid);
if (ha->flags.msix_enabled) {
@@ -1756,7 +1716,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha)
__constant_cpu_to_le32(BIT_18);
/* Use Disable MSIX Handshake mode for capable adapters */
- if (IS_MSIX_NACK_CAPABLE(ha)) {
+ if ((ha->fw_attributes & BIT_6) && (IS_MSIX_NACK_CAPABLE(ha)) &&
+ (ha->flags.msix_enabled)) {
icb->firmware_options_2 &=
__constant_cpu_to_le32(~BIT_22);
ha->flags.disable_msix_handshake = 1;
@@ -1800,7 +1761,6 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
struct req_que *req;
struct rsp_que *rsp;
- struct scsi_qla_host *vp;
struct mid_init_cb_24xx *mid_init_cb =
(struct mid_init_cb_24xx *) ha->init_cb;
@@ -1831,11 +1791,6 @@ qla2x00_init_rings(scsi_qla_host_t *vha)
}
spin_lock(&ha->vport_slock);
- /* Clear RSCN queue. */
- list_for_each_entry(vp, &ha->vp_list, list) {
- vp->rscn_in_ptr = 0;
- vp->rscn_out_ptr = 0;
- }
spin_unlock(&ha->vport_slock);
@@ -2028,7 +1983,7 @@ qla2x00_configure_hba(scsi_qla_host_t *vha)
&loop_id, &al_pa, &area, &domain, &topo, &sw_cap);
if (rval != QLA_SUCCESS) {
if (LOOP_TRANSITION(vha) || atomic_read(&ha->loop_down_timer) ||
- IS_QLA8XXX_TYPE(ha) ||
+ IS_CNA_CAPABLE(ha) ||
(rval == QLA_COMMAND_ERROR && loop_id == 0x7)) {
ql_dbg(ql_dbg_disc, vha, 0x2008,
"Loop is in a transition state.\n");
@@ -2120,7 +2075,7 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
uint16_t index;
struct qla_hw_data *ha = vha->hw;
int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
- !IS_QLA8XXX_TYPE(ha);
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha);
if (memcmp(model, BINZERO, len) != 0) {
strncpy(ha->model_number, model, len);
@@ -2596,13 +2551,11 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
if (ha->current_topology == ISP_CFG_FL &&
(test_bit(LOCAL_LOOP_UPDATE, &flags))) {
- vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
} else if (ha->current_topology == ISP_CFG_F &&
(test_bit(LOCAL_LOOP_UPDATE, &flags))) {
- vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
clear_bit(LOCAL_LOOP_UPDATE, &flags);
@@ -2612,7 +2565,6 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
} else if (!vha->flags.online ||
(test_bit(ABORT_ISP_ACTIVE, &flags))) {
- vha->flags.rscn_queue_overflow = 1;
set_bit(RSCN_UPDATE, &flags);
set_bit(LOCAL_LOOP_UPDATE, &flags);
}
@@ -2622,8 +2574,7 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
ql_dbg(ql_dbg_disc, vha, 0x2015,
"Loop resync needed, failing.\n");
rval = QLA_FUNCTION_FAILED;
- }
- else
+ } else
rval = qla2x00_configure_local_loop(vha);
}
@@ -2662,8 +2613,6 @@ qla2x00_configure_loop(scsi_qla_host_t *vha)
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
if (test_bit(RSCN_UPDATE, &save_flags)) {
set_bit(RSCN_UPDATE, &vha->dpc_flags);
- if (!IS_ALOGIO_CAPABLE(ha))
- vha->flags.rscn_queue_overflow = 1;
}
}
@@ -2699,7 +2648,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
found_devs = 0;
new_fcport = NULL;
- entries = MAX_FIBRE_DEVICES;
+ entries = MAX_FIBRE_DEVICES_LOOP;
ql_dbg(ql_dbg_disc, vha, 0x2016,
"Getting FCAL position map.\n");
@@ -2707,7 +2656,7 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha)
qla2x00_get_fcal_position_map(vha, NULL);
/* Get list of logged in devices. */
- memset(ha->gid_list, 0, GID_LIST_SIZE);
+ memset(ha->gid_list, 0, qla2x00_gid_list_size(ha));
rval = qla2x00_get_id_list(vha, ha->gid_list, ha->gid_list_dma,
&entries);
if (rval != QLA_SUCCESS)
@@ -2971,7 +2920,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
static int
qla2x00_configure_fabric(scsi_qla_host_t *vha)
{
- int rval, rval2;
+ int rval;
fc_port_t *fcport, *fcptemp;
uint16_t next_loopid;
uint16_t mb[MAILBOX_REGISTER_COUNT];
@@ -2995,12 +2944,6 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
}
vha->device_flags |= SWITCH_FOUND;
- /* Mark devices that need re-synchronization. */
- rval2 = qla2x00_device_resync(vha);
- if (rval2 == QLA_RSCNS_HANDLED) {
- /* No point doing the scan, just continue. */
- return (QLA_SUCCESS);
- }
do {
/* FDMI support. */
if (ql2xfdmienable &&
@@ -3012,8 +2955,12 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
loop_id = NPH_SNS;
else
loop_id = SIMPLE_NAME_SERVER;
- ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff,
- 0xfc, mb, BIT_1 | BIT_0);
+ rval = ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff,
+ 0xfc, mb, BIT_1|BIT_0);
+ if (rval != QLA_SUCCESS) {
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ return rval;
+ }
if (mb[0] != MBS_COMMAND_COMPLETE) {
ql_dbg(ql_dbg_disc, vha, 0x2042,
"Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x "
@@ -3044,6 +2991,13 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
}
}
+#define QLA_FCPORT_SCAN 1
+#define QLA_FCPORT_FOUND 2
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ fcport->scan_state = QLA_FCPORT_SCAN;
+ }
+
rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
if (rval != QLA_SUCCESS)
break;
@@ -3059,7 +3013,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha)
if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
continue;
- if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) {
+ if (fcport->scan_state == QLA_FCPORT_SCAN &&
+ atomic_read(&fcport->state) == FCS_ONLINE) {
qla2x00_mark_device_lost(vha, fcport,
ql2xplogiabsentdevice, 0);
if (fcport->loop_id != FC_NO_LOOP_ID &&
@@ -3184,20 +3139,21 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
rval = QLA_SUCCESS;
/* Try GID_PT to get device list, else GAN. */
- swl = kcalloc(MAX_FIBRE_DEVICES, sizeof(sw_info_t), GFP_KERNEL);
+ if (!ha->swl)
+ ha->swl = kcalloc(ha->max_fibre_devices, sizeof(sw_info_t),
+ GFP_KERNEL);
+ swl = ha->swl;
if (!swl) {
/*EMPTY*/
ql_dbg(ql_dbg_disc, vha, 0x2054,
"GID_PT allocations failed, fallback on GA_NXT.\n");
} else {
+ memset(swl, 0, ha->max_fibre_devices * sizeof(sw_info_t));
if (qla2x00_gid_pt(vha, swl) != QLA_SUCCESS) {
- kfree(swl);
swl = NULL;
} else if (qla2x00_gpn_id(vha, swl) != QLA_SUCCESS) {
- kfree(swl);
swl = NULL;
} else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) {
- kfree(swl);
swl = NULL;
} else if (ql2xiidmaenable &&
qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) {
@@ -3215,7 +3171,6 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
if (new_fcport == NULL) {
ql_log(ql_log_warn, vha, 0x205e,
"Failed to allocate memory for fcport.\n");
- kfree(swl);
return (QLA_MEMORY_ALLOC_FAILED);
}
new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
@@ -3332,6 +3287,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
WWN_SIZE))
continue;
+ fcport->scan_state = QLA_FCPORT_FOUND;
+
found++;
/* Update port state. */
@@ -3368,6 +3325,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
fcport->flags |= FCF_LOGIN_NEEDED;
if (fcport->loop_id != FC_NO_LOOP_ID &&
(fcport->flags & FCF_FCP2_DEVICE) == 0 &&
+ (fcport->flags & FCF_ASYNC_SENT) == 0 &&
fcport->port_type != FCT_INITIATOR &&
fcport->port_type != FCT_BROADCAST) {
ha->isp_ops->fabric_logout(vha, fcport->loop_id,
@@ -3390,14 +3348,12 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
if (new_fcport == NULL) {
ql_log(ql_log_warn, vha, 0x2066,
"Memory allocation failed for fcport.\n");
- kfree(swl);
return (QLA_MEMORY_ALLOC_FAILED);
}
new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
new_fcport->d_id.b24 = nxt_d_id.b24;
}
- kfree(swl);
kfree(new_fcport);
return (rval);
@@ -3470,6 +3426,9 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
/* If not in use then it is free to use. */
if (!found) {
+ ql_dbg(ql_dbg_disc, dev->vha, 0x2086,
+ "Assigning new loopid=%x, portid=%x.\n",
+ dev->loop_id, dev->d_id.b24);
break;
}
@@ -3488,110 +3447,6 @@ qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
}
/*
- * qla2x00_device_resync
- * Marks devices in the database that needs resynchronization.
- *
- * Input:
- * ha = adapter block pointer.
- *
- * Context:
- * Kernel context.
- */
-static int
-qla2x00_device_resync(scsi_qla_host_t *vha)
-{
- int rval;
- uint32_t mask;
- fc_port_t *fcport;
- uint32_t rscn_entry;
- uint8_t rscn_out_iter;
- uint8_t format;
- port_id_t d_id = {};
-
- rval = QLA_RSCNS_HANDLED;
-
- while (vha->rscn_out_ptr != vha->rscn_in_ptr ||
- vha->flags.rscn_queue_overflow) {
-
- rscn_entry = vha->rscn_queue[vha->rscn_out_ptr];
- format = MSB(MSW(rscn_entry));
- d_id.b.domain = LSB(MSW(rscn_entry));
- d_id.b.area = MSB(LSW(rscn_entry));
- d_id.b.al_pa = LSB(LSW(rscn_entry));
-
- ql_dbg(ql_dbg_disc, vha, 0x2020,
- "RSCN queue entry[%d] = [%02x/%02x%02x%02x].\n",
- vha->rscn_out_ptr, format, d_id.b.domain, d_id.b.area,
- d_id.b.al_pa);
-
- vha->rscn_out_ptr++;
- if (vha->rscn_out_ptr == MAX_RSCN_COUNT)
- vha->rscn_out_ptr = 0;
-
- /* Skip duplicate entries. */
- for (rscn_out_iter = vha->rscn_out_ptr;
- !vha->flags.rscn_queue_overflow &&
- rscn_out_iter != vha->rscn_in_ptr;
- rscn_out_iter = (rscn_out_iter ==
- (MAX_RSCN_COUNT - 1)) ? 0: rscn_out_iter + 1) {
-
- if (rscn_entry != vha->rscn_queue[rscn_out_iter])
- break;
-
- ql_dbg(ql_dbg_disc, vha, 0x2021,
- "Skipping duplicate RSCN queue entry found at "
- "[%d].\n", rscn_out_iter);
-
- vha->rscn_out_ptr = rscn_out_iter;
- }
-
- /* Queue overflow, set switch default case. */
- if (vha->flags.rscn_queue_overflow) {
- ql_dbg(ql_dbg_disc, vha, 0x2022,
- "device_resync: rscn overflow.\n");
-
- format = 3;
- vha->flags.rscn_queue_overflow = 0;
- }
-
- switch (format) {
- case 0:
- mask = 0xffffff;
- break;
- case 1:
- mask = 0xffff00;
- break;
- case 2:
- mask = 0xff0000;
- break;
- default:
- mask = 0x0;
- d_id.b24 = 0;
- vha->rscn_out_ptr = vha->rscn_in_ptr;
- break;
- }
-
- rval = QLA_SUCCESS;
-
- list_for_each_entry(fcport, &vha->vp_fcports, list) {
- if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
- (fcport->d_id.b24 & mask) != d_id.b24 ||
- fcport->port_type == FCT_BROADCAST)
- continue;
-
- if (atomic_read(&fcport->state) == FCS_ONLINE) {
- if (format != 3 ||
- fcport->port_type != FCT_INITIATOR) {
- qla2x00_mark_device_lost(vha, fcport,
- 0, 0);
- }
- }
- }
- }
- return (rval);
-}
-
-/*
* qla2x00_fabric_dev_login
* Login fabric target device and update FC port database.
*
@@ -3644,6 +3499,9 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
} else {
qla2x00_update_fcport(vha, fcport);
}
+ } else {
+ /* Retry Login. */
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
}
return (rval);
@@ -3684,9 +3542,12 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
fcport->d_id.b.area, fcport->d_id.b.al_pa);
/* Login fcport on switch. */
- ha->isp_ops->fabric_login(vha, fcport->loop_id,
+ rval = ha->isp_ops->fabric_login(vha, fcport->loop_id,
fcport->d_id.b.domain, fcport->d_id.b.area,
fcport->d_id.b.al_pa, mb, BIT_0);
+ if (rval != QLA_SUCCESS) {
+ return rval;
+ }
if (mb[0] == MBS_PORT_ID_USED) {
/*
* Device has another loop ID. The firmware team
@@ -4100,15 +3961,8 @@ qla2x00_abort_isp(scsi_qla_host_t *vha)
ha->isp_abort_cnt = 0;
clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
- if (IS_QLA81XX(ha))
- qla2x00_get_fw_version(vha,
- &ha->fw_major_version,
- &ha->fw_minor_version,
- &ha->fw_subminor_version,
- &ha->fw_attributes, &ha->fw_memory_size,
- ha->mpi_version, &ha->mpi_capabilities,
- ha->phy_version);
-
+ if (IS_QLA81XX(ha) || IS_QLA8031(ha))
+ qla2x00_get_fw_version(vha);
if (ha->fce) {
ha->flags.fce_enabled = 1;
memset(ha->fce, 0,
@@ -4974,7 +4828,6 @@ try_blob_fw:
ql_log(ql_log_info, vha, 0x009a, "Update operational firmware.\n");
ha->flags.running_gold_fw = 1;
-
return rval;
}
@@ -5009,6 +4862,7 @@ int
qla24xx_configure_vhba(scsi_qla_host_t *vha)
{
int rval = QLA_SUCCESS;
+ int rval2;
uint16_t mb[MAILBOX_REGISTER_COUNT];
struct qla_hw_data *ha = vha->hw;
struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
@@ -5033,12 +4887,18 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha)
vha->flags.management_server_logged_in = 0;
/* Login to SNS first */
- ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb, BIT_1);
- if (mb[0] != MBS_COMMAND_COMPLETE) {
- ql_dbg(ql_dbg_init, vha, 0x0103,
- "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x "
- "mb[6]=%x mb[7]=%x.\n",
- NPH_SNS, mb[0], mb[1], mb[2], mb[6], mb[7]);
+ rval2 = ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb,
+ BIT_1);
+ if (rval2 != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) {
+ if (rval2 == QLA_MEMORY_ALLOC_FAILED)
+ ql_dbg(ql_dbg_init, vha, 0x0120,
+ "Failed SNS login: loop_id=%x, rval2=%d\n",
+ NPH_SNS, rval2);
+ else
+ ql_dbg(ql_dbg_init, vha, 0x0103,
+ "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
+ "mb[2]=%x mb[6]=%x mb[7]=%x.\n",
+ NPH_SNS, mb[0], mb[1], mb[2], mb[6], mb[7]);
return (QLA_FUNCTION_FAILED);
}
@@ -5214,10 +5074,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
nv->reset_delay = 5;
nv->max_luns_per_target = __constant_cpu_to_le16(128);
nv->port_down_retry_count = __constant_cpu_to_le16(30);
- nv->link_down_timeout = __constant_cpu_to_le16(30);
+ nv->link_down_timeout = __constant_cpu_to_le16(180);
nv->enode_mac[0] = 0x00;
- nv->enode_mac[1] = 0x02;
- nv->enode_mac[2] = 0x03;
+ nv->enode_mac[1] = 0xC0;
+ nv->enode_mac[2] = 0xDD;
nv->enode_mac[3] = 0x04;
nv->enode_mac[4] = 0x05;
nv->enode_mac[5] = 0x06 + ha->port_no;
@@ -5248,9 +5108,9 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
memcpy(icb->enode_mac, nv->enode_mac, sizeof(icb->enode_mac));
/* Some boards (with valid NVRAMs) still have NULL enode_mac!! */
if (!memcmp(icb->enode_mac, "\0\0\0\0\0\0", sizeof(icb->enode_mac))) {
- icb->enode_mac[0] = 0x01;
- icb->enode_mac[1] = 0x02;
- icb->enode_mac[2] = 0x03;
+ icb->enode_mac[0] = 0x00;
+ icb->enode_mac[1] = 0xC0;
+ icb->enode_mac[2] = 0xDD;
icb->enode_mac[3] = 0x04;
icb->enode_mac[4] = 0x05;
icb->enode_mac[5] = 0x06 + ha->port_no;
@@ -5353,6 +5213,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha)
if (ql2xloginretrycount)
ha->login_retry_count = ql2xloginretrycount;
+ /* if not running MSI-X we need handshaking on interrupts */
+ if (!vha->hw->flags.msix_enabled && IS_QLA83XX(ha))
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22);
+
/* Enable ZIO. */
if (!vha->flags.init_done) {
ha->zio_mode = le32_to_cpu(icb->firmware_options_2) &
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
index 7cc4f36cd539..6e457643c639 100644
--- a/drivers/scsi/qla2xxx/qla_inline.h
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -72,16 +72,19 @@ static inline void
qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp)
{
struct dsd_dma *dsd_ptr, *tdsd_ptr;
+ struct crc_context *ctx;
+
+ ctx = (struct crc_context *)GET_CMD_CTX_SP(sp);
/* clean up allocated prev pool */
list_for_each_entry_safe(dsd_ptr, tdsd_ptr,
- &((struct crc_context *)sp->ctx)->dsd_list, list) {
+ &ctx->dsd_list, list) {
dma_pool_free(ha->dl_dma_pool, dsd_ptr->dsd_addr,
dsd_ptr->dsd_list_dma);
list_del(&dsd_ptr->list);
kfree(dsd_ptr);
}
- INIT_LIST_HEAD(&((struct crc_context *)sp->ctx)->dsd_list);
+ INIT_LIST_HEAD(&ctx->dsd_list);
}
static inline void
@@ -113,8 +116,7 @@ qla2x00_hba_err_chk_enabled(srb_t *sp)
return 0;
*
*/
-
- switch (scsi_get_prot_op(sp->cmd)) {
+ switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
case SCSI_PROT_READ_STRIP:
case SCSI_PROT_WRITE_INSERT:
if (ql2xenablehba_err_chk >= 1)
@@ -144,3 +146,44 @@ qla2x00_reset_active(scsi_qla_host_t *vha)
test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
}
+
+static inline srb_t *
+qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag)
+{
+ srb_t *sp = NULL;
+ struct qla_hw_data *ha = vha->hw;
+ uint8_t bail;
+
+ QLA_VHA_MARK_BUSY(vha, bail);
+ if (unlikely(bail))
+ return NULL;
+
+ sp = mempool_alloc(ha->srb_mempool, flag);
+ if (!sp)
+ goto done;
+
+ memset(sp, 0, sizeof(*sp));
+ sp->fcport = fcport;
+ sp->iocbs = 1;
+done:
+ if (!sp)
+ QLA_VHA_MARK_NOT_BUSY(vha);
+ return sp;
+}
+
+static inline void
+qla2x00_init_timer(srb_t *sp, unsigned long tmo)
+{
+ init_timer(&sp->u.iocb_cmd.timer);
+ sp->u.iocb_cmd.timer.expires = jiffies + tmo * HZ;
+ sp->u.iocb_cmd.timer.data = (unsigned long)sp;
+ sp->u.iocb_cmd.timer.function = qla2x00_sp_timeout;
+ add_timer(&sp->u.iocb_cmd.timer);
+ sp->free = qla2x00_sp_free;
+}
+
+static inline int
+qla2x00_gid_list_size(struct qla_hw_data *ha)
+{
+ return sizeof(struct gid_list_info) * ha->max_fibre_devices;
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
index 55a96761b5a4..eac950924497 100644
--- a/drivers/scsi/qla2xxx/qla_iocb.c
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -22,18 +22,19 @@ static inline uint16_t
qla2x00_get_cmd_direction(srb_t *sp)
{
uint16_t cflags;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
cflags = 0;
/* Set transfer direction */
- if (sp->cmd->sc_data_direction == DMA_TO_DEVICE) {
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
cflags = CF_WRITE;
sp->fcport->vha->hw->qla_stats.output_bytes +=
- scsi_bufflen(sp->cmd);
- } else if (sp->cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ scsi_bufflen(cmd);
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
cflags = CF_READ;
sp->fcport->vha->hw->qla_stats.input_bytes +=
- scsi_bufflen(sp->cmd);
+ scsi_bufflen(cmd);
}
return (cflags);
}
@@ -143,12 +144,13 @@ qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
static inline int
qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts)
{
- uint8_t guard = scsi_host_get_guard(sp->cmd->device->host);
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ uint8_t guard = scsi_host_get_guard(cmd->device->host);
/* We only support T10 DIF right now */
if (guard != SHOST_DIX_GUARD_CRC) {
ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3007,
- "Unsupported guard: %d for cmd=%p.\n", guard, sp->cmd);
+ "Unsupported guard: %d for cmd=%p.\n", guard, cmd);
return 0;
}
@@ -156,7 +158,7 @@ qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts)
*fw_prot_opts = 0;
/* Translate SCSI opcode to a protection opcode */
- switch (scsi_get_prot_op(sp->cmd)) {
+ switch (scsi_get_prot_op(cmd)) {
case SCSI_PROT_READ_STRIP:
*fw_prot_opts |= PO_MODE_DIF_REMOVE;
break;
@@ -180,7 +182,7 @@ qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts)
break;
}
- return scsi_prot_sg_count(sp->cmd);
+ return scsi_prot_sg_count(cmd);
}
/*
@@ -201,7 +203,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
struct scatterlist *sg;
int i;
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
/* Update entry type to indicate Command Type 2 IOCB */
*((uint32_t *)(&cmd_pkt->entry_type)) =
@@ -259,7 +261,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
struct scatterlist *sg;
int i;
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
/* Update entry type to indicate Command Type 3 IOCB */
*((uint32_t *)(&cmd_pkt->entry_type)) =
@@ -333,7 +335,7 @@ qla2x00_start_scsi(srb_t *sp)
vha = sp->fcport->vha;
ha = vha->hw;
reg = &ha->iobase->isp;
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
req = ha->req_q_map[0];
rsp = ha->rsp_q_map[0];
/* So we know we haven't pci_map'ed anything yet */
@@ -391,7 +393,7 @@ qla2x00_start_scsi(srb_t *sp)
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
sp->handle = handle;
- sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
cmd_pkt = (cmd_entry_t *)req->ring_ptr;
@@ -403,7 +405,7 @@ qla2x00_start_scsi(srb_t *sp)
/* Set target ID and LUN number*/
SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id);
- cmd_pkt->lun = cpu_to_le16(sp->cmd->device->lun);
+ cmd_pkt->lun = cpu_to_le16(cmd->device->lun);
/* Update tagged queuing modifier */
if (scsi_populate_tag_msg(cmd, tag)) {
@@ -473,7 +475,6 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
{
struct qla_hw_data *ha = vha->hw;
device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
- struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
if (IS_QLA82XX(ha)) {
qla82xx_start_iocbs(vha);
@@ -487,9 +488,9 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
req->ring_ptr++;
/* Set chip new ring index. */
- if (ha->mqenable) {
- WRT_REG_DWORD(&reg->isp25mq.req_q_in, req->ring_index);
- RD_REG_DWORD(&ioreg->hccr);
+ if (ha->mqenable || IS_QLA83XX(ha)) {
+ WRT_REG_DWORD(req->req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr);
} else if (IS_FWI2_CAPABLE(ha)) {
WRT_REG_DWORD(&reg->isp24.req_q_in, req->ring_index);
RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
@@ -609,7 +610,7 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt,
struct dsd_dma *dsd_ptr;
struct ct6_dsd *ctx;
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
/* Update entry type to indicate Command Type 3 IOCB */
*((uint32_t *)(&cmd_pkt->entry_type)) =
@@ -636,7 +637,7 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt,
}
cur_seg = scsi_sglist(cmd);
- ctx = sp->ctx;
+ ctx = GET_CMD_CTX_SP(sp);
while (tot_dsds) {
avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ?
@@ -725,7 +726,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
int i;
struct req_que *req;
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
/* Update entry type to indicate Command Type 3 IOCB */
*((uint32_t *)(&cmd_pkt->entry_type)) =
@@ -745,12 +746,12 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
cmd_pkt->task_mgmt_flags =
__constant_cpu_to_le16(TMF_WRITE_DATA);
sp->fcport->vha->hw->qla_stats.output_bytes +=
- scsi_bufflen(sp->cmd);
+ scsi_bufflen(cmd);
} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
cmd_pkt->task_mgmt_flags =
__constant_cpu_to_le16(TMF_READ_DATA);
sp->fcport->vha->hw->qla_stats.input_bytes +=
- scsi_bufflen(sp->cmd);
+ scsi_bufflen(cmd);
}
/* One DSD is available in the Command Type 3 IOCB */
@@ -797,7 +798,7 @@ static inline void
qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt,
unsigned int protcnt)
{
- struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
scsi_qla_host_t *vha = shost_priv(cmd->device->host);
switch (scsi_get_prot_type(cmd)) {
@@ -952,16 +953,16 @@ qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp,
struct qla2_sgx sgx;
dma_addr_t sle_dma;
uint32_t sle_dma_len, tot_prot_dma_len = 0;
- struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
prot_int = cmd->device->sector_size;
memset(&sgx, 0, sizeof(struct qla2_sgx));
- sgx.tot_bytes = scsi_bufflen(sp->cmd);
- sgx.cur_sg = scsi_sglist(sp->cmd);
+ sgx.tot_bytes = scsi_bufflen(cmd);
+ sgx.cur_sg = scsi_sglist(cmd);
sgx.sp = sp;
- sg_prot = scsi_prot_sglist(sp->cmd);
+ sg_prot = scsi_prot_sglist(cmd);
while (qla24xx_get_one_block_sg(prot_int, &sgx, &partial)) {
@@ -995,7 +996,7 @@ alloc_and_fill:
}
list_add_tail(&dsd_ptr->list,
- &((struct crc_context *)sp->ctx)->dsd_list);
+ &((struct crc_context *)sp->u.scmd.ctx)->dsd_list);
sp->flags |= SRB_CRC_CTX_DSD_VALID;
@@ -1044,11 +1045,12 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd,
uint32_t *cur_dsd = dsd;
int i;
uint16_t used_dsds = tot_dsds;
- scsi_qla_host_t *vha = shost_priv(sp->cmd->device->host);
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
uint8_t *cp;
- scsi_for_each_sg(sp->cmd, sg, tot_dsds, i) {
+ scsi_for_each_sg(cmd, sg, tot_dsds, i) {
dma_addr_t sle_dma;
/* Allocate additional continuation packets? */
@@ -1078,7 +1080,7 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd,
}
list_add_tail(&dsd_ptr->list,
- &((struct crc_context *)sp->ctx)->dsd_list);
+ &((struct crc_context *)sp->u.scmd.ctx)->dsd_list);
sp->flags |= SRB_CRC_CTX_DSD_VALID;
@@ -1091,17 +1093,16 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd,
sle_dma = sg_dma_address(sg);
ql_dbg(ql_dbg_io, vha, 0x300a,
"sg entry %d - addr=0x%x 0x%x, " "len=%d for cmd=%p.\n",
- i, LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg),
- sp->cmd);
+ i, LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg), cmd);
*cur_dsd++ = cpu_to_le32(LSD(sle_dma));
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
*cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
avail_dsds--;
- if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) {
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) {
cp = page_address(sg_page(sg)) + sg->offset;
ql_dbg(ql_dbg_io, vha, 0x300b,
- "User data buffer=%p for cmd=%p.\n", cp, sp->cmd);
+ "User data buffer=%p for cmd=%p.\n", cp, cmd);
}
}
/* Null termination */
@@ -1128,8 +1129,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
uint8_t *cp;
-
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) {
dma_addr_t sle_dma;
@@ -1160,7 +1160,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
}
list_add_tail(&dsd_ptr->list,
- &((struct crc_context *)sp->ctx)->dsd_list);
+ &((struct crc_context *)sp->u.scmd.ctx)->dsd_list);
sp->flags |= SRB_CRC_CTX_DSD_VALID;
@@ -1171,7 +1171,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
cur_dsd = (uint32_t *)next_dsd;
}
sle_dma = sg_dma_address(sg);
- if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) {
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) {
ql_dbg(ql_dbg_io, vha, 0x3027,
"%s(): %p, sg_entry %d - "
"addr=0x%x0x%x, len=%d.\n",
@@ -1182,7 +1182,7 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
*cur_dsd++ = cpu_to_le32(MSD(sle_dma));
*cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
- if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) {
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) {
cp = page_address(sg_page(sg)) + sg->offset;
ql_dbg(ql_dbg_io, vha, 0x3028,
"%s(): Protection Data buffer = %p.\n", __func__,
@@ -1228,7 +1228,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
dma_addr_t crc_ctx_dma;
char tag[2];
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
sgc = 0;
/* Update entry type to indicate Command Type CRC_2 IOCB */
@@ -1256,15 +1256,15 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
__constant_cpu_to_le16(CF_READ_DATA);
}
- if ((scsi_get_prot_op(sp->cmd) == SCSI_PROT_READ_INSERT) ||
- (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_STRIP) ||
- (scsi_get_prot_op(sp->cmd) == SCSI_PROT_READ_STRIP) ||
- (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_INSERT))
+ if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT))
bundling = 0;
/* Allocate CRC context from global pool */
- crc_ctx_pkt = sp->ctx = dma_pool_alloc(ha->dl_dma_pool,
- GFP_ATOMIC, &crc_ctx_dma);
+ crc_ctx_pkt = sp->u.scmd.ctx =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
if (!crc_ctx_pkt)
goto crc_queuing_error;
@@ -1310,7 +1310,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
fcp_cmnd->additional_cdb_len |= 2;
- int_to_scsilun(sp->cmd->device->lun, &fcp_cmnd->lun);
+ int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun);
memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len);
cmd_pkt->fcp_cmnd_dseg_address[0] = cpu_to_le32(
@@ -1345,7 +1345,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
blk_size = cmd->device->sector_size;
dif_bytes = (data_bytes / blk_size) * 8;
- switch (scsi_get_prot_op(sp->cmd)) {
+ switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
case SCSI_PROT_READ_INSERT:
case SCSI_PROT_WRITE_STRIP:
total_bytes = data_bytes;
@@ -1445,7 +1445,7 @@ qla24xx_start_scsi(srb_t *sp)
uint16_t tot_dsds;
struct req_que *req = NULL;
struct rsp_que *rsp = NULL;
- struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
struct scsi_qla_host *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
char tag[2];
@@ -1510,7 +1510,7 @@ qla24xx_start_scsi(srb_t *sp)
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
sp->handle = handle;
- sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
@@ -1529,7 +1529,7 @@ qla24xx_start_scsi(srb_t *sp)
cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
cmd_pkt->vp_index = sp->fcport->vp_idx;
- int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun);
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
/* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */
@@ -1611,7 +1611,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
uint16_t fw_prot_opts = 0;
struct req_que *req = NULL;
struct rsp_que *rsp = NULL;
- struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
struct scsi_qla_host *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
struct cmd_type_crc_2 *cmd_pkt;
@@ -1728,7 +1728,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
sp->handle = handle;
- sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
/* Fill-in common area */
@@ -1744,7 +1744,7 @@ qla24xx_dif_start_scsi(srb_t *sp)
cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
- int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun);
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
/* Total Data and protection segment(s) */
@@ -1797,7 +1797,7 @@ queuing_error:
static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp)
{
- struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
struct qla_hw_data *ha = sp->fcport->vha->hw;
int affinity = cmd->request->cpu;
@@ -1818,7 +1818,6 @@ qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp)
uint32_t index, handle;
request_t *pkt;
uint16_t cnt, req_cnt;
- struct srb_ctx *ctx;
pkt = NULL;
req_cnt = 1;
@@ -1848,15 +1847,13 @@ qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp)
sp->handle = handle;
/* Adjust entry-counts as needed. */
- if (sp->ctx) {
- ctx = sp->ctx;
- req_cnt = ctx->iocbs;
- }
+ if (sp->type != SRB_SCSI_CMD)
+ req_cnt = sp->iocbs;
skip_cmd_array:
/* Check for room on request queue. */
if (req->cnt < req_cnt) {
- if (ha->mqenable)
+ if (ha->mqenable || IS_QLA83XX(ha))
cnt = RD_REG_DWORD(&reg->isp25mq.req_q_out);
else if (IS_QLA82XX(ha))
cnt = RD_REG_DWORD(&reg->isp82.req_q_out);
@@ -1889,8 +1886,7 @@ queuing_error:
static void
qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
{
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *lio = ctx->u.iocb_cmd;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI);
@@ -1909,8 +1905,7 @@ static void
qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
{
struct qla_hw_data *ha = sp->fcport->vha->hw;
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *lio = ctx->u.iocb_cmd;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
uint16_t opts;
mbx->entry_type = MBX_IOCB_TYPE;
@@ -1999,8 +1994,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
struct fc_port *fcport = sp->fcport;
scsi_qla_host_t *vha = fcport->vha;
struct qla_hw_data *ha = vha->hw;
- struct srb_ctx *ctx = sp->ctx;
- struct srb_iocb *iocb = ctx->u.iocb_cmd;
+ struct srb_iocb *iocb = &sp->u.iocb_cmd;
struct req_que *req = vha->req;
flags = iocb->u.tmf.flags;
@@ -2027,7 +2021,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
static void
qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
{
- struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
els_iocb->entry_type = ELS_IOCB_TYPE;
els_iocb->entry_count = 1;
@@ -2041,7 +2035,7 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
els_iocb->opcode =
- (((struct srb_ctx *)sp->ctx)->type == SRB_ELS_CMD_RPT) ?
+ sp->type == SRB_ELS_CMD_RPT ?
bsg_job->request->rqst_data.r_els.els_code :
bsg_job->request->rqst_data.h_els.command_code;
els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
@@ -2078,7 +2072,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb)
uint16_t tot_dsds;
scsi_qla_host_t *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
- struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
int loop_iterartion = 0;
int cont_iocb_prsnt = 0;
int entry_count = 1;
@@ -2155,7 +2149,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
uint16_t tot_dsds;
scsi_qla_host_t *vha = sp->fcport->vha;
struct qla_hw_data *ha = vha->hw;
- struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
int loop_iterartion = 0;
int cont_iocb_prsnt = 0;
int entry_count = 1;
@@ -2245,12 +2239,12 @@ qla82xx_start_scsi(srb_t *sp)
struct qla_hw_data *ha = vha->hw;
struct req_que *req = NULL;
struct rsp_que *rsp = NULL;
- char tag[2];
+ char tag[2];
/* Setup device pointers. */
ret = 0;
reg = &ha->iobase->isp82;
- cmd = sp->cmd;
+ cmd = GET_CMD_SP(sp);
req = vha->req;
rsp = ha->rsp_q_map[0];
@@ -2354,12 +2348,14 @@ sufficient_dsds:
if (req->cnt < (req_cnt + 2))
goto queuing_error;
- ctx = sp->ctx = mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
- if (!sp->ctx) {
+ ctx = sp->u.scmd.ctx =
+ mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
+ if (!ctx) {
ql_log(ql_log_fatal, vha, 0x3010,
"Failed to allocate ctx for cmd=%p.\n", cmd);
goto queuing_error;
}
+
memset(ctx, 0, sizeof(struct ct6_dsd));
ctx->fcp_cmnd = dma_pool_alloc(ha->fcp_cmnd_dma_pool,
GFP_ATOMIC, &ctx->fcp_cmnd_dma);
@@ -2410,12 +2406,12 @@ sufficient_dsds:
if (qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds))
goto queuing_error_fcp_cmnd;
- int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun);
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
/* build FCP_CMND IU */
memset(ctx->fcp_cmnd, 0, sizeof(struct fcp_cmnd));
- int_to_scsilun(sp->cmd->device->lun, &ctx->fcp_cmnd->lun);
+ int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun);
ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len;
if (cmd->sc_data_direction == DMA_TO_DEVICE)
@@ -2495,9 +2491,9 @@ sufficient_dsds:
cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
cmd_pkt->vp_index = sp->fcport->vp_idx;
- int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun);
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
- sizeof(cmd_pkt->lun));
+ sizeof(cmd_pkt->lun));
/*
* Update tagged queuing modifier -- default is TSK_SIMPLE (0).
@@ -2538,7 +2534,7 @@ sufficient_dsds:
req->current_outstanding_cmd = handle;
req->outstanding_cmds[handle] = sp;
sp->handle = handle;
- sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
req->cnt -= req_cnt;
wmb();
@@ -2584,9 +2580,9 @@ queuing_error:
if (tot_dsds)
scsi_dma_unmap(cmd);
- if (sp->ctx) {
- mempool_free(sp->ctx, ha->ctx_mempool);
- sp->ctx = NULL;
+ if (sp->u.scmd.ctx) {
+ mempool_free(sp->u.scmd.ctx, ha->ctx_mempool);
+ sp->u.scmd.ctx = NULL;
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -2599,7 +2595,6 @@ qla2x00_start_sp(srb_t *sp)
int rval;
struct qla_hw_data *ha = sp->fcport->vha->hw;
void *pkt;
- struct srb_ctx *ctx = sp->ctx;
unsigned long flags;
rval = QLA_FUNCTION_FAILED;
@@ -2612,7 +2607,7 @@ qla2x00_start_sp(srb_t *sp)
}
rval = QLA_SUCCESS;
- switch (ctx->type) {
+ switch (sp->type) {
case SRB_LOGIN_CMD:
IS_FWI2_CAPABLE(ha) ?
qla24xx_login_iocb(sp, pkt) :
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
index 349843ea32f6..f79844ce7122 100644
--- a/drivers/scsi/qla2xxx/qla_isr.c
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -44,8 +44,8 @@ qla2100_intr_handler(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0x505d,
+ "%s: NULL response queue pointer.\n", __func__);
return (IRQ_NONE);
}
@@ -141,8 +141,8 @@ qla2300_intr_handler(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0x5058,
+ "%s: NULL response queue pointer.\n", __func__);
return (IRQ_NONE);
}
@@ -289,7 +289,7 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
mb[cnt] = RD_REG_WORD(wptr);
ql_dbg(ql_dbg_async, vha, 0x5021,
- "Inter-Driver Commucation %s -- "
+ "Inter-Driver Communication %s -- "
"%04x %04x %04x %04x %04x %04x %04x.\n",
event[aen & 0xff], mb[0], mb[1], mb[2], mb[3],
mb[4], mb[5], mb[6]);
@@ -318,7 +318,7 @@ void
qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
{
#define LS_UNKNOWN 2
- static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
+ static char *link_speeds[] = { "1", "2", "?", "4", "8", "16", "10" };
char *link_speed;
uint16_t handle_cnt;
uint16_t cnt, mbx;
@@ -328,12 +328,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
uint32_t rscn_entry, host_pid;
- uint8_t rscn_queue_index;
unsigned long flags;
/* Setup to process RIO completion. */
handle_cnt = 0;
- if (IS_QLA8XXX_TYPE(ha))
+ if (IS_CNA_CAPABLE(ha))
goto skip_rio;
switch (mb[0]) {
case MBA_SCSI_COMPLETION:
@@ -405,7 +404,8 @@ skip_rio:
break;
case MBA_SYSTEM_ERR: /* System Error */
- mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox7) : 0;
+ mbx = (IS_QLA81XX(ha) || IS_QLA83XX(ha)) ?
+ RD_REG_WORD(&reg24->mailbox7) : 0;
ql_log(ql_log_warn, vha, 0x5003,
"ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
"mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
@@ -418,6 +418,7 @@ skip_rio:
"Unrecoverable Hardware Error: adapter "
"marked OFFLINE!\n");
vha->flags.online = 0;
+ vha->device_flags |= DFLG_DEV_FAILED;
} else {
/* Check to see if MPI timeout occurred */
if ((mbx & MBX_3) && (ha->flags.port0))
@@ -431,6 +432,7 @@ skip_rio:
"Unrecoverable Hardware Error: adapter marked "
"OFFLINE!\n");
vha->flags.online = 0;
+ vha->device_flags |= DFLG_DEV_FAILED;
} else
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
break;
@@ -482,10 +484,10 @@ skip_rio:
ha->link_data_rate = PORT_SPEED_1GB;
} else {
link_speed = link_speeds[LS_UNKNOWN];
- if (mb[1] < 5)
+ if (mb[1] < 6)
link_speed = link_speeds[mb[1]];
else if (mb[1] == 0x13)
- link_speed = link_speeds[5];
+ link_speed = link_speeds[6];
ha->link_data_rate = mb[1];
}
@@ -497,7 +499,8 @@ skip_rio:
break;
case MBA_LOOP_DOWN: /* Loop Down Event */
- mbx = IS_QLA81XX(ha) ? RD_REG_WORD(&reg24->mailbox4) : 0;
+ mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha))
+ ? RD_REG_WORD(&reg24->mailbox4) : 0;
mbx = IS_QLA82XX(ha) ? RD_REG_WORD(&reg82->mailbox_out[4]) : mbx;
ql_dbg(ql_dbg_async, vha, 0x500b,
"LOOP DOWN detected (%x %x %x %x).\n",
@@ -547,7 +550,7 @@ skip_rio:
if (IS_QLA2100(ha))
break;
- if (IS_QLA8XXX_TYPE(ha)) {
+ if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA8031(ha)) {
ql_dbg(ql_dbg_async, vha, 0x500d,
"DCBX Completed -- %04x %04x %04x.\n",
mb[1], mb[2], mb[3]);
@@ -681,8 +684,6 @@ skip_rio:
qla2x00_mark_all_devices_lost(vha, 1);
- vha->flags.rscn_queue_overflow = 1;
-
set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
break;
@@ -711,15 +712,6 @@ skip_rio:
/* Ignore reserved bits from RSCN-payload. */
rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2];
- rscn_queue_index = vha->rscn_in_ptr + 1;
- if (rscn_queue_index == MAX_RSCN_COUNT)
- rscn_queue_index = 0;
- if (rscn_queue_index != vha->rscn_out_ptr) {
- vha->rscn_queue[vha->rscn_in_ptr] = rscn_entry;
- vha->rscn_in_ptr = rscn_queue_index;
- } else {
- vha->flags.rscn_queue_overflow = 1;
- }
atomic_set(&vha->loop_down_timer, 0);
vha->flags.management_server_logged_in = 0;
@@ -809,6 +801,10 @@ skip_rio:
case MBA_IDC_TIME_EXT:
qla81xx_idc_event(vha, mb[0], mb[1]);
break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x5057,
+ "Unknown AEN:%04x %04x %04x %04x\n",
+ mb[0], mb[1], mb[2], mb[3]);
}
if (!vha->vp_idx && ha->num_vhosts)
@@ -845,8 +841,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha,
req->outstanding_cmds[index] = NULL;
/* Save ISP completion status */
- sp->cmd->result = DID_OK << 16;
- qla2x00_sp_compl(ha, sp);
+ sp->done(ha, sp, DID_OK << 16);
} else {
ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n");
@@ -903,7 +898,6 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
fc_port_t *fcport;
srb_t *sp;
struct srb_iocb *lio;
- struct srb_ctx *ctx;
uint16_t *data;
uint16_t status;
@@ -911,9 +905,8 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
if (!sp)
return;
- ctx = sp->ctx;
- lio = ctx->u.iocb_cmd;
- type = ctx->name;
+ lio = &sp->u.iocb_cmd;
+ type = sp->name;
fcport = sp->fcport;
data = lio->u.logio.data;
@@ -937,7 +930,7 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
}
status = le16_to_cpu(mbx->status);
- if (status == 0x30 && ctx->type == SRB_LOGIN_CMD &&
+ if (status == 0x30 && sp->type == SRB_LOGIN_CMD &&
le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE)
status = 0;
if (!status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) {
@@ -948,7 +941,7 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
le16_to_cpu(mbx->mb1));
data[0] = MBS_COMMAND_COMPLETE;
- if (ctx->type == SRB_LOGIN_CMD) {
+ if (sp->type == SRB_LOGIN_CMD) {
fcport->port_type = FCT_TARGET;
if (le16_to_cpu(mbx->mb1) & BIT_0)
fcport->port_type = FCT_INITIATOR;
@@ -979,7 +972,7 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
le16_to_cpu(mbx->mb7));
logio_done:
- lio->done(sp);
+ sp->done(vha, sp, 0);
}
static void
@@ -988,29 +981,18 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
{
const char func[] = "CT_IOCB";
const char *type;
- struct qla_hw_data *ha = vha->hw;
srb_t *sp;
- struct srb_ctx *sp_bsg;
struct fc_bsg_job *bsg_job;
uint16_t comp_status;
+ int res;
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp)
return;
- sp_bsg = sp->ctx;
- bsg_job = sp_bsg->u.bsg_job;
+ bsg_job = sp->u.bsg_job;
- type = NULL;
- switch (sp_bsg->type) {
- case SRB_CT_CMD:
- type = "ct pass-through";
- break;
- default:
- ql_log(ql_log_warn, vha, 0x5047,
- "Unrecognized SRB: (%p) type=%d.\n", sp, sp_bsg->type);
- return;
- }
+ type = "ct pass-through";
comp_status = le16_to_cpu(pkt->comp_status);
@@ -1022,7 +1004,7 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
if (comp_status != CS_COMPLETE) {
if (comp_status == CS_DATA_UNDERRUN) {
- bsg_job->reply->result = DID_OK << 16;
+ res = DID_OK << 16;
bsg_job->reply->reply_payload_rcv_len =
le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len);
@@ -1035,30 +1017,19 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
ql_log(ql_log_warn, vha, 0x5049,
"CT pass-through-%s error "
"comp_status-status=0x%x.\n", type, comp_status);
- bsg_job->reply->result = DID_ERROR << 16;
+ res = DID_ERROR << 16;
bsg_job->reply->reply_payload_rcv_len = 0;
}
ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035,
(uint8_t *)pkt, sizeof(*pkt));
} else {
- bsg_job->reply->result = DID_OK << 16;
+ res = DID_OK << 16;
bsg_job->reply->reply_payload_rcv_len =
bsg_job->reply_payload.payload_len;
bsg_job->reply_len = 0;
}
- dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
- bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
-
- dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
- bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
-
- if (sp_bsg->type == SRB_ELS_CMD_HST || sp_bsg->type == SRB_CT_CMD)
- kfree(sp->fcport);
-
- kfree(sp->ctx);
- mempool_free(sp, ha->srb_mempool);
- bsg_job->job_done(bsg_job);
+ sp->done(vha, sp, res);
}
static void
@@ -1067,22 +1038,20 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
{
const char func[] = "ELS_CT_IOCB";
const char *type;
- struct qla_hw_data *ha = vha->hw;
srb_t *sp;
- struct srb_ctx *sp_bsg;
struct fc_bsg_job *bsg_job;
uint16_t comp_status;
uint32_t fw_status[3];
uint8_t* fw_sts_ptr;
+ int res;
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (!sp)
return;
- sp_bsg = sp->ctx;
- bsg_job = sp_bsg->u.bsg_job;
+ bsg_job = sp->u.bsg_job;
type = NULL;
- switch (sp_bsg->type) {
+ switch (sp->type) {
case SRB_ELS_CMD_RPT:
case SRB_ELS_CMD_HST:
type = "els";
@@ -1091,8 +1060,8 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
type = "ct pass-through";
break;
default:
- ql_log(ql_log_warn, vha, 0x503e,
- "Unrecognized SRB: (%p) type=%d.\n", sp, sp_bsg->type);
+ ql_dbg(ql_dbg_user, vha, 0x503e,
+ "Unrecognized SRB: (%p) type=%d.\n", sp, sp->type);
return;
}
@@ -1108,11 +1077,11 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
if (comp_status != CS_COMPLETE) {
if (comp_status == CS_DATA_UNDERRUN) {
- bsg_job->reply->result = DID_OK << 16;
+ res = DID_OK << 16;
bsg_job->reply->reply_payload_rcv_len =
- le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count);
+ le16_to_cpu(((struct els_sts_entry_24xx *)pkt)->total_byte_count);
- ql_log(ql_log_info, vha, 0x503f,
+ ql_dbg(ql_dbg_user, vha, 0x503f,
"ELS-CT pass-through-%s error hdl=%x comp_status-status=0x%x "
"error subcode 1=0x%x error subcode 2=0x%x total_byte = 0x%x.\n",
type, sp->handle, comp_status, fw_status[1], fw_status[2],
@@ -1122,7 +1091,7 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
}
else {
- ql_log(ql_log_info, vha, 0x5040,
+ ql_dbg(ql_dbg_user, vha, 0x5040,
"ELS-CT pass-through-%s error hdl=%x comp_status-status=0x%x "
"error subcode 1=0x%x error subcode 2=0x%x.\n",
type, sp->handle, comp_status,
@@ -1130,32 +1099,21 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
pkt)->error_subcode_1),
le16_to_cpu(((struct els_sts_entry_24xx *)
pkt)->error_subcode_2));
- bsg_job->reply->result = DID_ERROR << 16;
+ res = DID_ERROR << 16;
bsg_job->reply->reply_payload_rcv_len = 0;
fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
}
- ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5056,
+ ql_dump_buffer(ql_dbg_user + ql_dbg_buffer, vha, 0x5056,
(uint8_t *)pkt, sizeof(*pkt));
}
else {
- bsg_job->reply->result = DID_OK << 16;
+ res = DID_OK << 16;
bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
bsg_job->reply_len = 0;
}
- dma_unmap_sg(&ha->pdev->dev,
- bsg_job->request_payload.sg_list,
- bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
- dma_unmap_sg(&ha->pdev->dev,
- bsg_job->reply_payload.sg_list,
- bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
- if ((sp_bsg->type == SRB_ELS_CMD_HST) ||
- (sp_bsg->type == SRB_CT_CMD))
- kfree(sp->fcport);
- kfree(sp->ctx);
- mempool_free(sp, ha->srb_mempool);
- bsg_job->job_done(bsg_job);
+ sp->done(vha, sp, res);
}
static void
@@ -1167,7 +1125,6 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
fc_port_t *fcport;
srb_t *sp;
struct srb_iocb *lio;
- struct srb_ctx *ctx;
uint16_t *data;
uint32_t iop[2];
@@ -1175,9 +1132,8 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
if (!sp)
return;
- ctx = sp->ctx;
- lio = ctx->u.iocb_cmd;
- type = ctx->name;
+ lio = &sp->u.iocb_cmd;
+ type = sp->name;
fcport = sp->fcport;
data = lio->u.logio.data;
@@ -1185,7 +1141,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
QLA_LOGIO_LOGIN_RETRIED : 0;
if (logio->entry_status) {
- ql_log(ql_log_warn, vha, 0x5034,
+ ql_log(ql_log_warn, fcport->vha, 0x5034,
"Async-%s error entry - hdl=%x"
"portid=%02x%02x%02x entry-status=%x.\n",
type, sp->handle, fcport->d_id.b.domain,
@@ -1198,14 +1154,14 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
}
if (le16_to_cpu(logio->comp_status) == CS_COMPLETE) {
- ql_dbg(ql_dbg_async, vha, 0x5036,
+ ql_dbg(ql_dbg_async, fcport->vha, 0x5036,
"Async-%s complete - hdl=%x portid=%02x%02x%02x "
"iop0=%x.\n", type, sp->handle, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa,
le32_to_cpu(logio->io_parameter[0]));
data[0] = MBS_COMMAND_COMPLETE;
- if (ctx->type != SRB_LOGIN_CMD)
+ if (sp->type != SRB_LOGIN_CMD)
goto logio_done;
iop[0] = le32_to_cpu(logio->io_parameter[0]);
@@ -1239,7 +1195,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
break;
}
- ql_dbg(ql_dbg_async, vha, 0x5037,
+ ql_dbg(ql_dbg_async, fcport->vha, 0x5037,
"Async-%s failed - hdl=%x portid=%02x%02x%02x comp=%x "
"iop0=%x iop1=%x.\n", type, sp->handle, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa,
@@ -1248,7 +1204,7 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
le32_to_cpu(logio->io_parameter[1]));
logio_done:
- lio->done(sp);
+ sp->done(vha, sp, 0);
}
static void
@@ -1260,7 +1216,6 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
fc_port_t *fcport;
srb_t *sp;
struct srb_iocb *iocb;
- struct srb_ctx *ctx;
struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
int error = 1;
@@ -1268,30 +1223,29 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
if (!sp)
return;
- ctx = sp->ctx;
- iocb = ctx->u.iocb_cmd;
- type = ctx->name;
+ iocb = &sp->u.iocb_cmd;
+ type = sp->name;
fcport = sp->fcport;
if (sts->entry_status) {
- ql_log(ql_log_warn, vha, 0x5038,
+ ql_log(ql_log_warn, fcport->vha, 0x5038,
"Async-%s error - hdl=%x entry-status(%x).\n",
type, sp->handle, sts->entry_status);
} else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
- ql_log(ql_log_warn, vha, 0x5039,
+ ql_log(ql_log_warn, fcport->vha, 0x5039,
"Async-%s error - hdl=%x completion status(%x).\n",
type, sp->handle, sts->comp_status);
} else if (!(le16_to_cpu(sts->scsi_status) &
SS_RESPONSE_INFO_LEN_VALID)) {
- ql_log(ql_log_warn, vha, 0x503a,
+ ql_log(ql_log_warn, fcport->vha, 0x503a,
"Async-%s error - hdl=%x no response info(%x).\n",
type, sp->handle, sts->scsi_status);
} else if (le32_to_cpu(sts->rsp_data_len) < 4) {
- ql_log(ql_log_warn, vha, 0x503b,
+ ql_log(ql_log_warn, fcport->vha, 0x503b,
"Async-%s error - hdl=%x not enough response(%d).\n",
type, sp->handle, sts->rsp_data_len);
} else if (sts->data[3]) {
- ql_log(ql_log_warn, vha, 0x503c,
+ ql_log(ql_log_warn, fcport->vha, 0x503c,
"Async-%s error - hdl=%x response(%x).\n",
type, sp->handle, sts->data[3]);
} else {
@@ -1304,7 +1258,7 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
(uint8_t *)sts, sizeof(*sts));
}
- iocb->done(sp);
+ sp->done(vha, sp, 0);
}
/**
@@ -1390,25 +1344,32 @@ qla2x00_process_response_queue(struct rsp_que *rsp)
static inline void
qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len,
- uint32_t sense_len, struct rsp_que *rsp)
+ uint32_t sense_len, struct rsp_que *rsp, int res)
{
struct scsi_qla_host *vha = sp->fcport->vha;
- struct scsi_cmnd *cp = sp->cmd;
+ struct scsi_cmnd *cp = GET_CMD_SP(sp);
+ uint32_t track_sense_len;
if (sense_len >= SCSI_SENSE_BUFFERSIZE)
sense_len = SCSI_SENSE_BUFFERSIZE;
- sp->request_sense_length = sense_len;
- sp->request_sense_ptr = cp->sense_buffer;
- if (sp->request_sense_length > par_sense_len)
+ SET_CMD_SENSE_LEN(sp, sense_len);
+ SET_CMD_SENSE_PTR(sp, cp->sense_buffer);
+ track_sense_len = sense_len;
+
+ if (sense_len > par_sense_len)
sense_len = par_sense_len;
memcpy(cp->sense_buffer, sense_data, sense_len);
- sp->request_sense_ptr += sense_len;
- sp->request_sense_length -= sense_len;
- if (sp->request_sense_length != 0)
+ SET_CMD_SENSE_PTR(sp, cp->sense_buffer + sense_len);
+ track_sense_len -= sense_len;
+ SET_CMD_SENSE_LEN(sp, track_sense_len);
+
+ if (track_sense_len != 0) {
rsp->status_srb = sp;
+ cp->result = res;
+ }
if (sense_len) {
ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x301c,
@@ -1436,7 +1397,7 @@ static inline int
qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
{
struct scsi_qla_host *vha = sp->fcport->vha;
- struct scsi_cmnd *cmd = sp->cmd;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
uint8_t *ap = &sts24->data[12];
uint8_t *ep = &sts24->data[20];
uint32_t e_ref_tag, a_ref_tag;
@@ -1580,6 +1541,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
uint16_t que;
struct req_que *req;
int logit = 1;
+ int res = 0;
sts = (sts_entry_t *) pkt;
sts24 = (struct sts_entry_24xx *) pkt;
@@ -1619,7 +1581,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
qla2xxx_wake_dpc(vha);
return;
}
- cp = sp->cmd;
+ cp = GET_CMD_SP(sp);
if (cp == NULL) {
ql_dbg(ql_dbg_io, vha, 0x3018,
"Command already returned (0x%x/%p).\n",
@@ -1668,11 +1630,11 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
par_sense_len -= rsp_info_len;
}
if (rsp_info_len > 3 && rsp_info[3]) {
- ql_dbg(ql_dbg_io, vha, 0x3019,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3019,
"FCP I/O protocol failure (0x%x/0x%x).\n",
rsp_info_len, rsp_info[3]);
- cp->result = DID_BUS_BUSY << 16;
+ res = DID_BUS_BUSY << 16;
goto out;
}
}
@@ -1689,7 +1651,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
case CS_COMPLETE:
case CS_QUEUE_FULL:
if (scsi_status == 0) {
- cp->result = DID_OK << 16;
+ res = DID_OK << 16;
break;
}
if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) {
@@ -1699,19 +1661,19 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
if (!lscsi_status &&
((unsigned)(scsi_bufflen(cp) - resid) <
cp->underflow)) {
- ql_dbg(ql_dbg_io, vha, 0x301a,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301a,
"Mid-layer underflow "
"detected (0x%x of 0x%x bytes).\n",
resid, scsi_bufflen(cp));
- cp->result = DID_ERROR << 16;
+ res = DID_ERROR << 16;
break;
}
}
- cp->result = DID_OK << 16 | lscsi_status;
+ res = DID_OK << 16 | lscsi_status;
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
- ql_dbg(ql_dbg_io, vha, 0x301b,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301b,
"QUEUE FULL detected.\n");
break;
}
@@ -1724,7 +1686,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
break;
qla2x00_handle_sense(sp, sense_data, par_sense_len, sense_len,
- rsp);
+ rsp, res);
break;
case CS_DATA_UNDERRUN:
@@ -1733,36 +1695,36 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
scsi_set_resid(cp, resid);
if (scsi_status & SS_RESIDUAL_UNDER) {
if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
- ql_dbg(ql_dbg_io, vha, 0x301d,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301d,
"Dropped frame(s) detected "
"(0x%x of 0x%x bytes).\n",
resid, scsi_bufflen(cp));
- cp->result = DID_ERROR << 16 | lscsi_status;
+ res = DID_ERROR << 16 | lscsi_status;
goto check_scsi_status;
}
if (!lscsi_status &&
((unsigned)(scsi_bufflen(cp) - resid) <
cp->underflow)) {
- ql_dbg(ql_dbg_io, vha, 0x301e,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301e,
"Mid-layer underflow "
"detected (0x%x of 0x%x bytes).\n",
resid, scsi_bufflen(cp));
- cp->result = DID_ERROR << 16;
+ res = DID_ERROR << 16;
break;
}
} else {
- ql_dbg(ql_dbg_io, vha, 0x301f,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301f,
"Dropped frame(s) detected (0x%x "
"of 0x%x bytes).\n", resid, scsi_bufflen(cp));
- cp->result = DID_ERROR << 16 | lscsi_status;
+ res = DID_ERROR << 16 | lscsi_status;
goto check_scsi_status;
}
- cp->result = DID_OK << 16 | lscsi_status;
+ res = DID_OK << 16 | lscsi_status;
logit = 0;
check_scsi_status:
@@ -1772,7 +1734,7 @@ check_scsi_status:
*/
if (lscsi_status != 0) {
if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
- ql_dbg(ql_dbg_io, vha, 0x3020,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3020,
"QUEUE FULL detected.\n");
logit = 1;
break;
@@ -1785,7 +1747,7 @@ check_scsi_status:
break;
qla2x00_handle_sense(sp, sense_data, par_sense_len,
- sense_len, rsp);
+ sense_len, rsp, res);
}
break;
@@ -1802,7 +1764,7 @@ check_scsi_status:
* while we try to recover so instruct the mid layer
* to requeue until the class decides how to handle this.
*/
- cp->result = DID_TRANSPORT_DISRUPTED << 16;
+ res = DID_TRANSPORT_DISRUPTED << 16;
if (comp_status == CS_TIMEOUT) {
if (IS_FWI2_CAPABLE(ha))
@@ -1812,7 +1774,7 @@ check_scsi_status:
break;
}
- ql_dbg(ql_dbg_io, vha, 0x3021,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3021,
"Port down status: port-state=0x%x.\n",
atomic_read(&fcport->state));
@@ -1821,25 +1783,25 @@ check_scsi_status:
break;
case CS_ABORTED:
- cp->result = DID_RESET << 16;
+ res = DID_RESET << 16;
break;
case CS_DIF_ERROR:
logit = qla2x00_handle_dif_error(sp, sts24);
break;
default:
- cp->result = DID_ERROR << 16;
+ res = DID_ERROR << 16;
break;
}
out:
if (logit)
- ql_dbg(ql_dbg_io, vha, 0x3022,
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3022,
"FCP command status: 0x%x-0x%x (0x%x) "
"nexus=%ld:%d:%d portid=%02x%02x%02x oxid=0x%x "
"cdb=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x len=0x%x "
"rsp_info=0x%x resid=0x%x fw_resid=0x%x.\n",
- comp_status, scsi_status, cp->result, vha->host_no,
+ comp_status, scsi_status, res, vha->host_no,
cp->device->id, cp->device->lun, fcport->d_id.b.domain,
fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id,
cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3],
@@ -1848,7 +1810,7 @@ out:
resid_len, fw_resid_len);
if (rsp->status_srb == NULL)
- qla2x00_sp_compl(ha, sp);
+ sp->done(ha, sp, res);
}
/**
@@ -1861,84 +1823,52 @@ out:
static void
qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
{
- uint8_t sense_sz = 0;
+ uint8_t sense_sz = 0;
struct qla_hw_data *ha = rsp->hw;
struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
- srb_t *sp = rsp->status_srb;
+ srb_t *sp = rsp->status_srb;
struct scsi_cmnd *cp;
+ uint32_t sense_len;
+ uint8_t *sense_ptr;
- if (sp != NULL && sp->request_sense_length != 0) {
- cp = sp->cmd;
- if (cp == NULL) {
- ql_log(ql_log_warn, vha, 0x3025,
- "cmd is NULL: already returned to OS (sp=%p).\n",
- sp);
+ if (!sp || !GET_CMD_SENSE_LEN(sp))
+ return;
- rsp->status_srb = NULL;
- return;
- }
+ sense_len = GET_CMD_SENSE_LEN(sp);
+ sense_ptr = GET_CMD_SENSE_PTR(sp);
- if (sp->request_sense_length > sizeof(pkt->data)) {
- sense_sz = sizeof(pkt->data);
- } else {
- sense_sz = sp->request_sense_length;
- }
+ cp = GET_CMD_SP(sp);
+ if (cp == NULL) {
+ ql_log(ql_log_warn, vha, 0x3025,
+ "cmd is NULL: already returned to OS (sp=%p).\n", sp);
- /* Move sense data. */
- if (IS_FWI2_CAPABLE(ha))
- host_to_fcp_swap(pkt->data, sizeof(pkt->data));
- memcpy(sp->request_sense_ptr, pkt->data, sense_sz);
- ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
- sp->request_sense_ptr, sense_sz);
-
- sp->request_sense_ptr += sense_sz;
- sp->request_sense_length -= sense_sz;
-
- /* Place command on done queue. */
- if (sp->request_sense_length == 0) {
- rsp->status_srb = NULL;
- qla2x00_sp_compl(ha, sp);
- }
+ rsp->status_srb = NULL;
+ return;
}
-}
-static int
-qla2x00_free_sp_ctx(scsi_qla_host_t *vha, srb_t *sp)
-{
- struct qla_hw_data *ha = vha->hw;
- struct srb_ctx *ctx;
+ if (sense_len > sizeof(pkt->data))
+ sense_sz = sizeof(pkt->data);
+ else
+ sense_sz = sense_len;
- if (!sp->ctx)
- return 1;
+ /* Move sense data. */
+ if (IS_FWI2_CAPABLE(ha))
+ host_to_fcp_swap(pkt->data, sizeof(pkt->data));
+ memcpy(sense_ptr, pkt->data, sense_sz);
+ ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
+ sense_ptr, sense_sz);
- ctx = sp->ctx;
+ sense_len -= sense_sz;
+ sense_ptr += sense_sz;
- if (ctx->type == SRB_LOGIN_CMD ||
- ctx->type == SRB_LOGOUT_CMD ||
- ctx->type == SRB_TM_CMD) {
- ctx->u.iocb_cmd->done(sp);
- return 0;
- } else if (ctx->type == SRB_ADISC_CMD) {
- ctx->u.iocb_cmd->free(sp);
- return 0;
- } else {
- struct fc_bsg_job *bsg_job;
-
- bsg_job = ctx->u.bsg_job;
- if (ctx->type == SRB_ELS_CMD_HST ||
- ctx->type == SRB_CT_CMD)
- kfree(sp->fcport);
-
- bsg_job->reply->reply_data.ctels_reply.status =
- FC_CTELS_STATUS_OK;
- bsg_job->reply->result = DID_ERROR << 16;
- bsg_job->reply->reply_payload_rcv_len = 0;
- kfree(sp->ctx);
- mempool_free(sp, ha->srb_mempool);
- bsg_job->job_done(bsg_job);
- return 0;
+ SET_CMD_SENSE_PTR(sp, sense_ptr);
+ SET_CMD_SENSE_LEN(sp, sense_len);
+
+ /* Place command on done queue. */
+ if (sense_len == 0) {
+ rsp->status_srb = NULL;
+ sp->done(ha, sp, cp->result);
}
- return 1;
}
/**
@@ -1953,53 +1883,34 @@ qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
struct qla_hw_data *ha = vha->hw;
const char func[] = "ERROR-IOCB";
uint16_t que = MSW(pkt->handle);
- struct req_que *req = ha->req_q_map[que];
-
- if (pkt->entry_status & RF_INV_E_ORDER)
- ql_dbg(ql_dbg_async, vha, 0x502a,
- "Invalid Entry Order.\n");
- else if (pkt->entry_status & RF_INV_E_COUNT)
- ql_dbg(ql_dbg_async, vha, 0x502b,
- "Invalid Entry Count.\n");
- else if (pkt->entry_status & RF_INV_E_PARAM)
- ql_dbg(ql_dbg_async, vha, 0x502c,
- "Invalid Entry Parameter.\n");
- else if (pkt->entry_status & RF_INV_E_TYPE)
- ql_dbg(ql_dbg_async, vha, 0x502d,
- "Invalid Entry Type.\n");
- else if (pkt->entry_status & RF_BUSY)
- ql_dbg(ql_dbg_async, vha, 0x502e,
- "Busy.\n");
- else
- ql_dbg(ql_dbg_async, vha, 0x502f,
- "UNKNOWN flag error.\n");
+ struct req_que *req = NULL;
+ int res = DID_ERROR << 16;
+
+ ql_dbg(ql_dbg_async, vha, 0x502a,
+ "type of error status in response: 0x%x\n", pkt->entry_status);
+
+ if (que >= ha->max_req_queues || !ha->req_q_map[que])
+ goto fatal;
+
+ req = ha->req_q_map[que];
+
+ if (pkt->entry_status & RF_BUSY)
+ res = DID_BUS_BUSY << 16;
sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
if (sp) {
- if (qla2x00_free_sp_ctx(vha, sp)) {
- if (pkt->entry_status &
- (RF_INV_E_ORDER | RF_INV_E_COUNT |
- RF_INV_E_PARAM | RF_INV_E_TYPE)) {
- sp->cmd->result = DID_ERROR << 16;
- } else if (pkt->entry_status & RF_BUSY) {
- sp->cmd->result = DID_BUS_BUSY << 16;
- } else {
- sp->cmd->result = DID_ERROR << 16;
- }
- qla2x00_sp_compl(ha, sp);
- }
- } else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type ==
- COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7
- || pkt->entry_type == COMMAND_TYPE_6) {
- ql_log(ql_log_warn, vha, 0x5030,
- "Error entry - invalid handle.\n");
-
- if (IS_QLA82XX(ha))
- set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
- else
- set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
- qla2xxx_wake_dpc(vha);
+ sp->done(ha, sp, res);
+ return;
}
+fatal:
+ ql_log(ql_log_warn, vha, 0x5030,
+ "Error entry - invalid handle/queue.\n");
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
}
/**
@@ -2127,7 +2038,7 @@ qla2xxx_check_risc_status(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
- if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha))
+ if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
return;
rval = QLA_SUCCESS;
@@ -2168,7 +2079,7 @@ done:
}
/**
- * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
+ * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP24xx.
* @irq:
* @dev_id: SCSI driver HA context
*
@@ -2192,8 +2103,8 @@ qla24xx_intr_handler(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0x5059,
+ "%s: NULL response queue pointer.\n", __func__);
return IRQ_NONE;
}
@@ -2276,8 +2187,8 @@ qla24xx_msix_rsp_q(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0x505a,
+ "%s: NULL response queue pointer.\n", __func__);
return IRQ_NONE;
}
ha = rsp->hw;
@@ -2306,8 +2217,8 @@ qla25xx_msix_rsp_q(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0x505b,
+ "%s: NULL response queue pointer.\n", __func__);
return IRQ_NONE;
}
ha = rsp->hw;
@@ -2340,8 +2251,8 @@ qla24xx_msix_default(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0x505c,
+ "%s: NULL response queue pointer.\n", __func__);
return IRQ_NONE;
}
ha = rsp->hw;
@@ -2530,8 +2441,14 @@ msix_failed:
}
/* Enable MSI-X vector for response queue update for queue 0 */
- if (ha->mqiobase && (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
- ha->mqenable = 1;
+ if (IS_QLA83XX(ha)) {
+ if (ha->msixbase && ha->mqiobase &&
+ (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
+ ha->mqenable = 1;
+ } else
+ if (ha->mqiobase
+ && (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
+ ha->mqenable = 1;
ql_dbg(ql_dbg_multiq, vha, 0xc005,
"mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n",
ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues);
@@ -2552,8 +2469,8 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
/* If possible, enable MSI-X. */
- if (!IS_QLA2432(ha) && !IS_QLA2532(ha) &&
- !IS_QLA8432(ha) && !IS_QLA8XXX_TYPE(ha))
+ if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
goto skip_msi;
if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_HP &&
@@ -2615,7 +2532,7 @@ clear_risc_ints:
* FIXME: Noted that 8014s were being dropped during NK testing.
* Timing deltas during MSI-X/INTa transitions?
*/
- if (IS_QLA81XX(ha) || IS_QLA82XX(ha))
+ if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA83XX(ha))
goto fail;
spin_lock_irq(&ha->hardware_lock);
if (IS_FWI2_CAPABLE(ha)) {
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 08f1d01bdc1c..b4a23394a7bd 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -46,17 +46,17 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
struct qla_hw_data *ha = vha->hw;
scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
- ql_dbg(ql_dbg_mbx, base_vha, 0x1000, "Entered %s.\n", __func__);
+ ql_dbg(ql_dbg_mbx, vha, 0x1000, "Entered %s.\n", __func__);
if (ha->pdev->error_state > pci_channel_io_frozen) {
- ql_log(ql_log_warn, base_vha, 0x1001,
+ ql_log(ql_log_warn, vha, 0x1001,
"error_state is greater than pci_channel_io_frozen, "
"exiting.\n");
return QLA_FUNCTION_TIMEOUT;
}
if (vha->device_flags & DFLG_DEV_FAILED) {
- ql_log(ql_log_warn, base_vha, 0x1002,
+ ql_log(ql_log_warn, vha, 0x1002,
"Device in failed state, exiting.\n");
return QLA_FUNCTION_TIMEOUT;
}
@@ -69,7 +69,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
if (ha->flags.pci_channel_io_perm_failure) {
- ql_log(ql_log_warn, base_vha, 0x1003,
+ ql_log(ql_log_warn, vha, 0x1003,
"Perm failure on EEH timeout MBX, exiting.\n");
return QLA_FUNCTION_TIMEOUT;
}
@@ -77,7 +77,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
if (ha->flags.isp82xx_fw_hung) {
/* Setting Link-Down error */
mcp->mb[0] = MBS_LINK_DOWN_ERROR;
- ql_log(ql_log_warn, base_vha, 0x1004,
+ ql_log(ql_log_warn, vha, 0x1004,
"FW hung = %d.\n", ha->flags.isp82xx_fw_hung);
return QLA_FUNCTION_TIMEOUT;
}
@@ -89,8 +89,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
*/
if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) {
/* Timeout occurred. Return error. */
- ql_log(ql_log_warn, base_vha, 0x1005,
- "Cmd access timeout, Exiting.\n");
+ ql_log(ql_log_warn, vha, 0x1005,
+ "Cmd access timeout, cmd=0x%x, Exiting.\n",
+ mcp->mb[0]);
return QLA_FUNCTION_TIMEOUT;
}
@@ -98,7 +99,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
/* Save mailbox command for debug */
ha->mcp = mcp;
- ql_dbg(ql_dbg_mbx, base_vha, 0x1006,
+ ql_dbg(ql_dbg_mbx, vha, 0x1006,
"Prepare to issue mbox cmd=0x%x.\n", mcp->mb[0]);
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -127,28 +128,28 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
iptr++;
}
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1111,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1111,
"Loaded MBX registers (displayed in bytes) =.\n");
- ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1112,
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1112,
(uint8_t *)mcp->mb, 16);
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1113,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1113,
".\n");
- ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1114,
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1114,
((uint8_t *)mcp->mb + 0x10), 16);
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1115,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1115,
".\n");
- ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1116,
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1116,
((uint8_t *)mcp->mb + 0x20), 8);
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1117,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1117,
"I/O Address = %p.\n", optr);
- ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x100e);
+ ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x100e);
/* Issue set host interrupt command to send cmd out. */
ha->flags.mbox_int = 0;
clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
/* Unlock mbx registers and wait for interrupt */
- ql_dbg(ql_dbg_mbx, base_vha, 0x100f,
+ ql_dbg(ql_dbg_mbx, vha, 0x100f,
"Going to unlock irq & waiting for interrupts. "
"jiffies=%lx.\n", jiffies);
@@ -163,7 +164,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
spin_unlock_irqrestore(&ha->hardware_lock,
flags);
ha->flags.mbox_busy = 0;
- ql_dbg(ql_dbg_mbx, base_vha, 0x1010,
+ ql_dbg(ql_dbg_mbx, vha, 0x1010,
"Pending mailbox timeout, exiting.\n");
rval = QLA_FUNCTION_TIMEOUT;
goto premature_exit;
@@ -180,7 +181,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
} else {
- ql_dbg(ql_dbg_mbx, base_vha, 0x1011,
+ ql_dbg(ql_dbg_mbx, vha, 0x1011,
"Cmd=%x Polling Mode.\n", command);
if (IS_QLA82XX(ha)) {
@@ -189,7 +190,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
spin_unlock_irqrestore(&ha->hardware_lock,
flags);
ha->flags.mbox_busy = 0;
- ql_dbg(ql_dbg_mbx, base_vha, 0x1012,
+ ql_dbg(ql_dbg_mbx, vha, 0x1012,
"Pending mailbox timeout, exiting.\n");
rval = QLA_FUNCTION_TIMEOUT;
goto premature_exit;
@@ -214,7 +215,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
command == MBC_LOAD_RISC_RAM_EXTENDED))
msleep(10);
} /* while */
- ql_dbg(ql_dbg_mbx, base_vha, 0x1013,
+ ql_dbg(ql_dbg_mbx, vha, 0x1013,
"Waited %d sec.\n",
(uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ));
}
@@ -223,7 +224,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
if (ha->flags.mbox_int) {
uint16_t *iptr2;
- ql_dbg(ql_dbg_mbx, base_vha, 0x1014,
+ ql_dbg(ql_dbg_mbx, vha, 0x1014,
"Cmd=%x completed.\n", command);
/* Got interrupt. Clear the flag. */
@@ -236,7 +237,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
mcp->mb[0] = MBS_LINK_DOWN_ERROR;
ha->mcp = NULL;
rval = QLA_FUNCTION_FAILED;
- ql_log(ql_log_warn, base_vha, 0x1015,
+ ql_log(ql_log_warn, vha, 0x1015,
"FW hung = %d.\n", ha->flags.isp82xx_fw_hung);
goto premature_exit;
}
@@ -268,13 +269,19 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
mb0 = RD_MAILBOX_REG(ha, &reg->isp, 0);
ictrl = RD_REG_WORD(&reg->isp.ictrl);
}
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1119,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119,
"MBX Command timeout for cmd %x.\n", command);
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x111a,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111a,
"iocontrol=%x jiffies=%lx.\n", ictrl, jiffies);
- ql_dbg(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x111b,
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111b,
"mb[0] = 0x%x.\n", mb0);
- ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, base_vha, 0x1019);
+ ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019);
+
+ /*
+ * Attempt to capture a firmware dump for further analysis
+ * of the current firmware state
+ */
+ ha->isp_ops->fw_dump(vha, 0);
rval = QLA_FUNCTION_TIMEOUT;
}
@@ -285,7 +292,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
ha->mcp = NULL;
if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) {
- ql_dbg(ql_dbg_mbx, base_vha, 0x101a,
+ ql_dbg(ql_dbg_mbx, vha, 0x101a,
"Checking for additional resp interrupt.\n");
/* polling mode for non isp_abort commands. */
@@ -297,7 +304,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
if (!io_lock_on || (mcp->flags & IOCTL_CMD) ||
ha->flags.eeh_busy) {
/* not in dpc. schedule it for dpc to take over. */
- ql_dbg(ql_dbg_mbx, base_vha, 0x101b,
+ ql_dbg(ql_dbg_mbx, vha, 0x101b,
"Timeout, schedule isp_abort_needed.\n");
if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
@@ -313,15 +320,16 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
CRB_NIU_XG_PAUSE_CTL_P1);
}
ql_log(ql_log_info, base_vha, 0x101c,
- "Mailbox cmd timeout occured. "
- "Scheduling ISP abort eeh_busy=0x%x.\n",
- ha->flags.eeh_busy);
+ "Mailbox cmd timeout occured, cmd=0x%x, "
+ "mb[0]=0x%x, eeh_busy=0x%x. Scheduling ISP "
+ "abort.\n", command, mcp->mb[0],
+ ha->flags.eeh_busy);
set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
qla2xxx_wake_dpc(vha);
}
} else if (!abort_active) {
/* call abort directly since we are in the DPC thread */
- ql_dbg(ql_dbg_mbx, base_vha, 0x101d,
+ ql_dbg(ql_dbg_mbx, vha, 0x101d,
"Timeout, calling abort_isp.\n");
if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
@@ -337,9 +345,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
CRB_NIU_XG_PAUSE_CTL_P1);
}
ql_log(ql_log_info, base_vha, 0x101e,
- "Mailbox cmd timeout occured. "
- "Scheduling ISP abort.\n");
-
+ "Mailbox cmd timeout occured, cmd=0x%x, "
+ "mb[0]=0x%x. Scheduling ISP abort ",
+ command, mcp->mb[0]);
set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
/* Allow next mbx cmd to come in. */
@@ -350,7 +358,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
&vha->dpc_flags);
}
clear_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
- ql_dbg(ql_dbg_mbx, base_vha, 0x101f,
+ ql_dbg(ql_dbg_mbx, vha, 0x101f,
"Finished abort_isp.\n");
goto mbx_done;
}
@@ -364,8 +372,8 @@ premature_exit:
mbx_done:
if (rval) {
ql_dbg(ql_dbg_mbx, base_vha, 0x1020,
- "**** Failed mbx[0]=%x, mb[1]=%x, mb[2]=%x, cmd=%x ****.\n",
- mcp->mb[0], mcp->mb[1], mcp->mb[2], command);
+ "**** Failed mbx[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x, cmd=%x ****.\n",
+ mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], command);
} else {
ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__);
}
@@ -455,7 +463,7 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
mcp->mb[1] = MSW(risc_addr);
mcp->mb[2] = LSW(risc_addr);
mcp->mb[3] = 0;
- if (IS_QLA81XX(ha)) {
+ if (IS_QLA81XX(ha) || IS_QLA83XX(ha)) {
struct nvram_81xx *nv = ha->nvram;
mcp->mb[4] = (nv->enhanced_features &
EXTENDED_BB_CREDITS);
@@ -508,21 +516,22 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
* Kernel context.
*/
int
-qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
- uint16_t *subminor, uint16_t *attributes, uint32_t *memory, uint8_t *mpi,
- uint32_t *mpi_caps, uint8_t *phy)
+qla2x00_get_fw_version(scsi_qla_host_t *vha)
{
int rval;
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
ql_dbg(ql_dbg_mbx, vha, 0x1029, "Entered %s.\n", __func__);
mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
- if (IS_QLA81XX(vha->hw))
+ if (IS_QLA81XX(vha->hw) || IS_QLA8031(ha))
mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8;
+ if (IS_QLA83XX(vha->hw))
+ mcp->in_mb |= MBX_17|MBX_16|MBX_15;
mcp->flags = 0;
mcp->tov = MBX_TOV_SECONDS;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -530,23 +539,37 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor,
goto failed;
/* Return mailbox data. */
- *major = mcp->mb[1];
- *minor = mcp->mb[2];
- *subminor = mcp->mb[3];
- *attributes = mcp->mb[6];
+ ha->fw_major_version = mcp->mb[1];
+ ha->fw_minor_version = mcp->mb[2];
+ ha->fw_subminor_version = mcp->mb[3];
+ ha->fw_attributes = mcp->mb[6];
if (IS_QLA2100(vha->hw) || IS_QLA2200(vha->hw))
- *memory = 0x1FFFF; /* Defaults to 128KB. */
+ ha->fw_memory_size = 0x1FFFF; /* Defaults to 128KB. */
else
- *memory = (mcp->mb[5] << 16) | mcp->mb[4];
- if (IS_QLA81XX(vha->hw)) {
- mpi[0] = mcp->mb[10] & 0xff;
- mpi[1] = mcp->mb[11] >> 8;
- mpi[2] = mcp->mb[11] & 0xff;
- *mpi_caps = (mcp->mb[12] << 16) | mcp->mb[13];
- phy[0] = mcp->mb[8] & 0xff;
- phy[1] = mcp->mb[9] >> 8;
- phy[2] = mcp->mb[9] & 0xff;
+ ha->fw_memory_size = (mcp->mb[5] << 16) | mcp->mb[4];
+ if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw)) {
+ ha->mpi_version[0] = mcp->mb[10] & 0xff;
+ ha->mpi_version[1] = mcp->mb[11] >> 8;
+ ha->mpi_version[2] = mcp->mb[11] & 0xff;
+ ha->mpi_capabilities = (mcp->mb[12] << 16) | mcp->mb[13];
+ ha->phy_version[0] = mcp->mb[8] & 0xff;
+ ha->phy_version[1] = mcp->mb[9] >> 8;
+ ha->phy_version[2] = mcp->mb[9] & 0xff;
+ }
+ if (IS_QLA83XX(ha)) {
+ if (mcp->mb[6] & BIT_15) {
+ ha->fw_attributes_h = mcp->mb[15];
+ ha->fw_attributes_ext[0] = mcp->mb[16];
+ ha->fw_attributes_ext[1] = mcp->mb[17];
+ ql_dbg(ql_dbg_mbx, vha, 0x1139,
+ "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n",
+ __func__, mcp->mb[15], mcp->mb[6]);
+ } else
+ ql_dbg(ql_dbg_mbx, vha, 0x112f,
+ "%s: FwAttributes [Upper] invalid, MB6:%04x\n",
+ __func__, mcp->mb[6]);
}
+
failed:
if (rval != QLA_SUCCESS) {
/*EMPTY*/
@@ -859,6 +882,7 @@ qla2x00_abort_command(srb_t *sp)
scsi_qla_host_t *vha = fcport->vha;
struct qla_hw_data *ha = vha->hw;
struct req_que *req = vha->req;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
ql_dbg(ql_dbg_mbx, vha, 0x103b, "Entered %s.\n", __func__);
@@ -881,7 +905,7 @@ qla2x00_abort_command(srb_t *sp)
mcp->mb[1] = fcport->loop_id << 8;
mcp->mb[2] = (uint16_t)handle;
mcp->mb[3] = (uint16_t)(handle >> 16);
- mcp->mb[6] = (uint16_t)sp->cmd->device->lun;
+ mcp->mb[6] = (uint16_t)cmd->device->lun;
mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->tov = MBX_TOV_SECONDS;
@@ -1028,7 +1052,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
mcp->mb[9] = vha->vp_idx;
mcp->out_mb = MBX_9|MBX_0;
mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- if (IS_QLA8XXX_TYPE(vha->hw))
+ if (IS_CNA_CAPABLE(vha->hw))
mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
@@ -1052,7 +1076,7 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
} else {
ql_dbg(ql_dbg_mbx, vha, 0x1048, "Done %s.\n", __func__);
- if (IS_QLA8XXX_TYPE(vha->hw)) {
+ if (IS_CNA_CAPABLE(vha->hw)) {
vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
vha->fcoe_fcf_idx = mcp->mb[10];
vha->fcoe_vn_port_mac[5] = mcp->mb[11] >> 8;
@@ -1163,7 +1187,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
mcp->mb[6] = MSW(MSD(ha->init_cb_dma));
mcp->mb[7] = LSW(MSD(ha->init_cb_dma));
mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- if (IS_QLA81XX(ha) && ha->ex_init_cb->ex_version) {
+ if ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) && ha->ex_init_cb->ex_version) {
mcp->mb[1] = BIT_0;
mcp->mb[10] = MSW(ha->ex_init_cb_dma);
mcp->mb[11] = LSW(ha->ex_init_cb_dma);
@@ -1172,7 +1196,11 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
mcp->mb[14] = sizeof(*ha->ex_init_cb);
mcp->out_mb |= MBX_14|MBX_13|MBX_12|MBX_11|MBX_10;
}
- mcp->in_mb = MBX_0;
+ /* 1 and 2 should normally be captured. */
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA83XX(ha))
+ /* mb3 is additional info about the installed SFP. */
+ mcp->in_mb |= MBX_3;
mcp->buf_size = size;
mcp->flags = MBX_DMA_OUT;
mcp->tov = MBX_TOV_SECONDS;
@@ -1181,7 +1209,8 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
if (rval != QLA_SUCCESS) {
/*EMPTY*/
ql_dbg(ql_dbg_mbx, vha, 0x104d,
- "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ "Failed=%x mb[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x,.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
} else {
/*EMPTY*/
ql_dbg(ql_dbg_mbx, vha, 0x104e, "Done %s.\n", __func__);
@@ -1260,6 +1289,7 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
goto gpd_error_out;
if (IS_FWI2_CAPABLE(ha)) {
+ uint64_t zero = 0;
pd24 = (struct port_database_24xx *) pd;
/* Check for logged in state. */
@@ -1273,6 +1303,14 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
goto gpd_error_out;
}
+ if (fcport->loop_id == FC_NO_LOOP_ID ||
+ (memcmp(fcport->port_name, (uint8_t *)&zero, 8) &&
+ memcmp(fcport->port_name, pd24->port_name, 8))) {
+ /* We lost the device mid way. */
+ rval = QLA_NOT_LOGGED_IN;
+ goto gpd_error_out;
+ }
+
/* Names are little-endian. */
memcpy(fcport->node_name, pd24->node_name, WWN_SIZE);
memcpy(fcport->port_name, pd24->port_name, WWN_SIZE);
@@ -1289,6 +1327,8 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
else
fcport->port_type = FCT_TARGET;
} else {
+ uint64_t zero = 0;
+
/* Check for logged in state. */
if (pd->master_state != PD_STATE_PORT_LOGGED_IN &&
pd->slave_state != PD_STATE_PORT_LOGGED_IN) {
@@ -1301,6 +1341,14 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
goto gpd_error_out;
}
+ if (fcport->loop_id == FC_NO_LOOP_ID ||
+ (memcmp(fcport->port_name, (uint8_t *)&zero, 8) &&
+ memcmp(fcport->port_name, pd->port_name, 8))) {
+ /* We lost the device mid way. */
+ rval = QLA_NOT_LOGGED_IN;
+ goto gpd_error_out;
+ }
+
/* Names are little-endian. */
memcpy(fcport->node_name, pd->node_name, WWN_SIZE);
memcpy(fcport->port_name, pd->port_name, WWN_SIZE);
@@ -1481,7 +1529,7 @@ qla2x00_lip_reset(scsi_qla_host_t *vha)
ql_dbg(ql_dbg_mbx, vha, 0x105a, "Entered %s.\n", __func__);
- if (IS_QLA8XXX_TYPE(vha->hw)) {
+ if (IS_CNA_CAPABLE(vha->hw)) {
/* Logout across all FCFs. */
mcp->mb[0] = MBC_LIP_FULL_LOGIN;
mcp->mb[1] = BIT_1;
@@ -1622,7 +1670,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
lg->port_id[1] = area;
lg->port_id[2] = domain;
lg->vp_index = vha->vp_idx;
- rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
+ rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+ (ha->r_a_tov / 10 * 2) + 2);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x1063,
"Failed to issue login IOCB (%x).\n", rval);
@@ -1885,8 +1934,8 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
lg->port_id[1] = area;
lg->port_id[2] = domain;
lg->vp_index = vha->vp_idx;
-
- rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0);
+ rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+ (ha->r_a_tov / 10 * 2) + 2);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_mbx, vha, 0x106f,
"Failed to issue logout IOCB (%x).\n", rval);
@@ -2094,7 +2143,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
mcp->out_mb = MBX_0;
mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
- if (IS_QLA81XX(vha->hw))
+ if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw))
mcp->in_mb |= MBX_12;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
@@ -2121,7 +2170,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
*orig_iocb_cnt = mcp->mb[10];
if (vha->hw->flags.npiv_supported && max_npiv_vports)
*max_npiv_vports = mcp->mb[11];
- if (IS_QLA81XX(vha->hw) && max_fcfs)
+ if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) && max_fcfs)
*max_fcfs = mcp->mb[12];
}
@@ -2686,7 +2735,8 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
ql_dbg(ql_dbg_mbx, vha, 0x10aa, "Entered %s.\n", __func__);
- if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw))
+ if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) &&
+ !IS_QLA83XX(vha->hw))
return QLA_FUNCTION_FAILED;
if (unlikely(pci_channel_offline(vha->hw->pdev)))
@@ -2828,7 +2878,7 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
mcp->mb[0] = MBC_PORT_PARAMS;
mcp->mb[1] = loop_id;
mcp->mb[2] = BIT_0;
- if (IS_QLA8XXX_TYPE(vha->hw))
+ if (IS_CNA_CAPABLE(vha->hw))
mcp->mb[3] = port_speed & (BIT_5|BIT_4|BIT_3|BIT_2|BIT_1|BIT_0);
else
mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0);
@@ -3298,6 +3348,8 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
mcp->mb[12] = req->qos;
mcp->mb[11] = req->vp_idx;
mcp->mb[13] = req->rid;
+ if (IS_QLA83XX(ha))
+ mcp->mb[15] = 0;
reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
QLA_QUE_PAGE * req->id);
@@ -3311,12 +3363,21 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->flags = MBX_DMA_OUT;
- mcp->tov = 60;
+ mcp->tov = MBX_TOV_SECONDS * 2;
+
+ if (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+ mcp->in_mb |= MBX_1;
+ if (IS_QLA83XX(ha)) {
+ mcp->out_mb |= MBX_15;
+ /* debug q create issue in SR-IOV */
+ mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
+ }
spin_lock_irqsave(&ha->hardware_lock, flags);
if (!(req->options & BIT_0)) {
WRT_REG_DWORD(&reg->req_q_in, 0);
- WRT_REG_DWORD(&reg->req_q_out, 0);
+ if (!IS_QLA83XX(ha))
+ WRT_REG_DWORD(&reg->req_q_out, 0);
}
req->req_q_in = &reg->req_q_in;
req->req_q_out = &reg->req_q_out;
@@ -3354,6 +3415,8 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
mcp->mb[5] = rsp->length;
mcp->mb[14] = rsp->msix->entry;
mcp->mb[13] = rsp->rid;
+ if (IS_QLA83XX(ha))
+ mcp->mb[15] = 0;
reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
QLA_QUE_PAGE * rsp->id);
@@ -3367,12 +3430,23 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
mcp->in_mb = MBX_0;
mcp->flags = MBX_DMA_OUT;
- mcp->tov = 60;
+ mcp->tov = MBX_TOV_SECONDS * 2;
+
+ if (IS_QLA81XX(ha)) {
+ mcp->out_mb |= MBX_12|MBX_11|MBX_10;
+ mcp->in_mb |= MBX_1;
+ } else if (IS_QLA83XX(ha)) {
+ mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10;
+ mcp->in_mb |= MBX_1;
+ /* debug q create issue in SR-IOV */
+ mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
+ }
spin_lock_irqsave(&ha->hardware_lock, flags);
if (!(rsp->options & BIT_0)) {
WRT_REG_DWORD(&reg->rsp_q_out, 0);
- WRT_REG_DWORD(&reg->rsp_q_in, 0);
+ if (!IS_QLA83XX(ha))
+ WRT_REG_DWORD(&reg->rsp_q_in, 0);
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -3424,7 +3498,7 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size)
ql_dbg(ql_dbg_mbx, vha, 0x10dc, "Entered %s.\n", __func__);
- if (!IS_QLA81XX(vha->hw))
+ if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
@@ -3454,7 +3528,7 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable)
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_QLA81XX(vha->hw))
+ if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
return QLA_FUNCTION_FAILED;
ql_dbg(ql_dbg_mbx, vha, 0x10df, "Entered %s.\n", __func__);
@@ -3486,7 +3560,7 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish)
mbx_cmd_t mc;
mbx_cmd_t *mcp = &mc;
- if (!IS_QLA81XX(vha->hw))
+ if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
return QLA_FUNCTION_FAILED;
ql_dbg(ql_dbg_mbx, vha, 0x10e2, "Entered %s.\n", __func__);
@@ -3641,7 +3715,7 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
ql_dbg(ql_dbg_mbx, vha, 0x10ee, "Entered %s.\n", __func__);
- if (!IS_QLA8XXX_TYPE(vha->hw))
+ if (!IS_CNA_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_GET_XGMAC_STATS;
@@ -3680,7 +3754,7 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
ql_dbg(ql_dbg_mbx, vha, 0x10f1, "Entered %s.\n", __func__);
- if (!IS_QLA8XXX_TYPE(vha->hw))
+ if (!IS_CNA_CAPABLE(vha->hw))
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_GET_DCBX_PARAMS;
@@ -3775,7 +3849,7 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
mcp->out_mb = MBX_21|MBX_20|MBX_19|MBX_18|MBX_17|MBX_16|MBX_15|
MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0;
- if (IS_QLA8XXX_TYPE(vha->hw))
+ if (IS_CNA_CAPABLE(vha->hw))
mcp->out_mb |= MBX_2;
mcp->in_mb = MBX_19|MBX_18|MBX_3|MBX_2|MBX_1|MBX_0;
@@ -3813,7 +3887,7 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
memset(mcp->mb, 0 , sizeof(mcp->mb));
mcp->mb[0] = MBC_DIAGNOSTIC_ECHO;
mcp->mb[1] = mreq->options | BIT_6; /* BIT_6 specifies 64bit address */
- if (IS_QLA8XXX_TYPE(ha)) {
+ if (IS_CNA_CAPABLE(ha)) {
mcp->mb[1] |= BIT_15;
mcp->mb[2] = vha->fcoe_fcf_idx;
}
@@ -3831,13 +3905,14 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
mcp->out_mb = MBX_21|MBX_20|MBX_17|MBX_16|MBX_15|
MBX_14|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0;
- if (IS_QLA8XXX_TYPE(ha))
+ if (IS_CNA_CAPABLE(ha))
mcp->out_mb |= MBX_2;
mcp->in_mb = MBX_0;
- if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha))
+ if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
+ IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
mcp->in_mb |= MBX_1;
- if (IS_QLA8XXX_TYPE(ha))
+ if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
mcp->in_mb |= MBX_3;
mcp->tov = MBX_TOV_SECONDS;
@@ -3976,6 +4051,7 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
return rval;
}
+
int
qla2x00_get_data_rate(scsi_qla_host_t *vha)
{
@@ -3993,6 +4069,8 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha)
mcp->mb[1] = 0;
mcp->out_mb = MBX_1|MBX_0;
mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA83XX(ha))
+ mcp->in_mb |= MBX_3;
mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -4018,7 +4096,7 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb)
ql_dbg(ql_dbg_mbx, vha, 0x1109, "Entered %s.\n", __func__);
- if (!IS_QLA81XX(ha))
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
return QLA_FUNCTION_FAILED;
mcp->mb[0] = MBC_GET_PORT_CONFIG;
mcp->out_mb = MBX_0;
@@ -4299,6 +4377,90 @@ qla82xx_md_get_template(scsi_qla_host_t *vha)
}
int
+qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1133, "Entered %s.\n", __func__);
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_SET_LED_CONFIG;
+ mcp->mb[1] = led_cfg[0];
+ mcp->mb[2] = led_cfg[1];
+ if (IS_QLA8031(ha)) {
+ mcp->mb[3] = led_cfg[2];
+ mcp->mb[4] = led_cfg[3];
+ mcp->mb[5] = led_cfg[4];
+ mcp->mb[6] = led_cfg[5];
+ }
+
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA8031(ha))
+ mcp->out_mb |= MBX_6|MBX_5|MBX_4|MBX_3;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1134,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1135, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1136, "Entered %s.\n", __func__);
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_GET_LED_CONFIG;
+
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA8031(ha))
+ mcp->in_mb |= MBX_6|MBX_5|MBX_4|MBX_3;
+ mcp->tov = 30;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1137,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ led_cfg[0] = mcp->mb[1];
+ led_cfg[1] = mcp->mb[2];
+ if (IS_QLA8031(ha)) {
+ led_cfg[2] = mcp->mb[3];
+ led_cfg[3] = mcp->mb[4];
+ led_cfg[4] = mcp->mb[5];
+ led_cfg[5] = mcp->mb[6];
+ }
+ ql_dbg(ql_dbg_mbx, vha, 0x1138, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
{
int rval;
@@ -4321,7 +4483,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
mcp->out_mb = MBX_7|MBX_0;
mcp->in_mb = MBX_0;
- mcp->tov = 30;
+ mcp->tov = MBX_TOV_SECONDS;
mcp->flags = 0;
rval = qla2x00_mailbox_command(vha, mcp);
@@ -4335,3 +4497,75 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
return rval;
}
+
+int
+qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA83XX(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1130, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_WRITE_REMOTE_REG;
+ mcp->mb[1] = LSW(reg);
+ mcp->mb[2] = MSW(reg);
+ mcp->mb[3] = LSW(data);
+ mcp->mb[4] = MSW(data);
+ mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1131,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1132,
+ "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x113b,
+ "Implicit LOGO Unsupported.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+
+ ql_dbg(ql_dbg_mbx, vha, 0x113c, "Done %s.\n", __func__);
+
+ /* Perform Implicit LOGO. */
+ mcp->mb[0] = MBC_PORT_LOGOUT;
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = BIT_15;
+ mcp->out_mb = MBX_10|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ ql_dbg(ql_dbg_mbx, vha, 0x113d,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ else
+ ql_dbg(ql_dbg_mbx, vha, 0x113e, "Done %s.\n", __func__);
+
+ return rval;
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
index f488cc69fc79..aa062a1b0ca4 100644
--- a/drivers/scsi/qla2xxx/qla_mid.c
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -479,7 +479,7 @@ qla24xx_create_vhost(struct fc_vport *fc_vport)
host->max_channel = MAX_BUSES - 1;
host->max_lun = ql2xmaxlun;
host->unique_id = host->host_no;
- host->max_id = MAX_TARGETS_2200;
+ host->max_id = ha->max_fibre_devices;
host->transportt = qla2xxx_transport_vport_template;
ql_dbg(ql_dbg_vport, vha, 0xa007,
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 270ba3130fde..f0528539bbbc 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -908,27 +908,37 @@ qla82xx_wait_rom_done(struct qla_hw_data *ha)
return 0;
}
+int
+qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag)
+{
+ uint32_t off_value, rval = 0;
+
+ WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase),
+ (off & 0xFFFF0000));
+
+ /* Read back value to make sure write has gone through */
+ RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+ off_value = (off & 0x0000FFFF);
+
+ if (flag)
+ WRT_REG_DWORD((void *)
+ (off_value + CRB_INDIRECT_2M + ha->nx_pcibase),
+ data);
+ else
+ rval = RD_REG_DWORD((void *)
+ (off_value + CRB_INDIRECT_2M + ha->nx_pcibase));
+
+ return rval;
+}
+
static int
qla82xx_do_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
{
- scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ /* Dword reads to flash. */
+ qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW, (addr & 0xFFFF0000), 1);
+ *valp = qla82xx_md_rw_32(ha, MD_DIRECT_ROM_READ_BASE +
+ (addr & 0x0000FFFF), 0, 0);
- qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr);
- qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0);
- qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3);
- qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0xb);
- qla82xx_wait_rom_busy(ha);
- if (qla82xx_wait_rom_done(ha)) {
- ql_log(ql_log_fatal, vha, 0x00ba,
- "Error waiting for rom done.\n");
- return -1;
- }
- /* Reset abyte_cnt and dummy_byte_cnt */
- qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0);
- udelay(10);
- cond_resched();
- qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0);
- *valp = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA);
return 0;
}
@@ -2040,8 +2050,8 @@ qla82xx_intr_handler(int irq, void *dev_id)
rsp = (struct rsp_que *) dev_id;
if (!rsp) {
- printk(KERN_INFO
- "%s(): NULL response queue pointer.\n", __func__);
+ ql_log(ql_log_info, NULL, 0xb054,
+ "%s: NULL response queue pointer.\n", __func__);
return IRQ_NONE;
}
ha = rsp->hw;
@@ -3136,12 +3146,7 @@ qla82xx_check_md_needed(scsi_qla_host_t *vha)
fw_minor_version = ha->fw_minor_version;
fw_subminor_version = ha->fw_subminor_version;
- rval = qla2x00_get_fw_version(vha, &ha->fw_major_version,
- &ha->fw_minor_version, &ha->fw_subminor_version,
- &ha->fw_attributes, &ha->fw_memory_size,
- ha->mpi_version, &ha->mpi_capabilities,
- ha->phy_version);
-
+ rval = qla2x00_get_fw_version(vha);
if (rval != QLA_SUCCESS)
return rval;
@@ -3150,7 +3155,6 @@ qla82xx_check_md_needed(scsi_qla_host_t *vha)
if (fw_major_version != ha->fw_major_version ||
fw_minor_version != ha->fw_minor_version ||
fw_subminor_version != ha->fw_subminor_version) {
-
ql_log(ql_log_info, vha, 0xb02d,
"Firmware version differs "
"Previous version: %d:%d:%d - "
@@ -3614,7 +3618,7 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha)
for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
sp = req->outstanding_cmds[cnt];
if (sp) {
- if (!sp->ctx ||
+ if (!sp->u.scmd.ctx ||
(sp->flags & SRB_FCP_CMND_DMA_VALID)) {
spin_unlock_irqrestore(
&ha->hardware_lock, flags);
@@ -3645,29 +3649,6 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha)
}
/* Minidump related functions */
-int
-qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag)
-{
- uint32_t off_value, rval = 0;
-
- WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase),
- (off & 0xFFFF0000));
-
- /* Read back value to make sure write has gone through */
- RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
- off_value = (off & 0x0000FFFF);
-
- if (flag)
- WRT_REG_DWORD((void *)
- (off_value + CRB_INDIRECT_2M + ha->nx_pcibase),
- data);
- else
- rval = RD_REG_DWORD((void *)
- (off_value + CRB_INDIRECT_2M + ha->nx_pcibase));
-
- return rval;
-}
-
static int
qla82xx_minidump_process_control(scsi_qla_host_t *vha,
qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
@@ -4117,8 +4098,9 @@ qla82xx_md_collect(scsi_qla_host_t *vha)
data_ptr = (uint32_t *)ha->md_dump;
if (ha->fw_dumped) {
- ql_log(ql_log_info, vha, 0xb037,
- "Firmware dump available to retrive\n");
+ ql_log(ql_log_warn, vha, 0xb037,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n", ha->fw_dump);
goto md_failed;
}
@@ -4161,7 +4143,7 @@ qla82xx_md_collect(scsi_qla_host_t *vha)
total_data_size = ha->md_dump_size;
- ql_dbg(ql_log_info, vha, 0xb03d,
+ ql_dbg(ql_dbg_p3p, vha, 0xb03d,
"Total minidump data_size 0x%x to be captured\n", total_data_size);
/* Check whether template obtained is valid */
@@ -4284,7 +4266,7 @@ skip_nxt_entry:
}
if (data_collected != total_data_size) {
- ql_dbg(ql_log_warn, vha, 0xb043,
+ ql_dbg(ql_dbg_p3p, vha, 0xb043,
"MiniDump data mismatch: Data collected: [0x%x],"
"total_data_size:[0x%x]\n",
data_collected, total_data_size);
diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h
index 57a226be339a..4ac50e274661 100644
--- a/drivers/scsi/qla2xxx/qla_nx.h
+++ b/drivers/scsi/qla2xxx/qla_nx.h
@@ -888,7 +888,8 @@ struct ct6_dsd {
};
#define MBC_TOGGLE_INTERRUPT 0x10
-#define MBC_SET_LED_CONFIG 0x125
+#define MBC_SET_LED_CONFIG 0x125 /* FCoE specific LED control */
+#define MBC_GET_LED_CONFIG 0x126 /* FCoE specific LED control */
/* Flash offset */
#define FLT_REG_BOOTLOAD_82XX 0x72
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index 036030c95339..a2f999273a5f 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -304,7 +304,6 @@ static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t,
struct req_que **, struct rsp_que **);
static void qla2x00_free_fw_dump(struct qla_hw_data *);
static void qla2x00_mem_free(struct qla_hw_data *);
-static void qla2x00_sp_free_dma(srb_t *);
/* -------------------------------------------------------------------------- */
static int qla2x00_alloc_queues(struct qla_hw_data *ha)
@@ -559,28 +558,75 @@ qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str)
return str;
}
-static inline srb_t *
-qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport,
- struct scsi_cmnd *cmd)
+void
+qla2x00_sp_free_dma(void *vha, void *ptr)
{
- srb_t *sp;
- struct qla_hw_data *ha = vha->hw;
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ void *ctx = GET_CMD_CTX_SP(sp);
- sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC);
- if (!sp) {
- ql_log(ql_log_warn, vha, 0x3006,
- "Memory allocation failed for sp.\n");
- return sp;
+ if (sp->flags & SRB_DMA_VALID) {
+ scsi_dma_unmap(cmd);
+ sp->flags &= ~SRB_DMA_VALID;
}
- atomic_set(&sp->ref_count, 1);
- sp->fcport = fcport;
- sp->cmd = cmd;
- sp->flags = 0;
- CMD_SP(cmd) = (void *)sp;
- sp->ctx = NULL;
+ if (sp->flags & SRB_CRC_PROT_DMA_VALID) {
+ dma_unmap_sg(&ha->pdev->dev, scsi_prot_sglist(cmd),
+ scsi_prot_sg_count(cmd), cmd->sc_data_direction);
+ sp->flags &= ~SRB_CRC_PROT_DMA_VALID;
+ }
- return sp;
+ if (sp->flags & SRB_CRC_CTX_DSD_VALID) {
+ /* List assured to be having elements */
+ qla2x00_clean_dsd_pool(ha, sp);
+ sp->flags &= ~SRB_CRC_CTX_DSD_VALID;
+ }
+
+ if (sp->flags & SRB_CRC_CTX_DMA_VALID) {
+ dma_pool_free(ha->dl_dma_pool, ctx,
+ ((struct crc_context *)ctx)->crc_ctx_dma);
+ sp->flags &= ~SRB_CRC_CTX_DMA_VALID;
+ }
+
+ if (sp->flags & SRB_FCP_CMND_DMA_VALID) {
+ struct ct6_dsd *ctx1 = (struct ct6_dsd *)ctx;
+
+ dma_pool_free(ha->fcp_cmnd_dma_pool, ctx1->fcp_cmnd,
+ ctx1->fcp_cmnd_dma);
+ list_splice(&ctx1->dsd_list, &ha->gbl_dsd_list);
+ ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt;
+ ha->gbl_dsd_avail += ctx1->dsd_use_cnt;
+ mempool_free(ctx1, ha->ctx_mempool);
+ ctx1 = NULL;
+ }
+
+ CMD_SP(cmd) = NULL;
+ mempool_free(sp, ha->srb_mempool);
+}
+
+static void
+qla2x00_sp_compl(void *data, void *ptr, int res)
+{
+ struct qla_hw_data *ha = (struct qla_hw_data *)data;
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+
+ cmd->result = res;
+
+ if (atomic_read(&sp->ref_count) == 0) {
+ ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015,
+ "SP reference-count to ZERO -- sp=%p cmd=%p.\n",
+ sp, GET_CMD_SP(sp));
+ if (ql2xextended_error_logging & ql_dbg_io)
+ BUG();
+ return;
+ }
+ if (!atomic_dec_and_test(&sp->ref_count))
+ return;
+
+ qla2x00_sp_free_dma(ha, sp);
+ cmd->scsi_done(cmd);
}
static int
@@ -644,10 +690,17 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
goto qc24_target_busy;
}
- sp = qla2x00_get_new_sp(base_vha, fcport, cmd);
+ sp = qla2x00_get_sp(base_vha, fcport, GFP_ATOMIC);
if (!sp)
goto qc24_host_busy;
+ sp->u.scmd.cmd = cmd;
+ sp->type = SRB_SCSI_CMD;
+ atomic_set(&sp->ref_count, 1);
+ CMD_SP(cmd) = (void *)sp;
+ sp->free = qla2x00_sp_free_dma;
+ sp->done = qla2x00_sp_compl;
+
rval = ha->isp_ops->start_scsi(sp);
if (rval != QLA_SUCCESS) {
ql_dbg(ql_dbg_io, vha, 0x3013,
@@ -658,8 +711,7 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
return 0;
qc24_host_busy_free_sp:
- qla2x00_sp_free_dma(sp);
- mempool_free(sp, ha->srb_mempool);
+ qla2x00_sp_free_dma(ha, sp);
qc24_host_busy:
return SCSI_MLQUEUE_HOST_BUSY;
@@ -893,7 +945,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
}
spin_lock_irqsave(&ha->hardware_lock, flags);
- qla2x00_sp_compl(ha, sp);
+ sp->done(ha, sp, 0);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
/* Did the command return during mailbox execution? */
@@ -925,6 +977,7 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
struct qla_hw_data *ha = vha->hw;
struct req_que *req;
srb_t *sp;
+ struct scsi_cmnd *cmd;
status = QLA_SUCCESS;
@@ -935,28 +988,29 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
sp = req->outstanding_cmds[cnt];
if (!sp)
continue;
- if ((sp->ctx) && !IS_PROT_IO(sp))
+ if (sp->type != SRB_SCSI_CMD)
continue;
if (vha->vp_idx != sp->fcport->vha->vp_idx)
continue;
match = 0;
+ cmd = GET_CMD_SP(sp);
switch (type) {
case WAIT_HOST:
match = 1;
break;
case WAIT_TARGET:
- match = sp->cmd->device->id == t;
+ match = cmd->device->id == t;
break;
case WAIT_LUN:
- match = (sp->cmd->device->id == t &&
- sp->cmd->device->lun == l);
+ match = (cmd->device->id == t &&
+ cmd->device->lun == l);
break;
}
if (!match)
continue;
spin_unlock_irqrestore(&ha->hardware_lock, flags);
- status = qla2x00_eh_wait_on_command(sp->cmd);
+ status = qla2x00_eh_wait_on_command(cmd);
spin_lock_irqsave(&ha->hardware_lock, flags);
}
spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -1219,7 +1273,7 @@ qla2x00_loop_reset(scsi_qla_host_t *vha)
}
}
- if (ha->flags.enable_lip_full_login && !IS_QLA8XXX_TYPE(ha)) {
+ if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) {
ret = qla2x00_full_login_lip(vha);
if (ret != QLA_SUCCESS) {
ql_dbg(ql_dbg_taskm, vha, 0x802d,
@@ -1249,7 +1303,6 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
int que, cnt;
unsigned long flags;
srb_t *sp;
- struct srb_ctx *ctx;
struct qla_hw_data *ha = vha->hw;
struct req_que *req;
@@ -1262,31 +1315,7 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
sp = req->outstanding_cmds[cnt];
if (sp) {
req->outstanding_cmds[cnt] = NULL;
- if (!sp->ctx ||
- (sp->flags & SRB_FCP_CMND_DMA_VALID) ||
- IS_PROT_IO(sp)) {
- sp->cmd->result = res;
- qla2x00_sp_compl(ha, sp);
- } else {
- ctx = sp->ctx;
- if (ctx->type == SRB_ELS_CMD_RPT ||
- ctx->type == SRB_ELS_CMD_HST ||
- ctx->type == SRB_CT_CMD) {
- struct fc_bsg_job *bsg_job =
- ctx->u.bsg_job;
- if (bsg_job->request->msgcode
- == FC_BSG_HST_CT)
- kfree(sp->fcport);
- bsg_job->req->errors = 0;
- bsg_job->reply->result = res;
- bsg_job->job_done(bsg_job);
- kfree(sp->ctx);
- mempool_free(sp,
- ha->srb_mempool);
- } else {
- ctx->u.iocb_cmd->free(sp);
- }
- }
+ sp->done(vha, sp, res);
}
}
}
@@ -1488,9 +1517,6 @@ qla2x00_iospace_config(struct qla_hw_data *ha)
uint16_t msix;
int cpus;
- if (IS_QLA82XX(ha))
- return qla82xx_iospace_config(ha);
-
if (pci_request_selected_regions(ha->pdev, ha->bars,
QLA2XXX_DRIVER_NAME)) {
ql_log_pci(ql_log_fatal, ha->pdev, 0x0011,
@@ -1593,6 +1619,96 @@ iospace_error_exit:
}
+static int
+qla83xx_iospace_config(struct qla_hw_data *ha)
+{
+ uint16_t msix;
+ int cpus;
+
+ if (pci_request_selected_regions(ha->pdev, ha->bars,
+ QLA2XXX_DRIVER_NAME)) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0117,
+ "Failed to reserve PIO/MMIO regions (%s), aborting.\n",
+ pci_name(ha->pdev));
+
+ goto iospace_error_exit;
+ }
+
+ /* Use MMIO operations for all accesses. */
+ if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) {
+ ql_log_pci(ql_log_warn, ha->pdev, 0x0118,
+ "Invalid pci I/O region size (%s).\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+ if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
+ ql_log_pci(ql_log_warn, ha->pdev, 0x0119,
+ "Invalid PCI mem region size (%s), aborting\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ ha->iobase = ioremap(pci_resource_start(ha->pdev, 0), MIN_IOBASE_LEN);
+ if (!ha->iobase) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x011a,
+ "Cannot remap MMIO (%s), aborting.\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ /* 64bit PCI BAR - BAR2 will correspoond to region 4 */
+ /* 83XX 26XX always use MQ type access for queues
+ * - mbar 2, a.k.a region 4 */
+ ha->max_req_queues = ha->max_rsp_queues = 1;
+ ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 4),
+ pci_resource_len(ha->pdev, 4));
+
+ if (!ha->mqiobase) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x011d,
+ "BAR2/region4 not enabled\n");
+ goto mqiobase_exit;
+ }
+
+ ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2),
+ pci_resource_len(ha->pdev, 2));
+ if (ha->msixbase) {
+ /* Read MSIX vector size of the board */
+ pci_read_config_word(ha->pdev,
+ QLA_83XX_PCI_MSIX_CONTROL, &msix);
+ ha->msix_count = msix;
+ /* Max queues are bounded by available msix vectors */
+ /* queue 0 uses two msix vectors */
+ if (ql2xmultique_tag) {
+ cpus = num_online_cpus();
+ ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ?
+ (cpus + 1) : (ha->msix_count - 1);
+ ha->max_req_queues = 2;
+ } else if (ql2xmaxqueues > 1) {
+ ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
+ QLA_MQ_SIZE : ql2xmaxqueues;
+ ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc00c,
+ "QoS mode set, max no of request queues:%d.\n",
+ ha->max_req_queues);
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
+ "QoS mode set, max no of request queues:%d.\n",
+ ha->max_req_queues);
+ }
+ ql_log_pci(ql_log_info, ha->pdev, 0x011c,
+ "MSI-X vector count: %d.\n", msix);
+ } else
+ ql_log_pci(ql_log_info, ha->pdev, 0x011e,
+ "BAR 1 not enabled.\n");
+
+mqiobase_exit:
+ ha->msix_count = ha->max_rsp_queues + 1;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011f,
+ "MSIX Count:%d.\n", ha->msix_count);
+ return 0;
+
+iospace_error_exit:
+ return -ENOMEM;
+}
+
static struct isp_operations qla2100_isp_ops = {
.pci_config = qla2100_pci_config,
.reset_chip = qla2x00_reset_chip,
@@ -1769,7 +1885,7 @@ static struct isp_operations qla81xx_isp_ops = {
.fw_dump = qla81xx_fw_dump,
.beacon_on = qla24xx_beacon_on,
.beacon_off = qla24xx_beacon_off,
- .beacon_blink = qla24xx_beacon_blink,
+ .beacon_blink = qla83xx_beacon_blink,
.read_optrom = qla25xx_read_optrom_data,
.write_optrom = qla24xx_write_optrom_data,
.get_flash_version = qla24xx_get_flash_version,
@@ -1815,6 +1931,43 @@ static struct isp_operations qla82xx_isp_ops = {
.iospace_config = qla82xx_iospace_config,
};
+static struct isp_operations qla83xx_isp_ops = {
+ .pci_config = qla25xx_pci_config,
+ .reset_chip = qla24xx_reset_chip,
+ .chip_diag = qla24xx_chip_diag,
+ .config_rings = qla24xx_config_rings,
+ .reset_adapter = qla24xx_reset_adapter,
+ .nvram_config = qla81xx_nvram_config,
+ .update_fw_options = qla81xx_update_fw_options,
+ .load_risc = qla81xx_load_risc,
+ .pci_info_str = qla24xx_pci_info_str,
+ .fw_version_str = qla24xx_fw_version_str,
+ .intr_handler = qla24xx_intr_handler,
+ .enable_intrs = qla24xx_enable_intrs,
+ .disable_intrs = qla24xx_disable_intrs,
+ .abort_command = qla24xx_abort_command,
+ .target_reset = qla24xx_abort_target,
+ .lun_reset = qla24xx_lun_reset,
+ .fabric_login = qla24xx_login_fabric,
+ .fabric_logout = qla24xx_fabric_logout,
+ .calc_req_entries = NULL,
+ .build_iocbs = NULL,
+ .prep_ms_iocb = qla24xx_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
+ .read_nvram = NULL,
+ .write_nvram = NULL,
+ .fw_dump = qla83xx_fw_dump,
+ .beacon_on = qla24xx_beacon_on,
+ .beacon_off = qla24xx_beacon_off,
+ .beacon_blink = qla83xx_beacon_blink,
+ .read_optrom = qla25xx_read_optrom_data,
+ .write_optrom = qla24xx_write_optrom_data,
+ .get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_dif_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla83xx_iospace_config,
+};
+
static inline void
qla2x00_set_isp_flags(struct qla_hw_data *ha)
{
@@ -1909,6 +2062,22 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha)
/* Initialize 82XX ISP flags */
qla82xx_init_flags(ha);
break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2031:
+ ha->device_type |= DT_ISP2031;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->device_type |= DT_T10_PI;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP8031:
+ ha->device_type |= DT_ISP8031;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->device_type |= DT_T10_PI;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
}
if (IS_QLA82XX(ha))
@@ -1966,7 +2135,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
char pci_info[30];
char fw_str[30];
struct scsi_host_template *sht;
- int bars, max_id, mem_only = 0;
+ int bars, mem_only = 0;
uint16_t req_length = 0, rsp_length = 0;
struct req_que *req = NULL;
struct rsp_que *rsp = NULL;
@@ -1980,7 +2149,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 ||
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 ||
pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001 ||
- pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021) {
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2031 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8031) {
bars = pci_select_bars(pdev, IORESOURCE_MEM);
mem_only = 1;
ql_dbg_pci(ql_dbg_init, pdev, 0x0007,
@@ -2020,9 +2191,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
qla2x00_set_isp_flags(ha);
/* Set EEH reset type to fundamental if required by hba */
- if ( IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha)) {
+ if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha))
pdev->needs_freset = 1;
- }
ha->prev_topology = 0;
ha->init_cb_size = sizeof(init_cb_t);
@@ -2030,9 +2200,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->optrom_size = OPTROM_SIZE_2300;
/* Assign ISP specific operations. */
- max_id = MAX_TARGETS_2200;
if (IS_QLA2100(ha)) {
- max_id = MAX_TARGETS_2100;
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
req_length = REQUEST_ENTRY_CNT_2100;
rsp_length = RESPONSE_ENTRY_CNT_2100;
@@ -2044,6 +2213,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->nvram_data_off = ~0;
ha->isp_ops = &qla2100_isp_ops;
} else if (IS_QLA2200(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
ha->mbx_count = MAILBOX_REGISTER_COUNT_2200;
req_length = REQUEST_ENTRY_CNT_2200;
rsp_length = RESPONSE_ENTRY_CNT_2100;
@@ -2055,6 +2225,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->nvram_data_off = ~0;
ha->isp_ops = &qla2100_isp_ops;
} else if (IS_QLA23XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
req_length = REQUEST_ENTRY_CNT_2200;
rsp_length = RESPONSE_ENTRY_CNT_2300;
@@ -2068,6 +2239,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->nvram_data_off = ~0;
ha->isp_ops = &qla2300_isp_ops;
} else if (IS_QLA24XX_TYPE(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
req_length = REQUEST_ENTRY_CNT_24XX;
rsp_length = RESPONSE_ENTRY_CNT_2300;
@@ -2082,6 +2254,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
} else if (IS_QLA25XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
req_length = REQUEST_ENTRY_CNT_24XX;
rsp_length = RESPONSE_ENTRY_CNT_2300;
@@ -2096,6 +2269,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
} else if (IS_QLA81XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
req_length = REQUEST_ENTRY_CNT_24XX;
rsp_length = RESPONSE_ENTRY_CNT_2300;
@@ -2110,6 +2284,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->nvram_conf_off = ~0;
ha->nvram_data_off = ~0;
} else if (IS_QLA82XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
ha->mbx_count = MAILBOX_REGISTER_COUNT;
req_length = REQUEST_ENTRY_CNT_82XX;
rsp_length = RESPONSE_ENTRY_CNT_82XX;
@@ -2123,14 +2298,31 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
+ } else if (IS_QLA83XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
+ ha->gid_list_info_size = 8;
+ ha->optrom_size = OPTROM_SIZE_83XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+ ha->isp_ops = &qla83xx_isp_ops;
+ ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
+ ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
+ ha->nvram_conf_off = ~0;
+ ha->nvram_data_off = ~0;
}
+
ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
"mbx_count=%d, req_length=%d, "
"rsp_length=%d, max_loop_id=%d, init_cb_size=%d, "
- "gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, .\n",
+ "gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, "
+ "max_fibre_devices=%d.\n",
ha->mbx_count, req_length, rsp_length, ha->max_loop_id,
ha->init_cb_size, ha->gid_list_info_size, ha->optrom_size,
- ha->nvram_npiv_size);
+ ha->nvram_npiv_size, ha->max_fibre_devices);
ql_dbg_pci(ql_dbg_init, pdev, 0x001f,
"isp_ops=%p, flash_conf_off=%d, "
"flash_data_off=%d, nvram_conf_off=%d, nvram_data_off=%d.\n",
@@ -2204,7 +2396,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
"mgmt_svr_loop_id=%d, sg_tablesize=%d.\n",
host->can_queue, base_vha->req,
base_vha->mgmt_svr_loop_id, host->sg_tablesize);
- host->max_id = max_id;
+ host->max_id = ha->max_fibre_devices;
host->this_id = 255;
host->cmd_per_lun = 3;
host->unique_id = host->host_no;
@@ -2251,7 +2443,7 @@ que_init:
req->req_q_out = &ha->iobase->isp24.req_q_out;
rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in;
rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out;
- if (ha->mqenable) {
+ if (ha->mqenable || IS_QLA83XX(ha)) {
req->req_q_in = &ha->mqiobase->isp25mq.req_q_in;
req->req_q_out = &ha->mqiobase->isp25mq.req_q_out;
rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in;
@@ -2552,6 +2744,9 @@ qla2x00_remove_one(struct pci_dev *pdev)
if (ha->mqiobase)
iounmap(ha->mqiobase);
+
+ if (IS_QLA83XX(ha) && ha->msixbase)
+ iounmap(ha->msixbase);
}
pci_release_selected_regions(ha->pdev, ha->bars);
@@ -2751,8 +2946,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
if (!ha->init_cb)
goto fail;
- ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE,
- &ha->gid_list_dma, GFP_KERNEL);
+ ha->gid_list = dma_alloc_coherent(&ha->pdev->dev,
+ qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL);
if (!ha->gid_list)
goto fail_free_init_cb;
@@ -2893,7 +3088,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
ha->npiv_info = NULL;
/* Get consistent memory allocated for EX-INIT-CB. */
- if (IS_QLA8XXX_TYPE(ha)) {
+ if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) {
ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
&ha->ex_init_cb_dma);
if (!ha->ex_init_cb)
@@ -2967,7 +3162,8 @@ fail_free_srb_mempool:
mempool_destroy(ha->srb_mempool);
ha->srb_mempool = NULL;
fail_free_gid_list:
- dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
+ dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+ ha->gid_list,
ha->gid_list_dma);
ha->gid_list = NULL;
ha->gid_list_dma = 0;
@@ -3045,9 +3241,6 @@ qla2x00_mem_free(struct qla_hw_data *ha)
if (ha->sfp_data)
dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
- if (ha->edc_data)
- dma_pool_free(ha->s_dma_pool, ha->edc_data, ha->edc_data_dma);
-
if (ha->ms_iocb)
dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
@@ -3062,8 +3255,8 @@ qla2x00_mem_free(struct qla_hw_data *ha)
dma_pool_destroy(ha->s_dma_pool);
if (ha->gid_list)
- dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list,
- ha->gid_list_dma);
+ dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+ ha->gid_list, ha->gid_list_dma);
if (IS_QLA82XX(ha)) {
if (!list_empty(&ha->gbl_dsd_list)) {
@@ -3095,6 +3288,7 @@ qla2x00_mem_free(struct qla_hw_data *ha)
vfree(ha->optrom_buffer);
kfree(ha->nvram);
kfree(ha->npiv_info);
+ kfree(ha->swl);
ha->srb_mempool = NULL;
ha->ctx_mempool = NULL;
@@ -3661,75 +3855,6 @@ qla2x00_rst_aen(scsi_qla_host_t *vha)
}
}
-static void
-qla2x00_sp_free_dma(srb_t *sp)
-{
- struct scsi_cmnd *cmd = sp->cmd;
- struct qla_hw_data *ha = sp->fcport->vha->hw;
-
- if (sp->flags & SRB_DMA_VALID) {
- scsi_dma_unmap(cmd);
- sp->flags &= ~SRB_DMA_VALID;
- }
-
- if (sp->flags & SRB_CRC_PROT_DMA_VALID) {
- dma_unmap_sg(&ha->pdev->dev, scsi_prot_sglist(cmd),
- scsi_prot_sg_count(cmd), cmd->sc_data_direction);
- sp->flags &= ~SRB_CRC_PROT_DMA_VALID;
- }
-
- if (sp->flags & SRB_CRC_CTX_DSD_VALID) {
- /* List assured to be having elements */
- qla2x00_clean_dsd_pool(ha, sp);
- sp->flags &= ~SRB_CRC_CTX_DSD_VALID;
- }
-
- if (sp->flags & SRB_CRC_CTX_DMA_VALID) {
- dma_pool_free(ha->dl_dma_pool, sp->ctx,
- ((struct crc_context *)sp->ctx)->crc_ctx_dma);
- sp->flags &= ~SRB_CRC_CTX_DMA_VALID;
- }
-
- if (sp->flags & SRB_FCP_CMND_DMA_VALID) {
- struct ct6_dsd *ctx = sp->ctx;
- dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd,
- ctx->fcp_cmnd_dma);
- list_splice(&ctx->dsd_list, &ha->gbl_dsd_list);
- ha->gbl_dsd_inuse -= ctx->dsd_use_cnt;
- ha->gbl_dsd_avail += ctx->dsd_use_cnt;
- mempool_free(sp->ctx, ha->ctx_mempool);
- sp->ctx = NULL;
- }
-
- CMD_SP(cmd) = NULL;
-}
-
-static void
-qla2x00_sp_final_compl(struct qla_hw_data *ha, srb_t *sp)
-{
- struct scsi_cmnd *cmd = sp->cmd;
-
- qla2x00_sp_free_dma(sp);
- mempool_free(sp, ha->srb_mempool);
- cmd->scsi_done(cmd);
-}
-
-void
-qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp)
-{
- if (atomic_read(&sp->ref_count) == 0) {
- ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015,
- "SP reference-count to ZERO -- sp=%p cmd=%p.\n",
- sp, sp->cmd);
- if (ql2xextended_error_logging & ql_dbg_io)
- BUG();
- return;
- }
- if (!atomic_dec_and_test(&sp->ref_count))
- return;
- qla2x00_sp_final_compl(ha, sp);
-}
-
/**************************************************************************
* qla2x00_timer
*
@@ -3800,7 +3925,7 @@ qla2x00_timer(scsi_qla_host_t *vha)
sp = req->outstanding_cmds[index];
if (!sp)
continue;
- if (sp->ctx && !IS_PROT_IO(sp))
+ if (sp->type != SRB_SCSI_CMD)
continue;
sfcp = sp->fcport;
if (!(sfcp->flags & FCF_FCP2_DEVICE))
@@ -3889,7 +4014,7 @@ qla2x00_timer(scsi_qla_host_t *vha)
/* Firmware interface routines. */
-#define FW_BLOBS 8
+#define FW_BLOBS 10
#define FW_ISP21XX 0
#define FW_ISP22XX 1
#define FW_ISP2300 2
@@ -3898,6 +4023,8 @@ qla2x00_timer(scsi_qla_host_t *vha)
#define FW_ISP25XX 5
#define FW_ISP81XX 6
#define FW_ISP82XX 7
+#define FW_ISP2031 8
+#define FW_ISP8031 9
#define FW_FILE_ISP21XX "ql2100_fw.bin"
#define FW_FILE_ISP22XX "ql2200_fw.bin"
@@ -3907,6 +4034,8 @@ qla2x00_timer(scsi_qla_host_t *vha)
#define FW_FILE_ISP25XX "ql2500_fw.bin"
#define FW_FILE_ISP81XX "ql8100_fw.bin"
#define FW_FILE_ISP82XX "ql8200_fw.bin"
+#define FW_FILE_ISP2031 "ql2600_fw.bin"
+#define FW_FILE_ISP8031 "ql8300_fw.bin"
static DEFINE_MUTEX(qla_fw_lock);
@@ -3919,6 +4048,8 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
{ .name = FW_FILE_ISP25XX, },
{ .name = FW_FILE_ISP81XX, },
{ .name = FW_FILE_ISP82XX, },
+ { .name = FW_FILE_ISP2031, },
+ { .name = FW_FILE_ISP8031, },
};
struct fw_blob *
@@ -3927,7 +4058,6 @@ qla2x00_request_firmware(scsi_qla_host_t *vha)
struct qla_hw_data *ha = vha->hw;
struct fw_blob *blob;
- blob = NULL;
if (IS_QLA2100(ha)) {
blob = &qla_fw_blobs[FW_ISP21XX];
} else if (IS_QLA2200(ha)) {
@@ -3944,6 +4074,12 @@ qla2x00_request_firmware(scsi_qla_host_t *vha)
blob = &qla_fw_blobs[FW_ISP81XX];
} else if (IS_QLA82XX(ha)) {
blob = &qla_fw_blobs[FW_ISP82XX];
+ } else if (IS_QLA2031(ha)) {
+ blob = &qla_fw_blobs[FW_ISP2031];
+ } else if (IS_QLA8031(ha)) {
+ blob = &qla_fw_blobs[FW_ISP8031];
+ } else {
+ return NULL;
}
mutex_lock(&qla_fw_lock);
@@ -4265,6 +4401,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) },
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) },
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) },
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) },
{ PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) },
{ 0 },
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
index 16bc72844a97..3c13c0a6be63 100644
--- a/drivers/scsi/qla2xxx/qla_sup.c
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -568,6 +568,9 @@ qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start)
else if (IS_QLA82XX(ha)) {
*start = FA_FLASH_LAYOUT_ADDR_82;
goto end;
+ } else if (IS_QLA83XX(ha)) {
+ *start = FA_FLASH_LAYOUT_ADDR_83;
+ goto end;
}
/* Begin with first PCI expansion ROM header. */
buf = (uint8_t *)req->ring;
@@ -721,13 +724,22 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
le32_to_cpu(region->size));
switch (le32_to_cpu(region->code) & 0xff) {
+ case FLT_REG_FCOE_FW:
+ if (!IS_QLA8031(ha))
+ break;
+ ha->flt_region_fw = start;
+ break;
case FLT_REG_FW:
+ if (IS_QLA8031(ha))
+ break;
ha->flt_region_fw = start;
break;
case FLT_REG_BOOT_CODE:
ha->flt_region_boot = start;
break;
case FLT_REG_VPD_0:
+ if (IS_QLA8031(ha))
+ break;
ha->flt_region_vpd_nvram = start;
if (IS_QLA82XX(ha))
break;
@@ -735,16 +747,20 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
ha->flt_region_vpd = start;
break;
case FLT_REG_VPD_1:
- if (IS_QLA82XX(ha))
+ if (IS_QLA82XX(ha) || IS_QLA8031(ha))
break;
if (!ha->flags.port0)
ha->flt_region_vpd = start;
break;
case FLT_REG_NVRAM_0:
+ if (IS_QLA8031(ha))
+ break;
if (ha->flags.port0)
ha->flt_region_nvram = start;
break;
case FLT_REG_NVRAM_1:
+ if (IS_QLA8031(ha))
+ break;
if (!ha->flags.port0)
ha->flt_region_nvram = start;
break;
@@ -785,6 +801,31 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
case FLT_REG_VPD_82XX:
ha->flt_region_vpd = start;
break;
+ case FLT_REG_FCOE_VPD_0:
+ if (!IS_QLA8031(ha))
+ break;
+ ha->flt_region_vpd_nvram = start;
+ if (ha->flags.port0)
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_FCOE_VPD_1:
+ if (!IS_QLA8031(ha))
+ break;
+ if (!ha->flags.port0)
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_FCOE_NVRAM_0:
+ if (!IS_QLA8031(ha))
+ break;
+ if (ha->flags.port0)
+ ha->flt_region_nvram = start;
+ break;
+ case FLT_REG_FCOE_NVRAM_1:
+ if (!IS_QLA8031(ha))
+ break;
+ if (!ha->flags.port0)
+ ha->flt_region_nvram = start;
+ break;
}
}
goto done;
@@ -804,15 +845,12 @@ no_flash_data:
def_npiv_conf0[def] : def_npiv_conf1[def];
done:
ql_dbg(ql_dbg_init, vha, 0x004a,
- "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x.\n",
- loc, ha->flt_region_boot,
- ha->flt_region_fw, ha->flt_region_vpd_nvram,
- ha->flt_region_vpd);
- ql_dbg(ql_dbg_init, vha, 0x004b,
- "nvram=0x%x fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n",
- ha->flt_region_nvram,
- ha->flt_region_fdt, ha->flt_region_flt,
- ha->flt_region_npiv_conf, ha->flt_region_fcp_prio);
+ "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram=0x%x "
+ "fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n",
+ loc, ha->flt_region_boot, ha->flt_region_fw,
+ ha->flt_region_vpd_nvram, ha->flt_region_vpd, ha->flt_region_nvram,
+ ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_npiv_conf,
+ ha->flt_region_fcp_prio);
}
static void
@@ -948,7 +986,8 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha)
uint32_t flt_addr;
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA8XXX_TYPE(ha))
+ if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
return QLA_SUCCESS;
ret = qla2xxx_find_flt_start(vha, &flt_addr);
@@ -974,7 +1013,8 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
struct qla_npiv_entry *entry;
struct qla_hw_data *ha = vha->hw;
- if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA8XXX_TYPE(ha))
+ if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
return;
ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr,
@@ -1144,8 +1184,8 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
struct qla_hw_data *ha = vha->hw;
/* Prepare burst-capable write on supported ISPs. */
- if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && !(faddr & 0xfff) &&
- dwords > OPTROM_BURST_DWORDS) {
+ if ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha)) &&
+ !(faddr & 0xfff) && dwords > OPTROM_BURST_DWORDS) {
optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
&optrom_dma, GFP_KERNEL);
if (!optrom) {
@@ -1619,6 +1659,71 @@ qla24xx_beacon_blink(struct scsi_qla_host *vha)
spin_unlock_irqrestore(&ha->hardware_lock, flags);
}
+void
+qla83xx_beacon_blink(struct scsi_qla_host *vha)
+{
+ uint32_t led_select_value;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t led_cfg[6];
+ uint16_t orig_led_cfg[6];
+
+ if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha))
+ return;
+
+ if (IS_QLA2031(ha) && ha->beacon_blink_led) {
+ if (ha->flags.port0)
+ led_select_value = 0x00201320;
+ else
+ led_select_value = 0x00201328;
+
+ qla83xx_write_remote_reg(vha, led_select_value, 0x40002000);
+ qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40002000);
+ msleep(1000);
+ qla83xx_write_remote_reg(vha, led_select_value, 0x40004000);
+ qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40004000);
+ } else if ((IS_QLA8031(ha) || IS_QLA81XX(ha)) && ha->beacon_blink_led) {
+ int rval;
+
+ /* Save Current */
+ rval = qla81xx_get_led_config(vha, orig_led_cfg);
+ /* Do the blink */
+ if (rval == QLA_SUCCESS) {
+ if (IS_QLA81XX(ha)) {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x2000;
+ led_cfg[2] = 0;
+ led_cfg[3] = 0;
+ led_cfg[4] = 0;
+ led_cfg[5] = 0;
+ } else {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x4000;
+ led_cfg[2] = 0x4000;
+ led_cfg[3] = 0x2000;
+ led_cfg[4] = 0;
+ led_cfg[5] = 0x2000;
+ }
+ rval = qla81xx_set_led_config(vha, led_cfg);
+ msleep(1000);
+ if (IS_QLA81XX(ha)) {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x2000;
+ led_cfg[2] = 0;
+ } else {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x2000;
+ led_cfg[2] = 0x4000;
+ led_cfg[3] = 0x4000;
+ led_cfg[4] = 0;
+ led_cfg[5] = 0x2000;
+ }
+ rval = qla81xx_set_led_config(vha, led_cfg);
+ }
+ /* On exit, restore original (presumes no status change) */
+ qla81xx_set_led_config(vha, orig_led_cfg);
+ }
+}
+
int
qla24xx_beacon_on(struct scsi_qla_host *vha)
{
@@ -1630,6 +1735,9 @@ qla24xx_beacon_on(struct scsi_qla_host *vha)
if (IS_QLA82XX(ha))
return QLA_SUCCESS;
+ if (IS_QLA8031(ha) || IS_QLA81XX(ha))
+ goto skip_gpio; /* let blink handle it */
+
if (ha->beacon_blink_led == 0) {
/* Enable firmware for update */
ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
@@ -1644,6 +1752,9 @@ qla24xx_beacon_on(struct scsi_qla_host *vha)
return QLA_FUNCTION_FAILED;
}
+ if (IS_QLA2031(ha))
+ goto skip_gpio;
+
spin_lock_irqsave(&ha->hardware_lock, flags);
gpio_data = RD_REG_DWORD(&reg->gpiod);
@@ -1658,6 +1769,7 @@ qla24xx_beacon_on(struct scsi_qla_host *vha)
/* So all colors blink together. */
ha->beacon_color_state = 0;
+skip_gpio:
/* Let the per HBA timer kick off the blinking process. */
ha->beacon_blink_led = 1;
@@ -1676,6 +1788,13 @@ qla24xx_beacon_off(struct scsi_qla_host *vha)
return QLA_SUCCESS;
ha->beacon_blink_led = 0;
+
+ if (IS_QLA2031(ha))
+ goto set_fw_options;
+
+ if (IS_QLA8031(ha) || IS_QLA81XX(ha))
+ return QLA_SUCCESS;
+
ha->beacon_color_state = QLA_LED_ALL_ON;
ha->isp_ops->beacon_blink(vha); /* Will flip to all off. */
@@ -1690,6 +1809,7 @@ qla24xx_beacon_off(struct scsi_qla_host *vha)
RD_REG_DWORD(&reg->gpiod);
spin_unlock_irqrestore(&ha->hardware_lock, flags);
+set_fw_options:
ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
diff --git a/drivers/scsi/qla4xxx/ql4_def.h b/drivers/scsi/qla4xxx/ql4_def.h
index bfe68545203f..7f2492e88be7 100644
--- a/drivers/scsi/qla4xxx/ql4_def.h
+++ b/drivers/scsi/qla4xxx/ql4_def.h
@@ -150,8 +150,6 @@
#define QL4_SESS_RECOVERY_TMO 120 /* iSCSI session */
/* recovery timeout */
-#define MSB(x) ((uint8_t)((uint16_t)(x) >> 8))
-#define LSW(x) ((uint16_t)(x))
#define LSDW(x) ((u32)((u64)(x)))
#define MSDW(x) ((u32)((((u64)(x)) >> 16) >> 16))
@@ -223,6 +221,15 @@ struct srb {
uint16_t reserved2;
};
+/* Mailbox request block structure */
+struct mrb {
+ struct scsi_qla_host *ha;
+ struct mbox_cmd_iocb *mbox;
+ uint32_t mbox_cmd;
+ uint16_t iocb_cnt; /* Number of used iocbs */
+ uint32_t pid;
+};
+
/*
* Asynchronous Event Queue structure
*/
@@ -265,7 +272,7 @@ struct ddb_entry {
* retried */
uint32_t default_time2wait; /* Default Min time between
* relogins (+aens) */
-
+ uint16_t chap_tbl_idx;
};
struct qla_ddb_index {
@@ -284,6 +291,7 @@ struct ql4_tuple_ddb {
uint16_t options;
#define DDB_OPT_IPV6 0x0e0e
#define DDB_OPT_IPV4 0x0f0f
+ uint8_t isid[6];
};
/*
@@ -303,7 +311,28 @@ struct ql4_tuple_ddb {
#define DF_ISNS_DISCOVERED 2 /* Device was discovered via iSNS */
#define DF_FO_MASKED 3
+enum qla4_work_type {
+ QLA4_EVENT_AEN,
+ QLA4_EVENT_PING_STATUS,
+};
+struct qla4_work_evt {
+ struct list_head list;
+ enum qla4_work_type type;
+ union {
+ struct {
+ enum iscsi_host_event_code code;
+ uint32_t data_size;
+ uint8_t data[0];
+ } aen;
+ struct {
+ uint32_t status;
+ uint32_t pid;
+ uint32_t data_size;
+ uint8_t data[0];
+ } ping;
+ } u;
+};
struct ql82xx_hw_data {
/* Offsets for flash/nvram access (set to ~0 if not used). */
@@ -657,6 +686,7 @@ struct scsi_qla_host {
struct dma_pool *chap_dma_pool;
uint8_t *chap_list; /* CHAP table cache */
struct mutex chap_sem;
+
#define CHAP_DMA_BLOCK_SIZE 512
struct workqueue_struct *task_wq;
unsigned long ddb_idx_map[MAX_DDB_ENTRIES / BITS_PER_LONG];
@@ -674,6 +704,15 @@ struct scsi_qla_host {
uint16_t sec_ddb_idx;
int is_reset;
uint16_t temperature;
+
+ /* event work list */
+ struct list_head work_list;
+ spinlock_t work_lock;
+
+ /* mbox iocb */
+#define MAX_MRB 128
+ struct mrb *active_mrb_array[MAX_MRB];
+ uint32_t mrb_index;
};
struct ql4_task_data {
diff --git a/drivers/scsi/qla4xxx/ql4_fw.h b/drivers/scsi/qla4xxx/ql4_fw.h
index 7825c141bc1a..210cd1d64475 100644
--- a/drivers/scsi/qla4xxx/ql4_fw.h
+++ b/drivers/scsi/qla4xxx/ql4_fw.h
@@ -331,6 +331,10 @@ struct qla_flt_region {
/* Mailbox command definitions */
#define MBOX_CMD_ABOUT_FW 0x0009
#define MBOX_CMD_PING 0x000B
+#define PING_IPV6_PROTOCOL_ENABLE 0x1
+#define PING_IPV6_LINKLOCAL_ADDR 0x4
+#define PING_IPV6_ADDR0 0x8
+#define PING_IPV6_ADDR1 0xC
#define MBOX_CMD_ENABLE_INTRS 0x0010
#define INTR_DISABLE 0
#define INTR_ENABLE 1
@@ -396,6 +400,10 @@ struct qla_flt_region {
#define FW_ADDSTATE_DHCPv4_LEASE_EXPIRED 0x0008
#define FW_ADDSTATE_LINK_UP 0x0010
#define FW_ADDSTATE_ISNS_SVC_ENABLED 0x0020
+#define FW_ADDSTATE_LINK_SPEED_10MBPS 0x0100
+#define FW_ADDSTATE_LINK_SPEED_100MBPS 0x0200
+#define FW_ADDSTATE_LINK_SPEED_1GBPS 0x0400
+#define FW_ADDSTATE_LINK_SPEED_10GBPS 0x0800
#define MBOX_CMD_GET_DATABASE_ENTRY_DEFAULTS 0x006B
#define IPV6_DEFAULT_DDB_ENTRY 0x0001
@@ -918,6 +926,8 @@ struct qla4_header {
#define ET_CMND_T3 0x19
#define ET_PASSTHRU0 0x3A
#define ET_PASSTHRU_STATUS 0x3C
+#define ET_MBOX_CMD 0x38
+#define ET_MBOX_STATUS 0x39
uint8_t entryStatus;
uint8_t systemDefined;
@@ -1118,6 +1128,20 @@ struct passthru_status {
uint8_t res4[16]; /* 30-3F */
};
+struct mbox_cmd_iocb {
+ struct qla4_header hdr; /* 00-03 */
+ uint32_t handle; /* 04-07 */
+ uint32_t in_mbox[8]; /* 08-25 */
+ uint32_t res1[6]; /* 26-3F */
+};
+
+struct mbox_status_iocb {
+ struct qla4_header hdr; /* 00-03 */
+ uint32_t handle; /* 04-07 */
+ uint32_t out_mbox[8]; /* 08-25 */
+ uint32_t res1[6]; /* 26-3F */
+};
+
/*
* ISP queue - response queue entry definition.
*/
diff --git a/drivers/scsi/qla4xxx/ql4_glbl.h b/drivers/scsi/qla4xxx/ql4_glbl.h
index d0dd4b330206..910536667cf5 100644
--- a/drivers/scsi/qla4xxx/ql4_glbl.h
+++ b/drivers/scsi/qla4xxx/ql4_glbl.h
@@ -81,6 +81,8 @@ int qla4xxx_set_flash(struct scsi_qla_host *ha, dma_addr_t dma_addr,
uint32_t offset, uint32_t length, uint32_t options);
int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
uint8_t outCount, uint32_t *mbx_cmd, uint32_t *mbx_sts);
+int qla4xxx_get_chap_index(struct scsi_qla_host *ha, char *username,
+ char *password, int bidi, uint16_t *chap_index);
void qla4xxx_queue_iocb(struct scsi_qla_host *ha);
void qla4xxx_complete_iocb(struct scsi_qla_host *ha);
@@ -181,6 +183,13 @@ int qla4xxx_flash_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
int qla4xxx_ddb_change(struct scsi_qla_host *ha, uint32_t fw_ddb_index,
struct ddb_entry *ddb_entry, uint32_t state);
void qla4xxx_build_ddb_list(struct scsi_qla_host *ha, int is_reset);
+int qla4xxx_post_aen_work(struct scsi_qla_host *ha, uint32_t aen_code,
+ uint32_t data_size, uint8_t *data);
+int qla4xxx_ping_iocb(struct scsi_qla_host *ha, uint32_t options,
+ uint32_t payload_size, uint32_t pid, uint8_t *ipaddr);
+int qla4xxx_post_ping_evt_work(struct scsi_qla_host *ha,
+ uint32_t status, uint32_t pid,
+ uint32_t data_size, uint8_t *data);
/* BSG Functions */
int qla4xxx_bsg_request(struct bsg_job *bsg_job);
diff --git a/drivers/scsi/qla4xxx/ql4_init.c b/drivers/scsi/qla4xxx/ql4_init.c
index 90614f38b55d..90ee5d8fa731 100644
--- a/drivers/scsi/qla4xxx/ql4_init.c
+++ b/drivers/scsi/qla4xxx/ql4_init.c
@@ -86,6 +86,7 @@ static void qla4xxx_init_response_q_entries(struct scsi_qla_host *ha)
int qla4xxx_init_rings(struct scsi_qla_host *ha)
{
unsigned long flags = 0;
+ int i;
/* Initialize request queue. */
spin_lock_irqsave(&ha->hardware_lock, flags);
@@ -125,6 +126,10 @@ int qla4xxx_init_rings(struct scsi_qla_host *ha)
qla4xxx_init_response_q_entries(ha);
+ /* Initialize mabilbox active array */
+ for (i = 0; i < MAX_MRB; i++)
+ ha->active_mrb_array[i] = NULL;
+
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return QLA_SUCCESS;
diff --git a/drivers/scsi/qla4xxx/ql4_iocb.c b/drivers/scsi/qla4xxx/ql4_iocb.c
index 410669351906..2a2022a6bb9b 100644
--- a/drivers/scsi/qla4xxx/ql4_iocb.c
+++ b/drivers/scsi/qla4xxx/ql4_iocb.c
@@ -445,3 +445,95 @@ queuing_error:
spin_unlock_irqrestore(&ha->hardware_lock, flags);
return ret;
}
+
+static struct mrb *qla4xxx_get_new_mrb(struct scsi_qla_host *ha)
+{
+ struct mrb *mrb;
+
+ mrb = kzalloc(sizeof(*mrb), GFP_KERNEL);
+ if (!mrb)
+ return mrb;
+
+ mrb->ha = ha;
+ return mrb;
+}
+
+static int qla4xxx_send_mbox_iocb(struct scsi_qla_host *ha, struct mrb *mrb,
+ uint32_t *in_mbox)
+{
+ int rval = QLA_SUCCESS;
+ uint32_t i;
+ unsigned long flags;
+ uint32_t index = 0;
+
+ /* Acquire hardware specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Get pointer to the queue entry for the marker */
+ rval = qla4xxx_get_req_pkt(ha, (struct queue_entry **) &(mrb->mbox));
+ if (rval != QLA_SUCCESS)
+ goto exit_mbox_iocb;
+
+ index = ha->mrb_index;
+ /* get valid mrb index*/
+ for (i = 0; i < MAX_MRB; i++) {
+ index++;
+ if (index == MAX_MRB)
+ index = 1;
+ if (ha->active_mrb_array[index] == NULL) {
+ ha->mrb_index = index;
+ break;
+ }
+ }
+
+ mrb->iocb_cnt = 1;
+ ha->active_mrb_array[index] = mrb;
+ mrb->mbox->handle = index;
+ mrb->mbox->hdr.entryType = ET_MBOX_CMD;
+ mrb->mbox->hdr.entryCount = mrb->iocb_cnt;
+ memcpy(mrb->mbox->in_mbox, in_mbox, 32);
+ mrb->mbox_cmd = in_mbox[0];
+ wmb();
+
+ ha->isp_ops->queue_iocb(ha);
+exit_mbox_iocb:
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return rval;
+}
+
+int qla4xxx_ping_iocb(struct scsi_qla_host *ha, uint32_t options,
+ uint32_t payload_size, uint32_t pid, uint8_t *ipaddr)
+{
+ uint32_t in_mbox[8];
+ struct mrb *mrb = NULL;
+ int rval = QLA_SUCCESS;
+
+ memset(in_mbox, 0, sizeof(in_mbox));
+
+ mrb = qla4xxx_get_new_mrb(ha);
+ if (!mrb) {
+ DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: fail to get new mrb\n",
+ __func__));
+ rval = QLA_ERROR;
+ goto exit_ping;
+ }
+
+ in_mbox[0] = MBOX_CMD_PING;
+ in_mbox[1] = options;
+ memcpy(&in_mbox[2], &ipaddr[0], 4);
+ memcpy(&in_mbox[3], &ipaddr[4], 4);
+ memcpy(&in_mbox[4], &ipaddr[8], 4);
+ memcpy(&in_mbox[5], &ipaddr[12], 4);
+ in_mbox[6] = payload_size;
+
+ mrb->pid = pid;
+ rval = qla4xxx_send_mbox_iocb(ha, mrb, in_mbox);
+
+ if (rval != QLA_SUCCESS)
+ goto exit_ping;
+
+ return rval;
+exit_ping:
+ kfree(mrb);
+ return rval;
+}
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
index 95828862eea0..7c9f28b7da72 100644
--- a/drivers/scsi/qla4xxx/ql4_isr.c
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -385,6 +385,71 @@ static void qla4xxx_passthru_status_entry(struct scsi_qla_host *ha,
queue_work(ha->task_wq, &task_data->task_work);
}
+static struct mrb *qla4xxx_del_mrb_from_active_array(struct scsi_qla_host *ha,
+ uint32_t index)
+{
+ struct mrb *mrb = NULL;
+
+ /* validate handle and remove from active array */
+ if (index >= MAX_MRB)
+ return mrb;
+
+ mrb = ha->active_mrb_array[index];
+ ha->active_mrb_array[index] = NULL;
+ if (!mrb)
+ return mrb;
+
+ /* update counters */
+ ha->req_q_count += mrb->iocb_cnt;
+ ha->iocb_cnt -= mrb->iocb_cnt;
+
+ return mrb;
+}
+
+static void qla4xxx_mbox_status_entry(struct scsi_qla_host *ha,
+ struct mbox_status_iocb *mbox_sts_entry)
+{
+ struct mrb *mrb;
+ uint32_t status;
+ uint32_t data_size;
+
+ mrb = qla4xxx_del_mrb_from_active_array(ha,
+ le32_to_cpu(mbox_sts_entry->handle));
+
+ if (mrb == NULL) {
+ ql4_printk(KERN_WARNING, ha, "%s: mrb[%d] is null\n", __func__,
+ mbox_sts_entry->handle);
+ return;
+ }
+
+ switch (mrb->mbox_cmd) {
+ case MBOX_CMD_PING:
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: mbox_cmd = 0x%x, "
+ "mbox_sts[0] = 0x%x, mbox_sts[6] = 0x%x\n",
+ __func__, mrb->mbox_cmd,
+ mbox_sts_entry->out_mbox[0],
+ mbox_sts_entry->out_mbox[6]));
+
+ if (mbox_sts_entry->out_mbox[0] == MBOX_STS_COMMAND_COMPLETE)
+ status = QLA_SUCCESS;
+ else
+ status = QLA_ERROR;
+
+ data_size = sizeof(mbox_sts_entry->out_mbox);
+
+ qla4xxx_post_ping_evt_work(ha, status, mrb->pid, data_size,
+ (uint8_t *) mbox_sts_entry->out_mbox);
+ break;
+
+ default:
+ DEBUG2(ql4_printk(KERN_WARNING, ha, "%s: invalid mbox_cmd = "
+ "0x%x\n", __func__, mrb->mbox_cmd));
+ }
+
+ kfree(mrb);
+ return;
+}
+
/**
* qla4xxx_process_response_queue - process response queue completions
* @ha: Pointer to host adapter structure.
@@ -461,6 +526,13 @@ void qla4xxx_process_response_queue(struct scsi_qla_host *ha)
"ignoring\n", ha->host_no, __func__));
break;
+ case ET_MBOX_STATUS:
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: mbox status IOCB\n", __func__));
+ qla4xxx_mbox_status_entry(ha,
+ (struct mbox_status_iocb *)sts_entry);
+ break;
+
default:
/*
* Invalid entry in response queue, reset RISC
@@ -576,6 +648,9 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
set_bit(DPC_LINK_CHANGED, &ha->dpc_flags);
ql4_printk(KERN_INFO, ha, "%s: LINK UP\n", __func__);
+ qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKUP,
+ sizeof(mbox_sts),
+ (uint8_t *) mbox_sts);
break;
case MBOX_ASTS_LINK_DOWN:
@@ -584,6 +659,9 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
set_bit(DPC_LINK_CHANGED, &ha->dpc_flags);
ql4_printk(KERN_INFO, ha, "%s: LINK DOWN\n", __func__);
+ qla4xxx_post_aen_work(ha, ISCSI_EVENT_LINKDOWN,
+ sizeof(mbox_sts),
+ (uint8_t *) mbox_sts);
break;
case MBOX_ASTS_HEARTBEAT:
diff --git a/drivers/scsi/qla4xxx/ql4_mbx.c b/drivers/scsi/qla4xxx/ql4_mbx.c
index e1e66a45e4d0..7ac21dabbf22 100644
--- a/drivers/scsi/qla4xxx/ql4_mbx.c
+++ b/drivers/scsi/qla4xxx/ql4_mbx.c
@@ -622,7 +622,7 @@ int qla4xxx_get_firmware_status(struct scsi_qla_host * ha)
return QLA_ERROR;
}
- ql4_printk(KERN_INFO, ha, "%ld firmare IOCBs available (%d).\n",
+ ql4_printk(KERN_INFO, ha, "%ld firmware IOCBs available (%d).\n",
ha->host_no, mbox_sts[2]);
return QLA_SUCCESS;
@@ -661,6 +661,8 @@ int qla4xxx_get_fwddb_entry(struct scsi_qla_host *ha,
}
memset(&mbox_cmd, 0, sizeof(mbox_cmd));
memset(&mbox_sts, 0, sizeof(mbox_sts));
+ if (fw_ddb_entry)
+ memset(fw_ddb_entry, 0, sizeof(struct dev_db_entry));
mbox_cmd[0] = MBOX_CMD_GET_DATABASE_ENTRY;
mbox_cmd[1] = (uint32_t) fw_ddb_index;
@@ -1424,8 +1426,8 @@ exit_set_chap:
* match is found. If a match is not found then add the entry in FLASH and
* return the index at which entry is written in the FLASH.
**/
-static int qla4xxx_get_chap_index(struct scsi_qla_host *ha, char *username,
- char *password, int bidi, uint16_t *chap_index)
+int qla4xxx_get_chap_index(struct scsi_qla_host *ha, char *username,
+ char *password, int bidi, uint16_t *chap_index)
{
int i, rval;
int free_index = -1;
@@ -1444,6 +1446,11 @@ static int qla4xxx_get_chap_index(struct scsi_qla_host *ha, char *username,
return QLA_ERROR;
}
+ if (!username || !password) {
+ ql4_printk(KERN_ERR, ha, "Do not have username and psw\n");
+ return QLA_ERROR;
+ }
+
mutex_lock(&ha->chap_sem);
for (i = 0; i < max_chap_entries; i++) {
chap_table = (struct ql4_chap_table *)ha->chap_list + i;
@@ -1600,7 +1607,7 @@ int qla4xxx_set_param_ddbentry(struct scsi_qla_host *ha,
char *ip;
uint16_t iscsi_opts = 0;
uint32_t options = 0;
- uint16_t idx;
+ uint16_t idx, *ptid;
fw_ddb_entry = dma_alloc_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
&fw_ddb_entry_dma, GFP_KERNEL);
@@ -1626,6 +1633,14 @@ int qla4xxx_set_param_ddbentry(struct scsi_qla_host *ha,
goto exit_set_param;
}
+ ptid = (uint16_t *)&fw_ddb_entry->isid[1];
+ *ptid = cpu_to_le16((uint16_t)ddb_entry->sess->target_id);
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "ISID [%02x%02x%02x%02x%02x%02x]\n",
+ fw_ddb_entry->isid[5], fw_ddb_entry->isid[4],
+ fw_ddb_entry->isid[3], fw_ddb_entry->isid[2],
+ fw_ddb_entry->isid[1], fw_ddb_entry->isid[0]));
+
iscsi_opts = le16_to_cpu(fw_ddb_entry->iscsi_options);
memset(fw_ddb_entry->iscsi_alias, 0, sizeof(fw_ddb_entry->iscsi_alias));
diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c
index 65253dfbe962..e1e46b6dac75 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.c
+++ b/drivers/scsi/qla4xxx/ql4_nx.c
@@ -841,11 +841,8 @@ qla4_8xxx_rom_lock(struct scsi_qla_host *ha)
done = qla4_8xxx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_LOCK));
if (done == 1)
break;
- if (timeout >= qla4_8xxx_rom_lock_timeout) {
- ql4_printk(KERN_WARNING, ha,
- "%s: Failed to acquire rom lock", __func__);
+ if (timeout >= qla4_8xxx_rom_lock_timeout)
return -1;
- }
timeout++;
@@ -996,18 +993,6 @@ qla4_8xxx_pinit_from_rom(struct scsi_qla_host *ha, int verbose)
else
qla4_8xxx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xffffffff);
- /* reset ms */
- val = qla4_8xxx_rd_32(ha, QLA82XX_CRB_QDR_NET + 0xe4);
- val |= (1 << 1);
- qla4_8xxx_wr_32(ha, QLA82XX_CRB_QDR_NET + 0xe4, val);
-
- msleep(20);
- /* unreset ms */
- val = qla4_8xxx_rd_32(ha, QLA82XX_CRB_QDR_NET + 0xe4);
- val &= ~(1 << 1);
- qla4_8xxx_wr_32(ha, QLA82XX_CRB_QDR_NET + 0xe4, val);
- msleep(20);
-
qla4_8xxx_rom_unlock(ha);
/* Read the signature value from the flash.
diff --git a/drivers/scsi/qla4xxx/ql4_nx.h b/drivers/scsi/qla4xxx/ql4_nx.h
index dc45ac923691..dc7500e47b8b 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.h
+++ b/drivers/scsi/qla4xxx/ql4_nx.h
@@ -623,6 +623,7 @@ struct crb_addr_pair {
#define ADDR_ERROR ((unsigned long) 0xffffffff)
#define MAX_CTL_CHECK 1000
+#define QLA82XX_FWERROR_CODE(code) ((code >> 8) & 0x1fffff)
/***************************************************************************
* PCI related defines.
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index edf503437e96..3d9419460e0c 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -32,14 +32,14 @@ static struct kmem_cache *srb_cachep;
/*
* Module parameter information and variables
*/
-int ql4xdisablesysfsboot = 1;
+static int ql4xdisablesysfsboot = 1;
module_param(ql4xdisablesysfsboot, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ql4xdisablesysfsboot,
" Set to disable exporting boot targets to sysfs.\n"
"\t\t 0 - Export boot targets\n"
"\t\t 1 - Do not export boot targets (Default)");
-int ql4xdontresethba = 0;
+int ql4xdontresethba;
module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ql4xdontresethba,
" Don't reset the HBA for driver recovery.\n"
@@ -71,7 +71,7 @@ MODULE_PARM_DESC(ql4xmaxqdepth,
static int ql4xsess_recovery_tmo = QL4_SESS_RECOVERY_TMO;
module_param(ql4xsess_recovery_tmo, int, S_IRUGO);
MODULE_PARM_DESC(ql4xsess_recovery_tmo,
- "Target Session Recovery Timeout.\n"
+ " Target Session Recovery Timeout.\n"
"\t\t Default: 120 sec.");
static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha);
@@ -83,6 +83,8 @@ static void qla4xxx_config_dma_addressing(struct scsi_qla_host *ha);
/*
* iSCSI template entry points
*/
+static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess,
+ enum iscsi_param param, char *buf);
static int qla4xxx_conn_get_param(struct iscsi_cls_conn *conn,
enum iscsi_param param, char *buf);
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
@@ -118,6 +120,13 @@ static void qla4xxx_task_cleanup(struct iscsi_task *);
static void qla4xxx_fail_session(struct iscsi_cls_session *cls_session);
static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn,
struct iscsi_stats *stats);
+static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num,
+ uint32_t iface_type, uint32_t payload_size,
+ uint32_t pid, struct sockaddr *dst_addr);
+static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx,
+ uint32_t *num_entries, char *buf);
+static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx);
+
/*
* SCSI host template entry points
*/
@@ -179,7 +188,7 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
.destroy_conn = qla4xxx_conn_destroy,
.set_param = iscsi_set_param,
.get_conn_param = qla4xxx_conn_get_param,
- .get_session_param = iscsi_session_get_param,
+ .get_session_param = qla4xxx_session_get_param,
.get_ep_param = qla4xxx_get_ep_param,
.ep_connect = qla4xxx_ep_connect,
.ep_poll = qla4xxx_ep_poll,
@@ -194,10 +203,93 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
.set_iface_param = qla4xxx_iface_set_param,
.get_iface_param = qla4xxx_get_iface_param,
.bsg_request = qla4xxx_bsg_request,
+ .send_ping = qla4xxx_send_ping,
+ .get_chap = qla4xxx_get_chap_list,
+ .delete_chap = qla4xxx_delete_chap,
};
static struct scsi_transport_template *qla4xxx_scsi_transport;
+static int qla4xxx_send_ping(struct Scsi_Host *shost, uint32_t iface_num,
+ uint32_t iface_type, uint32_t payload_size,
+ uint32_t pid, struct sockaddr *dst_addr)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct sockaddr_in *addr;
+ struct sockaddr_in6 *addr6;
+ uint32_t options = 0;
+ uint8_t ipaddr[IPv6_ADDR_LEN];
+ int rval;
+
+ memset(ipaddr, 0, IPv6_ADDR_LEN);
+ /* IPv4 to IPv4 */
+ if ((iface_type == ISCSI_IFACE_TYPE_IPV4) &&
+ (dst_addr->sa_family == AF_INET)) {
+ addr = (struct sockaddr_in *)dst_addr;
+ memcpy(ipaddr, &addr->sin_addr.s_addr, IP_ADDR_LEN);
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv4 Ping src: %pI4 "
+ "dest: %pI4\n", __func__,
+ &ha->ip_config.ip_address, ipaddr));
+ rval = qla4xxx_ping_iocb(ha, options, payload_size, pid,
+ ipaddr);
+ if (rval)
+ rval = -EINVAL;
+ } else if ((iface_type == ISCSI_IFACE_TYPE_IPV6) &&
+ (dst_addr->sa_family == AF_INET6)) {
+ /* IPv6 to IPv6 */
+ addr6 = (struct sockaddr_in6 *)dst_addr;
+ memcpy(ipaddr, &addr6->sin6_addr.in6_u.u6_addr8, IPv6_ADDR_LEN);
+
+ options |= PING_IPV6_PROTOCOL_ENABLE;
+
+ /* Ping using LinkLocal address */
+ if ((iface_num == 0) || (iface_num == 1)) {
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: LinkLocal Ping "
+ "src: %pI6 dest: %pI6\n", __func__,
+ &ha->ip_config.ipv6_link_local_addr,
+ ipaddr));
+ options |= PING_IPV6_LINKLOCAL_ADDR;
+ rval = qla4xxx_ping_iocb(ha, options, payload_size,
+ pid, ipaddr);
+ } else {
+ ql4_printk(KERN_WARNING, ha, "%s: iface num = %d "
+ "not supported\n", __func__, iface_num);
+ rval = -ENOSYS;
+ goto exit_send_ping;
+ }
+
+ /*
+ * If ping using LinkLocal address fails, try ping using
+ * IPv6 address
+ */
+ if (rval != QLA_SUCCESS) {
+ options &= ~PING_IPV6_LINKLOCAL_ADDR;
+ if (iface_num == 0) {
+ options |= PING_IPV6_ADDR0;
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv6 "
+ "Ping src: %pI6 "
+ "dest: %pI6\n", __func__,
+ &ha->ip_config.ipv6_addr0,
+ ipaddr));
+ } else if (iface_num == 1) {
+ options |= PING_IPV6_ADDR1;
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: IPv6 "
+ "Ping src: %pI6 "
+ "dest: %pI6\n", __func__,
+ &ha->ip_config.ipv6_addr1,
+ ipaddr));
+ }
+ rval = qla4xxx_ping_iocb(ha, options, payload_size,
+ pid, ipaddr);
+ if (rval)
+ rval = -EINVAL;
+ }
+ } else
+ rval = -ENOSYS;
+exit_send_ping:
+ return rval;
+}
+
static umode_t ql4_attr_is_visible(int param_type, int param)
{
switch (param_type) {
@@ -206,6 +298,8 @@ static umode_t ql4_attr_is_visible(int param_type, int param)
case ISCSI_HOST_PARAM_HWADDRESS:
case ISCSI_HOST_PARAM_IPADDRESS:
case ISCSI_HOST_PARAM_INITIATOR_NAME:
+ case ISCSI_HOST_PARAM_PORT_STATE:
+ case ISCSI_HOST_PARAM_PORT_SPEED:
return S_IRUGO;
default:
return 0;
@@ -225,6 +319,12 @@ static umode_t ql4_attr_is_visible(int param_type, int param)
case ISCSI_PARAM_MAX_RECV_DLENGTH:
case ISCSI_PARAM_MAX_XMIT_DLENGTH:
case ISCSI_PARAM_IFACE_NAME:
+ case ISCSI_PARAM_CHAP_OUT_IDX:
+ case ISCSI_PARAM_CHAP_IN_IDX:
+ case ISCSI_PARAM_USERNAME:
+ case ISCSI_PARAM_PASSWORD:
+ case ISCSI_PARAM_USERNAME_IN:
+ case ISCSI_PARAM_PASSWORD_IN:
return S_IRUGO;
default:
return 0;
@@ -255,6 +355,189 @@ static umode_t ql4_attr_is_visible(int param_type, int param)
return 0;
}
+static int qla4xxx_get_chap_list(struct Scsi_Host *shost, uint16_t chap_tbl_idx,
+ uint32_t *num_entries, char *buf)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct ql4_chap_table *chap_table;
+ struct iscsi_chap_rec *chap_rec;
+ int max_chap_entries = 0;
+ int valid_chap_entries = 0;
+ int ret = 0, i;
+
+ if (is_qla8022(ha))
+ max_chap_entries = (ha->hw.flt_chap_size / 2) /
+ sizeof(struct ql4_chap_table);
+ else
+ max_chap_entries = MAX_CHAP_ENTRIES_40XX;
+
+ ql4_printk(KERN_INFO, ha, "%s: num_entries = %d, CHAP idx = %d\n",
+ __func__, *num_entries, chap_tbl_idx);
+
+ if (!buf) {
+ ret = -ENOMEM;
+ goto exit_get_chap_list;
+ }
+
+ chap_rec = (struct iscsi_chap_rec *) buf;
+ mutex_lock(&ha->chap_sem);
+ for (i = chap_tbl_idx; i < max_chap_entries; i++) {
+ chap_table = (struct ql4_chap_table *)ha->chap_list + i;
+ if (chap_table->cookie !=
+ __constant_cpu_to_le16(CHAP_VALID_COOKIE))
+ continue;
+
+ chap_rec->chap_tbl_idx = i;
+ strncpy(chap_rec->username, chap_table->name,
+ ISCSI_CHAP_AUTH_NAME_MAX_LEN);
+ strncpy(chap_rec->password, chap_table->secret,
+ QL4_CHAP_MAX_SECRET_LEN);
+ chap_rec->password_length = chap_table->secret_len;
+
+ if (chap_table->flags & BIT_7) /* local */
+ chap_rec->chap_type = CHAP_TYPE_OUT;
+
+ if (chap_table->flags & BIT_6) /* peer */
+ chap_rec->chap_type = CHAP_TYPE_IN;
+
+ chap_rec++;
+
+ valid_chap_entries++;
+ if (valid_chap_entries == *num_entries)
+ break;
+ else
+ continue;
+ }
+ mutex_unlock(&ha->chap_sem);
+
+exit_get_chap_list:
+ ql4_printk(KERN_INFO, ha, "%s: Valid CHAP Entries = %d\n",
+ __func__, valid_chap_entries);
+ *num_entries = valid_chap_entries;
+ return ret;
+}
+
+static int __qla4xxx_is_chap_active(struct device *dev, void *data)
+{
+ int ret = 0;
+ uint16_t *chap_tbl_idx = (uint16_t *) data;
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *sess;
+ struct ddb_entry *ddb_entry;
+
+ if (!iscsi_is_session_dev(dev))
+ goto exit_is_chap_active;
+
+ cls_session = iscsi_dev_to_session(dev);
+ sess = cls_session->dd_data;
+ ddb_entry = sess->dd_data;
+
+ if (iscsi_session_chkready(cls_session))
+ goto exit_is_chap_active;
+
+ if (ddb_entry->chap_tbl_idx == *chap_tbl_idx)
+ ret = 1;
+
+exit_is_chap_active:
+ return ret;
+}
+
+static int qla4xxx_is_chap_active(struct Scsi_Host *shost,
+ uint16_t chap_tbl_idx)
+{
+ int ret = 0;
+
+ ret = device_for_each_child(&shost->shost_gendev, &chap_tbl_idx,
+ __qla4xxx_is_chap_active);
+
+ return ret;
+}
+
+static int qla4xxx_delete_chap(struct Scsi_Host *shost, uint16_t chap_tbl_idx)
+{
+ struct scsi_qla_host *ha = to_qla_host(shost);
+ struct ql4_chap_table *chap_table;
+ dma_addr_t chap_dma;
+ int max_chap_entries = 0;
+ uint32_t offset = 0;
+ uint32_t chap_size;
+ int ret = 0;
+
+ chap_table = dma_pool_alloc(ha->chap_dma_pool, GFP_KERNEL, &chap_dma);
+ if (chap_table == NULL)
+ return -ENOMEM;
+
+ memset(chap_table, 0, sizeof(struct ql4_chap_table));
+
+ if (is_qla8022(ha))
+ max_chap_entries = (ha->hw.flt_chap_size / 2) /
+ sizeof(struct ql4_chap_table);
+ else
+ max_chap_entries = MAX_CHAP_ENTRIES_40XX;
+
+ if (chap_tbl_idx > max_chap_entries) {
+ ret = -EINVAL;
+ goto exit_delete_chap;
+ }
+
+ /* Check if chap index is in use.
+ * If chap is in use don't delet chap entry */
+ ret = qla4xxx_is_chap_active(shost, chap_tbl_idx);
+ if (ret) {
+ ql4_printk(KERN_INFO, ha, "CHAP entry %d is in use, cannot "
+ "delete from flash\n", chap_tbl_idx);
+ ret = -EBUSY;
+ goto exit_delete_chap;
+ }
+
+ chap_size = sizeof(struct ql4_chap_table);
+ if (is_qla40XX(ha))
+ offset = FLASH_CHAP_OFFSET | (chap_tbl_idx * chap_size);
+ else {
+ offset = FLASH_RAW_ACCESS_ADDR + (ha->hw.flt_region_chap << 2);
+ /* flt_chap_size is CHAP table size for both ports
+ * so divide it by 2 to calculate the offset for second port
+ */
+ if (ha->port_num == 1)
+ offset += (ha->hw.flt_chap_size / 2);
+ offset += (chap_tbl_idx * chap_size);
+ }
+
+ ret = qla4xxx_get_flash(ha, chap_dma, offset, chap_size);
+ if (ret != QLA_SUCCESS) {
+ ret = -EINVAL;
+ goto exit_delete_chap;
+ }
+
+ DEBUG2(ql4_printk(KERN_INFO, ha, "Chap Cookie: x%x\n",
+ __le16_to_cpu(chap_table->cookie)));
+
+ if (__le16_to_cpu(chap_table->cookie) != CHAP_VALID_COOKIE) {
+ ql4_printk(KERN_ERR, ha, "No valid chap entry found\n");
+ goto exit_delete_chap;
+ }
+
+ chap_table->cookie = __constant_cpu_to_le16(0xFFFF);
+
+ offset = FLASH_CHAP_OFFSET |
+ (chap_tbl_idx * sizeof(struct ql4_chap_table));
+ ret = qla4xxx_set_flash(ha, chap_dma, offset, chap_size,
+ FLASH_OPT_RMW_COMMIT);
+ if (ret == QLA_SUCCESS && ha->chap_list) {
+ mutex_lock(&ha->chap_sem);
+ /* Update ha chap_list cache */
+ memcpy((struct ql4_chap_table *)ha->chap_list + chap_tbl_idx,
+ chap_table, sizeof(struct ql4_chap_table));
+ mutex_unlock(&ha->chap_sem);
+ }
+ if (ret != QLA_SUCCESS)
+ ret = -EINVAL;
+
+exit_delete_chap:
+ dma_pool_free(ha->chap_dma_pool, chap_table, chap_dma);
+ return ret;
+}
+
static int qla4xxx_get_iface_param(struct iscsi_iface *iface,
enum iscsi_param_type param_type,
int param, char *buf)
@@ -548,6 +831,43 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc)
return ret;
}
+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);
+ uint32_t speed = ISCSI_PORT_SPEED_UNKNOWN;
+
+ qla4xxx_get_firmware_state(ha);
+
+ switch (ha->addl_fw_state & 0x0F00) {
+ case FW_ADDSTATE_LINK_SPEED_10MBPS:
+ speed = ISCSI_PORT_SPEED_10MBPS;
+ break;
+ case FW_ADDSTATE_LINK_SPEED_100MBPS:
+ speed = ISCSI_PORT_SPEED_100MBPS;
+ break;
+ case FW_ADDSTATE_LINK_SPEED_1GBPS:
+ speed = ISCSI_PORT_SPEED_1GBPS;
+ break;
+ case FW_ADDSTATE_LINK_SPEED_10GBPS:
+ speed = ISCSI_PORT_SPEED_10GBPS;
+ break;
+ }
+ ihost->port_speed = speed;
+}
+
+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);
+ uint32_t state = ISCSI_PORT_STATE_DOWN;
+
+ if (test_bit(AF_LINK_UP, &ha->flags))
+ state = ISCSI_PORT_STATE_UP;
+
+ ihost->port_state = state;
+}
+
static int qla4xxx_host_get_param(struct Scsi_Host *shost,
enum iscsi_host_param param, char *buf)
{
@@ -564,6 +884,14 @@ static int qla4xxx_host_get_param(struct Scsi_Host *shost,
case ISCSI_HOST_PARAM_INITIATOR_NAME:
len = sprintf(buf, "%s\n", ha->name_string);
break;
+ case ISCSI_HOST_PARAM_PORT_STATE:
+ qla4xxx_set_port_state(shost);
+ len = sprintf(buf, "%s\n", iscsi_get_port_state_name(shost));
+ break;
+ case ISCSI_HOST_PARAM_PORT_SPEED:
+ qla4xxx_set_port_speed(shost);
+ len = sprintf(buf, "%s\n", iscsi_get_port_speed_name(shost));
+ break;
default:
return -ENOSYS;
}
@@ -968,6 +1296,41 @@ exit_init_fw_cb:
return rval;
}
+static int qla4xxx_session_get_param(struct iscsi_cls_session *cls_sess,
+ enum iscsi_param param, char *buf)
+{
+ struct iscsi_session *sess = cls_sess->dd_data;
+ struct ddb_entry *ddb_entry = sess->dd_data;
+ struct scsi_qla_host *ha = ddb_entry->ha;
+ int rval, len;
+ uint16_t idx;
+
+ switch (param) {
+ case ISCSI_PARAM_CHAP_IN_IDX:
+ rval = qla4xxx_get_chap_index(ha, sess->username_in,
+ sess->password_in, BIDI_CHAP,
+ &idx);
+ if (rval)
+ return -EINVAL;
+
+ len = sprintf(buf, "%hu\n", idx);
+ break;
+ case ISCSI_PARAM_CHAP_OUT_IDX:
+ rval = qla4xxx_get_chap_index(ha, sess->username,
+ sess->password, LOCAL_CHAP,
+ &idx);
+ if (rval)
+ return -EINVAL;
+
+ len = sprintf(buf, "%hu\n", idx);
+ break;
+ default:
+ return iscsi_session_get_param(cls_sess, param, buf);
+ }
+
+ return len;
+}
+
static int qla4xxx_conn_get_param(struct iscsi_cls_conn *cls_conn,
enum iscsi_param param, char *buf)
{
@@ -1506,13 +1869,17 @@ static void qla4xxx_copy_fwddb_param(struct scsi_qla_host *ha,
{
int buflen = 0;
struct iscsi_session *sess;
+ struct ddb_entry *ddb_entry;
struct iscsi_conn *conn;
char ip_addr[DDB_IPADDR_LEN];
uint16_t options = 0;
sess = cls_sess->dd_data;
+ ddb_entry = sess->dd_data;
conn = cls_conn->dd_data;
+ ddb_entry->chap_tbl_idx = le16_to_cpu(fw_ddb_entry->chap_tbl_idx);
+
conn->max_recv_dlength = BYTE_UNITS *
le16_to_cpu(fw_ddb_entry->iscsi_max_rcv_data_seg_len);
@@ -1552,6 +1919,8 @@ static void qla4xxx_copy_fwddb_param(struct scsi_qla_host *ha,
(char *)ha->name_string, buflen);
iscsi_set_param(cls_conn, ISCSI_PARAM_PERSISTENT_ADDRESS,
(char *)ip_addr, buflen);
+ iscsi_set_param(cls_conn, ISCSI_PARAM_TARGET_ALIAS,
+ (char *)fw_ddb_entry->iscsi_alias, buflen);
}
void qla4xxx_update_session_conn_fwddb_param(struct scsi_qla_host *ha,
@@ -1638,6 +2007,7 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,
le16_to_cpu(fw_ddb_entry->iscsi_def_time2wait);
/* Update params */
+ ddb_entry->chap_tbl_idx = le16_to_cpu(fw_ddb_entry->chap_tbl_idx);
conn->max_recv_dlength = BYTE_UNITS *
le16_to_cpu(fw_ddb_entry->iscsi_max_rcv_data_seg_len);
@@ -1666,6 +2036,9 @@ void qla4xxx_update_session_conn_param(struct scsi_qla_host *ha,
memcpy(sess->initiatorname, ha->name_string,
min(sizeof(ha->name_string), sizeof(sess->initiatorname)));
+ iscsi_set_param(cls_conn, ISCSI_PARAM_TARGET_ALIAS,
+ (char *)fw_ddb_entry->iscsi_alias, 0);
+
exit_session_conn_param:
if (fw_ddb_entry)
dma_free_coherent(&ha->pdev->dev, sizeof(*fw_ddb_entry),
@@ -2113,7 +2486,7 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
halt_status = qla4_8xxx_rd_32(ha,
QLA82XX_PEG_HALT_STATUS1);
- if (LSW(MSB(halt_status)) == 0x67)
+ if (QLA82XX_FWERROR_CODE(halt_status) == 0x67)
ql4_printk(KERN_ERR, ha, "%s:"
" Firmware aborted with"
" error code 0x00006700."
@@ -2230,6 +2603,10 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
}
}
+ /* Process any deferred work. */
+ if (!list_empty(&ha->work_list))
+ start_dpc++;
+
/* Wakeup the dpc routine for this adapter, if needed. */
if (start_dpc ||
test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
@@ -2795,6 +3172,109 @@ void qla4xxx_wake_dpc(struct scsi_qla_host *ha)
queue_work(ha->dpc_thread, &ha->dpc_work);
}
+static struct qla4_work_evt *
+qla4xxx_alloc_work(struct scsi_qla_host *ha, uint32_t data_size,
+ enum qla4_work_type type)
+{
+ struct qla4_work_evt *e;
+ uint32_t size = sizeof(struct qla4_work_evt) + data_size;
+
+ e = kzalloc(size, GFP_ATOMIC);
+ if (!e)
+ return NULL;
+
+ INIT_LIST_HEAD(&e->list);
+ e->type = type;
+ return e;
+}
+
+static void qla4xxx_post_work(struct scsi_qla_host *ha,
+ struct qla4_work_evt *e)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->work_lock, flags);
+ list_add_tail(&e->list, &ha->work_list);
+ spin_unlock_irqrestore(&ha->work_lock, flags);
+ qla4xxx_wake_dpc(ha);
+}
+
+int qla4xxx_post_aen_work(struct scsi_qla_host *ha,
+ enum iscsi_host_event_code aen_code,
+ uint32_t data_size, uint8_t *data)
+{
+ struct qla4_work_evt *e;
+
+ e = qla4xxx_alloc_work(ha, data_size, QLA4_EVENT_AEN);
+ if (!e)
+ return QLA_ERROR;
+
+ e->u.aen.code = aen_code;
+ e->u.aen.data_size = data_size;
+ memcpy(e->u.aen.data, data, data_size);
+
+ qla4xxx_post_work(ha, e);
+
+ return QLA_SUCCESS;
+}
+
+int qla4xxx_post_ping_evt_work(struct scsi_qla_host *ha,
+ uint32_t status, uint32_t pid,
+ uint32_t data_size, uint8_t *data)
+{
+ struct qla4_work_evt *e;
+
+ e = qla4xxx_alloc_work(ha, data_size, QLA4_EVENT_PING_STATUS);
+ if (!e)
+ return QLA_ERROR;
+
+ e->u.ping.status = status;
+ e->u.ping.pid = pid;
+ e->u.ping.data_size = data_size;
+ memcpy(e->u.ping.data, data, data_size);
+
+ qla4xxx_post_work(ha, e);
+
+ return QLA_SUCCESS;
+}
+
+static void qla4xxx_do_work(struct scsi_qla_host *ha)
+{
+ struct qla4_work_evt *e, *tmp;
+ unsigned long flags;
+ LIST_HEAD(work);
+
+ spin_lock_irqsave(&ha->work_lock, flags);
+ list_splice_init(&ha->work_list, &work);
+ spin_unlock_irqrestore(&ha->work_lock, flags);
+
+ list_for_each_entry_safe(e, tmp, &work, list) {
+ list_del_init(&e->list);
+
+ switch (e->type) {
+ case QLA4_EVENT_AEN:
+ iscsi_post_host_event(ha->host_no,
+ &qla4xxx_iscsi_transport,
+ e->u.aen.code,
+ e->u.aen.data_size,
+ e->u.aen.data);
+ break;
+ case QLA4_EVENT_PING_STATUS:
+ iscsi_ping_comp_event(ha->host_no,
+ &qla4xxx_iscsi_transport,
+ e->u.ping.status,
+ e->u.ping.pid,
+ e->u.ping.data_size,
+ e->u.ping.data);
+ break;
+ default:
+ ql4_printk(KERN_WARNING, ha, "event type: 0x%x not "
+ "supported", e->type);
+ }
+ kfree(e);
+ }
+}
+
/**
* qla4xxx_do_dpc - dpc routine
* @data: in our case pointer to adapter structure
@@ -2826,6 +3306,9 @@ static void qla4xxx_do_dpc(struct work_struct *work)
return;
}
+ /* post events to application */
+ qla4xxx_do_work(ha);
+
if (is_qla8022(ha)) {
if (test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags)) {
qla4_8xxx_idc_lock(ha);
@@ -3341,9 +3824,8 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
/* Check Boot Mode */
val = rd_nvram_byte(ha, addr);
if (!(val & 0x07)) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Failed Boot options : 0x%x\n",
- __func__, val));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Adapter boot "
+ "options : 0x%x\n", __func__, val));
ret = QLA_ERROR;
goto exit_boot_info;
}
@@ -3388,9 +3870,8 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
}
/* Check Boot Mode */
if (!(buf[1] & 0x07)) {
- DEBUG2(ql4_printk(KERN_INFO, ha,
- "Failed: Boot options : 0x%x\n",
- buf[1]));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "Firmware boot options"
+ " : 0x%x\n", buf[1]));
ret = QLA_ERROR;
goto exit_boot_info_free;
}
@@ -3411,12 +3892,11 @@ static int get_fw_boot_info(struct scsi_qla_host *ha, uint16_t ddb_index[])
" target ID %d\n", __func__, ddb_index[0],
ddb_index[1]));
- ha->pri_ddb_idx = ddb_index[0];
- ha->sec_ddb_idx = ddb_index[1];
-
exit_boot_info_free:
dma_free_coherent(&ha->pdev->dev, size, buf, buf_dma);
exit_boot_info:
+ ha->pri_ddb_idx = ddb_index[0];
+ ha->sec_ddb_idx = ddb_index[1];
return ret;
}
@@ -3497,8 +3977,8 @@ static int qla4xxx_get_boot_target(struct scsi_qla_host *ha,
if (qla4xxx_bootdb_by_index(ha, fw_ddb_entry,
fw_ddb_entry_dma, ddb_index)) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Flash DDB read Failed\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: No Flash DDB found at "
+ "index [%d]\n", __func__, ddb_index));
ret = QLA_ERROR;
goto exit_boot_target;
}
@@ -3576,8 +4056,8 @@ static int qla4xxx_get_boot_info(struct scsi_qla_host *ha)
ddb_index[1] = 0xffff;
ret = get_fw_boot_info(ha, ddb_index);
if (ret != QLA_SUCCESS) {
- DEBUG2(ql4_printk(KERN_ERR, ha,
- "%s: Failed to set boot info.\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha,
+ "%s: No boot target configured.\n", __func__));
return ret;
}
@@ -3590,8 +4070,8 @@ static int qla4xxx_get_boot_info(struct scsi_qla_host *ha)
rval = qla4xxx_get_boot_target(ha, &(ha->boot_tgt.boot_pri_sess),
ddb_index[0]);
if (rval != QLA_SUCCESS) {
- DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Failed to get "
- "primary target\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Primary boot target not "
+ "configured\n", __func__));
} else
ret = QLA_SUCCESS;
@@ -3602,8 +4082,8 @@ sec_target:
rval = qla4xxx_get_boot_target(ha, &(ha->boot_tgt.boot_sec_sess),
ddb_index[1]);
if (rval != QLA_SUCCESS) {
- DEBUG2(ql4_printk(KERN_ERR, ha, "%s: Failed to get "
- "secondary target\n", __func__));
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Secondary boot target not"
+ " configured\n", __func__));
} else
ret = QLA_SUCCESS;
@@ -3772,11 +4252,13 @@ static void qla4xxx_convert_param_ddb(struct dev_db_entry *fw_ddb_entry,
sprintf(tddb->ip_addr, "%pI4", fw_ddb_entry->ip_addr);
tddb->port = le16_to_cpu(fw_ddb_entry->port);
+ memcpy(&tddb->isid[0], &fw_ddb_entry->isid[0], sizeof(tddb->isid));
}
static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
struct ql4_tuple_ddb *old_tddb,
- struct ql4_tuple_ddb *new_tddb)
+ struct ql4_tuple_ddb *new_tddb,
+ uint8_t is_isid_compare)
{
if (strcmp(old_tddb->iscsi_name, new_tddb->iscsi_name))
return QLA_ERROR;
@@ -3787,6 +4269,26 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
if (old_tddb->port != new_tddb->port)
return QLA_ERROR;
+ /* For multi sessions, driver generates the ISID, so do not compare
+ * ISID in reset path since it would be a comparision between the
+ * driver generated ISID and firmware generated ISID. This could
+ * lead to adding duplicated DDBs in the list as driver generated
+ * ISID would not match firmware generated ISID.
+ */
+ if (is_isid_compare) {
+ DEBUG2(ql4_printk(KERN_INFO, ha, "%s: old ISID [%02x%02x%02x"
+ "%02x%02x%02x] New ISID [%02x%02x%02x%02x%02x%02x]\n",
+ __func__, old_tddb->isid[5], old_tddb->isid[4],
+ old_tddb->isid[3], old_tddb->isid[2], old_tddb->isid[1],
+ old_tddb->isid[0], new_tddb->isid[5], new_tddb->isid[4],
+ new_tddb->isid[3], new_tddb->isid[2], new_tddb->isid[1],
+ new_tddb->isid[0]));
+
+ if (memcmp(&old_tddb->isid[0], &new_tddb->isid[0],
+ sizeof(old_tddb->isid)))
+ return QLA_ERROR;
+ }
+
DEBUG2(ql4_printk(KERN_INFO, ha,
"Match Found, fw[%d,%d,%s,%s], [%d,%d,%s,%s]",
old_tddb->port, old_tddb->tpgt, old_tddb->ip_addr,
@@ -3829,7 +4331,7 @@ static int qla4xxx_is_session_exists(struct scsi_qla_host *ha,
continue;
qla4xxx_get_param_ddb(ddb_entry, tmp_tddb);
- if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb)) {
+ if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb, false)) {
ret = QLA_SUCCESS; /* found */
goto exit_check;
}
@@ -3872,7 +4374,7 @@ static int qla4xxx_is_flash_ddb_exists(struct scsi_qla_host *ha,
list_for_each_entry_safe(nt_ddb_idx, nt_ddb_idx_tmp, list_nt, list) {
qla4xxx_convert_param_ddb(&nt_ddb_idx->fw_ddb, tmp_tddb);
- if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb)) {
+ if (!qla4xxx_compare_tuple_ddb(ha, fw_tddb, tmp_tddb, true)) {
ret = QLA_SUCCESS; /* found */
goto exit_check;
}
@@ -4038,6 +4540,10 @@ static void qla4xxx_build_st_list(struct scsi_qla_host *ha,
if (ret == QLA_ERROR)
break;
+ /* Ignore DDB if invalid state (unassigned) */
+ if (state == DDB_DS_UNASSIGNED)
+ goto continue_next_st;
+
/* Check if ST, add to the list_st */
if (strlen((char *) fw_ddb_entry->iscsi_name) != 0)
goto continue_next_st;
@@ -4397,6 +4903,9 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
spin_lock_init(&ha->hardware_lock);
+ /* Initialize work list */
+ INIT_LIST_HEAD(&ha->work_list);
+
/* Allocate dma buffers */
if (qla4xxx_mem_alloc(ha)) {
ql4_printk(KERN_WARNING, ha,
@@ -4524,8 +5033,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
ha->patch_number, ha->build_number);
if (qla4xxx_setup_boot_info(ha))
- ql4_printk(KERN_ERR, ha, "%s:ISCSI boot info setup failed\n",
- __func__);
+ ql4_printk(KERN_ERR, ha,
+ "%s: No iSCSI boot target configured\n", __func__);
/* Perform the build ddb list and login to each */
qla4xxx_build_ddb_list(ha, INIT_ADAPTER);
diff --git a/drivers/scsi/qla4xxx/ql4_version.h b/drivers/scsi/qla4xxx/ql4_version.h
index 133989b3a9f4..ede9af944141 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-k12"
+#define QLA4XXX_DRIVER_VERSION "5.02.00-k15"
diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c
index e40dc1cb09a0..b191dd549207 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.c b/drivers/scsi/scsi.c
index 2aeb2e9c4d3b..07322ecff90d 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -782,12 +782,6 @@ static void scsi_done(struct scsi_cmnd *cmd)
blk_complete_request(cmd->request);
}
-/* Move this to a header if it becomes more generally useful */
-static struct scsi_driver *scsi_cmd_to_driver(struct scsi_cmnd *cmd)
-{
- return *(struct scsi_driver **)cmd->request->rq_disk->private_data;
-}
-
/**
* scsi_finish_command - cleanup and pass command back to upper layer
* @cmd: the command
diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 68da6c092f65..591856131c4e 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -126,6 +126,7 @@ static const char * scsi_debug_version_date = "20100324";
#define SCSI_DEBUG_OPT_TRANSPORT_ERR 16
#define SCSI_DEBUG_OPT_DIF_ERR 32
#define SCSI_DEBUG_OPT_DIX_ERR 64
+#define SCSI_DEBUG_OPT_MAC_TIMEOUT 128
/* When "every_nth" > 0 then modulo "every_nth" commands:
* - a no response is simulated if SCSI_DEBUG_OPT_TIMEOUT is set
* - a RECOVERED_ERROR is simulated on successful read and write
@@ -2220,7 +2221,7 @@ static int resp_get_lba_status(struct scsi_cmnd * scmd,
mapped = map_state(lba, &num);
memset(arr, 0, SDEBUG_GET_LBA_STATUS_LEN);
- put_unaligned_be32(16, &arr[0]); /* Parameter Data Length */
+ put_unaligned_be32(20, &arr[0]); /* Parameter Data Length */
put_unaligned_be64(lba, &arr[8]); /* LBA */
put_unaligned_be32(num, &arr[16]); /* Number of blocks */
arr[20] = !mapped; /* mapped = 0, unmapped = 1 */
@@ -3615,6 +3616,9 @@ int scsi_debug_queuecommand_lck(struct scsi_cmnd *SCpnt, done_funct_t done)
scsi_debug_every_nth = -1;
if (SCSI_DEBUG_OPT_TIMEOUT & scsi_debug_opts)
return 0; /* ignore command causing timeout */
+ else if (SCSI_DEBUG_OPT_MAC_TIMEOUT & scsi_debug_opts &&
+ scsi_medium_access_command(SCpnt))
+ return 0; /* time out reads and writes */
else if (SCSI_DEBUG_OPT_RECOVERED_ERR & scsi_debug_opts)
inj_recovered = 1; /* to reads and writes below */
else if (SCSI_DEBUG_OPT_TRANSPORT_ERR & scsi_debug_opts)
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 5f84a148eb14..2cfcbffa41fd 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -30,6 +30,7 @@
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_device.h>
+#include <scsi/scsi_driver.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_host.h>
@@ -141,11 +142,11 @@ enum blk_eh_timer_return scsi_times_out(struct request *req)
else if (host->hostt->eh_timed_out)
rtn = host->hostt->eh_timed_out(scmd);
+ scmd->result |= DID_TIME_OUT << 16;
+
if (unlikely(rtn == BLK_EH_NOT_HANDLED &&
- !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
- scmd->result |= DID_TIME_OUT << 16;
+ !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)))
rtn = BLK_EH_HANDLED;
- }
return rtn;
}
@@ -366,6 +367,14 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
return TARGET_ERROR;
case ILLEGAL_REQUEST:
+ if (sshdr.asc == 0x20 || /* Invalid command operation code */
+ sshdr.asc == 0x21 || /* Logical block address out of range */
+ sshdr.asc == 0x24 || /* Invalid field in cdb */
+ sshdr.asc == 0x26) { /* Parameter value invalid */
+ return TARGET_ERROR;
+ }
+ return SUCCESS;
+
default:
return SUCCESS;
}
@@ -770,6 +779,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
int cmnd_size, int timeout, unsigned sense_bytes)
{
struct scsi_device *sdev = scmd->device;
+ struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd);
struct Scsi_Host *shost = sdev->host;
DECLARE_COMPLETION_ONSTACK(done);
unsigned long timeleft;
@@ -824,6 +834,10 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
}
scsi_eh_restore_cmnd(scmd, &ses);
+
+ if (sdrv->eh_action)
+ rtn = sdrv->eh_action(scmd, cmnd, cmnd_size, rtn);
+
return rtn;
}
@@ -1540,7 +1554,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
* Need to modify host byte to signal a
* permanent target failure
*/
- scmd->result |= (DID_TARGET_FAILURE << 16);
+ set_host_byte(scmd, DID_TARGET_FAILURE);
rtn = SUCCESS;
}
/* if rtn == FAILED, we have no sense information;
@@ -1560,7 +1574,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
case RESERVATION_CONFLICT:
sdev_printk(KERN_INFO, scmd->device,
"reservation conflict\n");
- scmd->result |= (DID_NEXUS_FAILURE << 16);
+ set_host_byte(scmd, DID_NEXUS_FAILURE);
return SUCCESS; /* causes immediate i/o error */
default:
return FAILED;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index a33b2b66da67..ead6405f3e51 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -682,11 +682,11 @@ static int __scsi_error_from_host_byte(struct scsi_cmnd *cmd, int result)
error = -ENOLINK;
break;
case DID_TARGET_FAILURE:
- cmd->result |= (DID_OK << 16);
+ set_host_byte(cmd, DID_OK);
error = -EREMOTEIO;
break;
case DID_NEXUS_FAILURE:
- cmd->result |= (DID_OK << 16);
+ set_host_byte(cmd, DID_OK);
error = -EBADE;
break;
default:
@@ -880,6 +880,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
cmd->cmnd[0] == WRITE_SAME)) {
description = "Discard failure";
action = ACTION_FAIL;
+ error = -EREMOTEIO;
} else
action = ACTION_FAIL;
break;
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index f59d4a05ecd7..80fbe2ac0b47 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -313,7 +313,7 @@ static void fc_scsi_scan_rport(struct work_struct *work);
#define FC_STARGET_NUM_ATTRS 3
#define FC_RPORT_NUM_ATTRS 10
#define FC_VPORT_NUM_ATTRS 9
-#define FC_HOST_NUM_ATTRS 22
+#define FC_HOST_NUM_ATTRS 29
struct fc_internal {
struct scsi_transport_template t;
@@ -399,6 +399,20 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
fc_host->max_npiv_vports = 0;
memset(fc_host->serial_number, 0,
sizeof(fc_host->serial_number));
+ memset(fc_host->manufacturer, 0,
+ sizeof(fc_host->manufacturer));
+ memset(fc_host->model, 0,
+ sizeof(fc_host->model));
+ memset(fc_host->model_description, 0,
+ sizeof(fc_host->model_description));
+ memset(fc_host->hardware_version, 0,
+ sizeof(fc_host->hardware_version));
+ memset(fc_host->driver_version, 0,
+ sizeof(fc_host->driver_version));
+ memset(fc_host->firmware_version, 0,
+ sizeof(fc_host->firmware_version));
+ memset(fc_host->optionrom_version, 0,
+ sizeof(fc_host->optionrom_version));
fc_host->port_id = -1;
fc_host->port_type = FC_PORTTYPE_UNKNOWN;
@@ -1513,6 +1527,13 @@ fc_private_host_rd_attr_cast(permanent_port_name, "0x%llx\n", 20,
fc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20);
fc_private_host_rd_attr(max_npiv_vports, "%u\n", 20);
fc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1));
+fc_private_host_rd_attr(manufacturer, "%s\n", FC_SERIAL_NUMBER_SIZE + 1);
+fc_private_host_rd_attr(model, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
+fc_private_host_rd_attr(model_description, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
+fc_private_host_rd_attr(hardware_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
+fc_private_host_rd_attr(driver_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
+fc_private_host_rd_attr(firmware_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
+fc_private_host_rd_attr(optionrom_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
/* Dynamic Host Attributes */
@@ -2208,6 +2229,13 @@ fc_attach_transport(struct fc_function_template *ft)
SETUP_HOST_ATTRIBUTE_RD_NS(npiv_vports_inuse);
}
SETUP_HOST_ATTRIBUTE_RD(serial_number);
+ SETUP_HOST_ATTRIBUTE_RD(manufacturer);
+ SETUP_HOST_ATTRIBUTE_RD(model);
+ SETUP_HOST_ATTRIBUTE_RD(model_description);
+ SETUP_HOST_ATTRIBUTE_RD(hardware_version);
+ SETUP_HOST_ATTRIBUTE_RD(driver_version);
+ SETUP_HOST_ATTRIBUTE_RD(firmware_version);
+ SETUP_HOST_ATTRIBUTE_RD(optionrom_version);
SETUP_HOST_ATTRIBUTE_RD(port_id);
SETUP_HOST_ATTRIBUTE_RD(port_type);
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c
index cfd491437239..fac31730addf 100644
--- a/drivers/scsi/scsi_transport_iscsi.c
+++ b/drivers/scsi/scsi_transport_iscsi.c
@@ -727,10 +727,11 @@ static void iscsi_session_release(struct device *dev)
kfree(session);
}
-static int iscsi_is_session_dev(const struct device *dev)
+int iscsi_is_session_dev(const struct device *dev)
{
return dev->release == iscsi_session_release;
}
+EXPORT_SYMBOL_GPL(iscsi_is_session_dev);
static int iscsi_iter_session_fn(struct device *dev, void *data)
{
@@ -1476,6 +1477,66 @@ void iscsi_conn_login_event(struct iscsi_cls_conn *conn,
}
EXPORT_SYMBOL_GPL(iscsi_conn_login_event);
+void iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
+ enum iscsi_host_event_code code, uint32_t data_size,
+ uint8_t *data)
+{
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ struct iscsi_uevent *ev;
+ int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n",
+ host_no, code);
+ return;
+ }
+
+ nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ ev->transport_handle = iscsi_handle(transport);
+ ev->type = ISCSI_KEVENT_HOST_EVENT;
+ ev->r.host_event.host_no = host_no;
+ ev->r.host_event.code = code;
+ ev->r.host_event.data_size = data_size;
+
+ if (data_size)
+ memcpy((char *)ev + sizeof(*ev), data, data_size);
+
+ iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
+}
+EXPORT_SYMBOL_GPL(iscsi_post_host_event);
+
+void iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
+ uint32_t status, uint32_t pid, uint32_t data_size,
+ uint8_t *data)
+{
+ struct nlmsghdr *nlh;
+ struct sk_buff *skb;
+ struct iscsi_uevent *ev;
+ int len = NLMSG_SPACE(sizeof(*ev) + data_size);
+
+ skb = alloc_skb(len, GFP_KERNEL);
+ if (!skb) {
+ printk(KERN_ERR "gracefully ignored ping comp: OOM\n");
+ return;
+ }
+
+ nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
+ ev = NLMSG_DATA(nlh);
+ ev->transport_handle = iscsi_handle(transport);
+ ev->type = ISCSI_KEVENT_PING_COMP;
+ ev->r.ping_comp.host_no = host_no;
+ ev->r.ping_comp.status = status;
+ ev->r.ping_comp.pid = pid;
+ 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);
+}
+EXPORT_SYMBOL_GPL(iscsi_ping_comp_event);
+
static int
iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
void *payload, int size)
@@ -1915,6 +1976,123 @@ iscsi_set_iface_params(struct iscsi_transport *transport,
}
static int
+iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev)
+{
+ struct Scsi_Host *shost;
+ struct sockaddr *dst_addr;
+ int err;
+
+ if (!transport->send_ping)
+ return -ENOSYS;
+
+ shost = scsi_host_lookup(ev->u.iscsi_ping.host_no);
+ if (!shost) {
+ printk(KERN_ERR "iscsi_ping could not find host no %u\n",
+ ev->u.iscsi_ping.host_no);
+ return -ENODEV;
+ }
+
+ dst_addr = (struct sockaddr *)((char *)ev + sizeof(*ev));
+ err = transport->send_ping(shost, ev->u.iscsi_ping.iface_num,
+ ev->u.iscsi_ping.iface_type,
+ ev->u.iscsi_ping.payload_size,
+ ev->u.iscsi_ping.pid,
+ dst_addr);
+ scsi_host_put(shost);
+ return err;
+}
+
+static int
+iscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh)
+{
+ struct iscsi_uevent *ev = NLMSG_DATA(nlh);
+ struct Scsi_Host *shost = NULL;
+ struct iscsi_chap_rec *chap_rec;
+ struct iscsi_internal *priv;
+ struct sk_buff *skbchap;
+ struct nlmsghdr *nlhchap;
+ struct iscsi_uevent *evchap;
+ uint32_t chap_buf_size;
+ int len, err = 0;
+ char *buf;
+
+ if (!transport->get_chap)
+ return -EINVAL;
+
+ priv = iscsi_if_transport_lookup(transport);
+ if (!priv)
+ return -EINVAL;
+
+ chap_buf_size = (ev->u.get_chap.num_entries * sizeof(*chap_rec));
+ len = NLMSG_SPACE(sizeof(*ev) + chap_buf_size);
+
+ shost = scsi_host_lookup(ev->u.get_chap.host_no);
+ if (!shost) {
+ printk(KERN_ERR "%s: failed. Cound not find host no %u\n",
+ __func__, ev->u.get_chap.host_no);
+ return -ENODEV;
+ }
+
+ do {
+ int actual_size;
+
+ skbchap = alloc_skb(len, GFP_KERNEL);
+ if (!skbchap) {
+ printk(KERN_ERR "can not deliver chap: OOM\n");
+ err = -ENOMEM;
+ goto exit_get_chap;
+ }
+
+ nlhchap = __nlmsg_put(skbchap, 0, 0, 0,
+ (len - sizeof(*nlhchap)), 0);
+ evchap = NLMSG_DATA(nlhchap);
+ memset(evchap, 0, sizeof(*evchap));
+ evchap->transport_handle = iscsi_handle(transport);
+ evchap->type = nlh->nlmsg_type;
+ evchap->u.get_chap.host_no = ev->u.get_chap.host_no;
+ evchap->u.get_chap.chap_tbl_idx = ev->u.get_chap.chap_tbl_idx;
+ evchap->u.get_chap.num_entries = ev->u.get_chap.num_entries;
+ buf = (char *) ((char *)evchap + sizeof(*evchap));
+ memset(buf, 0, chap_buf_size);
+
+ err = transport->get_chap(shost, ev->u.get_chap.chap_tbl_idx,
+ &evchap->u.get_chap.num_entries, buf);
+
+ actual_size = NLMSG_SPACE(sizeof(*ev) + chap_buf_size);
+ skb_trim(skbchap, NLMSG_ALIGN(actual_size));
+ nlhchap->nlmsg_len = actual_size;
+
+ err = iscsi_multicast_skb(skbchap, ISCSI_NL_GRP_ISCSID,
+ GFP_KERNEL);
+ } while (err < 0 && err != -ECONNREFUSED);
+
+exit_get_chap:
+ scsi_host_put(shost);
+ return err;
+}
+
+static int iscsi_delete_chap(struct iscsi_transport *transport,
+ struct iscsi_uevent *ev)
+{
+ struct Scsi_Host *shost;
+ int err = 0;
+
+ if (!transport->delete_chap)
+ return -ENOSYS;
+
+ shost = scsi_host_lookup(ev->u.delete_chap.host_no);
+ if (!shost) {
+ printk(KERN_ERR "%s could not find host no %u\n",
+ __func__, ev->u.delete_chap.host_no);
+ return -ENODEV;
+ }
+
+ err = transport->delete_chap(shost, ev->u.delete_chap.chap_tbl_idx);
+ scsi_host_put(shost);
+ return err;
+}
+
+static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
{
int err = 0;
@@ -1941,7 +2119,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
switch (nlh->nlmsg_type) {
case ISCSI_UEVENT_CREATE_SESSION:
err = iscsi_if_create_session(priv, ep, ev,
- NETLINK_CREDS(skb)->pid,
+ NETLINK_CB(skb).pid,
ev->u.c_session.initial_cmdsn,
ev->u.c_session.cmds_max,
ev->u.c_session.queue_depth);
@@ -1954,7 +2132,7 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
}
err = iscsi_if_create_session(priv, ep, ev,
- NETLINK_CREDS(skb)->pid,
+ NETLINK_CB(skb).pid,
ev->u.c_bound_session.initial_cmdsn,
ev->u.c_bound_session.cmds_max,
ev->u.c_bound_session.queue_depth);
@@ -2059,6 +2237,15 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
err = iscsi_set_iface_params(transport, ev,
nlmsg_attrlen(nlh, sizeof(*ev)));
break;
+ case ISCSI_UEVENT_PING:
+ err = iscsi_send_ping(transport, ev);
+ break;
+ case ISCSI_UEVENT_GET_CHAP:
+ err = iscsi_get_chap(transport, nlh);
+ break;
+ case ISCSI_UEVENT_DELETE_CHAP:
+ err = iscsi_delete_chap(transport, ev);
+ break;
default:
err = -ENOSYS;
break;
@@ -2108,9 +2295,11 @@ iscsi_if_rx(struct sk_buff *skb)
*/
if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
break;
+ if (ev->type == ISCSI_UEVENT_GET_CHAP && !err)
+ break;
err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
- } while (err < 0 && err != -ECONNREFUSED);
+ } while (err < 0 && err != -ECONNREFUSED && err != -ESRCH);
skb_pull(skb, rlen);
}
mutex_unlock(&rx_queue_mutex);
@@ -2286,6 +2475,8 @@ iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
+iscsi_session_attr(chap_out_idx, ISCSI_PARAM_CHAP_OUT_IDX, 1);
+iscsi_session_attr(chap_in_idx, ISCSI_PARAM_CHAP_IN_IDX, 1);
iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
@@ -2382,6 +2573,8 @@ static struct attribute *iscsi_session_attrs[] = {
&dev_attr_priv_sess_recovery_tmo.attr,
&dev_attr_priv_sess_state.attr,
&dev_attr_priv_sess_creator.attr,
+ &dev_attr_sess_chap_out_idx.attr,
+ &dev_attr_sess_chap_in_idx.attr,
NULL,
};
@@ -2413,6 +2606,10 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
param = ISCSI_PARAM_TARGET_NAME;
else if (attr == &dev_attr_sess_tpgt.attr)
param = ISCSI_PARAM_TPGT;
+ else if (attr == &dev_attr_sess_chap_in_idx.attr)
+ param = ISCSI_PARAM_CHAP_IN_IDX;
+ else if (attr == &dev_attr_sess_chap_out_idx.attr)
+ param = ISCSI_PARAM_CHAP_OUT_IDX;
else if (attr == &dev_attr_sess_password.attr)
param = ISCSI_PARAM_USERNAME;
else if (attr == &dev_attr_sess_password_in.attr)
@@ -2476,12 +2673,16 @@ iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
+iscsi_host_attr(port_state, ISCSI_HOST_PARAM_PORT_STATE);
+iscsi_host_attr(port_speed, ISCSI_HOST_PARAM_PORT_SPEED);
static struct attribute *iscsi_host_attrs[] = {
&dev_attr_host_netdev.attr,
&dev_attr_host_hwaddress.attr,
&dev_attr_host_ipaddress.attr,
&dev_attr_host_initiatorname.attr,
+ &dev_attr_host_port_state.attr,
+ &dev_attr_host_port_speed.attr,
NULL,
};
@@ -2501,6 +2702,10 @@ static umode_t iscsi_host_attr_is_visible(struct kobject *kobj,
param = ISCSI_HOST_PARAM_IPADDRESS;
else if (attr == &dev_attr_host_initiatorname.attr)
param = ISCSI_HOST_PARAM_INITIATOR_NAME;
+ else if (attr == &dev_attr_host_port_state.attr)
+ param = ISCSI_HOST_PARAM_PORT_STATE;
+ else if (attr == &dev_attr_host_port_speed.attr)
+ param = ISCSI_HOST_PARAM_PORT_SPEED;
else {
WARN_ONCE(1, "Invalid host attr");
return 0;
@@ -2514,6 +2719,61 @@ static struct attribute_group iscsi_host_group = {
.is_visible = iscsi_host_attr_is_visible,
};
+/* convert iscsi_port_speed values to ascii string name */
+static const struct {
+ enum iscsi_port_speed value;
+ char *name;
+} iscsi_port_speed_names[] = {
+ {ISCSI_PORT_SPEED_UNKNOWN, "Unknown" },
+ {ISCSI_PORT_SPEED_10MBPS, "10 Mbps" },
+ {ISCSI_PORT_SPEED_100MBPS, "100 Mbps" },
+ {ISCSI_PORT_SPEED_1GBPS, "1 Gbps" },
+ {ISCSI_PORT_SPEED_10GBPS, "10 Gbps" },
+};
+
+char *iscsi_get_port_speed_name(struct Scsi_Host *shost)
+{
+ int i;
+ char *speed = "Unknown!";
+ struct iscsi_cls_host *ihost = shost->shost_data;
+ uint32_t port_speed = ihost->port_speed;
+
+ for (i = 0; i < ARRAY_SIZE(iscsi_port_speed_names); i++) {
+ if (iscsi_port_speed_names[i].value & port_speed) {
+ speed = iscsi_port_speed_names[i].name;
+ break;
+ }
+ }
+ return speed;
+}
+EXPORT_SYMBOL_GPL(iscsi_get_port_speed_name);
+
+/* convert iscsi_port_state values to ascii string name */
+static const struct {
+ enum iscsi_port_state value;
+ char *name;
+} iscsi_port_state_names[] = {
+ {ISCSI_PORT_STATE_DOWN, "LINK DOWN" },
+ {ISCSI_PORT_STATE_UP, "LINK UP" },
+};
+
+char *iscsi_get_port_state_name(struct Scsi_Host *shost)
+{
+ int i;
+ char *state = "Unknown!";
+ struct iscsi_cls_host *ihost = shost->shost_data;
+ uint32_t port_state = ihost->port_state;
+
+ for (i = 0; i < ARRAY_SIZE(iscsi_port_state_names); i++) {
+ if (iscsi_port_state_names[i].value & port_state) {
+ state = iscsi_port_state_names[i].name;
+ break;
+ }
+ }
+ return state;
+}
+EXPORT_SYMBOL_GPL(iscsi_get_port_state_name);
+
static int iscsi_session_match(struct attribute_container *cont,
struct device *dev)
{
diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c
index 9d9330ae4213..f7565fc4f0e3 100644
--- a/drivers/scsi/scsi_transport_sas.c
+++ b/drivers/scsi/scsi_transport_sas.c
@@ -615,6 +615,7 @@ do_sas_phy_reset(struct device *dev, size_t count, int hard_reset)
error = i->f->phy_reset(phy, hard_reset);
if (error)
return error;
+ phy->enabled = 1;
return count;
};
@@ -652,9 +653,21 @@ sas_phy_linkerror_attr(running_disparity_error_count);
sas_phy_linkerror_attr(loss_of_dword_sync_count);
sas_phy_linkerror_attr(phy_reset_problem_count);
+static int sas_phy_setup(struct transport_container *tc, struct device *dev,
+ struct device *cdev)
+{
+ struct sas_phy *phy = dev_to_phy(dev);
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_internal *i = to_sas_internal(shost->transportt);
+
+ if (i->f->phy_setup)
+ i->f->phy_setup(phy);
+
+ return 0;
+}
static DECLARE_TRANSPORT_CLASS(sas_phy_class,
- "sas_phy", NULL, NULL, NULL);
+ "sas_phy", sas_phy_setup, NULL, NULL);
static int sas_phy_match(struct attribute_container *cont, struct device *dev)
{
@@ -678,7 +691,11 @@ static int sas_phy_match(struct attribute_container *cont, struct device *dev)
static void sas_phy_release(struct device *dev)
{
struct sas_phy *phy = dev_to_phy(dev);
+ struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+ struct sas_internal *i = to_sas_internal(shost->transportt);
+ if (i->f->phy_release)
+ i->f->phy_release(phy);
put_device(dev->parent);
kfree(phy);
}
@@ -1044,6 +1061,29 @@ int scsi_is_sas_port(const struct device *dev)
EXPORT_SYMBOL(scsi_is_sas_port);
/**
+ * sas_port_get_phy - try to take a reference on a port member
+ * @port: port to check
+ */
+struct sas_phy *sas_port_get_phy(struct sas_port *port)
+{
+ struct sas_phy *phy;
+
+ mutex_lock(&port->phy_list_mutex);
+ if (list_empty(&port->phy_list))
+ phy = NULL;
+ else {
+ struct list_head *ent = port->phy_list.next;
+
+ phy = list_entry(ent, typeof(*phy), port_siblings);
+ get_device(&phy->dev);
+ }
+ mutex_unlock(&port->phy_list_mutex);
+
+ return phy;
+}
+EXPORT_SYMBOL(sas_port_get_phy);
+
+/**
* sas_port_add_phy - add another phy to a port to form a wide port
* @port: port to add the phy to
* @phy: phy to add
@@ -1603,6 +1643,20 @@ sas_rphy_delete(struct sas_rphy *rphy)
EXPORT_SYMBOL(sas_rphy_delete);
/**
+ * sas_rphy_unlink - unlink SAS remote PHY
+ * @rphy: SAS remote phy to unlink from its parent port
+ *
+ * Removes port reference to an rphy
+ */
+void sas_rphy_unlink(struct sas_rphy *rphy)
+{
+ struct sas_port *parent = dev_to_sas_port(rphy->dev.parent);
+
+ parent->rphy = NULL;
+}
+EXPORT_SYMBOL(sas_rphy_unlink);
+
+/**
* sas_rphy_remove - remove SAS remote PHY
* @rphy: SAS remote phy to remove
*
@@ -1612,7 +1666,6 @@ void
sas_rphy_remove(struct sas_rphy *rphy)
{
struct device *dev = &rphy->dev;
- struct sas_port *parent = dev_to_sas_port(dev->parent);
switch (rphy->identify.device_type) {
case SAS_END_DEVICE:
@@ -1626,10 +1679,9 @@ sas_rphy_remove(struct sas_rphy *rphy)
break;
}
+ sas_rphy_unlink(rphy);
transport_remove_device(dev);
device_del(dev);
-
- parent->rphy = NULL;
}
EXPORT_SYMBOL(sas_rphy_remove);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index d173b90b25e9..09e3df42a402 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -107,6 +107,7 @@ static int sd_suspend(struct device *, pm_message_t state);
static int sd_resume(struct device *);
static void sd_rescan(struct device *);
static int sd_done(struct scsi_cmnd *);
+static int sd_eh_action(struct scsi_cmnd *, unsigned char *, int, int);
static void sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer);
static void scsi_disk_release(struct device *cdev);
static void sd_print_sense_hdr(struct scsi_disk *, struct scsi_sense_hdr *);
@@ -346,6 +347,31 @@ sd_store_provisioning_mode(struct device *dev, struct device_attribute *attr,
return count;
}
+static ssize_t
+sd_show_max_medium_access_timeouts(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+
+ return snprintf(buf, 20, "%u\n", sdkp->max_medium_access_timeouts);
+}
+
+static ssize_t
+sd_store_max_medium_access_timeouts(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct scsi_disk *sdkp = to_scsi_disk(dev);
+ int err;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+
+ err = kstrtouint(buf, 10, &sdkp->max_medium_access_timeouts);
+
+ return err ? err : count;
+}
+
static struct device_attribute sd_disk_attrs[] = {
__ATTR(cache_type, S_IRUGO|S_IWUSR, sd_show_cache_type,
sd_store_cache_type),
@@ -360,6 +386,9 @@ static struct device_attribute sd_disk_attrs[] = {
__ATTR(thin_provisioning, S_IRUGO, sd_show_thin_provisioning, NULL),
__ATTR(provisioning_mode, S_IRUGO|S_IWUSR, sd_show_provisioning_mode,
sd_store_provisioning_mode),
+ __ATTR(max_medium_access_timeouts, S_IRUGO|S_IWUSR,
+ sd_show_max_medium_access_timeouts,
+ sd_store_max_medium_access_timeouts),
__ATTR_NULL,
};
@@ -382,6 +411,7 @@ static struct scsi_driver sd_template = {
},
.rescan = sd_rescan,
.done = sd_done,
+ .eh_action = sd_eh_action,
};
/*
@@ -497,6 +527,8 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
max(sdkp->physical_block_size,
sdkp->unmap_granularity * logical_block_size);
+ sdkp->provisioning_mode = mode;
+
switch (mode) {
case SD_LBP_DISABLE:
@@ -524,8 +556,6 @@ static void sd_config_discard(struct scsi_disk *sdkp, unsigned int mode)
q->limits.max_discard_sectors = max_blocks * (logical_block_size >> 9);
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
-
- sdkp->provisioning_mode = mode;
}
/**
@@ -1313,6 +1343,55 @@ static const struct block_device_operations sd_fops = {
.unlock_native_capacity = sd_unlock_native_capacity,
};
+/**
+ * sd_eh_action - error handling callback
+ * @scmd: sd-issued command that has failed
+ * @eh_cmnd: The command that was sent during error handling
+ * @eh_cmnd_len: Length of eh_cmnd in bytes
+ * @eh_disp: The recovery disposition suggested by the midlayer
+ *
+ * This function is called by the SCSI midlayer upon completion of
+ * an error handling command (TEST UNIT READY, START STOP UNIT,
+ * etc.) The command sent to the device by the error handler is
+ * stored in eh_cmnd. The result of sending the eh command is
+ * passed in eh_disp.
+ **/
+static int sd_eh_action(struct scsi_cmnd *scmd, unsigned char *eh_cmnd,
+ int eh_cmnd_len, int eh_disp)
+{
+ struct scsi_disk *sdkp = scsi_disk(scmd->request->rq_disk);
+
+ if (!scsi_device_online(scmd->device) ||
+ !scsi_medium_access_command(scmd))
+ return eh_disp;
+
+ /*
+ * The device has timed out executing a medium access command.
+ * However, the TEST UNIT READY command sent during error
+ * handling completed successfully. Either the device is in the
+ * process of recovering or has it suffered an internal failure
+ * that prevents access to the storage medium.
+ */
+ if (host_byte(scmd->result) == DID_TIME_OUT && eh_disp == SUCCESS &&
+ eh_cmnd_len && eh_cmnd[0] == TEST_UNIT_READY)
+ sdkp->medium_access_timed_out++;
+
+ /*
+ * If the device keeps failing read/write commands but TEST UNIT
+ * READY always completes successfully we assume that medium
+ * access is no longer possible and take the device offline.
+ */
+ if (sdkp->medium_access_timed_out >= sdkp->max_medium_access_timeouts) {
+ scmd_printk(KERN_ERR, scmd,
+ "Medium access timeout failure. Offlining disk!\n");
+ scsi_device_set_state(scmd->device, SDEV_OFFLINE);
+
+ return FAILED;
+ }
+
+ return eh_disp;
+}
+
static unsigned int sd_completed_bytes(struct scsi_cmnd *scmd)
{
u64 start_lba = blk_rq_pos(scmd->request);
@@ -1402,6 +1481,8 @@ static int sd_done(struct scsi_cmnd *SCpnt)
(!sense_valid || sense_deferred))
goto out;
+ sdkp->medium_access_timed_out = 0;
+
switch (sshdr.sense_key) {
case HARDWARE_ERROR:
case MEDIUM_ERROR:
@@ -2523,6 +2604,7 @@ static void sd_probe_async(void *data, async_cookie_t cookie)
sdkp->RCD = 0;
sdkp->ATO = 0;
sdkp->first_scan = 1;
+ sdkp->max_medium_access_timeouts = SD_MAX_MEDIUM_TIMEOUTS;
sd_revalidate_disk(gd);
diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h
index 4163f2910e3d..f703f4827b6f 100644
--- a/drivers/scsi/sd.h
+++ b/drivers/scsi/sd.h
@@ -20,6 +20,7 @@
*/
#define SD_MAX_RETRIES 5
#define SD_PASSTHROUGH_RETRIES 1
+#define SD_MAX_MEDIUM_TIMEOUTS 2
/*
* Size of the initial data buffer for mode and read capacity data
@@ -59,6 +60,8 @@ struct scsi_disk {
u32 unmap_alignment;
u32 index;
unsigned int physical_block_size;
+ unsigned int max_medium_access_timeouts;
+ unsigned int medium_access_timed_out;
u8 media_present;
u8 write_prot;
u8 protection_type;/* Data Integrity Field */
@@ -88,6 +91,38 @@ static inline struct scsi_disk *scsi_disk(struct gendisk *disk)
(sdsk)->disk->disk_name, ##a) : \
sdev_printk(prefix, (sdsk)->device, fmt, ##a)
+static inline int scsi_medium_access_command(struct scsi_cmnd *scmd)
+{
+ switch (scmd->cmnd[0]) {
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ case READ_16:
+ case SYNCHRONIZE_CACHE:
+ case VERIFY:
+ case VERIFY_12:
+ case VERIFY_16:
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ case WRITE_16:
+ case WRITE_SAME:
+ case WRITE_SAME_16:
+ case UNMAP:
+ return 1;
+ case VARIABLE_LENGTH_CMD:
+ switch (scmd->cmnd[9]) {
+ case READ_32:
+ case VERIFY_32:
+ case WRITE_32:
+ case WRITE_SAME_32:
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/*
* A DIF-capable target device can be formatted with different
* protection schemes. Currently 0 through 3 are defined:
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 9b28f39bac26..a15f691f9d34 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>
@@ -1177,6 +1176,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp)
static int st_open(struct inode *inode, struct file *filp)
{
int i, retval = (-EIO);
+ int resumed = 0;
struct scsi_tape *STp;
struct st_partstat *STps;
int dev = TAPE_NR(inode);
@@ -1211,6 +1211,11 @@ static int st_open(struct inode *inode, struct file *filp)
write_unlock(&st_dev_arr_lock);
STp->rew_at_close = STp->autorew_dev = (iminor(inode) & 0x80) == 0;
+ if (scsi_autopm_get_device(STp->device) < 0) {
+ retval = -EIO;
+ goto err_out;
+ }
+ resumed = 1;
if (!scsi_block_when_processing_errors(STp->device)) {
retval = (-ENXIO);
goto err_out;
@@ -1258,6 +1263,8 @@ static int st_open(struct inode *inode, struct file *filp)
normalize_buffer(STp->buffer);
STp->in_use = 0;
scsi_tape_put(STp);
+ if (resumed)
+ scsi_autopm_put_device(STp->device);
mutex_unlock(&st_mutex);
return retval;
@@ -1391,6 +1398,7 @@ static int st_release(struct inode *inode, struct file *filp)
write_lock(&st_dev_arr_lock);
STp->in_use = 0;
write_unlock(&st_dev_arr_lock);
+ scsi_autopm_put_device(STp->device);
scsi_tape_put(STp);
return result;
@@ -4154,6 +4162,7 @@ static int st_probe(struct device *dev)
if (error)
goto out_free_tape;
}
+ scsi_autopm_put_device(SDp);
sdev_printk(KERN_NOTICE, SDp,
"Attached scsi tape %s\n", tape_name(tpnt));
@@ -4201,6 +4210,7 @@ static int st_remove(struct device *dev)
struct scsi_tape *tpnt;
int i, j, mode;
+ scsi_autopm_get_device(SDp);
write_lock(&st_dev_arr_lock);
for (i = 0; i < st_dev_max; i++) {
tpnt = scsi_tapes[i];
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
index baf7328de956..6e25889db9d4 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 fbba78e5722e..a3dd55d1d2fd 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 012c86edd59f..ac4eca6a5328 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 041eaaace2c3..d672d97fb84a 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 90e104d6b558..9c216e563568 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/ultrastor.c b/drivers/scsi/ultrastor.c
index 7e22b737dfd8..14e0c40a68c9 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/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
new file mode 100644
index 000000000000..efccd72c4a3e
--- /dev/null
+++ b/drivers/scsi/virtio_scsi.c
@@ -0,0 +1,594 @@
+/*
+ * Virtio SCSI HBA driver
+ *
+ * Copyright IBM Corp. 2010
+ * Copyright Red Hat, Inc. 2011
+ *
+ * Authors:
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mempool.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+
+#define VIRTIO_SCSI_MEMPOOL_SZ 64
+
+/* Command queue element */
+struct virtio_scsi_cmd {
+ struct scsi_cmnd *sc;
+ struct completion *comp;
+ union {
+ struct virtio_scsi_cmd_req cmd;
+ struct virtio_scsi_ctrl_tmf_req tmf;
+ struct virtio_scsi_ctrl_an_req an;
+ } req;
+ union {
+ struct virtio_scsi_cmd_resp cmd;
+ struct virtio_scsi_ctrl_tmf_resp tmf;
+ struct virtio_scsi_ctrl_an_resp an;
+ struct virtio_scsi_event evt;
+ } resp;
+} ____cacheline_aligned_in_smp;
+
+/* Driver instance state */
+struct virtio_scsi {
+ /* Protects ctrl_vq, req_vq and sg[] */
+ spinlock_t vq_lock;
+
+ struct virtio_device *vdev;
+ struct virtqueue *ctrl_vq;
+ struct virtqueue *event_vq;
+ struct virtqueue *req_vq;
+
+ /* For sglist construction when adding commands to the virtqueue. */
+ struct scatterlist sg[];
+};
+
+static struct kmem_cache *virtscsi_cmd_cache;
+static mempool_t *virtscsi_cmd_pool;
+
+static inline struct Scsi_Host *virtio_scsi_host(struct virtio_device *vdev)
+{
+ return vdev->priv;
+}
+
+static void virtscsi_compute_resid(struct scsi_cmnd *sc, u32 resid)
+{
+ if (!resid)
+ return;
+
+ if (!scsi_bidi_cmnd(sc)) {
+ scsi_set_resid(sc, resid);
+ return;
+ }
+
+ scsi_in(sc)->resid = min(resid, scsi_in(sc)->length);
+ scsi_out(sc)->resid = resid - scsi_in(sc)->resid;
+}
+
+/**
+ * virtscsi_complete_cmd - finish a scsi_cmd and invoke scsi_done
+ *
+ * Called with vq_lock held.
+ */
+static void virtscsi_complete_cmd(void *buf)
+{
+ struct virtio_scsi_cmd *cmd = buf;
+ struct scsi_cmnd *sc = cmd->sc;
+ struct virtio_scsi_cmd_resp *resp = &cmd->resp.cmd;
+
+ dev_dbg(&sc->device->sdev_gendev,
+ "cmd %p response %u status %#02x sense_len %u\n",
+ sc, resp->response, resp->status, resp->sense_len);
+
+ sc->result = resp->status;
+ virtscsi_compute_resid(sc, resp->resid);
+ switch (resp->response) {
+ case VIRTIO_SCSI_S_OK:
+ set_host_byte(sc, DID_OK);
+ break;
+ case VIRTIO_SCSI_S_OVERRUN:
+ set_host_byte(sc, DID_ERROR);
+ break;
+ case VIRTIO_SCSI_S_ABORTED:
+ set_host_byte(sc, DID_ABORT);
+ break;
+ case VIRTIO_SCSI_S_BAD_TARGET:
+ set_host_byte(sc, DID_BAD_TARGET);
+ break;
+ case VIRTIO_SCSI_S_RESET:
+ set_host_byte(sc, DID_RESET);
+ break;
+ case VIRTIO_SCSI_S_BUSY:
+ set_host_byte(sc, DID_BUS_BUSY);
+ break;
+ case VIRTIO_SCSI_S_TRANSPORT_FAILURE:
+ set_host_byte(sc, DID_TRANSPORT_DISRUPTED);
+ break;
+ case VIRTIO_SCSI_S_TARGET_FAILURE:
+ set_host_byte(sc, DID_TARGET_FAILURE);
+ break;
+ case VIRTIO_SCSI_S_NEXUS_FAILURE:
+ set_host_byte(sc, DID_NEXUS_FAILURE);
+ break;
+ default:
+ scmd_printk(KERN_WARNING, sc, "Unknown response %d",
+ resp->response);
+ /* fall through */
+ case VIRTIO_SCSI_S_FAILURE:
+ set_host_byte(sc, DID_ERROR);
+ break;
+ }
+
+ WARN_ON(resp->sense_len > VIRTIO_SCSI_SENSE_SIZE);
+ if (sc->sense_buffer) {
+ memcpy(sc->sense_buffer, resp->sense,
+ min_t(u32, resp->sense_len, VIRTIO_SCSI_SENSE_SIZE));
+ if (resp->sense_len)
+ set_driver_byte(sc, DRIVER_SENSE);
+ }
+
+ mempool_free(cmd, virtscsi_cmd_pool);
+ sc->scsi_done(sc);
+}
+
+static void virtscsi_vq_done(struct virtqueue *vq, void (*fn)(void *buf))
+{
+ struct Scsi_Host *sh = virtio_scsi_host(vq->vdev);
+ struct virtio_scsi *vscsi = shost_priv(sh);
+ void *buf;
+ unsigned long flags;
+ unsigned int len;
+
+ spin_lock_irqsave(&vscsi->vq_lock, flags);
+
+ do {
+ virtqueue_disable_cb(vq);
+ while ((buf = virtqueue_get_buf(vq, &len)) != NULL)
+ fn(buf);
+ } while (!virtqueue_enable_cb(vq));
+
+ spin_unlock_irqrestore(&vscsi->vq_lock, flags);
+}
+
+static void virtscsi_req_done(struct virtqueue *vq)
+{
+ virtscsi_vq_done(vq, virtscsi_complete_cmd);
+};
+
+static void virtscsi_complete_free(void *buf)
+{
+ struct virtio_scsi_cmd *cmd = buf;
+
+ if (cmd->comp)
+ complete_all(cmd->comp);
+ mempool_free(cmd, virtscsi_cmd_pool);
+}
+
+static void virtscsi_ctrl_done(struct virtqueue *vq)
+{
+ virtscsi_vq_done(vq, virtscsi_complete_free);
+};
+
+static void virtscsi_event_done(struct virtqueue *vq)
+{
+ virtscsi_vq_done(vq, virtscsi_complete_free);
+};
+
+static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
+ struct scsi_data_buffer *sdb)
+{
+ struct sg_table *table = &sdb->table;
+ struct scatterlist *sg_elem;
+ unsigned int idx = *p_idx;
+ int i;
+
+ for_each_sg(table->sgl, sg_elem, table->nents, i)
+ sg_set_buf(&sg[idx++], sg_virt(sg_elem), sg_elem->length);
+
+ *p_idx = idx;
+}
+
+/**
+ * virtscsi_map_cmd - map a scsi_cmd to a virtqueue scatterlist
+ * @vscsi : virtio_scsi state
+ * @cmd : command structure
+ * @out_num : number of read-only elements
+ * @in_num : number of write-only elements
+ * @req_size : size of the request buffer
+ * @resp_size : size of the response buffer
+ *
+ * Called with vq_lock held.
+ */
+static void virtscsi_map_cmd(struct virtio_scsi *vscsi,
+ struct virtio_scsi_cmd *cmd,
+ unsigned *out_num, unsigned *in_num,
+ size_t req_size, size_t resp_size)
+{
+ struct scsi_cmnd *sc = cmd->sc;
+ struct scatterlist *sg = vscsi->sg;
+ unsigned int idx = 0;
+
+ if (sc) {
+ struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev);
+ BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize);
+
+ /* TODO: check feature bit and fail if unsupported? */
+ BUG_ON(sc->sc_data_direction == DMA_BIDIRECTIONAL);
+ }
+
+ /* Request header. */
+ sg_set_buf(&sg[idx++], &cmd->req, req_size);
+
+ /* Data-out buffer. */
+ if (sc && sc->sc_data_direction != DMA_FROM_DEVICE)
+ virtscsi_map_sgl(sg, &idx, scsi_out(sc));
+
+ *out_num = idx;
+
+ /* Response header. */
+ sg_set_buf(&sg[idx++], &cmd->resp, resp_size);
+
+ /* Data-in buffer */
+ if (sc && sc->sc_data_direction != DMA_TO_DEVICE)
+ virtscsi_map_sgl(sg, &idx, scsi_in(sc));
+
+ *in_num = idx - *out_num;
+}
+
+static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtqueue *vq,
+ struct virtio_scsi_cmd *cmd,
+ size_t req_size, size_t resp_size, gfp_t gfp)
+{
+ unsigned int out_num, in_num;
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&vscsi->vq_lock, flags);
+
+ virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size);
+
+ ret = virtqueue_add_buf(vq, vscsi->sg, out_num, in_num, cmd, gfp);
+ if (ret >= 0)
+ virtqueue_kick(vq);
+
+ spin_unlock_irqrestore(&vscsi->vq_lock, flags);
+ return ret;
+}
+
+static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
+{
+ struct virtio_scsi *vscsi = shost_priv(sh);
+ struct virtio_scsi_cmd *cmd;
+ int ret;
+
+ dev_dbg(&sc->device->sdev_gendev,
+ "cmd %p CDB: %#02x\n", sc, sc->cmnd[0]);
+
+ ret = SCSI_MLQUEUE_HOST_BUSY;
+ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_ATOMIC);
+ if (!cmd)
+ goto out;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->sc = sc;
+ cmd->req.cmd = (struct virtio_scsi_cmd_req){
+ .lun[0] = 1,
+ .lun[1] = sc->device->id,
+ .lun[2] = (sc->device->lun >> 8) | 0x40,
+ .lun[3] = sc->device->lun & 0xff,
+ .tag = (unsigned long)sc,
+ .task_attr = VIRTIO_SCSI_S_SIMPLE,
+ .prio = 0,
+ .crn = 0,
+ };
+
+ BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
+ memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
+
+ if (virtscsi_kick_cmd(vscsi, vscsi->req_vq, cmd,
+ sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
+ GFP_ATOMIC) >= 0)
+ ret = 0;
+
+out:
+ return ret;
+}
+
+static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
+{
+ DECLARE_COMPLETION_ONSTACK(comp);
+ int ret;
+
+ cmd->comp = &comp;
+ ret = virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd,
+ sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
+ GFP_NOIO);
+ if (ret < 0)
+ return FAILED;
+
+ wait_for_completion(&comp);
+ if (cmd->resp.tmf.response != VIRTIO_SCSI_S_OK &&
+ cmd->resp.tmf.response != VIRTIO_SCSI_S_FUNCTION_SUCCEEDED)
+ return FAILED;
+
+ return SUCCESS;
+}
+
+static int virtscsi_device_reset(struct scsi_cmnd *sc)
+{
+ struct virtio_scsi *vscsi = shost_priv(sc->device->host);
+ struct virtio_scsi_cmd *cmd;
+
+ sdev_printk(KERN_INFO, sc->device, "device reset\n");
+ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_NOIO);
+ if (!cmd)
+ return FAILED;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->sc = sc;
+ cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){
+ .type = VIRTIO_SCSI_T_TMF,
+ .subtype = VIRTIO_SCSI_T_TMF_LOGICAL_UNIT_RESET,
+ .lun[0] = 1,
+ .lun[1] = sc->device->id,
+ .lun[2] = (sc->device->lun >> 8) | 0x40,
+ .lun[3] = sc->device->lun & 0xff,
+ };
+ return virtscsi_tmf(vscsi, cmd);
+}
+
+static int virtscsi_abort(struct scsi_cmnd *sc)
+{
+ struct virtio_scsi *vscsi = shost_priv(sc->device->host);
+ struct virtio_scsi_cmd *cmd;
+
+ scmd_printk(KERN_INFO, sc, "abort\n");
+ cmd = mempool_alloc(virtscsi_cmd_pool, GFP_NOIO);
+ if (!cmd)
+ return FAILED;
+
+ memset(cmd, 0, sizeof(*cmd));
+ cmd->sc = sc;
+ cmd->req.tmf = (struct virtio_scsi_ctrl_tmf_req){
+ .type = VIRTIO_SCSI_T_TMF,
+ .subtype = VIRTIO_SCSI_T_TMF_ABORT_TASK,
+ .lun[0] = 1,
+ .lun[1] = sc->device->id,
+ .lun[2] = (sc->device->lun >> 8) | 0x40,
+ .lun[3] = sc->device->lun & 0xff,
+ .tag = (unsigned long)sc,
+ };
+ return virtscsi_tmf(vscsi, cmd);
+}
+
+static struct scsi_host_template virtscsi_host_template = {
+ .module = THIS_MODULE,
+ .name = "Virtio SCSI HBA",
+ .proc_name = "virtio_scsi",
+ .queuecommand = virtscsi_queuecommand,
+ .this_id = -1,
+ .eh_abort_handler = virtscsi_abort,
+ .eh_device_reset_handler = virtscsi_device_reset,
+
+ .can_queue = 1024,
+ .dma_boundary = UINT_MAX,
+ .use_clustering = ENABLE_CLUSTERING,
+};
+
+#define virtscsi_config_get(vdev, fld) \
+ ({ \
+ typeof(((struct virtio_scsi_config *)0)->fld) __val; \
+ vdev->config->get(vdev, \
+ offsetof(struct virtio_scsi_config, fld), \
+ &__val, sizeof(__val)); \
+ __val; \
+ })
+
+#define virtscsi_config_set(vdev, fld, val) \
+ (void)({ \
+ typeof(((struct virtio_scsi_config *)0)->fld) __val = (val); \
+ vdev->config->set(vdev, \
+ offsetof(struct virtio_scsi_config, fld), \
+ &__val, sizeof(__val)); \
+ })
+
+static int virtscsi_init(struct virtio_device *vdev,
+ struct virtio_scsi *vscsi)
+{
+ int err;
+ struct virtqueue *vqs[3];
+ vq_callback_t *callbacks[] = {
+ virtscsi_ctrl_done,
+ virtscsi_event_done,
+ virtscsi_req_done
+ };
+ const char *names[] = {
+ "control",
+ "event",
+ "request"
+ };
+
+ /* Discover virtqueues and write information to configuration. */
+ err = vdev->config->find_vqs(vdev, 3, vqs, callbacks, names);
+ if (err)
+ return err;
+
+ vscsi->ctrl_vq = vqs[0];
+ vscsi->event_vq = vqs[1];
+ vscsi->req_vq = vqs[2];
+
+ virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
+ virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE);
+ return 0;
+}
+
+static int __devinit virtscsi_probe(struct virtio_device *vdev)
+{
+ struct Scsi_Host *shost;
+ struct virtio_scsi *vscsi;
+ int err;
+ u32 sg_elems;
+ u32 cmd_per_lun;
+
+ /* We need to know how many segments before we allocate.
+ * We need an extra sg elements at head and tail.
+ */
+ sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1;
+
+ /* Allocate memory and link the structs together. */
+ shost = scsi_host_alloc(&virtscsi_host_template,
+ sizeof(*vscsi) + sizeof(vscsi->sg[0]) * (sg_elems + 2));
+
+ if (!shost)
+ return -ENOMEM;
+
+ shost->sg_tablesize = sg_elems;
+ vscsi = shost_priv(shost);
+ vscsi->vdev = vdev;
+ vdev->priv = shost;
+
+ /* Random initializations. */
+ spin_lock_init(&vscsi->vq_lock);
+ sg_init_table(vscsi->sg, sg_elems + 2);
+
+ err = virtscsi_init(vdev, vscsi);
+ if (err)
+ goto virtscsi_init_failed;
+
+ cmd_per_lun = virtscsi_config_get(vdev, cmd_per_lun) ?: 1;
+ shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue);
+ shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF;
+ shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1;
+ shost->max_id = virtscsi_config_get(vdev, max_target) + 1;
+ shost->max_channel = 0;
+ shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE;
+ err = scsi_add_host(shost, &vdev->dev);
+ if (err)
+ goto scsi_add_host_failed;
+
+ scsi_scan_host(shost);
+
+ return 0;
+
+scsi_add_host_failed:
+ vdev->config->del_vqs(vdev);
+virtscsi_init_failed:
+ scsi_host_put(shost);
+ return err;
+}
+
+static void virtscsi_remove_vqs(struct virtio_device *vdev)
+{
+ /* Stop all the virtqueues. */
+ vdev->config->reset(vdev);
+
+ vdev->config->del_vqs(vdev);
+}
+
+static void __devexit virtscsi_remove(struct virtio_device *vdev)
+{
+ struct Scsi_Host *shost = virtio_scsi_host(vdev);
+
+ scsi_remove_host(shost);
+
+ virtscsi_remove_vqs(vdev);
+ scsi_host_put(shost);
+}
+
+#ifdef CONFIG_PM
+static int virtscsi_freeze(struct virtio_device *vdev)
+{
+ virtscsi_remove_vqs(vdev);
+ return 0;
+}
+
+static int virtscsi_restore(struct virtio_device *vdev)
+{
+ struct Scsi_Host *sh = virtio_scsi_host(vdev);
+ struct virtio_scsi *vscsi = shost_priv(sh);
+
+ return virtscsi_init(vdev, vscsi);
+}
+#endif
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_SCSI, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static struct virtio_driver virtio_scsi_driver = {
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = virtscsi_probe,
+#ifdef CONFIG_PM
+ .freeze = virtscsi_freeze,
+ .restore = virtscsi_restore,
+#endif
+ .remove = __devexit_p(virtscsi_remove),
+};
+
+static int __init init(void)
+{
+ int ret = -ENOMEM;
+
+ virtscsi_cmd_cache = KMEM_CACHE(virtio_scsi_cmd, 0);
+ if (!virtscsi_cmd_cache) {
+ printk(KERN_ERR "kmem_cache_create() for "
+ "virtscsi_cmd_cache failed\n");
+ goto error;
+ }
+
+
+ virtscsi_cmd_pool =
+ mempool_create_slab_pool(VIRTIO_SCSI_MEMPOOL_SZ,
+ virtscsi_cmd_cache);
+ if (!virtscsi_cmd_pool) {
+ printk(KERN_ERR "mempool_create() for"
+ "virtscsi_cmd_pool failed\n");
+ goto error;
+ }
+ ret = register_virtio_driver(&virtio_scsi_driver);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+error:
+ if (virtscsi_cmd_pool) {
+ mempool_destroy(virtscsi_cmd_pool);
+ virtscsi_cmd_pool = NULL;
+ }
+ if (virtscsi_cmd_cache) {
+ kmem_cache_destroy(virtscsi_cmd_cache);
+ virtscsi_cmd_cache = NULL;
+ }
+ return ret;
+}
+
+static void __exit fini(void)
+{
+ unregister_virtio_driver(&virtio_scsi_driver);
+ mempool_destroy(virtscsi_cmd_pool);
+ kmem_cache_destroy(virtscsi_cmd_cache);
+}
+module_init(init);
+module_exit(fini);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio SCSI HBA driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
index 9ee0afef2d16..d89a5dfd3ade 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/clk/cpg.c b/drivers/sh/clk/cpg.c
index 92d314a73f69..91b6d52f74eb 100644
--- a/drivers/sh/clk/cpg.c
+++ b/drivers/sh/clk/cpg.c
@@ -26,7 +26,7 @@ static void sh_clk_mstp32_disable(struct clk *clk)
clk->mapped_reg);
}
-static struct clk_ops sh_clk_mstp32_clk_ops = {
+static struct sh_clk_ops sh_clk_mstp32_clk_ops = {
.enable = sh_clk_mstp32_enable,
.disable = sh_clk_mstp32_disable,
.recalc = followparent_recalc,
@@ -150,7 +150,7 @@ static void sh_clk_div6_disable(struct clk *clk)
iowrite32(value, clk->mapped_reg);
}
-static struct clk_ops sh_clk_div6_clk_ops = {
+static struct sh_clk_ops sh_clk_div6_clk_ops = {
.recalc = sh_clk_div6_recalc,
.round_rate = sh_clk_div_round_rate,
.set_rate = sh_clk_div6_set_rate,
@@ -158,7 +158,7 @@ static struct clk_ops sh_clk_div6_clk_ops = {
.disable = sh_clk_div6_disable,
};
-static struct clk_ops sh_clk_div6_reparent_clk_ops = {
+static struct sh_clk_ops sh_clk_div6_reparent_clk_ops = {
.recalc = sh_clk_div6_recalc,
.round_rate = sh_clk_div_round_rate,
.set_rate = sh_clk_div6_set_rate,
@@ -200,7 +200,7 @@ static int __init sh_clk_init_parent(struct clk *clk)
}
static int __init sh_clk_div6_register_ops(struct clk *clks, int nr,
- struct clk_ops *ops)
+ struct sh_clk_ops *ops)
{
struct clk *clkp;
void *freq_table;
@@ -317,13 +317,13 @@ static void sh_clk_div4_disable(struct clk *clk)
iowrite32(ioread32(clk->mapped_reg) | (1 << 8), clk->mapped_reg);
}
-static struct clk_ops sh_clk_div4_clk_ops = {
+static struct sh_clk_ops sh_clk_div4_clk_ops = {
.recalc = sh_clk_div4_recalc,
.set_rate = sh_clk_div4_set_rate,
.round_rate = sh_clk_div_round_rate,
};
-static struct clk_ops sh_clk_div4_enable_clk_ops = {
+static struct sh_clk_ops sh_clk_div4_enable_clk_ops = {
.recalc = sh_clk_div4_recalc,
.set_rate = sh_clk_div4_set_rate,
.round_rate = sh_clk_div_round_rate,
@@ -331,7 +331,7 @@ static struct clk_ops sh_clk_div4_enable_clk_ops = {
.disable = sh_clk_div4_disable,
};
-static struct clk_ops sh_clk_div4_reparent_clk_ops = {
+static struct sh_clk_ops sh_clk_div4_reparent_clk_ops = {
.recalc = sh_clk_div4_recalc,
.set_rate = sh_clk_div4_set_rate,
.round_rate = sh_clk_div_round_rate,
@@ -341,7 +341,7 @@ static struct clk_ops sh_clk_div4_reparent_clk_ops = {
};
static int __init sh_clk_div4_register_ops(struct clk *clks, int nr,
- struct clk_div4_table *table, struct clk_ops *ops)
+ struct clk_div4_table *table, struct sh_clk_ops *ops)
{
struct clk *clkp;
void *freq_table;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 0b06e360628a..3ed748355b98 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -293,7 +293,7 @@ config SPI_RSPI
config SPI_S3C24XX
tristate "Samsung S3C24XX series SPI"
- depends on ARCH_S3C2410 && EXPERIMENTAL
+ depends on ARCH_S3C24XX && EXPERIMENTAL
select SPI_BITBANG
help
SPI driver for Samsung S3C24XX series ARM SoCs
diff --git a/drivers/spi/spi-omap-uwire.c b/drivers/spi/spi-omap-uwire.c
index 610f7391456e..9b0d71696039 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 13448c832c44..e496f799b7a9 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-s3c24xx.c b/drivers/spi/spi-s3c24xx.c
index fc064535f4fc..8ee7d790ce49 100644
--- a/drivers/spi/spi-s3c24xx.c
+++ b/drivers/spi/spi-s3c24xx.c
@@ -24,10 +24,10 @@
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
+#include <linux/spi/s3c24xx.h>
#include <linux/module.h>
#include <plat/regs-spi.h>
-#include <mach/spi.h>
#include <plat/fiq.h>
#include <asm/fiq.h>
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index f1abfb179b47..97d412d91458 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -64,8 +64,6 @@ source "drivers/staging/phison/Kconfig"
source "drivers/staging/line6/Kconfig"
-source "drivers/gpu/drm/nouveau/Kconfig"
-
source "drivers/staging/octeon/Kconfig"
source "drivers/staging/serqt_usb2/Kconfig"
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index db1fd63aaab3..bf185e2807d1 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 c9e8c4785768..915157d47805 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 fd274e9c7b78..13e9c8071696 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 8cd51a7aad8e..647e116e10de 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 3f4d79515026..b3a550bd5b06 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 a81f9298b0a1..a9e36336d097 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 84c87938a831..8cdaa7a34814 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 3f919babe79b..886f5650444e 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 7569aa0f24d1..c4a8a0a26eb5 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/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c
index c0ca7093e0ed..02cc23420b90 100644
--- a/drivers/staging/iio/gyro/adis16060_core.c
+++ b/drivers/staging/iio/gyro/adis16060_core.c
@@ -14,7 +14,6 @@
#include <linux/spi/spi.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
-#include <linux/module.h>
#include "../iio.h"
#include "../sysfs.h"
diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig
index 7e5caa39ed3f..4f4b7d6281a7 100644
--- a/drivers/staging/media/Kconfig
+++ b/drivers/staging/media/Kconfig
@@ -6,7 +6,7 @@ menuconfig STAGING_MEDIA
don't have the "normal" Linux kernel quality level.
Most of them don't follow properly the V4L, DVB and/or RC API's,
so, they won't likely work fine with the existing applications.
- That also means that, one fixed, their API's will change to match
+ That also means that, once fixed, their API's will change to match
the existing ones.
If you wish to work on these drivers, to help improve them, or
diff --git a/drivers/staging/media/as102/as102_drv.c b/drivers/staging/media/as102/as102_drv.c
index aae0505a36c4..ea4f992de235 100644
--- a/drivers/staging/media/as102/as102_drv.c
+++ b/drivers/staging/media/as102/as102_drv.c
@@ -27,7 +27,7 @@
#include <linux/uaccess.h>
#include <linux/usb.h>
-/* header file for Usb device driver*/
+/* header file for usb device driver*/
#include "as102_drv.h"
#include "as102_fw.h"
#include "dvbdev.h"
diff --git a/drivers/staging/media/as102/as102_drv.h b/drivers/staging/media/as102/as102_drv.h
index 957f0ed0d81a..b0e5a23bd532 100644
--- a/drivers/staging/media/as102/as102_drv.h
+++ b/drivers/staging/media/as102/as102_drv.h
@@ -76,7 +76,7 @@ struct as102_dev_t {
struct as10x_bus_adapter_t bus_adap;
struct list_head device_entry;
struct kref kref;
- unsigned long minor;
+ uint8_t elna_cfg;
struct dvb_adapter dvb_adap;
struct dvb_frontend dvb_fe;
diff --git a/drivers/staging/media/as102/as102_fe.c b/drivers/staging/media/as102/as102_fe.c
index bdc5a38cddf7..5917657b9d0f 100644
--- a/drivers/staging/media/as102/as102_fe.c
+++ b/drivers/staging/media/as102/as102_fe.c
@@ -265,7 +265,7 @@ static int as102_fe_ts_bus_ctrl(struct dvb_frontend *fe, int acquire)
if (acquire) {
if (elna_enable)
- as10x_cmd_set_context(&dev->bus_adap, 1010, 0xC0);
+ as10x_cmd_set_context(&dev->bus_adap, CONTEXT_LNA, dev->elna_cfg);
ret = as10x_cmd_turn_on(&dev->bus_adap);
} else {
@@ -337,7 +337,7 @@ int as102_dvb_register_fe(struct as102_dev_t *as102_dev,
strncpy(dvb_fe->ops.info.name, as102_dev->name,
sizeof(dvb_fe->ops.info.name));
- /* register dbvb frontend */
+ /* register dvb frontend */
errno = dvb_register_frontend(dvb_adap, dvb_fe);
if (errno == 0)
dvb_fe->tuner_priv = as102_dev;
@@ -349,7 +349,7 @@ static void as10x_fe_copy_tps_parameters(struct dtv_frontend_properties *fe_tps,
struct as10x_tps *as10x_tps)
{
- /* extract consteallation */
+ /* extract constellation */
switch (as10x_tps->modulation) {
case CONST_QPSK:
fe_tps->modulation = QPSK;
diff --git a/drivers/staging/media/as102/as102_fw.h b/drivers/staging/media/as102/as102_fw.h
index bd21f0554392..4bfc6849d95a 100644
--- a/drivers/staging/media/as102/as102_fw.h
+++ b/drivers/staging/media/as102/as102_fw.h
@@ -29,7 +29,7 @@ struct as10x_fw_pkt_t {
union {
unsigned char request[2];
unsigned char length[2];
- } u;
+ } __packed u;
struct as10x_raw_fw_pkt raw;
} __packed;
diff --git a/drivers/staging/media/as102/as102_usb_drv.c b/drivers/staging/media/as102/as102_usb_drv.c
index d775be0173ea..0f6bfe7eccba 100644
--- a/drivers/staging/media/as102/as102_usb_drv.c
+++ b/drivers/staging/media/as102/as102_usb_drv.c
@@ -57,6 +57,17 @@ static const char * const as102_device_names[] = {
NULL /* Terminating entry */
};
+/* eLNA configuration: devices built on the reference design work best
+ with 0xA0, while custom designs seem to require 0xC0 */
+static uint8_t const as102_elna_cfg[] = {
+ 0xA0,
+ 0xC0,
+ 0xC0,
+ 0xA0,
+ 0xA0,
+ 0x00 /* Terminating entry */
+};
+
struct usb_driver as102_usb_driver = {
.name = DRIVER_FULL_NAME,
.probe = as102_usb_probe,
@@ -270,6 +281,8 @@ static int as102_alloc_usb_stream_buffer(struct as102_dev_t *dev)
}
urb->transfer_buffer = dev->stream + (i * AS102_USB_BUF_SIZE);
+ urb->transfer_dma = dev->dma_addr + (i * AS102_USB_BUF_SIZE);
+ urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP;
urb->transfer_buffer_length = AS102_USB_BUF_SIZE;
dev->stream_urb[i] = urb;
@@ -369,8 +382,10 @@ static int as102_usb_probe(struct usb_interface *intf,
/* Assign the user-friendly device name */
for (i = 0; i < (sizeof(as102_usb_id_table) /
sizeof(struct usb_device_id)); i++) {
- if (id == &as102_usb_id_table[i])
+ if (id == &as102_usb_id_table[i]) {
as102_dev->name = as102_device_names[i];
+ as102_dev->elna_cfg = as102_elna_cfg[i];
+ }
}
if (as102_dev->name == NULL)
diff --git a/drivers/staging/media/as102/as10x_cmd.h b/drivers/staging/media/as102/as10x_cmd.h
index 4ea249e7adab..e21ec6c702a9 100644
--- a/drivers/staging/media/as102/as10x_cmd.h
+++ b/drivers/staging/media/as102/as10x_cmd.h
@@ -99,14 +99,14 @@ union as10x_turn_on {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_turn_off {
@@ -114,14 +114,14 @@ union as10x_turn_off {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* error */
uint8_t err;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_set_tune {
@@ -131,14 +131,14 @@ union as10x_set_tune {
uint16_t proc_id;
/* tune params */
struct as10x_tune_args args;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* response error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_get_tune_status {
@@ -146,7 +146,7 @@ union as10x_get_tune_status {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -155,7 +155,7 @@ union as10x_get_tune_status {
uint8_t error;
/* tune status */
struct as10x_tune_status sts;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_get_tps {
@@ -163,7 +163,7 @@ union as10x_get_tps {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -172,7 +172,7 @@ union as10x_get_tps {
uint8_t error;
/* tps details */
struct as10x_tps tps;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_common {
@@ -180,14 +180,14 @@ union as10x_common {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* response error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_add_pid_filter {
@@ -201,7 +201,7 @@ union as10x_add_pid_filter {
uint8_t stream_type;
/* PID index in filter table */
uint8_t idx;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -210,7 +210,7 @@ union as10x_add_pid_filter {
uint8_t error;
/* Filter id */
uint8_t filter_id;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_del_pid_filter {
@@ -220,14 +220,14 @@ union as10x_del_pid_filter {
uint16_t proc_id;
/* PID to remove */
uint16_t pid;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* response error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_start_streaming {
@@ -235,14 +235,14 @@ union as10x_start_streaming {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_stop_streaming {
@@ -250,14 +250,14 @@ union as10x_stop_streaming {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_get_demod_stats {
@@ -265,7 +265,7 @@ union as10x_get_demod_stats {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -274,7 +274,7 @@ union as10x_get_demod_stats {
uint8_t error;
/* demod stats */
struct as10x_demod_stats stats;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_get_impulse_resp {
@@ -282,7 +282,7 @@ union as10x_get_impulse_resp {
struct {
/* request identifier */
uint16_t proc_id;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -291,7 +291,7 @@ union as10x_get_impulse_resp {
uint8_t error;
/* impulse response ready */
uint8_t is_ready;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_fw_context {
@@ -305,7 +305,7 @@ union as10x_fw_context {
uint16_t tag;
/* context request type */
uint16_t type;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -316,7 +316,7 @@ union as10x_fw_context {
uint16_t type;
/* error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_set_register {
@@ -328,14 +328,14 @@ union as10x_set_register {
struct as10x_register_addr reg_addr;
/* register content */
struct as10x_register_value reg_val;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_get_register {
@@ -345,7 +345,7 @@ union as10x_get_register {
uint16_t proc_id;
/* register description */
struct as10x_register_addr reg_addr;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -354,7 +354,7 @@ union as10x_get_register {
uint8_t error;
/* register content */
struct as10x_register_value reg_val;
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_cfg_change_mode {
@@ -364,14 +364,14 @@ union as10x_cfg_change_mode {
uint16_t proc_id;
/* mode */
uint8_t mode;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
uint16_t proc_id;
/* error */
uint8_t error;
- } rsp;
+ } __packed rsp;
} __packed;
struct as10x_cmd_header_t {
@@ -394,7 +394,7 @@ union as10x_dump_memory {
struct as10x_register_addr reg_addr;
/* nb blocks to read */
uint16_t num_blocks;
- } req;
+ } __packed req;
/* response */
struct {
/* response identifier */
@@ -408,8 +408,8 @@ union as10x_dump_memory {
uint8_t data8[DUMP_BLOCK_SIZE];
uint16_t data16[DUMP_BLOCK_SIZE / sizeof(uint16_t)];
uint32_t data32[DUMP_BLOCK_SIZE / sizeof(uint32_t)];
- } u;
- } rsp;
+ } __packed u;
+ } __packed rsp;
} __packed;
union as10x_dumplog_memory {
@@ -418,7 +418,7 @@ union as10x_dumplog_memory {
uint16_t proc_id;
/* dump memory type request */
uint8_t dump_req;
- } req;
+ } __packed req;
struct {
/* request identifier */
uint16_t proc_id;
@@ -428,7 +428,7 @@ union as10x_dumplog_memory {
uint8_t dump_rsp;
/* dump data */
uint8_t data[DUMP_BLOCK_SIZE];
- } rsp;
+ } __packed rsp;
} __packed;
union as10x_raw_data {
@@ -437,14 +437,14 @@ union as10x_raw_data {
uint16_t proc_id;
uint8_t data[64 - sizeof(struct as10x_cmd_header_t)
- 2 /* proc_id */];
- } req;
+ } __packed req;
/* response */
struct {
uint16_t proc_id;
uint8_t error;
uint8_t data[64 - sizeof(struct as10x_cmd_header_t)
- 2 /* proc_id */ - 1 /* rc */];
- } rsp;
+ } __packed rsp;
} __packed;
struct as10x_cmd_t {
@@ -469,7 +469,7 @@ struct as10x_cmd_t {
union as10x_dump_memory dump_memory;
union as10x_dumplog_memory dumplog_memory;
union as10x_raw_data raw_data;
- } body;
+ } __packed body;
} __packed;
struct as10x_token_cmd_t {
diff --git a/drivers/staging/media/as102/as10x_types.h b/drivers/staging/media/as102/as10x_types.h
index fde8140ae88b..af26e057d9a2 100644
--- a/drivers/staging/media/as102/as10x_types.h
+++ b/drivers/staging/media/as102/as10x_types.h
@@ -181,7 +181,7 @@ struct as10x_register_value {
uint8_t value8; /* 8 bit value */
uint16_t value16; /* 16 bit value */
uint32_t value32; /* 32 bit value */
- } u;
+ } __packed u;
} __packed;
struct as10x_register_addr {
diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c
index 3d439b790cc6..d0fe34afc2e5 100644
--- a/drivers/staging/media/easycap/easycap_main.c
+++ b/drivers/staging/media/easycap/easycap_main.c
@@ -2849,13 +2849,11 @@ static const struct v4l2_file_operations v4l2_fops = {
.poll = easycap_poll,
.mmap = easycap_mmap,
};
-/*****************************************************************************/
-/*---------------------------------------------------------------------------*/
+
/*
- * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE
- * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE.
+ * When the device is plugged, this function is called three times,
+ * one for each interface.
*/
-/*---------------------------------------------------------------------------*/
static int easycap_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
@@ -2884,7 +2882,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
usbdev = interface_to_usbdev(intf);
-/*---------------------------------------------------------------------------*/
alt = usb_altnum_to_altsetting(intf, 0);
if (!alt) {
SAY("ERROR: usb_host_interface not found\n");
@@ -2896,11 +2893,8 @@ static int easycap_usb_probe(struct usb_interface *intf,
SAY("ERROR: intf_descriptor is NULL\n");
return -EFAULT;
}
-/*---------------------------------------------------------------------------*/
-/*
- * GET PROPERTIES OF PROBED INTERFACE
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Get properties of probed interface */
bInterfaceNumber = interface->bInterfaceNumber;
bInterfaceClass = interface->bInterfaceClass;
bInterfaceSubClass = interface->bInterfaceSubClass;
@@ -2912,28 +2906,23 @@ static int easycap_usb_probe(struct usb_interface *intf,
(long int)(intf->cur_altsetting - intf->altsetting));
JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n",
bInterfaceNumber, bInterfaceClass, bInterfaceSubClass);
-/*---------------------------------------------------------------------------*/
-/*
- * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED.
- * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS
- * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS
- * PHYSICALLY UNPLUGGED.
- *
- * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN
- * INTERFACES 1 AND 2 ARE PROBED.
-*/
-/*---------------------------------------------------------------------------*/
+
+ /*
+ * A new struct easycap is always allocated when interface 0 is probed.
+ * It is not possible here to free any existing struct easycap.
+ * This should have been done by easycap_delete() when the device was
+ * physically unplugged.
+ * The allocated struct easycap is saved for later usage when
+ * interfaces 1 and 2 are probed.
+ */
if (0 == bInterfaceNumber) {
peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL);
if (!peasycap) {
SAY("ERROR: Could not allocate peasycap\n");
return -ENOMEM;
}
-/*---------------------------------------------------------------------------*/
-/*
- * PERFORM URGENT INTIALIZATIONS ...
-*/
-/*---------------------------------------------------------------------------*/
+
+ /* Perform urgent initializations */
peasycap->minor = -1;
kref_init(&peasycap->kref);
JOM(8, "intf[%i]: after kref_init(..._video) "
@@ -2976,11 +2965,7 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->allocation_video_struct = sizeof(struct easycap);
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND FURTHER INITIALIZE THE STRUCTURE
-*/
-/*---------------------------------------------------------------------------*/
+ /* and further initialize the structure */
peasycap->pusb_device = usbdev;
peasycap->pusb_interface = intf;
@@ -3002,11 +2987,7 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->frame_buffer_many = FRAME_BUFFER_MANY;
-/*---------------------------------------------------------------------------*/
-/*
- * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ...
- */
-/*---------------------------------------------------------------------------*/
+ /* Dynamically fill in the available formats */
rc = easycap_video_fillin_formats();
if (0 > rc) {
SAM("ERROR: fillin_formats() rc = %i\n", rc);
@@ -3014,10 +2995,8 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
JOM(4, "%i formats available\n", rc);
- /* ... AND POPULATE easycap.inputset[] */
-
+ /* Populate easycap.inputset[] */
inputset = peasycap->inputset;
-
fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN;
m = 0;
mask = 0;
@@ -3030,7 +3009,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
mask = easycap_standard[i].mask;
}
}
-
if (1 != m) {
SAM("ERROR: "
"inputset->standard_offset unpopulated, %i=m\n", m);
@@ -3089,14 +3067,13 @@ static int easycap_usb_probe(struct usb_interface *intf,
JOM(4, "populated inputset[]\n");
JOM(4, "finished initialization\n");
} else {
-/*---------------------------------------------------------------------------*/
-/*
- * FIXME
- *
- * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2.
- * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE.
- */
-/*---------------------------------------------------------------------------*/
+
+ /*
+ * FIXME: Identify the appropriate pointer
+ * peasycap for interfaces 1 and 2.
+ * The address of peasycap->pusb_device
+ * is reluctantly used for this purpose.
+ */
for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
if (usbdev == easycapdc60_dongle[ndong].peasycap->
pusb_device) {
@@ -3117,7 +3094,7 @@ static int easycap_usb_probe(struct usb_interface *intf,
return -ENODEV;
}
}
-/*---------------------------------------------------------------------------*/
+
if ((USB_CLASS_VIDEO == bInterfaceClass) ||
(USB_CLASS_VENDOR_SPEC == bInterfaceClass)) {
if (-1 == peasycap->video_interface) {
@@ -3149,14 +3126,12 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
}
}
-/*---------------------------------------------------------------------------*/
-/*
- * INVESTIGATE ALL ALTSETTINGS.
- * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS.
- */
-/*---------------------------------------------------------------------------*/
- isokalt = 0;
+ /*
+ * Investigate all altsettings. This is done in detail
+ * because USB device 05e1:0408 has disparate incarnations.
+ */
+ isokalt = 0;
for (i = 0; i < intf->num_altsetting; i++) {
alt = usb_altnum_to_altsetting(intf, i);
if (!alt) {
@@ -3172,7 +3147,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
if (0 == interface->bNumEndpoints)
JOM(4, "intf[%i]alt[%i] has no endpoints\n",
bInterfaceNumber, i);
-/*---------------------------------------------------------------------------*/
for (j = 0; j < interface->bNumEndpoints; j++) {
ep = &alt->endpoint[j].desc;
if (!ep) {
@@ -3312,19 +3286,12 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
}
}
-/*---------------------------------------------------------------------------*/
-/*
- * PERFORM INITIALIZATION OF THE PROBED INTERFACE
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Perform initialization of the probed interface */
JOM(4, "initialization begins for interface %i\n",
interface->bInterfaceNumber);
switch (bInterfaceNumber) {
-/*---------------------------------------------------------------------------*/
-/*
- * INTERFACE 0 IS THE VIDEO INTERFACE
- */
-/*---------------------------------------------------------------------------*/
+ /* 0: Video interface */
case 0: {
if (!peasycap) {
SAM("MISTAKE: peasycap is NULL\n");
@@ -3337,11 +3304,8 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->video_altsetting_on = okalt[isokalt - 1];
JOM(4, "%i=video_altsetting_on <====\n",
peasycap->video_altsetting_on);
-/*---------------------------------------------------------------------------*/
-/*
- * DECIDE THE VIDEO STREAMING PARAMETERS
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Decide video streaming parameters */
peasycap->video_endpointnumber = okepn[isokalt - 1];
JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber);
maxpacketsize = okmps[isokalt - 1];
@@ -3373,7 +3337,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n");
return -EFAULT;
}
-/*---------------------------------------------------------------------------*/
if (-1 == peasycap->video_interface) {
SAM("MISTAKE: video_interface is unset\n");
return -EFAULT;
@@ -3398,14 +3361,13 @@ static int easycap_usb_probe(struct usb_interface *intf,
SAM("MISTAKE: video_isoc_buffer_size is unset\n");
return -EFAULT;
}
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
- */
-/*---------------------------------------------------------------------------*/
+
+ /*
+ * Allocate memory for video buffers.
+ * Lists must be initialized first.
+ */
INIT_LIST_HEAD(&(peasycap->urb_video_head));
peasycap->purb_video_head = &(peasycap->urb_video_head);
-/*---------------------------------------------------------------------------*/
JOM(4, "allocating %i frame buffers of size %li\n",
FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE);
JOM(4, ".... each scattered over %li pages\n",
@@ -3436,7 +3398,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->frame_read = 0;
JOM(4, "allocation of frame buffers done: %i pages\n", k *
m);
-/*---------------------------------------------------------------------------*/
JOM(4, "allocating %i field buffers of size %li\n",
FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE);
JOM(4, ".... each scattered over %li pages\n",
@@ -3468,7 +3429,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->field_read = 0;
JOM(4, "allocation of field buffers done: %i pages\n", k *
m);
-/*---------------------------------------------------------------------------*/
JOM(4, "allocating %i isoc video buffers of size %i\n",
VIDEO_ISOC_BUFFER_MANY,
peasycap->video_isoc_buffer_size);
@@ -3492,11 +3452,8 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
JOM(4, "allocation of isoc video buffers done: %i pages\n",
k * (0x01 << VIDEO_ISOC_ORDER));
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Allocate and initialize multiple struct usb */
JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY);
JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n",
peasycap->video_isoc_framesperdesc);
@@ -3515,7 +3472,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
peasycap->allocation_video_urb += 1;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
if (!pdata_urb) {
SAM("ERROR: Could not allocate struct data_urb.\n");
@@ -3530,11 +3486,8 @@ static int easycap_usb_probe(struct usb_interface *intf,
pdata_urb->length = 0;
list_add_tail(&(pdata_urb->list_head),
peasycap->purb_video_head);
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND INITIALIZE THEM
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Initialize allocated urbs */
if (!k) {
JOM(4, "initializing video urbs thus:\n");
JOM(4, " purb->interval = 1;\n");
@@ -3582,20 +3535,16 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
}
JOM(4, "allocation of %i struct urb done.\n", k);
-/*--------------------------------------------------------------------------*/
-/*
- * SAVE POINTER peasycap IN THIS INTERFACE.
- */
-/*--------------------------------------------------------------------------*/
+
+ /* Save pointer peasycap in this interface */
usb_set_intfdata(intf, peasycap);
-/*---------------------------------------------------------------------------*/
-/*
- * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER,
- * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE
- * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH.
- * BEWARE.
-*/
-/*---------------------------------------------------------------------------*/
+
+ /*
+ * It is essential to initialize the hardware before,
+ * rather than after, the device is registered,
+ * because some udev rules triggers easycap_open()
+ * immediately after registration, causing a clash.
+ */
peasycap->ntsc = easycap_ntsc;
JOM(8, "defaulting initially to %s\n",
easycap_ntsc ? "NTSC" : "PAL");
@@ -3604,27 +3553,20 @@ static int easycap_usb_probe(struct usb_interface *intf,
SAM("ERROR: reset() rc = %i\n", rc);
return -EFAULT;
}
-/*--------------------------------------------------------------------------*/
-/*
- * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
- */
-/*--------------------------------------------------------------------------*/
+
+ /* The video device can now be registered */
if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) {
SAM("v4l2_device_register() failed\n");
return -ENODEV;
}
JOM(4, "registered device instance: %s\n",
peasycap->v4l2_device.name);
-/*---------------------------------------------------------------------------*/
-/*
- * FIXME
- *
- *
- * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG:
-*/
-/*---------------------------------------------------------------------------*/
+
+ /*
+ * FIXME: This is believed to be harmless,
+ * but may well be unnecessary or wrong.
+ */
peasycap->video_device.v4l2_dev = NULL;
-/*---------------------------------------------------------------------------*/
strcpy(&peasycap->video_device.name[0], "easycapdc60");
@@ -3648,28 +3590,19 @@ static int easycap_usb_probe(struct usb_interface *intf,
break;
}
-/*--------------------------------------------------------------------------*/
-/*
- * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE
- * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE
- */
-/*--------------------------------------------------------------------------*/
+ /* 1: Audio control */
case 1: {
if (!peasycap) {
SAM("MISTAKE: peasycap is NULL\n");
return -EFAULT;
}
-/*--------------------------------------------------------------------------*/
-/*
- * SAVE POINTER peasycap IN INTERFACE 1
- */
-/*--------------------------------------------------------------------------*/
+ /* Save pointer peasycap in this interface */
usb_set_intfdata(intf, peasycap);
JOM(4, "no initialization required for interface %i\n",
interface->bInterfaceNumber);
break;
}
-/*--------------------------------------------------------------------------*/
+ /* 2: Audio streaming */
case 2: {
if (!peasycap) {
SAM("MISTAKE: peasycap is NULL\n");
@@ -3769,15 +3702,14 @@ static int easycap_usb_probe(struct usb_interface *intf,
SAM("MISTAKE: audio_isoc_buffer_size is unset\n");
return -EFAULT;
}
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
- */
-/*---------------------------------------------------------------------------*/
+
+ /*
+ * Allocate memory for audio buffers.
+ * Lists must be initialized first.
+ */
INIT_LIST_HEAD(&(peasycap->urb_audio_head));
peasycap->purb_audio_head = &(peasycap->urb_audio_head);
-/*---------------------------------------------------------------------------*/
JOM(4, "allocating %i isoc audio buffers of size %i\n",
AUDIO_ISOC_BUFFER_MANY,
peasycap->audio_isoc_buffer_size);
@@ -3800,11 +3732,8 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->audio_isoc_buffer[k].kount = k;
}
JOM(4, "allocation of isoc audio buffers done.\n");
-/*---------------------------------------------------------------------------*/
-/*
- * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Allocate and initialize urbs */
JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY);
JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n",
peasycap->audio_isoc_framesperdesc);
@@ -3822,7 +3751,6 @@ static int easycap_usb_probe(struct usb_interface *intf,
return -ENOMEM;
}
peasycap->allocation_audio_urb += 1 ;
-/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
if (!pdata_urb) {
usb_free_urb(purb);
@@ -3837,11 +3765,7 @@ static int easycap_usb_probe(struct usb_interface *intf,
pdata_urb->length = 0;
list_add_tail(&(pdata_urb->list_head),
peasycap->purb_audio_head);
-/*---------------------------------------------------------------------------*/
-/*
- * ... AND INITIALIZE THEM
- */
-/*---------------------------------------------------------------------------*/
+
if (!k) {
JOM(4, "initializing audio urbs thus:\n");
JOM(4, " purb->interval = 1;\n");
@@ -3889,17 +3813,11 @@ static int easycap_usb_probe(struct usb_interface *intf,
}
}
JOM(4, "allocation of %i struct urb done.\n", k);
-/*---------------------------------------------------------------------------*/
-/*
- * SAVE POINTER peasycap IN THIS INTERFACE.
- */
-/*---------------------------------------------------------------------------*/
+
+ /* Save pointer peasycap in this interface */
usb_set_intfdata(intf, peasycap);
-/*---------------------------------------------------------------------------*/
-/*
- * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
- */
-/*---------------------------------------------------------------------------*/
+
+ /* The audio device can now be registered */
JOM(4, "initializing ALSA card\n");
rc = easycap_alsa_probe(peasycap);
@@ -3915,11 +3833,7 @@ static int easycap_usb_probe(struct usb_interface *intf,
peasycap->registered_audio++;
break;
}
-/*---------------------------------------------------------------------------*/
-/*
- * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED
- */
-/*---------------------------------------------------------------------------*/
+ /* Interfaces other than 0,1,2 are unexpected */
default:
JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber);
return -EINVAL;
diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c
index 6c9279a6d606..ece2dd146487 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 b8cfa1a6eaeb..6bc82aaeef11 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 2b27d8da70a2..3ef4cd8b4de3 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"
@@ -1050,15 +1049,15 @@ static int vidioc_s_parm(struct file *filp, void *priv,
return 0;
}
-/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and
+/* VIDIOC_ENUMSTD on go7007 were used for enumerating the supported fps and
its resolution, when the device is not connected to TV.
- This were an API abuse, probably used by the lack of specific IOCTL's to
- enumberate it, by the time the driver were written.
+ This is were an API abuse, probably used by the lack of specific IOCTL's to
+ enumerate it, by the time the driver was written.
However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS
and VIDIOC_ENUM_FRAMESIZES) were added for this purpose.
- The two functions bellow implements the newer ioctls
+ The two functions below implement the newer ioctls
*/
static int vidioc_enum_framesizes(struct file *filp, void *priv,
struct v4l2_frmsizeenum *fsize)
diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c
index e7736a915530..014d38410c99 100644
--- a/drivers/staging/media/go7007/s2250-board.c
+++ b/drivers/staging/media/go7007/s2250-board.c
@@ -192,6 +192,7 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
{
struct go7007 *go = i2c_get_adapdata(client->adapter);
struct go7007_usb *usb;
+ int rc;
u8 *buf;
struct s2250 *dec = i2c_get_clientdata(client);
@@ -216,12 +217,13 @@ static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
kfree(buf);
return -EINTR;
}
- if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) {
+ rc = go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1);
+ mutex_unlock(&usb->i2c_lock);
+ if (rc < 0) {
kfree(buf);
- return -EFAULT;
+ return rc;
}
- mutex_unlock(&usb->i2c_lock);
if (buf[0] == 0) {
unsigned int subaddr, val_read;
@@ -254,6 +256,7 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val)
{
struct go7007 *go = i2c_get_adapdata(client->adapter);
struct go7007_usb *usb;
+ int rc;
u8 *buf;
if (go == NULL)
@@ -276,11 +279,12 @@ static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val)
kfree(buf);
return -EINTR;
}
- if (go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1) < 0) {
+ rc = go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1);
+ mutex_unlock(&usb->i2c_lock);
+ if (rc < 0) {
kfree(buf);
- return -EFAULT;
+ return rc;
}
- mutex_unlock(&usb->i2c_lock);
*val = (buf[0] << 8) | buf[1];
kfree(buf);
diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c
index d071c838ac2a..5af29ff68bfd 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 8dd8897ad860..3295ea63f3eb 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>
@@ -1282,7 +1281,7 @@ MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
/*
* some architectures (e.g. intel xscale) align the 8bit serial registers
* on 32bit word boundaries.
- * See linux-kernel/serial/8250.c serial_in()/out()
+ * See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out()
*/
module_param(ioshift, int, S_IRUGO);
MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c
index c94382b917ac..945d9623550b 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/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig
index 03dcac4ea4d0..63352de5eabf 100644
--- a/drivers/staging/media/solo6x10/Kconfig
+++ b/drivers/staging/media/solo6x10/Kconfig
@@ -5,4 +5,4 @@ config SOLO6X10
select SND_PCM
---help---
This driver supports the Softlogic based MPEG-4 and h.264 codec
- codec cards.
+ cards.
diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c
index f974f6412ad7..d2fd842e37cf 100644
--- a/drivers/staging/media/solo6x10/core.c
+++ b/drivers/staging/media/solo6x10/core.c
@@ -195,28 +195,28 @@ static int __devinit solo_pci_probe(struct pci_dev *pdev,
SOLO6010_SYS_CFG_OUTDIV(3);
solo_reg_write(solo_dev, SOLO_SYS_CFG, reg);
- if (solo_dev->flags & FLAGS_6110) {
- u32 sys_clock_MHz = SOLO_CLOCK_MHZ;
- u32 pll_DIVQ;
- u32 pll_DIVF;
-
- if (sys_clock_MHz < 125) {
- pll_DIVQ = 3;
- pll_DIVF = (sys_clock_MHz * 4) / 3;
- } else {
- pll_DIVQ = 2;
- pll_DIVF = (sys_clock_MHz * 2) / 3;
- }
-
- solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG,
+ if (solo_dev->flags & FLAGS_6110) {
+ u32 sys_clock_MHz = SOLO_CLOCK_MHZ;
+ u32 pll_DIVQ;
+ u32 pll_DIVF;
+
+ if (sys_clock_MHz < 125) {
+ pll_DIVQ = 3;
+ pll_DIVF = (sys_clock_MHz * 4) / 3;
+ } else {
+ pll_DIVQ = 2;
+ pll_DIVF = (sys_clock_MHz * 2) / 3;
+ }
+
+ solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG,
SOLO6110_PLL_RANGE_5_10MHZ |
SOLO6110_PLL_DIVR(9) |
SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) |
SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN);
- mdelay(1); // PLL Locking time (1ms)
+ mdelay(1); /* PLL Locking time (1ms) */
solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */
- } else
+ } else
solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */
solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1);
diff --git a/drivers/staging/mei/wd.c b/drivers/staging/mei/wd.c
index a6910da78a64..cf4c29d10e7f 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 4683d5f355c0..6183573f112f 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/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c
index e4ade550cfe5..4fe52f6b0034 100644
--- a/drivers/staging/rtl8187se/r8180_core.c
+++ b/drivers/staging/rtl8187se/r8180_core.c
@@ -4159,7 +4159,7 @@ void GPIOChangeRFWorkItemCallBack(struct work_struct *work)
argv[0] = RadioPowerPath;
argv[2] = NULL;
- call_usermodehelper(RadioPowerPath, argv, envp, 1);
+ call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC);
}
}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
index a7fa9aad6f2d..f026b7171f62 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
@@ -208,7 +208,7 @@ static void dm_check_ac_dc_power(struct net_device *dev)
if (priv->rtllib->state != RTLLIB_LINKED)
return;
- call_usermodehelper(ac_dc_check_script_path, argv, envp, 1);
+ call_usermodehelper(ac_dc_check_script_path, argv, envp, UMH_WAIT_PROC);
return;
};
@@ -2296,7 +2296,7 @@ void dm_CheckRfCtrlGPIO(void *data)
argv[0] = RadioPowerPath;
argv[2] = NULL;
- call_usermodehelper(RadioPowerPath, argv, envp, 1);
+ call_usermodehelper(RadioPowerPath, argv, envp, UMH_WAIT_PROC);
}
}
diff --git a/drivers/staging/sbe-2t3e3/io.c b/drivers/staging/sbe-2t3e3/io.c
index b458ff034067..9a50bcc59594 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 176f46900571..e4c03351420f 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/ixj.c b/drivers/staging/telephony/ixj.c
index d5f923bcdffe..f96027921f60 100644
--- a/drivers/staging/telephony/ixj.c
+++ b/drivers/staging/telephony/ixj.c
@@ -5927,7 +5927,8 @@ static void add_caps(IXJ *j)
j->caplist[j->caps].cap = PHONE_VENDOR_QUICKNET;
strcpy(j->caplist[j->caps].desc, "Quicknet Technologies, Inc. (www.quicknet.net)");
j->caplist[j->caps].captype = vendor;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
j->caplist[j->caps].captype = device;
switch (j->cardtype) {
case QTI_PHONEJACK:
@@ -5947,11 +5948,13 @@ static void add_caps(IXJ *j)
break;
}
j->caplist[j->caps].cap = j->cardtype;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "POTS");
j->caplist[j->caps].captype = port;
j->caplist[j->caps].cap = pots;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
/* add devices that can do speaker/mic */
switch (j->cardtype) {
@@ -5962,7 +5965,8 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "SPEAKER");
j->caplist[j->caps].captype = port;
j->caplist[j->caps].cap = speaker;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
default:
break;
}
@@ -5973,7 +5977,8 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "HANDSET");
j->caplist[j->caps].captype = port;
j->caplist[j->caps].cap = handset;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
break;
default:
break;
@@ -5985,7 +5990,8 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "PSTN");
j->caplist[j->caps].captype = port;
j->caplist[j->caps].cap = pstn;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
break;
default:
break;
@@ -5995,50 +6001,59 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "ULAW");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = ULAW;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "LINEAR 16 bit");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = LINEAR16;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "LINEAR 8 bit");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = LINEAR8;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "Windows Sound System");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = WSS;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
/* software ALAW codec, made from ULAW */
strcpy(j->caplist[j->caps].desc, "ALAW");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = ALAW;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
/* version 12 of the 8020 does the following codecs in a broken way */
if (j->dsp.low != 0x20 || j->ver.low != 0x12) {
strcpy(j->caplist[j->caps].desc, "G.723.1 6.3kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = G723_63;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "G.723.1 5.3kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = G723_53;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "TrueSpeech 4.8kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = TS48;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
strcpy(j->caplist[j->caps].desc, "TrueSpeech 4.1kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = TS41;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
}
/* 8020 chips can do TS8.5 native, and 8021/8022 can load it */
@@ -6046,7 +6061,8 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "TrueSpeech 8.5kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = TS85;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
}
/* 8021 chips can do G728 */
@@ -6054,7 +6070,8 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "G.728 16kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = G728;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
}
/* 8021/8022 chips can do G729 if loaded */
@@ -6062,13 +6079,15 @@ static void add_caps(IXJ *j)
strcpy(j->caplist[j->caps].desc, "G.729A 8kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = G729;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
}
if (j->dsp.low != 0x20 && j->flags.g729_loaded) {
strcpy(j->caplist[j->caps].desc, "G.729B 8kbps");
j->caplist[j->caps].captype = codec;
j->caplist[j->caps].cap = G729B;
- j->caplist[j->caps].handle = j->caps++;
+ j->caplist[j->caps].handle = j->caps;
+ j->caps++;
}
}
diff --git a/drivers/staging/telephony/phonedev.c b/drivers/staging/telephony/phonedev.c
index 1915af201175..1dd0b6717ccc 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 a2f31c69d12e..ed00d3da3205 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/hcf.c b/drivers/staging/wlags49_h2/hcf.c
index b008773323b3..5957c3a439ac 100644
--- a/drivers/staging/wlags49_h2/hcf.c
+++ b/drivers/staging/wlags49_h2/hcf.c
@@ -91,6 +91,7 @@
#include "hcf.h" // HCF and MSF common include file
#include "hcfdef.h" // HCF specific include file
#include "mmd.h" // MoreModularDriver common include file
+#include <linux/bug.h>
#include <linux/kernel.h>
#if ! defined offsetof
diff --git a/drivers/staging/wlags49_h2/wl_cs.c b/drivers/staging/wlags49_h2/wl_cs.c
index a2cbb29c3f59..7084f414846e 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 dab603e0f452..d5bf0a7012f2 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 9c16f5478a75..90820ff1aced 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 2bd9b84ace8e..3df990c7306a 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 b748a3ff7954..f104e6f1e980 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/staging/zcache/zcache-main.c b/drivers/staging/zcache/zcache-main.c
index ed2c800b3a7e..2734dacacbaf 100644
--- a/drivers/staging/zcache/zcache-main.c
+++ b/drivers/staging/zcache/zcache-main.c
@@ -1811,9 +1811,9 @@ static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize)
static struct cleancache_ops zcache_cleancache_ops = {
.put_page = zcache_cleancache_put_page,
.get_page = zcache_cleancache_get_page,
- .flush_page = zcache_cleancache_flush_page,
- .flush_inode = zcache_cleancache_flush_inode,
- .flush_fs = zcache_cleancache_flush_fs,
+ .invalidate_page = zcache_cleancache_flush_page,
+ .invalidate_inode = zcache_cleancache_flush_inode,
+ .invalidate_fs = zcache_cleancache_flush_fs,
.init_shared_fs = zcache_cleancache_init_shared_fs,
.init_fs = zcache_cleancache_init_fs
};
@@ -1921,8 +1921,8 @@ static void zcache_frontswap_init(unsigned ignored)
static struct frontswap_ops zcache_frontswap_ops = {
.put_page = zcache_frontswap_put_page,
.get_page = zcache_frontswap_get_page,
- .flush_page = zcache_frontswap_flush_page,
- .flush_area = zcache_frontswap_flush_area,
+ .invalidate_page = zcache_frontswap_flush_page,
+ .invalidate_area = zcache_frontswap_flush_area,
.init = zcache_frontswap_init
};
diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c
index 1c6f700f5faa..8b1d5e62ed40 100644
--- a/drivers/target/iscsi/iscsi_target.c
+++ b/drivers/target/iscsi/iscsi_target.c
@@ -781,7 +781,7 @@ static int iscsit_alloc_buffs(struct iscsi_cmd *cmd)
struct scatterlist *sgl;
u32 length = cmd->se_cmd.data_length;
int nents = DIV_ROUND_UP(length, PAGE_SIZE);
- int i = 0, ret;
+ int i = 0, j = 0, ret;
/*
* If no SCSI payload is present, allocate the default iovecs used for
* iSCSI PDU Header
@@ -822,17 +822,15 @@ static int iscsit_alloc_buffs(struct iscsi_cmd *cmd)
*/
ret = iscsit_allocate_iovecs(cmd);
if (ret < 0)
- goto page_alloc_failed;
+ return -ENOMEM;
return 0;
page_alloc_failed:
- while (i >= 0) {
- __free_page(sg_page(&sgl[i]));
- i--;
- }
- kfree(cmd->t_mem_sg);
- cmd->t_mem_sg = NULL;
+ while (j < i)
+ __free_page(sg_page(&sgl[j++]));
+
+ kfree(sgl);
return -ENOMEM;
}
@@ -1007,8 +1005,8 @@ done:
/*
* The CDB is going to an se_device_t.
*/
- ret = iscsit_get_lun_for_cmd(cmd, hdr->cdb,
- get_unaligned_le64(&hdr->lun));
+ ret = transport_lookup_cmd_lun(&cmd->se_cmd,
+ scsilun_to_int(&hdr->lun));
if (ret < 0) {
if (cmd->se_cmd.scsi_sense_reason == TCM_NON_EXISTENT_LUN) {
pr_debug("Responding to non-acl'ed,"
@@ -1364,7 +1362,7 @@ static int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)
* outstanding_r2ts reaches zero, go ahead and send the delayed
* TASK_ABORTED status.
*/
- if (atomic_read(&se_cmd->t_transport_aborted) != 0) {
+ if (se_cmd->transport_state & CMD_T_ABORTED) {
if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
if (--cmd->outstanding_r2ts < 1) {
iscsit_stop_dataout_timer(cmd);
@@ -1472,14 +1470,12 @@ static int iscsit_handle_nop_out(
unsigned char *ping_data = NULL;
int cmdsn_ret, niov = 0, ret = 0, rx_got, rx_size;
u32 checksum, data_crc, padding = 0, payload_length;
- u64 lun;
struct iscsi_cmd *cmd = NULL;
struct kvec *iov = NULL;
struct iscsi_nopout *hdr;
hdr = (struct iscsi_nopout *) buf;
payload_length = ntoh24(hdr->dlength);
- lun = get_unaligned_le64(&hdr->lun);
hdr->itt = be32_to_cpu(hdr->itt);
hdr->ttt = be32_to_cpu(hdr->ttt);
hdr->cmdsn = be32_to_cpu(hdr->cmdsn);
@@ -1689,13 +1685,11 @@ static int iscsit_handle_task_mgt_cmd(
struct se_tmr_req *se_tmr;
struct iscsi_tmr_req *tmr_req;
struct iscsi_tm *hdr;
- u32 payload_length;
int out_of_order_cmdsn = 0;
int ret;
u8 function;
hdr = (struct iscsi_tm *) buf;
- payload_length = ntoh24(hdr->dlength);
hdr->itt = be32_to_cpu(hdr->itt);
hdr->rtt = be32_to_cpu(hdr->rtt);
hdr->cmdsn = be32_to_cpu(hdr->cmdsn);
@@ -1747,8 +1741,8 @@ static int iscsit_handle_task_mgt_cmd(
* Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN
*/
if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
- ret = iscsit_get_lun_for_tmr(cmd,
- get_unaligned_le64(&hdr->lun));
+ ret = transport_lookup_tmr_lun(&cmd->se_cmd,
+ scsilun_to_int(&hdr->lun));
if (ret < 0) {
cmd->se_cmd.se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;
se_tmr->response = ISCSI_TMF_RSP_NO_LUN;
@@ -2207,14 +2201,10 @@ static int iscsit_handle_snack(
struct iscsi_conn *conn,
unsigned char *buf)
{
- u32 unpacked_lun;
- u64 lun;
struct iscsi_snack *hdr;
hdr = (struct iscsi_snack *) buf;
hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
- lun = get_unaligned_le64(&hdr->lun);
- unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
hdr->itt = be32_to_cpu(hdr->itt);
hdr->ttt = be32_to_cpu(hdr->ttt);
hdr->exp_statsn = be32_to_cpu(hdr->exp_statsn);
@@ -3514,7 +3504,6 @@ int iscsi_target_tx_thread(void *arg)
struct iscsi_cmd *cmd = NULL;
struct iscsi_conn *conn;
struct iscsi_queue_req *qr = NULL;
- struct se_cmd *se_cmd;
struct iscsi_thread_set *ts = arg;
/*
* Allow ourselves to be interrupted by SIGINT so that a
@@ -3697,8 +3686,6 @@ check_rsp_state:
goto transport_err;
}
- se_cmd = &cmd->se_cmd;
-
if (map_sg && !conn->conn_ops->IFMarker) {
if (iscsit_fe_sendpage_sg(cmd, conn) < 0) {
conn->tx_response_queue = 0;
@@ -4171,7 +4158,7 @@ int iscsit_close_connection(
if (!atomic_read(&sess->session_reinstatement) &&
atomic_read(&sess->session_fall_back_to_erl0)) {
spin_unlock_bh(&sess->conn_lock);
- iscsit_close_session(sess);
+ target_put_session(sess->se_sess);
return 0;
} else if (atomic_read(&sess->session_logout)) {
@@ -4292,7 +4279,7 @@ static void iscsit_logout_post_handler_closesession(
iscsit_dec_conn_usage_count(conn);
iscsit_stop_session(sess, 1, 1);
iscsit_dec_session_usage_count(sess);
- iscsit_close_session(sess);
+ target_put_session(sess->se_sess);
}
static void iscsit_logout_post_handler_samecid(
@@ -4458,7 +4445,7 @@ int iscsit_free_session(struct iscsi_session *sess)
} else
spin_unlock_bh(&sess->conn_lock);
- iscsit_close_session(sess);
+ target_put_session(sess->se_sess);
return 0;
}
diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c
index 6b35b37988ed..00c58cc82c85 100644
--- a/drivers/target/iscsi/iscsi_target_configfs.c
+++ b/drivers/target/iscsi/iscsi_target_configfs.c
@@ -812,9 +812,6 @@ static struct se_node_acl *lio_target_make_nodeacl(
if (!se_nacl_new)
return ERR_PTR(-ENOMEM);
- acl = container_of(se_nacl_new, struct iscsi_node_acl,
- se_node_acl);
-
cmdsn_depth = ISCSI_TPG_ATTRIB(tpg)->default_cmdsn_depth;
/*
* se_nacl_new may be released by core_tpg_add_initiator_node_acl()
@@ -825,7 +822,8 @@ static struct se_node_acl *lio_target_make_nodeacl(
if (IS_ERR(se_nacl))
return se_nacl;
- stats_cg = &acl->se_node_acl.acl_fabric_stat_group;
+ acl = container_of(se_nacl, struct iscsi_node_acl, se_node_acl);
+ stats_cg = &se_nacl->acl_fabric_stat_group;
stats_cg->default_groups = kzalloc(sizeof(struct config_group) * 2,
GFP_KERNEL);
@@ -1505,28 +1503,6 @@ static int iscsi_get_cmd_state(struct se_cmd *se_cmd)
return cmd->i_state;
}
-static int iscsi_is_state_remove(struct se_cmd *se_cmd)
-{
- struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd);
-
- return (cmd->i_state == ISTATE_REMOVE);
-}
-
-static int lio_sess_logged_in(struct se_session *se_sess)
-{
- struct iscsi_session *sess = se_sess->fabric_sess_ptr;
- int ret;
- /*
- * Called with spin_lock_bh(&tpg_lock); and
- * spin_lock(&se_tpg->session_lock); held.
- */
- spin_lock(&sess->conn_lock);
- ret = (sess->session_state != TARG_SESS_STATE_LOGGED_IN);
- spin_unlock(&sess->conn_lock);
-
- return ret;
-}
-
static u32 lio_sess_get_index(struct se_session *se_sess)
{
struct iscsi_session *sess = se_sess->fabric_sess_ptr;
@@ -1700,8 +1676,8 @@ static int lio_tpg_shutdown_session(struct se_session *se_sess)
atomic_set(&sess->session_reinstatement, 1);
spin_unlock(&sess->conn_lock);
- iscsit_inc_session_usage_count(sess);
iscsit_stop_time2retain_timer(sess);
+ iscsit_stop_session(sess, 1, 1);
return 1;
}
@@ -1717,28 +1693,9 @@ static void lio_tpg_close_session(struct se_session *se_sess)
* If the iSCSI Session for the iSCSI Initiator Node exists,
* forcefully shutdown the iSCSI NEXUS.
*/
- iscsit_stop_session(sess, 1, 1);
- iscsit_dec_session_usage_count(sess);
iscsit_close_session(sess);
}
-static void lio_tpg_stop_session(
- struct se_session *se_sess,
- int sess_sleep,
- int conn_sleep)
-{
- struct iscsi_session *sess = se_sess->fabric_sess_ptr;
-
- iscsit_stop_session(sess, sess_sleep, conn_sleep);
-}
-
-static void lio_tpg_fall_back_to_erl0(struct se_session *se_sess)
-{
- struct iscsi_session *sess = se_sess->fabric_sess_ptr;
-
- iscsit_fall_back_to_erl0(sess);
-}
-
static u32 lio_tpg_get_inst_index(struct se_portal_group *se_tpg)
{
struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr;
@@ -1802,9 +1759,6 @@ int iscsi_target_register_configfs(void)
fabric->tf_ops.release_cmd = &lio_release_cmd;
fabric->tf_ops.shutdown_session = &lio_tpg_shutdown_session;
fabric->tf_ops.close_session = &lio_tpg_close_session;
- fabric->tf_ops.stop_session = &lio_tpg_stop_session;
- fabric->tf_ops.fall_back_to_erl0 = &lio_tpg_fall_back_to_erl0;
- fabric->tf_ops.sess_logged_in = &lio_sess_logged_in;
fabric->tf_ops.sess_get_index = &lio_sess_get_index;
fabric->tf_ops.sess_get_initiator_sid = &lio_sess_get_initiator_sid;
fabric->tf_ops.write_pending = &lio_write_pending;
@@ -1818,7 +1772,6 @@ int iscsi_target_register_configfs(void)
fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp;
fabric->tf_ops.set_fabric_sense_len = &lio_set_fabric_sense_len;
fabric->tf_ops.get_fabric_sense_len = &lio_get_fabric_sense_len;
- fabric->tf_ops.is_state_remove = &iscsi_is_state_remove;
/*
* Setup function pointers for generic logic in target_core_fabric_configfs.c
*/
diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h
index 0ec3b77a0c27..2aaee7efa683 100644
--- a/drivers/target/iscsi/iscsi_target_core.h
+++ b/drivers/target/iscsi/iscsi_target_core.h
@@ -9,7 +9,7 @@
#include <scsi/iscsi_proto.h>
#include <target/target_core_base.h>
-#define ISCSIT_VERSION "v4.1.0-rc1"
+#define ISCSIT_VERSION "v4.1.0-rc2"
#define ISCSI_MAX_DATASN_MISSING_COUNT 16
#define ISCSI_TX_THREAD_TCP_TIMEOUT 2
#define ISCSI_RX_THREAD_TCP_TIMEOUT 2
diff --git a/drivers/target/iscsi/iscsi_target_device.c b/drivers/target/iscsi/iscsi_target_device.c
index f63ea35bc4ae..bcc409853a67 100644
--- a/drivers/target/iscsi/iscsi_target_device.c
+++ b/drivers/target/iscsi/iscsi_target_device.c
@@ -28,25 +28,6 @@
#include "iscsi_target_tpg.h"
#include "iscsi_target_util.h"
-int iscsit_get_lun_for_tmr(
- struct iscsi_cmd *cmd,
- u64 lun)
-{
- u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
-
- return transport_lookup_tmr_lun(&cmd->se_cmd, unpacked_lun);
-}
-
-int iscsit_get_lun_for_cmd(
- struct iscsi_cmd *cmd,
- unsigned char *cdb,
- u64 lun)
-{
- u32 unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun);
-
- return transport_lookup_cmd_lun(&cmd->se_cmd, unpacked_lun);
-}
-
void iscsit_determine_maxcmdsn(struct iscsi_session *sess)
{
struct se_node_acl *se_nacl;
diff --git a/drivers/target/iscsi/iscsi_target_device.h b/drivers/target/iscsi/iscsi_target_device.h
index bef1cada15f8..a0e2df9e8090 100644
--- a/drivers/target/iscsi/iscsi_target_device.h
+++ b/drivers/target/iscsi/iscsi_target_device.h
@@ -1,8 +1,6 @@
#ifndef ISCSI_TARGET_DEVICE_H
#define ISCSI_TARGET_DEVICE_H
-extern int iscsit_get_lun_for_tmr(struct iscsi_cmd *, u64);
-extern int iscsit_get_lun_for_cmd(struct iscsi_cmd *, unsigned char *, u64);
extern void iscsit_determine_maxcmdsn(struct iscsi_session *);
extern void iscsit_increment_maxcmdsn(struct iscsi_cmd *, struct iscsi_session *);
diff --git a/drivers/target/iscsi/iscsi_target_erl0.c b/drivers/target/iscsi/iscsi_target_erl0.c
index 478451167b62..1ab0560b0924 100644
--- a/drivers/target/iscsi/iscsi_target_erl0.c
+++ b/drivers/target/iscsi/iscsi_target_erl0.c
@@ -783,7 +783,7 @@ static void iscsit_handle_time2retain_timeout(unsigned long data)
}
spin_unlock_bh(&se_tpg->session_lock);
- iscsit_close_session(sess);
+ target_put_session(sess->se_sess);
}
extern void iscsit_start_time2retain_handler(struct iscsi_session *sess)
diff --git a/drivers/target/iscsi/iscsi_target_erl1.c b/drivers/target/iscsi/iscsi_target_erl1.c
index 27901e37c125..006f605edb08 100644
--- a/drivers/target/iscsi/iscsi_target_erl1.c
+++ b/drivers/target/iscsi/iscsi_target_erl1.c
@@ -416,7 +416,7 @@ static int iscsit_handle_recovery_datain(
struct iscsi_datain_req *dr;
struct se_cmd *se_cmd = &cmd->se_cmd;
- if (!atomic_read(&se_cmd->t_transport_complete)) {
+ if (!(se_cmd->transport_state & CMD_T_COMPLETE)) {
pr_err("Ignoring ITT: 0x%08x Data SNACK\n",
cmd->init_task_tag);
return 0;
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c
index 1ee33a8c3fab..a3656c9903a1 100644
--- a/drivers/target/iscsi/iscsi_target_login.c
+++ b/drivers/target/iscsi/iscsi_target_login.c
@@ -181,14 +181,16 @@ int iscsi_check_for_session_reinstatement(struct iscsi_conn *conn)
if (sess->session_state == TARG_SESS_STATE_FAILED) {
spin_unlock_bh(&sess->conn_lock);
iscsit_dec_session_usage_count(sess);
- return iscsit_close_session(sess);
+ target_put_session(sess->se_sess);
+ return 0;
}
spin_unlock_bh(&sess->conn_lock);
iscsit_stop_session(sess, 1, 1);
iscsit_dec_session_usage_count(sess);
- return iscsit_close_session(sess);
+ target_put_session(sess->se_sess);
+ return 0;
}
static void iscsi_login_set_conn_values(
@@ -881,7 +883,7 @@ fail:
static int __iscsi_target_login_thread(struct iscsi_np *np)
{
u8 buffer[ISCSI_HDR_LEN], iscsi_opcode, zero_tsih = 0;
- int err, ret = 0, ip_proto, sock_type, set_sctp_conn_flag, stop;
+ int err, ret = 0, set_sctp_conn_flag, stop;
struct iscsi_conn *conn = NULL;
struct iscsi_login *login;
struct iscsi_portal_group *tpg = NULL;
@@ -894,8 +896,6 @@ static int __iscsi_target_login_thread(struct iscsi_np *np)
flush_signals(current);
set_sctp_conn_flag = 0;
sock = np->np_socket;
- ip_proto = np->np_ip_proto;
- sock_type = np->np_sock_type;
spin_lock_bh(&np->np_thread_lock);
if (np->np_thread_state == ISCSI_NP_THREAD_RESET) {
diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c
index e89fa7457254..2dba448cac19 100644
--- a/drivers/target/iscsi/iscsi_target_nego.c
+++ b/drivers/target/iscsi/iscsi_target_nego.c
@@ -90,7 +90,7 @@ int extract_param(
return -1;
if (len > max_length) {
- pr_err("Length of input: %d exeeds max_length:"
+ pr_err("Length of input: %d exceeds max_length:"
" %d\n", len, max_length);
return -1;
}
@@ -173,13 +173,11 @@ static int iscsi_target_check_login_request(
struct iscsi_conn *conn,
struct iscsi_login *login)
{
- int req_csg, req_nsg, rsp_csg, rsp_nsg;
+ int req_csg, req_nsg;
u32 payload_length;
struct iscsi_login_req *login_req;
- struct iscsi_login_rsp *login_rsp;
login_req = (struct iscsi_login_req *) login->req;
- login_rsp = (struct iscsi_login_rsp *) login->rsp;
payload_length = ntoh24(login_req->dlength);
switch (login_req->opcode & ISCSI_OPCODE_MASK) {
@@ -203,9 +201,7 @@ static int iscsi_target_check_login_request(
}
req_csg = (login_req->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
- rsp_csg = (login_rsp->flags & ISCSI_FLAG_LOGIN_CURRENT_STAGE_MASK) >> 2;
req_nsg = (login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK);
- rsp_nsg = (login_rsp->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE_MASK);
if (req_csg != login->current_stage) {
pr_err("Initiator unexpectedly changed login stage"
@@ -753,12 +749,10 @@ static int iscsi_target_locate_portal(
struct iscsi_session *sess = conn->sess;
struct iscsi_tiqn *tiqn;
struct iscsi_login_req *login_req;
- struct iscsi_targ_login_rsp *login_rsp;
u32 payload_length;
int sessiontype = 0, ret = 0;
login_req = (struct iscsi_login_req *) login->req;
- login_rsp = (struct iscsi_targ_login_rsp *) login->rsp;
payload_length = ntoh24(login_req->dlength);
login->first_request = 1;
diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c
index b3c699c4fe8c..11dc2936af76 100644
--- a/drivers/target/iscsi/iscsi_target_nodeattrib.c
+++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c
@@ -49,7 +49,7 @@ void iscsit_set_default_node_attribues(
a->default_erl = NA_DEFAULT_ERL;
}
-extern int iscsit_na_dataout_timeout(
+int iscsit_na_dataout_timeout(
struct iscsi_node_acl *acl,
u32 dataout_timeout)
{
@@ -74,7 +74,7 @@ extern int iscsit_na_dataout_timeout(
return 0;
}
-extern int iscsit_na_dataout_timeout_retries(
+int iscsit_na_dataout_timeout_retries(
struct iscsi_node_acl *acl,
u32 dataout_timeout_retries)
{
@@ -100,7 +100,7 @@ extern int iscsit_na_dataout_timeout_retries(
return 0;
}
-extern int iscsit_na_nopin_timeout(
+int iscsit_na_nopin_timeout(
struct iscsi_node_acl *acl,
u32 nopin_timeout)
{
@@ -155,7 +155,7 @@ extern int iscsit_na_nopin_timeout(
return 0;
}
-extern int iscsit_na_nopin_response_timeout(
+int iscsit_na_nopin_response_timeout(
struct iscsi_node_acl *acl,
u32 nopin_response_timeout)
{
@@ -181,7 +181,7 @@ extern int iscsit_na_nopin_response_timeout(
return 0;
}
-extern int iscsit_na_random_datain_pdu_offsets(
+int iscsit_na_random_datain_pdu_offsets(
struct iscsi_node_acl *acl,
u32 random_datain_pdu_offsets)
{
@@ -201,7 +201,7 @@ extern int iscsit_na_random_datain_pdu_offsets(
return 0;
}
-extern int iscsit_na_random_datain_seq_offsets(
+int iscsit_na_random_datain_seq_offsets(
struct iscsi_node_acl *acl,
u32 random_datain_seq_offsets)
{
@@ -221,7 +221,7 @@ extern int iscsit_na_random_datain_seq_offsets(
return 0;
}
-extern int iscsit_na_random_r2t_offsets(
+int iscsit_na_random_r2t_offsets(
struct iscsi_node_acl *acl,
u32 random_r2t_offsets)
{
@@ -241,7 +241,7 @@ extern int iscsit_na_random_r2t_offsets(
return 0;
}
-extern int iscsit_na_default_erl(
+int iscsit_na_default_erl(
struct iscsi_node_acl *acl,
u32 default_erl)
{
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 5b773160200f..eb05c9d751ea 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -874,8 +874,8 @@ static int iscsi_check_numerical_value(struct iscsi_param *param, char *value_pt
static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *value)
{
char *left_val_ptr = NULL, *right_val_ptr = NULL;
- char *tilde_ptr = NULL, *tmp_ptr = NULL;
- u32 left_val, right_val, local_left_val, local_right_val;
+ char *tilde_ptr = NULL;
+ u32 left_val, right_val, local_left_val;
if (strcmp(param->name, IFMARKINT) &&
strcmp(param->name, OFMARKINT)) {
@@ -903,8 +903,8 @@ static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *va
if (iscsi_check_numerical_value(param, right_val_ptr) < 0)
return -1;
- left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0);
- right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0);
+ left_val = simple_strtoul(left_val_ptr, NULL, 0);
+ right_val = simple_strtoul(right_val_ptr, NULL, 0);
*tilde_ptr = '~';
if (right_val < left_val) {
@@ -928,8 +928,7 @@ static int iscsi_check_numerical_range_value(struct iscsi_param *param, char *va
left_val_ptr = param->value;
right_val_ptr = param->value + strlen(left_val_ptr) + 1;
- local_left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0);
- local_right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0);
+ local_left_val = simple_strtoul(left_val_ptr, NULL, 0);
*tilde_ptr = '~';
if (param->set_param) {
@@ -1189,7 +1188,7 @@ static int iscsi_check_proposer_state(struct iscsi_param *param, char *value)
if (IS_TYPE_NUMBER_RANGE(param)) {
u32 left_val = 0, right_val = 0, recieved_value = 0;
char *left_val_ptr = NULL, *right_val_ptr = NULL;
- char *tilde_ptr = NULL, *tmp_ptr = NULL;
+ char *tilde_ptr = NULL;
if (!strcmp(value, IRRELEVANT) || !strcmp(value, REJECT)) {
if (iscsi_update_param_value(param, value) < 0)
@@ -1213,9 +1212,9 @@ static int iscsi_check_proposer_state(struct iscsi_param *param, char *value)
left_val_ptr = param->value;
right_val_ptr = param->value + strlen(left_val_ptr) + 1;
- left_val = simple_strtoul(left_val_ptr, &tmp_ptr, 0);
- right_val = simple_strtoul(right_val_ptr, &tmp_ptr, 0);
- recieved_value = simple_strtoul(value, &tmp_ptr, 0);
+ left_val = simple_strtoul(left_val_ptr, NULL, 0);
+ right_val = simple_strtoul(right_val_ptr, NULL, 0);
+ recieved_value = simple_strtoul(value, NULL, 0);
*tilde_ptr = '~';
diff --git a/drivers/target/iscsi/iscsi_target_tmr.c b/drivers/target/iscsi/iscsi_target_tmr.c
index 255ed35da815..e01da9d2b37e 100644
--- a/drivers/target/iscsi/iscsi_target_tmr.c
+++ b/drivers/target/iscsi/iscsi_target_tmr.c
@@ -250,7 +250,7 @@ static int iscsit_task_reassign_complete_write(
* so if we have received all DataOUT we can safety ignore Initiator.
*/
if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
- if (!atomic_read(&cmd->se_cmd.t_transport_sent)) {
+ if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) {
pr_debug("WRITE ITT: 0x%08x: t_state: %d"
" never sent to transport\n",
cmd->init_task_tag, cmd->se_cmd.t_state);
@@ -314,7 +314,7 @@ static int iscsit_task_reassign_complete_read(
cmd->acked_data_sn = (tmr_req->exp_data_sn - 1);
}
- if (!atomic_read(&cmd->se_cmd.t_transport_sent)) {
+ if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) {
pr_debug("READ ITT: 0x%08x: t_state: %d never sent to"
" transport\n", cmd->init_task_tag,
cmd->se_cmd.t_state);
@@ -322,7 +322,7 @@ static int iscsit_task_reassign_complete_read(
return 0;
}
- if (!atomic_read(&se_cmd->t_transport_complete)) {
+ if (!(se_cmd->transport_state & CMD_T_COMPLETE)) {
pr_err("READ ITT: 0x%08x: t_state: %d, never returned"
" from transport\n", cmd->init_task_tag,
cmd->se_cmd.t_state);
diff --git a/drivers/target/iscsi/iscsi_target_tq.c b/drivers/target/iscsi/iscsi_target_tq.c
index 0baac5bcebd4..977e1cf90e83 100644
--- a/drivers/target/iscsi/iscsi_target_tq.c
+++ b/drivers/target/iscsi/iscsi_target_tq.c
@@ -536,12 +536,6 @@ int iscsi_thread_set_init(void)
return -ENOMEM;
}
- spin_lock_init(&active_ts_lock);
- spin_lock_init(&inactive_ts_lock);
- spin_lock_init(&ts_bitmap_lock);
- INIT_LIST_HEAD(&active_ts_list);
- INIT_LIST_HEAD(&inactive_ts_list);
-
return 0;
}
diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c
index 11287e1ece13..4eba86d2bd82 100644
--- a/drivers/target/iscsi/iscsi_target_util.c
+++ b/drivers/target/iscsi/iscsi_target_util.c
@@ -229,6 +229,7 @@ struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr(
{
struct iscsi_cmd *cmd;
struct se_cmd *se_cmd;
+ int rc;
u8 tcm_function;
cmd = iscsit_allocate_cmd(conn, GFP_KERNEL);
@@ -286,10 +287,8 @@ struct iscsi_cmd *iscsit_allocate_se_cmd_for_tmr(
goto out;
}
- se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd,
- cmd->tmr_req, tcm_function,
- GFP_KERNEL);
- if (!se_cmd->se_tmr_req)
+ rc = core_tmr_alloc_req(se_cmd, cmd->tmr_req, tcm_function, GFP_KERNEL);
+ if (rc < 0)
goto out;
cmd->tmr_req->se_tmr_req = se_cmd->se_tmr_req;
diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c
index c47ff7f59e57..a9b4eeefe9fc 100644
--- a/drivers/target/loopback/tcm_loop.c
+++ b/drivers/target/loopback/tcm_loop.c
@@ -44,138 +44,12 @@
/* Local pointer to allocated TCM configfs fabric module */
static struct target_fabric_configfs *tcm_loop_fabric_configfs;
+static struct workqueue_struct *tcm_loop_workqueue;
static struct kmem_cache *tcm_loop_cmd_cache;
static int tcm_loop_hba_no_cnt;
-/*
- * Allocate a tcm_loop cmd descriptor from target_core_mod code
- *
- * Can be called from interrupt context in tcm_loop_queuecommand() below
- */
-static struct se_cmd *tcm_loop_allocate_core_cmd(
- struct tcm_loop_hba *tl_hba,
- struct se_portal_group *se_tpg,
- struct scsi_cmnd *sc)
-{
- struct se_cmd *se_cmd;
- struct se_session *se_sess;
- struct tcm_loop_nexus *tl_nexus = tl_hba->tl_nexus;
- struct tcm_loop_cmd *tl_cmd;
- int sam_task_attr;
-
- if (!tl_nexus) {
- scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
- " does not exist\n");
- set_host_byte(sc, DID_ERROR);
- return NULL;
- }
- se_sess = tl_nexus->se_sess;
-
- tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC);
- if (!tl_cmd) {
- pr_err("Unable to allocate struct tcm_loop_cmd\n");
- set_host_byte(sc, DID_ERROR);
- return NULL;
- }
- se_cmd = &tl_cmd->tl_se_cmd;
- /*
- * Save the pointer to struct scsi_cmnd *sc
- */
- tl_cmd->sc = sc;
- /*
- * Locate the SAM Task Attr from struct scsi_cmnd *
- */
- if (sc->device->tagged_supported) {
- switch (sc->tag) {
- case HEAD_OF_QUEUE_TAG:
- sam_task_attr = MSG_HEAD_TAG;
- break;
- case ORDERED_QUEUE_TAG:
- sam_task_attr = MSG_ORDERED_TAG;
- break;
- default:
- sam_task_attr = MSG_SIMPLE_TAG;
- break;
- }
- } else
- sam_task_attr = MSG_SIMPLE_TAG;
-
- /*
- * Initialize struct se_cmd descriptor from target_core_mod infrastructure
- */
- transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess,
- scsi_bufflen(sc), sc->sc_data_direction, sam_task_attr,
- &tl_cmd->tl_sense_buf[0]);
-
- if (scsi_bidi_cmnd(sc))
- se_cmd->se_cmd_flags |= SCF_BIDI;
-
- /*
- * Locate the struct se_lun pointer and attach it to struct se_cmd
- */
- if (transport_lookup_cmd_lun(se_cmd, tl_cmd->sc->device->lun) < 0) {
- kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
- set_host_byte(sc, DID_NO_CONNECT);
- return NULL;
- }
-
- return se_cmd;
-}
-
-/*
- * Called by struct target_core_fabric_ops->new_cmd_map()
- *
- * Always called in process context. A non zero return value
- * here will signal to handle an exception based on the return code.
- */
-static int tcm_loop_new_cmd_map(struct se_cmd *se_cmd)
-{
- struct tcm_loop_cmd *tl_cmd = container_of(se_cmd,
- struct tcm_loop_cmd, tl_se_cmd);
- struct scsi_cmnd *sc = tl_cmd->sc;
- struct scatterlist *sgl_bidi = NULL;
- u32 sgl_bidi_count = 0;
- int ret;
- /*
- * Allocate the necessary tasks to complete the received CDB+data
- */
- ret = transport_generic_allocate_tasks(se_cmd, sc->cmnd);
- if (ret != 0)
- return ret;
- /*
- * For BIDI commands, pass in the extra READ buffer
- * to transport_generic_map_mem_to_cmd() below..
- */
- if (se_cmd->se_cmd_flags & SCF_BIDI) {
- struct scsi_data_buffer *sdb = scsi_in(sc);
-
- sgl_bidi = sdb->table.sgl;
- sgl_bidi_count = sdb->table.nents;
- }
- /*
- * Because some userspace code via scsi-generic do not memset their
- * associated read buffers, go ahead and do that here for type
- * SCF_SCSI_CONTROL_SG_IO_CDB. Also note that this is currently
- * guaranteed to be a single SGL for SCF_SCSI_CONTROL_SG_IO_CDB
- * by target core in transport_generic_allocate_tasks() ->
- * transport_generic_cmd_sequencer().
- */
- if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB &&
- se_cmd->data_direction == DMA_FROM_DEVICE) {
- struct scatterlist *sg = scsi_sglist(sc);
- unsigned char *buf = kmap(sg_page(sg)) + sg->offset;
-
- if (buf != NULL) {
- memset(buf, 0, sg->length);
- kunmap(sg_page(sg));
- }
- }
-
- /* Tell the core about our preallocated memory */
- return transport_generic_map_mem_to_cmd(se_cmd, scsi_sglist(sc),
- scsi_sg_count(sc), sgl_bidi, sgl_bidi_count);
-}
+static int tcm_loop_queue_status(struct se_cmd *se_cmd);
/*
* Called from struct target_core_fabric_ops->check_stop_free()
@@ -187,7 +61,7 @@ static int tcm_loop_check_stop_free(struct se_cmd *se_cmd)
* pointer. These will be released directly in tcm_loop_device_reset()
* with transport_generic_free_cmd().
*/
- if (se_cmd->se_tmr_req)
+ if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
return 0;
/*
* Release the struct se_cmd, which will make a callback to release
@@ -263,50 +137,152 @@ static int tcm_loop_change_queue_depth(
}
/*
- * Main entry point from struct scsi_host_template for incoming SCSI CDB+Data
- * from Linux/SCSI subsystem for SCSI low level device drivers (LLDs)
+ * Locate the SAM Task Attr from struct scsi_cmnd *
*/
-static int tcm_loop_queuecommand(
- struct Scsi_Host *sh,
- struct scsi_cmnd *sc)
+static int tcm_loop_sam_attr(struct scsi_cmnd *sc)
{
- struct se_cmd *se_cmd;
- struct se_portal_group *se_tpg;
+ if (sc->device->tagged_supported) {
+ switch (sc->tag) {
+ case HEAD_OF_QUEUE_TAG:
+ return MSG_HEAD_TAG;
+ case ORDERED_QUEUE_TAG:
+ return MSG_ORDERED_TAG;
+ default:
+ break;
+ }
+ }
+
+ return MSG_SIMPLE_TAG;
+}
+
+static void tcm_loop_submission_work(struct work_struct *work)
+{
+ struct tcm_loop_cmd *tl_cmd =
+ container_of(work, struct tcm_loop_cmd, work);
+ struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd;
+ struct scsi_cmnd *sc = tl_cmd->sc;
+ struct tcm_loop_nexus *tl_nexus;
struct tcm_loop_hba *tl_hba;
struct tcm_loop_tpg *tl_tpg;
+ struct scatterlist *sgl_bidi = NULL;
+ u32 sgl_bidi_count = 0;
+ int ret;
- pr_debug("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x"
- " scsi_buf_len: %u\n", sc->device->host->host_no,
- sc->device->id, sc->device->channel, sc->device->lun,
- sc->cmnd[0], scsi_bufflen(sc));
- /*
- * Locate the tcm_loop_hba_t pointer
- */
tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host);
tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id];
+
/*
* Ensure that this tl_tpg reference from the incoming sc->device->id
* has already been configured via tcm_loop_make_naa_tpg().
*/
if (!tl_tpg->tl_hba) {
set_host_byte(sc, DID_NO_CONNECT);
- sc->scsi_done(sc);
- return 0;
+ goto out_done;
}
- se_tpg = &tl_tpg->tl_se_tpg;
+
+ tl_nexus = tl_hba->tl_nexus;
+ if (!tl_nexus) {
+ scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus"
+ " does not exist\n");
+ set_host_byte(sc, DID_ERROR);
+ goto out_done;
+ }
+
+ transport_init_se_cmd(se_cmd, tl_tpg->tl_se_tpg.se_tpg_tfo,
+ tl_nexus->se_sess,
+ scsi_bufflen(sc), sc->sc_data_direction,
+ tcm_loop_sam_attr(sc), &tl_cmd->tl_sense_buf[0]);
+
+ if (scsi_bidi_cmnd(sc)) {
+ struct scsi_data_buffer *sdb = scsi_in(sc);
+
+ sgl_bidi = sdb->table.sgl;
+ sgl_bidi_count = sdb->table.nents;
+ se_cmd->se_cmd_flags |= SCF_BIDI;
+
+ }
+
+ if (transport_lookup_cmd_lun(se_cmd, tl_cmd->sc->device->lun) < 0) {
+ kmem_cache_free(tcm_loop_cmd_cache, tl_cmd);
+ set_host_byte(sc, DID_NO_CONNECT);
+ goto out_done;
+ }
+
/*
- * Determine the SAM Task Attribute and allocate tl_cmd and
- * tl_cmd->tl_se_cmd from TCM infrastructure
+ * Because some userspace code via scsi-generic do not memset their
+ * associated read buffers, go ahead and do that here for type
+ * SCF_SCSI_CONTROL_SG_IO_CDB. Also note that this is currently
+ * guaranteed to be a single SGL for SCF_SCSI_CONTROL_SG_IO_CDB
+ * by target core in transport_generic_allocate_tasks() ->
+ * transport_generic_cmd_sequencer().
*/
- se_cmd = tcm_loop_allocate_core_cmd(tl_hba, se_tpg, sc);
- if (!se_cmd) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB &&
+ se_cmd->data_direction == DMA_FROM_DEVICE) {
+ struct scatterlist *sg = scsi_sglist(sc);
+ unsigned char *buf = kmap(sg_page(sg)) + sg->offset;
+
+ if (buf != NULL) {
+ memset(buf, 0, sg->length);
+ kunmap(sg_page(sg));
+ }
+ }
+
+ ret = transport_generic_allocate_tasks(se_cmd, sc->cmnd);
+ if (ret == -ENOMEM) {
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ } else if (ret < 0) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+ tcm_loop_queue_status(se_cmd);
+ else
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ ret = transport_generic_map_mem_to_cmd(se_cmd, scsi_sglist(sc),
+ scsi_sg_count(sc), sgl_bidi, sgl_bidi_count);
+ if (ret) {
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+ transport_handle_cdb_direct(se_cmd);
+ return;
+
+out_done:
+ sc->scsi_done(sc);
+ return;
+}
+
+/*
+ * ->queuecommand can be and usually is called from interrupt context, so
+ * defer the actual submission to a workqueue.
+ */
+static int tcm_loop_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc)
+{
+ struct tcm_loop_cmd *tl_cmd;
+
+ pr_debug("tcm_loop_queuecommand() %d:%d:%d:%d got CDB: 0x%02x"
+ " scsi_buf_len: %u\n", sc->device->host->host_no,
+ sc->device->id, sc->device->channel, sc->device->lun,
+ sc->cmnd[0], scsi_bufflen(sc));
+
+ tl_cmd = kmem_cache_zalloc(tcm_loop_cmd_cache, GFP_ATOMIC);
+ if (!tl_cmd) {
+ pr_err("Unable to allocate struct tcm_loop_cmd\n");
+ set_host_byte(sc, DID_ERROR);
sc->scsi_done(sc);
return 0;
}
- /*
- * Queue up the newly allocated to be processed in TCM thread context.
- */
- transport_generic_handle_cdb_map(se_cmd);
+
+ tl_cmd->sc = sc;
+ INIT_WORK(&tl_cmd->work, tcm_loop_submission_work);
+ queue_work(tcm_loop_workqueue, &tl_cmd->work);
return 0;
}
@@ -324,7 +300,7 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
struct tcm_loop_nexus *tl_nexus;
struct tcm_loop_tmr *tl_tmr = NULL;
struct tcm_loop_tpg *tl_tpg;
- int ret = FAILED;
+ int ret = FAILED, rc;
/*
* Locate the tcm_loop_hba_t pointer
*/
@@ -365,12 +341,9 @@ static int tcm_loop_device_reset(struct scsi_cmnd *sc)
transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, 0,
DMA_NONE, MSG_SIMPLE_TAG,
&tl_cmd->tl_sense_buf[0]);
- /*
- * Allocate the LUN_RESET TMR
- */
- se_cmd->se_tmr_req = core_tmr_alloc_req(se_cmd, tl_tmr,
- TMR_LUN_RESET, GFP_KERNEL);
- if (IS_ERR(se_cmd->se_tmr_req))
+
+ rc = core_tmr_alloc_req(se_cmd, tl_tmr, TMR_LUN_RESET, GFP_KERNEL);
+ if (rc < 0)
goto release;
/*
* Locate the underlying TCM struct se_lun from sc->device->lun
@@ -762,22 +735,6 @@ static u32 tcm_loop_get_inst_index(struct se_portal_group *se_tpg)
return 1;
}
-static int tcm_loop_is_state_remove(struct se_cmd *se_cmd)
-{
- /*
- * Assume struct scsi_cmnd is not in remove state..
- */
- return 0;
-}
-
-static int tcm_loop_sess_logged_in(struct se_session *se_sess)
-{
- /*
- * Assume that TL Nexus is always active
- */
- return 1;
-}
-
static u32 tcm_loop_sess_get_index(struct se_session *se_sess)
{
return 1;
@@ -811,19 +768,6 @@ static void tcm_loop_close_session(struct se_session *se_sess)
return;
};
-static void tcm_loop_stop_session(
- struct se_session *se_sess,
- int sess_sleep,
- int conn_sleep)
-{
- return;
-}
-
-static void tcm_loop_fall_back_to_erl0(struct se_session *se_sess)
-{
- return;
-}
-
static int tcm_loop_write_pending(struct se_cmd *se_cmd)
{
/*
@@ -855,6 +799,9 @@ static int tcm_loop_queue_data_in(struct se_cmd *se_cmd)
sc->result = SAM_STAT_GOOD;
set_host_byte(sc, DID_OK);
+ if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) ||
+ (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT))
+ scsi_set_resid(sc, se_cmd->residual_count);
sc->scsi_done(sc);
return 0;
}
@@ -880,6 +827,9 @@ static int tcm_loop_queue_status(struct se_cmd *se_cmd)
sc->result = se_cmd->scsi_status;
set_host_byte(sc, DID_OK);
+ if ((se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) ||
+ (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT))
+ scsi_set_resid(sc, se_cmd->residual_count);
sc->scsi_done(sc);
return 0;
}
@@ -1361,7 +1311,6 @@ static struct configfs_attribute *tcm_loop_wwn_attrs[] = {
static int tcm_loop_register_configfs(void)
{
struct target_fabric_configfs *fabric;
- struct config_group *tf_cg;
int ret;
/*
* Set the TCM Loop HBA counter to zero
@@ -1407,14 +1356,10 @@ static int tcm_loop_register_configfs(void)
/*
* Used for setting up remaining TCM resources in process context
*/
- fabric->tf_ops.new_cmd_map = &tcm_loop_new_cmd_map;
fabric->tf_ops.check_stop_free = &tcm_loop_check_stop_free;
fabric->tf_ops.release_cmd = &tcm_loop_release_cmd;
fabric->tf_ops.shutdown_session = &tcm_loop_shutdown_session;
fabric->tf_ops.close_session = &tcm_loop_close_session;
- fabric->tf_ops.stop_session = &tcm_loop_stop_session;
- fabric->tf_ops.fall_back_to_erl0 = &tcm_loop_fall_back_to_erl0;
- fabric->tf_ops.sess_logged_in = &tcm_loop_sess_logged_in;
fabric->tf_ops.sess_get_index = &tcm_loop_sess_get_index;
fabric->tf_ops.sess_get_initiator_sid = NULL;
fabric->tf_ops.write_pending = &tcm_loop_write_pending;
@@ -1431,9 +1376,7 @@ static int tcm_loop_register_configfs(void)
fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp;
fabric->tf_ops.set_fabric_sense_len = &tcm_loop_set_fabric_sense_len;
fabric->tf_ops.get_fabric_sense_len = &tcm_loop_get_fabric_sense_len;
- fabric->tf_ops.is_state_remove = &tcm_loop_is_state_remove;
- tf_cg = &fabric->tf_group;
/*
* Setup function pointers for generic logic in target_core_fabric_configfs.c
*/
@@ -1490,7 +1433,11 @@ static void tcm_loop_deregister_configfs(void)
static int __init tcm_loop_fabric_init(void)
{
- int ret;
+ int ret = -ENOMEM;
+
+ tcm_loop_workqueue = alloc_workqueue("tcm_loop", 0, 0);
+ if (!tcm_loop_workqueue)
+ goto out;
tcm_loop_cmd_cache = kmem_cache_create("tcm_loop_cmd_cache",
sizeof(struct tcm_loop_cmd),
@@ -1499,20 +1446,27 @@ static int __init tcm_loop_fabric_init(void)
if (!tcm_loop_cmd_cache) {
pr_debug("kmem_cache_create() for"
" tcm_loop_cmd_cache failed\n");
- return -ENOMEM;
+ goto out_destroy_workqueue;
}
ret = tcm_loop_alloc_core_bus();
if (ret)
- return ret;
+ goto out_destroy_cache;
ret = tcm_loop_register_configfs();
- if (ret) {
- tcm_loop_release_core_bus();
- return ret;
- }
+ if (ret)
+ goto out_release_core_bus;
return 0;
+
+out_release_core_bus:
+ tcm_loop_release_core_bus();
+out_destroy_cache:
+ kmem_cache_destroy(tcm_loop_cmd_cache);
+out_destroy_workqueue:
+ destroy_workqueue(tcm_loop_workqueue);
+out:
+ return ret;
}
static void __exit tcm_loop_fabric_exit(void)
@@ -1520,6 +1474,7 @@ static void __exit tcm_loop_fabric_exit(void)
tcm_loop_deregister_configfs();
tcm_loop_release_core_bus();
kmem_cache_destroy(tcm_loop_cmd_cache);
+ destroy_workqueue(tcm_loop_workqueue);
}
MODULE_DESCRIPTION("TCM loopback virtual Linux/SCSI fabric module");
diff --git a/drivers/target/loopback/tcm_loop.h b/drivers/target/loopback/tcm_loop.h
index 15a036441471..7b54893db665 100644
--- a/drivers/target/loopback/tcm_loop.h
+++ b/drivers/target/loopback/tcm_loop.h
@@ -1,4 +1,4 @@
-#define TCM_LOOP_VERSION "v2.1-rc1"
+#define TCM_LOOP_VERSION "v2.1-rc2"
#define TL_WWN_ADDR_LEN 256
#define TL_TPGS_PER_HBA 32
@@ -12,9 +12,9 @@ struct tcm_loop_cmd {
u32 sc_cmd_state;
/* Pointer to the CDB+Data descriptor from Linux/SCSI subsystem */
struct scsi_cmnd *sc;
- struct list_head *tl_cmd_list;
/* The TCM I/O descriptor that is accessed via container_of() */
struct se_cmd tl_se_cmd;
+ struct work_struct work;
/* Sense buffer that will be mapped into outgoing status */
unsigned char tl_sense_buf[TRANSPORT_SENSE_BUFFER];
};
diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c
index 01a2691dfb47..c7746a3339d4 100644
--- a/drivers/target/target_core_alua.c
+++ b/drivers/target/target_core_alua.c
@@ -30,6 +30,7 @@
#include <linux/export.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
+#include <asm/unaligned.h>
#include <target/target_core_base.h>
#include <target/target_core_backend.h>
@@ -267,8 +268,7 @@ int target_emulate_set_target_port_groups(struct se_task *task)
* changed.
*/
if (primary) {
- tg_pt_id = ((ptr[2] << 8) & 0xff);
- tg_pt_id |= (ptr[3] & 0xff);
+ tg_pt_id = get_unaligned_be16(ptr + 2);
/*
* Locate the matching target port group ID from
* the global tg_pt_gp list
@@ -312,8 +312,7 @@ int target_emulate_set_target_port_groups(struct se_task *task)
* the Target Port in question for the the incoming
* SET_TARGET_PORT_GROUPS op.
*/
- rtpi = ((ptr[2] << 8) & 0xff);
- rtpi |= (ptr[3] & 0xff);
+ rtpi = get_unaligned_be16(ptr + 2);
/*
* Locate the matching relative target port identifer
* for the struct se_device storage object.
diff --git a/drivers/target/target_core_cdb.c b/drivers/target/target_core_cdb.c
index f3d71fa88a28..30a67707036f 100644
--- a/drivers/target/target_core_cdb.c
+++ b/drivers/target/target_core_cdb.c
@@ -66,32 +66,15 @@ target_fill_alua_data(struct se_port *port, unsigned char *buf)
}
static int
-target_emulate_inquiry_std(struct se_cmd *cmd)
+target_emulate_inquiry_std(struct se_cmd *cmd, char *buf)
{
struct se_lun *lun = cmd->se_lun;
struct se_device *dev = cmd->se_dev;
- struct se_portal_group *tpg = lun->lun_sep->sep_tpg;
- unsigned char *buf;
- /*
- * Make sure we at least have 6 bytes of INQUIRY response
- * payload going back for EVPD=0
- */
- if (cmd->data_length < 6) {
- pr_err("SCSI Inquiry payload length: %u"
- " too small for EVPD=0\n", cmd->data_length);
- return -EINVAL;
- }
+ /* Set RMB (removable media) for tape devices */
+ if (dev->transport->get_device_type(dev) == TYPE_TAPE)
+ buf[1] = 0x80;
- buf = transport_kmap_data_sg(cmd);
-
- if (dev == tpg->tpg_virt_lun0.lun_se_dev) {
- buf[0] = 0x3f; /* Not connected */
- } else {
- buf[0] = dev->transport->get_device_type(dev);
- if (buf[0] == TYPE_TAPE)
- buf[1] = 0x80;
- }
buf[2] = dev->transport->get_device_rev(dev);
/*
@@ -112,29 +95,13 @@ target_emulate_inquiry_std(struct se_cmd *cmd)
if (dev->se_sub_dev->t10_alua.alua_type == SPC3_ALUA_EMULATED)
target_fill_alua_data(lun->lun_sep, buf);
- if (cmd->data_length < 8) {
- buf[4] = 1; /* Set additional length to 1 */
- goto out;
- }
-
- buf[7] = 0x32; /* Sync=1 and CmdQue=1 */
-
- /*
- * Do not include vendor, product, reversion info in INQUIRY
- * response payload for cdbs with a small allocation length.
- */
- if (cmd->data_length < 36) {
- buf[4] = 3; /* Set additional length to 3 */
- goto out;
- }
+ buf[7] = 0x2; /* CmdQue=1 */
snprintf(&buf[8], 8, "LIO-ORG");
snprintf(&buf[16], 16, "%s", dev->se_sub_dev->t10_wwn.model);
snprintf(&buf[32], 4, "%s", dev->se_sub_dev->t10_wwn.revision);
buf[4] = 31; /* Set additional length to 31 */
-out:
- transport_kunmap_data_sg(cmd);
return 0;
}
@@ -152,12 +119,6 @@ target_emulate_evpd_80(struct se_cmd *cmd, unsigned char *buf)
unit_serial_len = strlen(dev->se_sub_dev->t10_wwn.unit_serial);
unit_serial_len++; /* For NULL Terminator */
- if (((len + 4) + unit_serial_len) > cmd->data_length) {
- len += unit_serial_len;
- buf[2] = ((len >> 8) & 0xff);
- buf[3] = (len & 0xff);
- return 0;
- }
len += sprintf(&buf[4], "%s",
dev->se_sub_dev->t10_wwn.unit_serial);
len++; /* Extra Byte for NULL Terminator */
@@ -229,9 +190,6 @@ target_emulate_evpd_83(struct se_cmd *cmd, unsigned char *buf)
if (!(dev->se_sub_dev->su_dev_flags & SDF_EMULATED_VPD_UNIT_SERIAL))
goto check_t10_vend_desc;
- if (off + 20 > cmd->data_length)
- goto check_t10_vend_desc;
-
/* CODE SET == Binary */
buf[off++] = 0x1;
@@ -283,12 +241,6 @@ check_t10_vend_desc:
strlen(&dev->se_sub_dev->t10_wwn.unit_serial[0]);
unit_serial_len++; /* For NULL Terminator */
- if ((len + (id_len + 4) +
- (prod_len + unit_serial_len)) >
- cmd->data_length) {
- len += (prod_len + unit_serial_len);
- goto check_port;
- }
id_len += sprintf(&buf[off+12], "%s:%s", prod,
&dev->se_sub_dev->t10_wwn.unit_serial[0]);
}
@@ -306,7 +258,6 @@ check_t10_vend_desc:
/*
* struct se_port is only set for INQUIRY VPD=1 through $FABRIC_MOD
*/
-check_port:
port = lun->lun_sep;
if (port) {
struct t10_alua_lu_gp *lu_gp;
@@ -323,10 +274,6 @@ check_port:
* Get the PROTOCOL IDENTIFIER as defined by spc4r17
* section 7.5.1 Table 362
*/
- if (((len + 4) + 8) > cmd->data_length) {
- len += 8;
- goto check_tpgi;
- }
buf[off] =
(tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
buf[off++] |= 0x1; /* CODE SET == Binary */
@@ -350,15 +297,10 @@ check_port:
* Get the PROTOCOL IDENTIFIER as defined by spc4r17
* section 7.5.1 Table 362
*/
-check_tpgi:
if (dev->se_sub_dev->t10_alua.alua_type !=
SPC3_ALUA_EMULATED)
goto check_scsi_name;
- if (((len + 4) + 8) > cmd->data_length) {
- len += 8;
- goto check_lu_gp;
- }
tg_pt_gp_mem = port->sep_alua_tg_pt_gp_mem;
if (!tg_pt_gp_mem)
goto check_lu_gp;
@@ -391,10 +333,6 @@ check_tpgi:
* section 7.7.3.8
*/
check_lu_gp:
- if (((len + 4) + 8) > cmd->data_length) {
- len += 8;
- goto check_scsi_name;
- }
lu_gp_mem = dev->dev_alua_lu_gp_mem;
if (!lu_gp_mem)
goto check_scsi_name;
@@ -435,10 +373,6 @@ check_scsi_name:
/* Header size + Designation descriptor */
scsi_name_len += 4;
- if (((len + 4) + scsi_name_len) > cmd->data_length) {
- len += scsi_name_len;
- goto set_len;
- }
buf[off] =
(tpg->se_tpg_tfo->get_fabric_proto_ident(tpg) << 4);
buf[off++] |= 0x3; /* CODE SET == UTF-8 */
@@ -474,7 +408,6 @@ check_scsi_name:
/* Header size + Designation descriptor */
len += (scsi_name_len + 4);
}
-set_len:
buf[2] = ((len >> 8) & 0xff);
buf[3] = (len & 0xff); /* Page Length for VPD 0x83 */
return 0;
@@ -484,9 +417,6 @@ set_len:
static int
target_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf)
{
- if (cmd->data_length < 60)
- return 0;
-
buf[3] = 0x3c;
/* Set HEADSUP, ORDSUP, SIMPSUP */
buf[5] = 0x07;
@@ -512,20 +442,6 @@ target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws)
have_tp = 1;
- if (cmd->data_length < (0x10 + 4)) {
- pr_debug("Received data_length: %u"
- " too small for EVPD 0xb0\n",
- cmd->data_length);
- return -EINVAL;
- }
-
- if (have_tp && cmd->data_length < (0x3c + 4)) {
- pr_debug("Received data_length: %u"
- " too small for TPE=1 EVPD 0xb0\n",
- cmd->data_length);
- have_tp = 0;
- }
-
buf[0] = dev->transport->get_device_type(dev);
buf[3] = have_tp ? 0x3c : 0x10;
@@ -540,7 +456,7 @@ target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
/*
* Set MAXIMUM TRANSFER LENGTH
*/
- put_unaligned_be32(dev->se_sub_dev->se_dev_attrib.max_sectors, &buf[8]);
+ put_unaligned_be32(dev->se_sub_dev->se_dev_attrib.fabric_max_sectors, &buf[8]);
/*
* Set OPTIMAL TRANSFER LENGTH
@@ -548,10 +464,9 @@ target_emulate_evpd_b0(struct se_cmd *cmd, unsigned char *buf)
put_unaligned_be32(dev->se_sub_dev->se_dev_attrib.optimal_sectors, &buf[12]);
/*
- * Exit now if we don't support TP or the initiator sent a too
- * short buffer.
+ * Exit now if we don't support TP.
*/
- if (!have_tp || cmd->data_length < (0x3c + 4))
+ if (!have_tp)
return 0;
/*
@@ -589,10 +504,7 @@ target_emulate_evpd_b1(struct se_cmd *cmd, unsigned char *buf)
buf[0] = dev->transport->get_device_type(dev);
buf[3] = 0x3c;
-
- if (cmd->data_length >= 5 &&
- dev->se_sub_dev->se_dev_attrib.is_nonrot)
- buf[5] = 1;
+ buf[5] = dev->se_sub_dev->se_dev_attrib.is_nonrot ? 1 : 0;
return 0;
}
@@ -671,8 +583,6 @@ target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
{
int p;
- if (cmd->data_length < 8)
- return 0;
/*
* Only report the INQUIRY EVPD=1 pages after a valid NAA
* Registered Extended LUN WWN has been set via ConfigFS
@@ -681,8 +591,7 @@ target_emulate_evpd_00(struct se_cmd *cmd, unsigned char *buf)
if (cmd->se_dev->se_sub_dev->su_dev_flags &
SDF_EMULATED_VPD_UNIT_SERIAL) {
buf[3] = ARRAY_SIZE(evpd_handlers);
- for (p = 0; p < min_t(int, ARRAY_SIZE(evpd_handlers),
- cmd->data_length - 4); ++p)
+ for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p)
buf[p + 4] = evpd_handlers[p].page;
}
@@ -693,45 +602,54 @@ int target_emulate_inquiry(struct se_task *task)
{
struct se_cmd *cmd = task->task_se_cmd;
struct se_device *dev = cmd->se_dev;
- unsigned char *buf;
+ struct se_portal_group *tpg = cmd->se_lun->lun_sep->sep_tpg;
+ unsigned char *buf, *map_buf;
unsigned char *cdb = cmd->t_task_cdb;
int p, ret;
+ map_buf = transport_kmap_data_sg(cmd);
+ /*
+ * If SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC is not set, then we
+ * know we actually allocated a full page. Otherwise, if the
+ * data buffer is too small, allocate a temporary buffer so we
+ * don't have to worry about overruns in all our INQUIRY
+ * emulation handling.
+ */
+ if (cmd->data_length < SE_INQUIRY_BUF &&
+ (cmd->se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC)) {
+ buf = kzalloc(SE_INQUIRY_BUF, GFP_KERNEL);
+ if (!buf) {
+ transport_kunmap_data_sg(cmd);
+ cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
+ return -ENOMEM;
+ }
+ } else {
+ buf = map_buf;
+ }
+
+ if (dev == tpg->tpg_virt_lun0.lun_se_dev)
+ buf[0] = 0x3f; /* Not connected */
+ else
+ buf[0] = dev->transport->get_device_type(dev);
+
if (!(cdb[1] & 0x1)) {
if (cdb[2]) {
pr_err("INQUIRY with EVPD==0 but PAGE CODE=%02x\n",
cdb[2]);
cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
- return -EINVAL;
+ ret = -EINVAL;
+ goto out;
}
- ret = target_emulate_inquiry_std(cmd);
+ ret = target_emulate_inquiry_std(cmd, buf);
goto out;
}
- /*
- * Make sure we at least have 4 bytes of INQUIRY response
- * payload for 0x00 going back for EVPD=1. Note that 0x80
- * and 0x83 will check for enough payload data length and
- * jump to set_len: label when there is not enough inquiry EVPD
- * payload length left for the next outgoing EVPD metadata
- */
- if (cmd->data_length < 4) {
- pr_err("SCSI Inquiry payload length: %u"
- " too small for EVPD=1\n", cmd->data_length);
- cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
- return -EINVAL;
- }
-
- buf = transport_kmap_data_sg(cmd);
-
- buf[0] = dev->transport->get_device_type(dev);
-
for (p = 0; p < ARRAY_SIZE(evpd_handlers); ++p) {
if (cdb[2] == evpd_handlers[p].page) {
buf[1] = cdb[2];
ret = evpd_handlers[p].emulate(cmd, buf);
- goto out_unmap;
+ goto out;
}
}
@@ -739,9 +657,13 @@ int target_emulate_inquiry(struct se_task *task)
cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD;
ret = -EINVAL;
-out_unmap:
- transport_kunmap_data_sg(cmd);
out:
+ if (buf != map_buf) {
+ memcpy(map_buf, buf, cmd->data_length);
+ kfree(buf);
+ }
+ transport_kunmap_data_sg(cmd);
+
if (!ret) {
task->task_scsi_status = GOOD;
transport_complete_task(task, 1);
@@ -772,11 +694,6 @@ int target_emulate_readcapacity(struct se_task *task)
buf[5] = (dev->se_sub_dev->se_dev_attrib.block_size >> 16) & 0xff;
buf[6] = (dev->se_sub_dev->se_dev_attrib.block_size >> 8) & 0xff;
buf[7] = dev->se_sub_dev->se_dev_attrib.block_size & 0xff;
- /*
- * Set max 32-bit blocks to signal SERVICE ACTION READ_CAPACITY_16
- */
- if (dev->se_sub_dev->se_dev_attrib.emulate_tpu || dev->se_sub_dev->se_dev_attrib.emulate_tpws)
- put_unaligned_be32(0xFFFFFFFF, &buf[0]);
transport_kunmap_data_sg(cmd);
diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c
index 6e043eeb1db9..cbb66537d230 100644
--- a/drivers/target/target_core_configfs.c
+++ b/drivers/target/target_core_configfs.c
@@ -52,8 +52,8 @@
extern struct t10_alua_lu_gp *default_lu_gp;
-static struct list_head g_tf_list;
-static struct mutex g_tf_lock;
+static LIST_HEAD(g_tf_list);
+static DEFINE_MUTEX(g_tf_lock);
struct target_core_configfs_attribute {
struct configfs_attribute attr;
@@ -421,18 +421,6 @@ static int target_fabric_tf_ops_check(
pr_err("Missing tfo->close_session()\n");
return -EINVAL;
}
- if (!tfo->stop_session) {
- pr_err("Missing tfo->stop_session()\n");
- return -EINVAL;
- }
- if (!tfo->fall_back_to_erl0) {
- pr_err("Missing tfo->fall_back_to_erl0()\n");
- return -EINVAL;
- }
- if (!tfo->sess_logged_in) {
- pr_err("Missing tfo->sess_logged_in()\n");
- return -EINVAL;
- }
if (!tfo->sess_get_index) {
pr_err("Missing tfo->sess_get_index()\n");
return -EINVAL;
@@ -477,10 +465,6 @@ static int target_fabric_tf_ops_check(
pr_err("Missing tfo->get_fabric_sense_len()\n");
return -EINVAL;
}
- if (!tfo->is_state_remove) {
- pr_err("Missing tfo->is_state_remove()\n");
- return -EINVAL;
- }
/*
* We at least require tfo->fabric_make_wwn(), tfo->fabric_drop_wwn()
* tfo->fabric_make_tpg() and tfo->fabric_drop_tpg() in
@@ -702,6 +686,9 @@ SE_DEV_ATTR_RO(hw_max_sectors);
DEF_DEV_ATTRIB(max_sectors);
SE_DEV_ATTR(max_sectors, S_IRUGO | S_IWUSR);
+DEF_DEV_ATTRIB(fabric_max_sectors);
+SE_DEV_ATTR(fabric_max_sectors, S_IRUGO | S_IWUSR);
+
DEF_DEV_ATTRIB(optimal_sectors);
SE_DEV_ATTR(optimal_sectors, S_IRUGO | S_IWUSR);
@@ -741,6 +728,7 @@ static struct configfs_attribute *target_core_dev_attrib_attrs[] = {
&target_core_dev_attrib_block_size.attr,
&target_core_dev_attrib_hw_max_sectors.attr,
&target_core_dev_attrib_max_sectors.attr,
+ &target_core_dev_attrib_fabric_max_sectors.attr,
&target_core_dev_attrib_optimal_sectors.attr,
&target_core_dev_attrib_hw_queue_depth.attr,
&target_core_dev_attrib_queue_depth.attr,
@@ -2304,7 +2292,7 @@ static ssize_t target_core_alua_tg_pt_gp_store_attr_alua_access_state(
if (!(tg_pt_gp->tg_pt_gp_alua_access_type & TPGS_IMPLICT_ALUA)) {
pr_err("Unable to process implict configfs ALUA"
- " transition while TPGS_IMPLICT_ALUA is diabled\n");
+ " transition while TPGS_IMPLICT_ALUA is disabled\n");
return -EINVAL;
}
@@ -2865,7 +2853,6 @@ static void target_core_drop_subdev(
struct se_subsystem_dev *se_dev = container_of(to_config_group(item),
struct se_subsystem_dev, se_dev_group);
struct se_hba *hba;
- struct se_subsystem_api *t;
struct config_item *df_item;
struct config_group *dev_cg, *tg_pt_gp_cg, *dev_stat_grp;
int i;
@@ -2873,7 +2860,6 @@ static void target_core_drop_subdev(
hba = item_to_hba(&se_dev->se_dev_hba->hba_group.cg_item);
mutex_lock(&hba->hba_access_mutex);
- t = hba->transport;
dev_stat_grp = &se_dev->dev_stat_grps.stat_group;
for (i = 0; dev_stat_grp->default_groups[i]; i++) {
@@ -3117,8 +3103,6 @@ static int __init target_core_init_configfs(void)
config_group_init(&subsys->su_group);
mutex_init(&subsys->su_mutex);
- INIT_LIST_HEAD(&g_tf_list);
- mutex_init(&g_tf_lock);
ret = init_se_kmem_caches();
if (ret < 0)
return ret;
diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c
index edbcabbf85f7..aa6267746383 100644
--- a/drivers/target/target_core_device.c
+++ b/drivers/target/target_core_device.c
@@ -72,7 +72,7 @@ int transport_lookup_cmd_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
}
spin_lock_irqsave(&se_sess->se_node_acl->device_list_lock, flags);
- se_cmd->se_deve = &se_sess->se_node_acl->device_list[unpacked_lun];
+ se_cmd->se_deve = se_sess->se_node_acl->device_list[unpacked_lun];
if (se_cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
struct se_dev_entry *deve = se_cmd->se_deve;
@@ -159,13 +159,8 @@ int transport_lookup_cmd_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
dev->read_bytes += se_cmd->data_length;
spin_unlock_irqrestore(&dev->stats_lock, flags);
- /*
- * Add the iscsi_cmd_t to the struct se_lun's cmd list. This list is used
- * for tracking state of struct se_cmds during LUN shutdown events.
- */
spin_lock_irqsave(&se_lun->lun_cmd_lock, flags);
list_add_tail(&se_cmd->se_lun_node, &se_lun->lun_cmd_list);
- atomic_set(&se_cmd->transport_lun_active, 1);
spin_unlock_irqrestore(&se_lun->lun_cmd_lock, flags);
return 0;
@@ -187,7 +182,7 @@ int transport_lookup_tmr_lun(struct se_cmd *se_cmd, u32 unpacked_lun)
}
spin_lock_irqsave(&se_sess->se_node_acl->device_list_lock, flags);
- se_cmd->se_deve = &se_sess->se_node_acl->device_list[unpacked_lun];
+ se_cmd->se_deve = se_sess->se_node_acl->device_list[unpacked_lun];
deve = se_cmd->se_deve;
if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {
@@ -245,7 +240,7 @@ struct se_dev_entry *core_get_se_deve_from_rtpi(
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
@@ -291,7 +286,7 @@ int core_free_device_list_for_node(
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
@@ -311,7 +306,7 @@ int core_free_device_list_for_node(
}
spin_unlock_irq(&nacl->device_list_lock);
- kfree(nacl->device_list);
+ array_free(nacl->device_list, TRANSPORT_MAX_LUNS_PER_TPG);
nacl->device_list = NULL;
return 0;
@@ -323,7 +318,7 @@ void core_dec_lacl_count(struct se_node_acl *se_nacl, struct se_cmd *se_cmd)
unsigned long flags;
spin_lock_irqsave(&se_nacl->device_list_lock, flags);
- deve = &se_nacl->device_list[se_cmd->orig_fe_lun];
+ deve = se_nacl->device_list[se_cmd->orig_fe_lun];
deve->deve_cmds--;
spin_unlock_irqrestore(&se_nacl->device_list_lock, flags);
}
@@ -336,7 +331,7 @@ void core_update_device_list_access(
struct se_dev_entry *deve;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[mapped_lun];
+ deve = nacl->device_list[mapped_lun];
if (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) {
deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_ONLY;
deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_WRITE;
@@ -361,7 +356,7 @@ int core_update_device_list_for_node(
int enable)
{
struct se_port *port = lun->lun_sep;
- struct se_dev_entry *deve = &nacl->device_list[mapped_lun];
+ struct se_dev_entry *deve = nacl->device_list[mapped_lun];
int trans = 0;
/*
* If the MappedLUN entry is being disabled, the entry in
@@ -475,7 +470,7 @@ void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg)
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (lun != deve->se_lun)
continue;
spin_unlock_irq(&nacl->device_list_lock);
@@ -652,12 +647,13 @@ int target_report_luns(struct se_task *se_task)
{
struct se_cmd *se_cmd = se_task->task_se_cmd;
struct se_dev_entry *deve;
- struct se_lun *se_lun;
struct se_session *se_sess = se_cmd->se_sess;
unsigned char *buf;
- u32 cdb_offset = 0, lun_count = 0, offset = 8, i;
+ u32 lun_count = 0, offset = 8, i;
- buf = (unsigned char *) transport_kmap_data_sg(se_cmd);
+ buf = transport_kmap_data_sg(se_cmd);
+ if (!buf)
+ return -ENOMEM;
/*
* If no struct se_session pointer is present, this struct se_cmd is
@@ -672,22 +668,20 @@ int target_report_luns(struct se_task *se_task)
spin_lock_irq(&se_sess->se_node_acl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &se_sess->se_node_acl->device_list[i];
+ deve = se_sess->se_node_acl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
- se_lun = deve->se_lun;
/*
* We determine the correct LUN LIST LENGTH even once we
* have reached the initial allocation length.
* See SPC2-R20 7.19.
*/
lun_count++;
- if ((cdb_offset + 8) >= se_cmd->data_length)
+ if ((offset + 8) > se_cmd->data_length)
continue;
int_to_scsilun(deve->mapped_lun, (struct scsi_lun *)&buf[offset]);
offset += 8;
- cdb_offset += 8;
}
spin_unlock_irq(&se_sess->se_node_acl->device_list_lock);
@@ -695,12 +689,12 @@ int target_report_luns(struct se_task *se_task)
* See SPC3 r07, page 159.
*/
done:
- transport_kunmap_data_sg(se_cmd);
lun_count *= 8;
buf[0] = ((lun_count >> 24) & 0xff);
buf[1] = ((lun_count >> 16) & 0xff);
buf[2] = ((lun_count >> 8) & 0xff);
buf[3] = (lun_count & 0xff);
+ transport_kunmap_data_sg(se_cmd);
se_task->task_scsi_status = GOOD;
transport_complete_task(se_task, 1);
@@ -894,10 +888,15 @@ void se_dev_set_default_attribs(
limits->logical_block_size);
dev->se_sub_dev->se_dev_attrib.max_sectors = limits->max_sectors;
/*
- * Set optimal_sectors from max_sectors, which can be lowered via
- * configfs.
+ * Set fabric_max_sectors, which is reported in block limits
+ * VPD page (B0h).
*/
- dev->se_sub_dev->se_dev_attrib.optimal_sectors = limits->max_sectors;
+ dev->se_sub_dev->se_dev_attrib.fabric_max_sectors = DA_FABRIC_MAX_SECTORS;
+ /*
+ * Set optimal_sectors from fabric_max_sectors, which can be
+ * lowered via configfs.
+ */
+ dev->se_sub_dev->se_dev_attrib.optimal_sectors = DA_FABRIC_MAX_SECTORS;
/*
* queue_depth is based on subsystem plugin dependent requirements.
*/
@@ -1229,6 +1228,54 @@ int se_dev_set_max_sectors(struct se_device *dev, u32 max_sectors)
return 0;
}
+int se_dev_set_fabric_max_sectors(struct se_device *dev, u32 fabric_max_sectors)
+{
+ if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
+ pr_err("dev[%p]: Unable to change SE Device"
+ " fabric_max_sectors while dev_export_obj: %d count exists\n",
+ dev, atomic_read(&dev->dev_export_obj.obj_access_count));
+ return -EINVAL;
+ }
+ if (!fabric_max_sectors) {
+ pr_err("dev[%p]: Illegal ZERO value for"
+ " fabric_max_sectors\n", dev);
+ return -EINVAL;
+ }
+ if (fabric_max_sectors < DA_STATUS_MAX_SECTORS_MIN) {
+ pr_err("dev[%p]: Passed fabric_max_sectors: %u less than"
+ " DA_STATUS_MAX_SECTORS_MIN: %u\n", dev, fabric_max_sectors,
+ DA_STATUS_MAX_SECTORS_MIN);
+ return -EINVAL;
+ }
+ if (dev->transport->transport_type == TRANSPORT_PLUGIN_PHBA_PDEV) {
+ if (fabric_max_sectors > dev->se_sub_dev->se_dev_attrib.hw_max_sectors) {
+ pr_err("dev[%p]: Passed fabric_max_sectors: %u"
+ " greater than TCM/SE_Device max_sectors:"
+ " %u\n", dev, fabric_max_sectors,
+ dev->se_sub_dev->se_dev_attrib.hw_max_sectors);
+ return -EINVAL;
+ }
+ } else {
+ if (fabric_max_sectors > DA_STATUS_MAX_SECTORS_MAX) {
+ pr_err("dev[%p]: Passed fabric_max_sectors: %u"
+ " greater than DA_STATUS_MAX_SECTORS_MAX:"
+ " %u\n", dev, fabric_max_sectors,
+ DA_STATUS_MAX_SECTORS_MAX);
+ return -EINVAL;
+ }
+ }
+ /*
+ * Align max_sectors down to PAGE_SIZE to follow transport_allocate_data_tasks()
+ */
+ fabric_max_sectors = se_dev_align_max_sectors(fabric_max_sectors,
+ dev->se_sub_dev->se_dev_attrib.block_size);
+
+ dev->se_sub_dev->se_dev_attrib.fabric_max_sectors = fabric_max_sectors;
+ pr_debug("dev[%p]: SE Device max_sectors changed to %u\n",
+ dev, fabric_max_sectors);
+ return 0;
+}
+
int se_dev_set_optimal_sectors(struct se_device *dev, u32 optimal_sectors)
{
if (atomic_read(&dev->dev_export_obj.obj_access_count)) {
@@ -1242,10 +1289,10 @@ int se_dev_set_optimal_sectors(struct se_device *dev, u32 optimal_sectors)
" changed for TCM/pSCSI\n", dev);
return -EINVAL;
}
- if (optimal_sectors > dev->se_sub_dev->se_dev_attrib.max_sectors) {
+ if (optimal_sectors > dev->se_sub_dev->se_dev_attrib.fabric_max_sectors) {
pr_err("dev[%p]: Passed optimal_sectors %u cannot be"
- " greater than max_sectors: %u\n", dev,
- optimal_sectors, dev->se_sub_dev->se_dev_attrib.max_sectors);
+ " greater than fabric_max_sectors: %u\n", dev,
+ optimal_sectors, dev->se_sub_dev->se_dev_attrib.fabric_max_sectors);
return -EINVAL;
}
@@ -1380,7 +1427,7 @@ struct se_lun *core_get_lun_from_tpg(struct se_portal_group *tpg, u32 unpacked_l
spin_unlock(&tpg->tpg_lun_lock);
return NULL;
}
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status != TRANSPORT_LUN_STATUS_FREE) {
pr_err("%s Logical Unit Number: %u is not free on"
@@ -1413,7 +1460,7 @@ static struct se_lun *core_dev_get_lun(struct se_portal_group *tpg, u32 unpacked
spin_unlock(&tpg->tpg_lun_lock);
return NULL;
}
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) {
pr_err("%s Logical Unit Number: %u is not active on"
diff --git a/drivers/target/target_core_fabric_configfs.c b/drivers/target/target_core_fabric_configfs.c
index 9a2ce11e1a6e..405cc98eaed6 100644
--- a/drivers/target/target_core_fabric_configfs.c
+++ b/drivers/target/target_core_fabric_configfs.c
@@ -108,7 +108,7 @@ static int target_fabric_mappedlun_link(
* tpg_1/attrib/demo_mode_write_protect=1
*/
spin_lock_irq(&lacl->se_lun_nacl->device_list_lock);
- deve = &lacl->se_lun_nacl->device_list[lacl->mapped_lun];
+ deve = lacl->se_lun_nacl->device_list[lacl->mapped_lun];
if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS)
lun_access = deve->lun_flags;
else
@@ -137,7 +137,7 @@ static int target_fabric_mappedlun_unlink(
struct se_lun_acl *lacl = container_of(to_config_group(lun_acl_ci),
struct se_lun_acl, se_lun_group);
struct se_node_acl *nacl = lacl->se_lun_nacl;
- struct se_dev_entry *deve = &nacl->device_list[lacl->mapped_lun];
+ struct se_dev_entry *deve = nacl->device_list[lacl->mapped_lun];
struct se_portal_group *se_tpg;
/*
* Determine if the underlying MappedLUN has already been released..
@@ -168,7 +168,7 @@ static ssize_t target_fabric_mappedlun_show_write_protect(
ssize_t len;
spin_lock_irq(&se_nacl->device_list_lock);
- deve = &se_nacl->device_list[lacl->mapped_lun];
+ deve = se_nacl->device_list[lacl->mapped_lun];
len = sprintf(page, "%d\n",
(deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) ?
1 : 0);
diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c
index 8572eae62da7..2ec299e8a73e 100644
--- a/drivers/target/target_core_iblock.c
+++ b/drivers/target/target_core_iblock.c
@@ -46,6 +46,9 @@
#include "target_core_iblock.h"
+#define IBLOCK_MAX_BIO_PER_TASK 32 /* max # of bios to submit at a time */
+#define IBLOCK_BIO_POOL_SIZE 128
+
static struct se_subsystem_api iblock_template;
static void iblock_bio_done(struct bio *, int);
@@ -56,51 +59,25 @@ static void iblock_bio_done(struct bio *, int);
*/
static int iblock_attach_hba(struct se_hba *hba, u32 host_id)
{
- struct iblock_hba *ib_host;
-
- ib_host = kzalloc(sizeof(struct iblock_hba), GFP_KERNEL);
- if (!ib_host) {
- pr_err("Unable to allocate memory for"
- " struct iblock_hba\n");
- return -ENOMEM;
- }
-
- ib_host->iblock_host_id = host_id;
-
- hba->hba_ptr = ib_host;
-
pr_debug("CORE_HBA[%d] - TCM iBlock HBA Driver %s on"
" Generic Target Core Stack %s\n", hba->hba_id,
IBLOCK_VERSION, TARGET_CORE_MOD_VERSION);
-
- pr_debug("CORE_HBA[%d] - Attached iBlock HBA: %u to Generic\n",
- hba->hba_id, ib_host->iblock_host_id);
-
return 0;
}
static void iblock_detach_hba(struct se_hba *hba)
{
- struct iblock_hba *ib_host = hba->hba_ptr;
-
- pr_debug("CORE_HBA[%d] - Detached iBlock HBA: %u from Generic"
- " Target Core\n", hba->hba_id, ib_host->iblock_host_id);
-
- kfree(ib_host);
- hba->hba_ptr = NULL;
}
static void *iblock_allocate_virtdevice(struct se_hba *hba, const char *name)
{
struct iblock_dev *ib_dev = NULL;
- struct iblock_hba *ib_host = hba->hba_ptr;
ib_dev = kzalloc(sizeof(struct iblock_dev), GFP_KERNEL);
if (!ib_dev) {
pr_err("Unable to allocate struct iblock_dev\n");
return NULL;
}
- ib_dev->ibd_host = ib_host;
pr_debug( "IBLOCK: Allocated ib_dev for %s\n", name);
@@ -126,10 +103,8 @@ static struct se_device *iblock_create_virtdevice(
return ERR_PTR(ret);
}
memset(&dev_limits, 0, sizeof(struct se_dev_limits));
- /*
- * These settings need to be made tunable..
- */
- ib_dev->ibd_bio_set = bioset_create(32, 0);
+
+ ib_dev->ibd_bio_set = bioset_create(IBLOCK_BIO_POOL_SIZE, 0);
if (!ib_dev->ibd_bio_set) {
pr_err("IBLOCK: Unable to create bioset()\n");
return ERR_PTR(-ENOMEM);
@@ -155,8 +130,8 @@ static struct se_device *iblock_create_virtdevice(
q = bdev_get_queue(bd);
limits = &dev_limits.limits;
limits->logical_block_size = bdev_logical_block_size(bd);
- limits->max_hw_sectors = queue_max_hw_sectors(q);
- limits->max_sectors = queue_max_sectors(q);
+ limits->max_hw_sectors = UINT_MAX;
+ limits->max_sectors = UINT_MAX;
dev_limits.hw_queue_depth = q->nr_requests;
dev_limits.queue_depth = q->nr_requests;
@@ -230,7 +205,7 @@ iblock_alloc_task(unsigned char *cdb)
return NULL;
}
- atomic_set(&ib_req->ib_bio_cnt, 0);
+ atomic_set(&ib_req->pending, 1);
return &ib_req->ib_task;
}
@@ -510,24 +485,35 @@ iblock_get_bio(struct se_task *task, sector_t lba, u32 sg_num)
bio->bi_destructor = iblock_bio_destructor;
bio->bi_end_io = &iblock_bio_done;
bio->bi_sector = lba;
- atomic_inc(&ib_req->ib_bio_cnt);
+ atomic_inc(&ib_req->pending);
pr_debug("Set bio->bi_sector: %llu\n", (unsigned long long)bio->bi_sector);
- pr_debug("Set ib_req->ib_bio_cnt: %d\n",
- atomic_read(&ib_req->ib_bio_cnt));
+ pr_debug("Set ib_req->pending: %d\n", atomic_read(&ib_req->pending));
return bio;
}
+static void iblock_submit_bios(struct bio_list *list, int rw)
+{
+ struct blk_plug plug;
+ struct bio *bio;
+
+ blk_start_plug(&plug);
+ while ((bio = bio_list_pop(list)))
+ submit_bio(rw, bio);
+ blk_finish_plug(&plug);
+}
+
static int iblock_do_task(struct se_task *task)
{
struct se_cmd *cmd = task->task_se_cmd;
struct se_device *dev = cmd->se_dev;
+ struct iblock_req *ibr = IBLOCK_REQ(task);
struct bio *bio;
struct bio_list list;
struct scatterlist *sg;
u32 i, sg_num = task->task_sg_nents;
sector_t block_lba;
- struct blk_plug plug;
+ unsigned bio_cnt;
int rw;
if (task->task_data_direction == DMA_TO_DEVICE) {
@@ -572,6 +558,7 @@ static int iblock_do_task(struct se_task *task)
bio_list_init(&list);
bio_list_add(&list, bio);
+ bio_cnt = 1;
for_each_sg(task->task_sg, sg, task->task_sg_nents, i) {
/*
@@ -581,10 +568,16 @@ static int iblock_do_task(struct se_task *task)
*/
while (bio_add_page(bio, sg_page(sg), sg->length, sg->offset)
!= sg->length) {
+ if (bio_cnt >= IBLOCK_MAX_BIO_PER_TASK) {
+ iblock_submit_bios(&list, rw);
+ bio_cnt = 0;
+ }
+
bio = iblock_get_bio(task, block_lba, sg_num);
if (!bio)
goto fail;
bio_list_add(&list, bio);
+ bio_cnt++;
}
/* Always in 512 byte units for Linux/Block */
@@ -592,11 +585,12 @@ static int iblock_do_task(struct se_task *task)
sg_num--;
}
- blk_start_plug(&plug);
- while ((bio = bio_list_pop(&list)))
- submit_bio(rw, bio);
- blk_finish_plug(&plug);
+ iblock_submit_bios(&list, rw);
+ if (atomic_dec_and_test(&ibr->pending)) {
+ transport_complete_task(task,
+ !atomic_read(&ibr->ib_bio_err_cnt));
+ }
return 0;
fail:
@@ -648,7 +642,7 @@ static void iblock_bio_done(struct bio *bio, int err)
bio_put(bio);
- if (!atomic_dec_and_test(&ibr->ib_bio_cnt))
+ if (!atomic_dec_and_test(&ibr->pending))
return;
pr_debug("done[%p] bio: %p task_lba: %llu bio_lba: %llu err=%d\n",
diff --git a/drivers/target/target_core_iblock.h b/drivers/target/target_core_iblock.h
index 5cf1860c10d0..e929370b6fd3 100644
--- a/drivers/target/target_core_iblock.h
+++ b/drivers/target/target_core_iblock.h
@@ -8,7 +8,7 @@
struct iblock_req {
struct se_task ib_task;
- atomic_t ib_bio_cnt;
+ atomic_t pending;
atomic_t ib_bio_err_cnt;
} ____cacheline_aligned;
@@ -19,11 +19,6 @@ struct iblock_dev {
u32 ibd_flags;
struct bio_set *ibd_bio_set;
struct block_device *ibd_bd;
- struct iblock_hba *ibd_host;
-} ____cacheline_aligned;
-
-struct iblock_hba {
- int iblock_host_id;
} ____cacheline_aligned;
#endif /* TARGET_CORE_IBLOCK_H */
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h
index 45001364788a..21c05638f158 100644
--- a/drivers/target/target_core_internal.h
+++ b/drivers/target/target_core_internal.h
@@ -53,6 +53,7 @@ int se_dev_set_is_nonrot(struct se_device *, int);
int se_dev_set_emulate_rest_reord(struct se_device *dev, int);
int se_dev_set_queue_depth(struct se_device *, u32);
int se_dev_set_max_sectors(struct se_device *, u32);
+int se_dev_set_fabric_max_sectors(struct se_device *, u32);
int se_dev_set_optimal_sectors(struct se_device *, u32);
int se_dev_set_block_size(struct se_device *, u32);
struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_hba *,
@@ -75,6 +76,8 @@ struct se_hba *core_alloc_hba(const char *, u32, u32);
int core_delete_hba(struct se_hba *);
/* target_core_tmr.c */
+void core_tmr_abort_task(struct se_device *, struct se_tmr_req *,
+ struct se_session *);
int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *,
struct list_head *, struct se_cmd *);
diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c
index 63e703bb6ac9..86f0c3b5d500 100644
--- a/drivers/target/target_core_pr.c
+++ b/drivers/target/target_core_pr.c
@@ -338,7 +338,7 @@ static int core_scsi3_pr_seq_non_holder(
return core_scsi2_reservation_seq_non_holder(cmd,
cdb, pr_reg_type);
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Determine if the registration should be ignored due to
* non-matching ISIDs in core_scsi3_pr_reservation_check().
@@ -1000,7 +1000,7 @@ int core_scsi3_check_aptpl_registration(
{
struct se_subsystem_dev *su_dev = dev->se_sub_dev;
struct se_node_acl *nacl = lun_acl->se_lun_nacl;
- struct se_dev_entry *deve = &nacl->device_list[lun_acl->mapped_lun];
+ struct se_dev_entry *deve = nacl->device_list[lun_acl->mapped_lun];
if (su_dev->t10_pr.res_type != SPC3_PERSISTENT_RESERVATIONS)
return 0;
@@ -1497,7 +1497,7 @@ static int core_scsi3_decode_spec_i_port(
struct se_dev_entry *dest_se_deve = NULL, *local_se_deve;
struct t10_pr_registration *dest_pr_reg, *local_pr_reg, *pr_reg_e;
struct t10_pr_registration *pr_reg_tmp, *pr_reg_tmp_safe;
- struct list_head tid_dest_list;
+ LIST_HEAD(tid_dest_list);
struct pr_transport_id_holder *tidh_new, *tidh, *tidh_tmp;
struct target_core_fabric_ops *tmp_tf_ops;
unsigned char *buf;
@@ -1508,9 +1508,8 @@ static int core_scsi3_decode_spec_i_port(
u32 dest_rtpi = 0;
memset(dest_iport, 0, 64);
- INIT_LIST_HEAD(&tid_dest_list);
- local_se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ local_se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Allocate a struct pr_transport_id_holder and setup the
* local_node_acl and local_se_deve pointers and add to
@@ -2127,7 +2126,7 @@ static int core_scsi3_emulate_pro_register(
return -EINVAL;
}
se_tpg = se_sess->se_tpg;
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
+ se_deve = se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
if (se_tpg->se_tpg_tfo->sess_get_initiator_sid) {
memset(&isid_buf[0], 0, PR_REG_ISID_LEN);
@@ -2427,9 +2426,7 @@ static int core_scsi3_pro_reserve(
u64 res_key)
{
struct se_session *se_sess = cmd->se_sess;
- struct se_dev_entry *se_deve;
struct se_lun *se_lun = cmd->se_lun;
- struct se_portal_group *se_tpg;
struct t10_pr_registration *pr_reg, *pr_res_holder;
struct t10_reservation *pr_tmpl = &dev->se_sub_dev->t10_pr;
char i_buf[PR_REG_ISID_ID_LEN];
@@ -2442,8 +2439,6 @@ static int core_scsi3_pro_reserve(
cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
return -EINVAL;
}
- se_tpg = se_sess->se_tpg;
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Locate the existing *pr_reg via struct se_node_acl pointers
*/
@@ -3001,10 +2996,9 @@ static int core_scsi3_pro_preempt(
int abort)
{
struct se_device *dev = cmd->se_dev;
- struct se_dev_entry *se_deve;
struct se_node_acl *pr_reg_nacl;
struct se_session *se_sess = cmd->se_sess;
- struct list_head preempt_and_abort_list;
+ LIST_HEAD(preempt_and_abort_list);
struct t10_pr_registration *pr_reg, *pr_reg_tmp, *pr_reg_n, *pr_res_holder;
struct t10_reservation *pr_tmpl = &dev->se_sub_dev->t10_pr;
u32 pr_res_mapped_lun = 0;
@@ -3016,7 +3010,6 @@ static int core_scsi3_pro_preempt(
return -EINVAL;
}
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
pr_reg_n = core_scsi3_locate_pr_reg(cmd->se_dev, se_sess->se_node_acl,
se_sess);
if (!pr_reg_n) {
@@ -3037,7 +3030,6 @@ static int core_scsi3_pro_preempt(
cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;
return -EINVAL;
}
- INIT_LIST_HEAD(&preempt_and_abort_list);
spin_lock(&dev->dev_reservation_lock);
pr_res_holder = dev->dev_pr_res_holder;
@@ -3353,7 +3345,7 @@ static int core_scsi3_emulate_pro_register_and_move(
{
struct se_session *se_sess = cmd->se_sess;
struct se_device *dev = cmd->se_dev;
- struct se_dev_entry *se_deve, *dest_se_deve = NULL;
+ struct se_dev_entry *dest_se_deve = NULL;
struct se_lun *se_lun = cmd->se_lun;
struct se_node_acl *pr_res_nacl, *pr_reg_nacl, *dest_node_acl = NULL;
struct se_port *se_port;
@@ -3378,7 +3370,6 @@ static int core_scsi3_emulate_pro_register_and_move(
memset(i_buf, 0, PR_REG_ISID_ID_LEN);
se_tpg = se_sess->se_tpg;
tf_ops = se_tpg->se_tpg_tfo;
- se_deve = &se_sess->se_node_acl->device_list[cmd->orig_fe_lun];
/*
* Follow logic from spc4r17 Section 5.7.8, Table 50 --
* Register behaviors for a REGISTER AND MOVE service action
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 8d4def30e9e8..94c905fcbceb 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -69,7 +69,7 @@ static int pscsi_attach_hba(struct se_hba *hba, u32 host_id)
return -ENOMEM;
}
phv->phv_host_id = host_id;
- phv->phv_mode = PHV_VIRUTAL_HOST_ID;
+ phv->phv_mode = PHV_VIRTUAL_HOST_ID;
hba->hba_ptr = phv;
@@ -114,7 +114,7 @@ static int pscsi_pmode_enable_hba(struct se_hba *hba, unsigned long mode_flag)
return 0;
phv->phv_lld_host = NULL;
- phv->phv_mode = PHV_VIRUTAL_HOST_ID;
+ phv->phv_mode = PHV_VIRTUAL_HOST_ID;
pr_debug("CORE_HBA[%d] - Disabled pSCSI HBA Passthrough"
" %s\n", hba->hba_id, (sh->hostt->name) ?
@@ -531,7 +531,7 @@ static struct se_device *pscsi_create_virtdevice(
return ERR_PTR(-ENODEV);
}
/*
- * For the newer PHV_VIRUTAL_HOST_ID struct scsi_device
+ * For the newer PHV_VIRTUAL_HOST_ID struct scsi_device
* reference, we enforce that udev_path has been set
*/
if (!(se_dev->su_dev_flags & SDF_USING_UDEV_PATH)) {
@@ -540,7 +540,7 @@ static struct se_device *pscsi_create_virtdevice(
return ERR_PTR(-EINVAL);
}
/*
- * If no scsi_host_id= was passed for PHV_VIRUTAL_HOST_ID,
+ * If no scsi_host_id= was passed for PHV_VIRTUAL_HOST_ID,
* use the original TCM hba ID to reference Linux/SCSI Host No
* and enable for PHV_LLD_SCSI_HOST_NO mode.
*/
@@ -569,8 +569,8 @@ static struct se_device *pscsi_create_virtdevice(
}
}
} else {
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID) {
- pr_err("pSCSI: PHV_VIRUTAL_HOST_ID set while"
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID) {
+ pr_err("pSCSI: PHV_VIRTUAL_HOST_ID set while"
" struct Scsi_Host exists\n");
return ERR_PTR(-EEXIST);
}
@@ -600,7 +600,7 @@ static struct se_device *pscsi_create_virtdevice(
}
if (!dev) {
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID)
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID)
scsi_host_put(sh);
else if (legacy_mode_enable) {
pscsi_pmode_enable_hba(hba, 0);
@@ -616,7 +616,7 @@ static struct se_device *pscsi_create_virtdevice(
pr_err("pSCSI: Unable to locate %d:%d:%d:%d\n", sh->host_no,
pdv->pdv_channel_id, pdv->pdv_target_id, pdv->pdv_lun_id);
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID)
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID)
scsi_host_put(sh);
else if (legacy_mode_enable) {
pscsi_pmode_enable_hba(hba, 0);
@@ -898,7 +898,7 @@ static ssize_t pscsi_show_configfs_dev_params(struct se_hba *hba,
ssize_t bl;
int i;
- if (phv->phv_mode == PHV_VIRUTAL_HOST_ID)
+ if (phv->phv_mode == PHV_VIRTUAL_HOST_ID)
snprintf(host_id, 16, "%d", pdv->pdv_host_id);
else
snprintf(host_id, 16, "PHBA Mode");
diff --git a/drivers/target/target_core_pscsi.h b/drivers/target/target_core_pscsi.h
index fdc17b6aefb3..43f1c419e8e5 100644
--- a/drivers/target/target_core_pscsi.h
+++ b/drivers/target/target_core_pscsi.h
@@ -49,7 +49,7 @@ struct pscsi_dev_virt {
} ____cacheline_aligned;
typedef enum phv_modes {
- PHV_VIRUTAL_HOST_ID,
+ PHV_VIRTUAL_HOST_ID,
PHV_LLD_SCSI_HOST_NO
} phv_modes_t;
diff --git a/drivers/target/target_core_stat.c b/drivers/target/target_core_stat.c
index f8c2d2cc3431..3d44beb0cf1f 100644
--- a/drivers/target/target_core_stat.c
+++ b/drivers/target/target_core_stat.c
@@ -954,7 +954,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_in_cmds(
{
struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
struct se_port *sep;
- struct se_portal_group *tpg;
ssize_t ret;
spin_lock(&lun->lun_sep_lock);
@@ -963,7 +962,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_in_cmds(
spin_unlock(&lun->lun_sep_lock);
return -ENODEV;
}
- tpg = sep->sep_tpg;
ret = snprintf(page, PAGE_SIZE, "%llu\n", sep->sep_stats.cmd_pdus);
spin_unlock(&lun->lun_sep_lock);
@@ -976,7 +974,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_write_mbytes(
{
struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
struct se_port *sep;
- struct se_portal_group *tpg;
ssize_t ret;
spin_lock(&lun->lun_sep_lock);
@@ -985,7 +982,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_write_mbytes(
spin_unlock(&lun->lun_sep_lock);
return -ENODEV;
}
- tpg = sep->sep_tpg;
ret = snprintf(page, PAGE_SIZE, "%u\n",
(u32)(sep->sep_stats.rx_data_octets >> 20));
@@ -999,7 +995,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_read_mbytes(
{
struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
struct se_port *sep;
- struct se_portal_group *tpg;
ssize_t ret;
spin_lock(&lun->lun_sep_lock);
@@ -1008,7 +1003,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_read_mbytes(
spin_unlock(&lun->lun_sep_lock);
return -ENODEV;
}
- tpg = sep->sep_tpg;
ret = snprintf(page, PAGE_SIZE, "%u\n",
(u32)(sep->sep_stats.tx_data_octets >> 20));
@@ -1022,7 +1016,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_hs_in_cmds(
{
struct se_lun *lun = container_of(pgrps, struct se_lun, port_stat_grps);
struct se_port *sep;
- struct se_portal_group *tpg;
ssize_t ret;
spin_lock(&lun->lun_sep_lock);
@@ -1031,7 +1024,6 @@ static ssize_t target_stat_scsi_tgt_port_show_attr_hs_in_cmds(
spin_unlock(&lun->lun_sep_lock);
return -ENODEV;
}
- tpg = sep->sep_tpg;
/* FIXME: scsiTgtPortHsInCommands */
ret = snprintf(page, PAGE_SIZE, "%u\n", 0);
@@ -1253,7 +1245,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_inst(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1275,16 +1267,14 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_dev(
struct se_node_acl *nacl = lacl->se_lun_nacl;
struct se_dev_entry *deve;
struct se_lun *lun;
- struct se_portal_group *tpg;
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
}
- tpg = nacl->se_tpg;
lun = deve->se_lun;
/* scsiDeviceIndex */
ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index);
@@ -1304,7 +1294,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_port(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1327,7 +1317,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_indx(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1349,7 +1339,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_dev_or_port(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1371,7 +1361,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_intr_name(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1393,7 +1383,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_map_indx(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1415,7 +1405,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_att_count(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1437,7 +1427,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_num_cmds(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1459,7 +1449,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_read_mbytes(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1481,7 +1471,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_write_mbytes(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1503,7 +1493,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_hs_num_cmds(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1525,7 +1515,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_creation_time(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1548,7 +1538,7 @@ static ssize_t target_stat_scsi_auth_intr_show_attr_row_status(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1621,7 +1611,7 @@ static ssize_t target_stat_scsi_att_intr_port_show_attr_inst(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1643,16 +1633,14 @@ static ssize_t target_stat_scsi_att_intr_port_show_attr_dev(
struct se_node_acl *nacl = lacl->se_lun_nacl;
struct se_dev_entry *deve;
struct se_lun *lun;
- struct se_portal_group *tpg;
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
}
- tpg = nacl->se_tpg;
lun = deve->se_lun;
/* scsiDeviceIndex */
ret = snprintf(page, PAGE_SIZE, "%u\n", lun->lun_se_dev->dev_index);
@@ -1672,7 +1660,7 @@ static ssize_t target_stat_scsi_att_intr_port_show_attr_port(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
@@ -1721,7 +1709,7 @@ static ssize_t target_stat_scsi_att_intr_port_show_attr_port_auth_indx(
ssize_t ret;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[lacl->mapped_lun];
+ deve = nacl->device_list[lacl->mapped_lun];
if (!deve->se_lun || !deve->se_lun_acl) {
spin_unlock_irq(&nacl->device_list_lock);
return -ENODEV;
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
index dcb0618c9388..f015839aef89 100644
--- a/drivers/target/target_core_tmr.c
+++ b/drivers/target/target_core_tmr.c
@@ -40,7 +40,7 @@
#include "target_core_alua.h"
#include "target_core_pr.h"
-struct se_tmr_req *core_tmr_alloc_req(
+int core_tmr_alloc_req(
struct se_cmd *se_cmd,
void *fabric_tmr_ptr,
u8 function,
@@ -48,17 +48,20 @@ struct se_tmr_req *core_tmr_alloc_req(
{
struct se_tmr_req *tmr;
- tmr = kmem_cache_zalloc(se_tmr_req_cache, gfp_flags);
+ tmr = kzalloc(sizeof(struct se_tmr_req), gfp_flags);
if (!tmr) {
pr_err("Unable to allocate struct se_tmr_req\n");
- return ERR_PTR(-ENOMEM);
+ return -ENOMEM;
}
+
+ se_cmd->se_cmd_flags |= SCF_SCSI_TMR_CDB;
+ se_cmd->se_tmr_req = tmr;
tmr->task_cmd = se_cmd;
tmr->fabric_tmr_ptr = fabric_tmr_ptr;
tmr->function = function;
INIT_LIST_HEAD(&tmr->tmr_list);
- return tmr;
+ return 0;
}
EXPORT_SYMBOL(core_tmr_alloc_req);
@@ -69,7 +72,7 @@ void core_tmr_release_req(
unsigned long flags;
if (!dev) {
- kmem_cache_free(se_tmr_req_cache, tmr);
+ kfree(tmr);
return;
}
@@ -77,7 +80,7 @@ void core_tmr_release_req(
list_del(&tmr->tmr_list);
spin_unlock_irqrestore(&dev->se_tmr_lock, flags);
- kmem_cache_free(se_tmr_req_cache, tmr);
+ kfree(tmr);
}
static void core_tmr_handle_tas_abort(
@@ -115,6 +118,70 @@ static int target_check_cdb_and_preempt(struct list_head *list,
return 1;
}
+void core_tmr_abort_task(
+ struct se_device *dev,
+ struct se_tmr_req *tmr,
+ struct se_session *se_sess)
+{
+ struct se_cmd *se_cmd, *tmp_cmd;
+ unsigned long flags;
+ int ref_tag;
+
+ spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
+ list_for_each_entry_safe(se_cmd, tmp_cmd,
+ &se_sess->sess_cmd_list, se_cmd_list) {
+
+ if (dev != se_cmd->se_dev)
+ continue;
+ ref_tag = se_cmd->se_tfo->get_task_tag(se_cmd);
+ if (tmr->ref_task_tag != ref_tag)
+ continue;
+
+ printk("ABORT_TASK: Found referenced %s task_tag: %u\n",
+ se_cmd->se_tfo->get_fabric_name(), ref_tag);
+
+ spin_lock_irq(&se_cmd->t_state_lock);
+ if (se_cmd->transport_state & CMD_T_COMPLETE) {
+ printk("ABORT_TASK: ref_tag: %u already complete, skipping\n", ref_tag);
+ spin_unlock_irq(&se_cmd->t_state_lock);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+ goto out;
+ }
+ se_cmd->transport_state |= CMD_T_ABORTED;
+ spin_unlock_irq(&se_cmd->t_state_lock);
+
+ list_del_init(&se_cmd->se_cmd_list);
+ kref_get(&se_cmd->cmd_kref);
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+ cancel_work_sync(&se_cmd->work);
+ transport_wait_for_tasks(se_cmd);
+ /*
+ * Now send SAM_STAT_TASK_ABORTED status for the referenced
+ * se_cmd descriptor..
+ */
+ transport_send_task_abort(se_cmd);
+ /*
+ * Also deal with possible extra acknowledge reference..
+ */
+ if (se_cmd->se_cmd_flags & SCF_ACK_KREF)
+ target_put_sess_cmd(se_sess, se_cmd);
+
+ target_put_sess_cmd(se_sess, se_cmd);
+
+ printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for"
+ " ref_tag: %d\n", ref_tag);
+ tmr->response = TMR_FUNCTION_COMPLETE;
+ return;
+ }
+ spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
+
+out:
+ printk("ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST for ref_tag: %d\n",
+ tmr->ref_task_tag);
+ tmr->response = TMR_TASK_DOES_NOT_EXIST;
+}
+
static void core_tmr_drain_tmr_list(
struct se_device *dev,
struct se_tmr_req *tmr,
@@ -150,7 +217,7 @@ static void core_tmr_drain_tmr_list(
continue;
spin_lock(&cmd->t_state_lock);
- if (!atomic_read(&cmd->t_transport_active)) {
+ if (!(cmd->transport_state & CMD_T_ACTIVE)) {
spin_unlock(&cmd->t_state_lock);
continue;
}
@@ -255,15 +322,15 @@ static void core_tmr_drain_task_list(
cmd->t_task_cdb[0]);
pr_debug("LUN_RESET: ITT[0x%08x] - pr_res_key: 0x%016Lx"
" t_task_cdbs: %d t_task_cdbs_left: %d"
- " t_task_cdbs_sent: %d -- t_transport_active: %d"
- " t_transport_stop: %d t_transport_sent: %d\n",
+ " t_task_cdbs_sent: %d -- CMD_T_ACTIVE: %d"
+ " CMD_T_STOP: %d CMD_T_SENT: %d\n",
cmd->se_tfo->get_task_tag(cmd), cmd->pr_res_key,
cmd->t_task_list_num,
atomic_read(&cmd->t_task_cdbs_left),
atomic_read(&cmd->t_task_cdbs_sent),
- atomic_read(&cmd->t_transport_active),
- atomic_read(&cmd->t_transport_stop),
- atomic_read(&cmd->t_transport_sent));
+ (cmd->transport_state & CMD_T_ACTIVE) != 0,
+ (cmd->transport_state & CMD_T_STOP) != 0,
+ (cmd->transport_state & CMD_T_SENT) != 0);
/*
* If the command may be queued onto a workqueue cancel it now.
@@ -287,19 +354,19 @@ static void core_tmr_drain_task_list(
}
fe_count = atomic_read(&cmd->t_fe_count);
- if (atomic_read(&cmd->t_transport_active)) {
- pr_debug("LUN_RESET: got t_transport_active = 1 for"
+ if (!(cmd->transport_state & CMD_T_ACTIVE)) {
+ pr_debug("LUN_RESET: got CMD_T_ACTIVE for"
" task: %p, t_fe_count: %d dev: %p\n", task,
fe_count, dev);
- atomic_set(&cmd->t_transport_aborted, 1);
+ cmd->transport_state |= CMD_T_ABORTED;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
continue;
}
- pr_debug("LUN_RESET: Got t_transport_active = 0 for task: %p,"
+ pr_debug("LUN_RESET: Got !CMD_T_ACTIVE for task: %p,"
" t_fe_count: %d dev: %p\n", task, fe_count, dev);
- atomic_set(&cmd->t_transport_aborted, 1);
+ cmd->transport_state |= CMD_T_ABORTED;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
@@ -339,7 +406,7 @@ static void core_tmr_drain_cmd_list(
if (prout_cmd == cmd)
continue;
- atomic_set(&cmd->t_transport_queue_active, 0);
+ cmd->transport_state &= ~CMD_T_QUEUED;
atomic_dec(&qobj->queue_cnt);
list_move_tail(&cmd->se_queue_node, &drain_cmd_list);
}
diff --git a/drivers/target/target_core_tpg.c b/drivers/target/target_core_tpg.c
index 06336ecd872d..70c3ffb981e7 100644
--- a/drivers/target/target_core_tpg.c
+++ b/drivers/target/target_core_tpg.c
@@ -64,7 +64,7 @@ static void core_clear_initiator_node_from_tpg(
spin_lock_irq(&nacl->device_list_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))
continue;
@@ -163,7 +163,7 @@ void core_tpg_add_node_to_devs(
spin_lock(&tpg->tpg_lun_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- lun = &tpg->tpg_lun_list[i];
+ lun = tpg->tpg_lun_list[i];
if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
continue;
@@ -222,6 +222,34 @@ static int core_set_queue_depth_for_node(
return 0;
}
+void array_free(void *array, int n)
+{
+ void **a = array;
+ int i;
+
+ for (i = 0; i < n; i++)
+ kfree(a[i]);
+ kfree(a);
+}
+
+static void *array_zalloc(int n, size_t size, gfp_t flags)
+{
+ void **a;
+ int i;
+
+ a = kzalloc(n * sizeof(void*), flags);
+ if (!a)
+ return NULL;
+ for (i = 0; i < n; i++) {
+ a[i] = kzalloc(size, flags);
+ if (!a[i]) {
+ array_free(a, n);
+ return NULL;
+ }
+ }
+ return a;
+}
+
/* core_create_device_list_for_node():
*
*
@@ -231,15 +259,15 @@ static int core_create_device_list_for_node(struct se_node_acl *nacl)
struct se_dev_entry *deve;
int i;
- nacl->device_list = kzalloc(sizeof(struct se_dev_entry) *
- TRANSPORT_MAX_LUNS_PER_TPG, GFP_KERNEL);
+ nacl->device_list = array_zalloc(TRANSPORT_MAX_LUNS_PER_TPG,
+ sizeof(struct se_dev_entry), GFP_KERNEL);
if (!nacl->device_list) {
pr_err("Unable to allocate memory for"
" struct se_node_acl->device_list\n");
return -ENOMEM;
}
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- deve = &nacl->device_list[i];
+ deve = nacl->device_list[i];
atomic_set(&deve->ua_count, 0);
atomic_set(&deve->pr_ref_count, 0);
@@ -274,6 +302,8 @@ struct se_node_acl *core_tpg_check_initiator_node_acl(
INIT_LIST_HEAD(&acl->acl_list);
INIT_LIST_HEAD(&acl->acl_sess_list);
+ kref_init(&acl->acl_kref);
+ init_completion(&acl->acl_free_comp);
spin_lock_init(&acl->device_list_lock);
spin_lock_init(&acl->nacl_sess_lock);
atomic_set(&acl->acl_pr_ref_count, 0);
@@ -329,19 +359,19 @@ void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *nacl)
void core_tpg_clear_object_luns(struct se_portal_group *tpg)
{
- int i, ret;
+ int i;
struct se_lun *lun;
spin_lock(&tpg->tpg_lun_lock);
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- lun = &tpg->tpg_lun_list[i];
+ lun = tpg->tpg_lun_list[i];
if ((lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) ||
(lun->lun_se_dev == NULL))
continue;
spin_unlock(&tpg->tpg_lun_lock);
- ret = core_dev_del_lun(tpg, lun->unpacked_lun);
+ core_dev_del_lun(tpg, lun->unpacked_lun);
spin_lock(&tpg->tpg_lun_lock);
}
spin_unlock(&tpg->tpg_lun_lock);
@@ -402,6 +432,8 @@ struct se_node_acl *core_tpg_add_initiator_node_acl(
INIT_LIST_HEAD(&acl->acl_list);
INIT_LIST_HEAD(&acl->acl_sess_list);
+ kref_init(&acl->acl_kref);
+ init_completion(&acl->acl_free_comp);
spin_lock_init(&acl->device_list_lock);
spin_lock_init(&acl->nacl_sess_lock);
atomic_set(&acl->acl_pr_ref_count, 0);
@@ -448,39 +480,47 @@ int core_tpg_del_initiator_node_acl(
struct se_node_acl *acl,
int force)
{
+ LIST_HEAD(sess_list);
struct se_session *sess, *sess_tmp;
- int dynamic_acl = 0;
+ unsigned long flags;
+ int rc;
spin_lock_irq(&tpg->acl_node_lock);
if (acl->dynamic_node_acl) {
acl->dynamic_node_acl = 0;
- dynamic_acl = 1;
}
list_del(&acl->acl_list);
tpg->num_node_acls--;
spin_unlock_irq(&tpg->acl_node_lock);
- spin_lock_bh(&tpg->session_lock);
- list_for_each_entry_safe(sess, sess_tmp,
- &tpg->tpg_sess_list, sess_list) {
- if (sess->se_node_acl != acl)
- continue;
- /*
- * Determine if the session needs to be closed by our context.
- */
- if (!tpg->se_tpg_tfo->shutdown_session(sess))
+ spin_lock_irqsave(&acl->nacl_sess_lock, flags);
+ acl->acl_stop = 1;
+
+ list_for_each_entry_safe(sess, sess_tmp, &acl->acl_sess_list,
+ sess_acl_list) {
+ if (sess->sess_tearing_down != 0)
continue;
- spin_unlock_bh(&tpg->session_lock);
- /*
- * If the $FABRIC_MOD session for the Initiator Node ACL exists,
- * forcefully shutdown the $FABRIC_MOD session/nexus.
- */
- tpg->se_tpg_tfo->close_session(sess);
+ target_get_session(sess);
+ list_move(&sess->sess_acl_list, &sess_list);
+ }
+ spin_unlock_irqrestore(&acl->nacl_sess_lock, flags);
+
+ list_for_each_entry_safe(sess, sess_tmp, &sess_list, sess_acl_list) {
+ list_del(&sess->sess_acl_list);
- spin_lock_bh(&tpg->session_lock);
+ rc = tpg->se_tpg_tfo->shutdown_session(sess);
+ target_put_session(sess);
+ if (!rc)
+ continue;
+ target_put_session(sess);
}
- spin_unlock_bh(&tpg->session_lock);
+ target_put_nacl(acl);
+ /*
+ * Wait for last target_put_nacl() to complete in target_complete_nacl()
+ * for active fabric session transport_deregister_session() callbacks.
+ */
+ wait_for_completion(&acl->acl_free_comp);
core_tpg_wait_for_nacl_pr_ref(acl);
core_clear_initiator_node_from_tpg(acl, tpg);
@@ -507,6 +547,7 @@ int core_tpg_set_initiator_node_queue_depth(
{
struct se_session *sess, *init_sess = NULL;
struct se_node_acl *acl;
+ unsigned long flags;
int dynamic_acl = 0;
spin_lock_irq(&tpg->acl_node_lock);
@@ -525,7 +566,7 @@ int core_tpg_set_initiator_node_queue_depth(
}
spin_unlock_irq(&tpg->acl_node_lock);
- spin_lock_bh(&tpg->session_lock);
+ spin_lock_irqsave(&tpg->session_lock, flags);
list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) {
if (sess->se_node_acl != acl)
continue;
@@ -537,7 +578,7 @@ int core_tpg_set_initiator_node_queue_depth(
" depth and force session reinstatement"
" use the \"force=1\" parameter.\n",
tpg->se_tpg_tfo->get_fabric_name(), initiatorname);
- spin_unlock_bh(&tpg->session_lock);
+ spin_unlock_irqrestore(&tpg->session_lock, flags);
spin_lock_irq(&tpg->acl_node_lock);
if (dynamic_acl)
@@ -567,7 +608,7 @@ int core_tpg_set_initiator_node_queue_depth(
acl->queue_depth = queue_depth;
if (core_set_queue_depth_for_node(tpg, acl) < 0) {
- spin_unlock_bh(&tpg->session_lock);
+ spin_unlock_irqrestore(&tpg->session_lock, flags);
/*
* Force session reinstatement if
* core_set_queue_depth_for_node() failed, because we assume
@@ -583,7 +624,7 @@ int core_tpg_set_initiator_node_queue_depth(
spin_unlock_irq(&tpg->acl_node_lock);
return -EINVAL;
}
- spin_unlock_bh(&tpg->session_lock);
+ spin_unlock_irqrestore(&tpg->session_lock, flags);
/*
* If the $FABRIC_MOD session for the Initiator Node ACL exists,
* forcefully shutdown the $FABRIC_MOD session/nexus.
@@ -647,8 +688,8 @@ int core_tpg_register(
struct se_lun *lun;
u32 i;
- se_tpg->tpg_lun_list = kzalloc((sizeof(struct se_lun) *
- TRANSPORT_MAX_LUNS_PER_TPG), GFP_KERNEL);
+ se_tpg->tpg_lun_list = array_zalloc(TRANSPORT_MAX_LUNS_PER_TPG,
+ sizeof(struct se_lun), GFP_KERNEL);
if (!se_tpg->tpg_lun_list) {
pr_err("Unable to allocate struct se_portal_group->"
"tpg_lun_list\n");
@@ -656,7 +697,7 @@ int core_tpg_register(
}
for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
- lun = &se_tpg->tpg_lun_list[i];
+ lun = se_tpg->tpg_lun_list[i];
lun->unpacked_lun = i;
lun->lun_status = TRANSPORT_LUN_STATUS_FREE;
atomic_set(&lun->lun_acl_count, 0);
@@ -742,7 +783,7 @@ int core_tpg_deregister(struct se_portal_group *se_tpg)
core_tpg_release_virtual_lun0(se_tpg);
se_tpg->se_tpg_fabric_ptr = NULL;
- kfree(se_tpg->tpg_lun_list);
+ array_free(se_tpg->tpg_lun_list, TRANSPORT_MAX_LUNS_PER_TPG);
return 0;
}
EXPORT_SYMBOL(core_tpg_deregister);
@@ -763,7 +804,7 @@ struct se_lun *core_tpg_pre_addlun(
}
spin_lock(&tpg->tpg_lun_lock);
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status == TRANSPORT_LUN_STATUS_ACTIVE) {
pr_err("TPG Logical Unit Number: %u is already active"
" on %s Target Portal Group: %u, ignoring request.\n",
@@ -821,7 +862,7 @@ struct se_lun *core_tpg_pre_dellun(
}
spin_lock(&tpg->tpg_lun_lock);
- lun = &tpg->tpg_lun_list[unpacked_lun];
+ lun = tpg->tpg_lun_list[unpacked_lun];
if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) {
pr_err("%s Logical Unit Number: %u is not active on"
" Target Portal Group: %u, ignoring request.\n",
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 929cc9364c8a..443704f84fd5 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -37,6 +37,7 @@
#include <linux/in.h>
#include <linux/cdrom.h>
#include <linux/module.h>
+#include <linux/ratelimit.h>
#include <asm/unaligned.h>
#include <net/sock.h>
#include <net/tcp.h>
@@ -58,7 +59,6 @@ static int sub_api_initialized;
static struct workqueue_struct *target_completion_wq;
static struct kmem_cache *se_sess_cache;
-struct kmem_cache *se_tmr_req_cache;
struct kmem_cache *se_ua_cache;
struct kmem_cache *t10_pr_reg_cache;
struct kmem_cache *t10_alua_lu_gp_cache;
@@ -77,26 +77,17 @@ static int transport_generic_get_mem(struct se_cmd *cmd);
static void transport_put_cmd(struct se_cmd *cmd);
static void transport_remove_cmd_from_queue(struct se_cmd *cmd);
static int transport_set_sense_codes(struct se_cmd *cmd, u8 asc, u8 ascq);
-static void transport_generic_request_failure(struct se_cmd *);
static void target_complete_ok_work(struct work_struct *work);
int init_se_kmem_caches(void)
{
- se_tmr_req_cache = kmem_cache_create("se_tmr_cache",
- sizeof(struct se_tmr_req), __alignof__(struct se_tmr_req),
- 0, NULL);
- if (!se_tmr_req_cache) {
- pr_err("kmem_cache_create() for struct se_tmr_req"
- " failed\n");
- goto out;
- }
se_sess_cache = kmem_cache_create("se_sess_cache",
sizeof(struct se_session), __alignof__(struct se_session),
0, NULL);
if (!se_sess_cache) {
pr_err("kmem_cache_create() for struct se_session"
" failed\n");
- goto out_free_tmr_req_cache;
+ goto out;
}
se_ua_cache = kmem_cache_create("se_ua_cache",
sizeof(struct se_ua), __alignof__(struct se_ua),
@@ -169,8 +160,6 @@ out_free_ua_cache:
kmem_cache_destroy(se_ua_cache);
out_free_sess_cache:
kmem_cache_destroy(se_sess_cache);
-out_free_tmr_req_cache:
- kmem_cache_destroy(se_tmr_req_cache);
out:
return -ENOMEM;
}
@@ -178,7 +167,6 @@ out:
void release_se_kmem_caches(void)
{
destroy_workqueue(target_completion_wq);
- kmem_cache_destroy(se_tmr_req_cache);
kmem_cache_destroy(se_sess_cache);
kmem_cache_destroy(se_ua_cache);
kmem_cache_destroy(t10_pr_reg_cache);
@@ -258,13 +246,14 @@ struct se_session *transport_init_session(void)
INIT_LIST_HEAD(&se_sess->sess_cmd_list);
INIT_LIST_HEAD(&se_sess->sess_wait_list);
spin_lock_init(&se_sess->sess_cmd_lock);
+ kref_init(&se_sess->sess_kref);
return se_sess;
}
EXPORT_SYMBOL(transport_init_session);
/*
- * Called with spin_lock_bh(&struct se_portal_group->session_lock called.
+ * Called with spin_lock_irqsave(&struct se_portal_group->session_lock called.
*/
void __transport_register_session(
struct se_portal_group *se_tpg,
@@ -293,6 +282,8 @@ void __transport_register_session(
&buf[0], PR_REG_ISID_LEN);
se_sess->sess_bin_isid = get_unaligned_be64(&buf[0]);
}
+ kref_get(&se_nacl->acl_kref);
+
spin_lock_irq(&se_nacl->nacl_sess_lock);
/*
* The se_nacl->nacl_sess pointer will be set to the
@@ -317,12 +308,48 @@ void transport_register_session(
struct se_session *se_sess,
void *fabric_sess_ptr)
{
- spin_lock_bh(&se_tpg->session_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&se_tpg->session_lock, flags);
__transport_register_session(se_tpg, se_nacl, se_sess, fabric_sess_ptr);
- spin_unlock_bh(&se_tpg->session_lock);
+ spin_unlock_irqrestore(&se_tpg->session_lock, flags);
}
EXPORT_SYMBOL(transport_register_session);
+static void target_release_session(struct kref *kref)
+{
+ struct se_session *se_sess = container_of(kref,
+ struct se_session, sess_kref);
+ struct se_portal_group *se_tpg = se_sess->se_tpg;
+
+ se_tpg->se_tpg_tfo->close_session(se_sess);
+}
+
+void target_get_session(struct se_session *se_sess)
+{
+ kref_get(&se_sess->sess_kref);
+}
+EXPORT_SYMBOL(target_get_session);
+
+int target_put_session(struct se_session *se_sess)
+{
+ return kref_put(&se_sess->sess_kref, target_release_session);
+}
+EXPORT_SYMBOL(target_put_session);
+
+static void target_complete_nacl(struct kref *kref)
+{
+ struct se_node_acl *nacl = container_of(kref,
+ struct se_node_acl, acl_kref);
+
+ complete(&nacl->acl_free_comp);
+}
+
+void target_put_nacl(struct se_node_acl *nacl)
+{
+ kref_put(&nacl->acl_kref, target_complete_nacl);
+}
+
void transport_deregister_session_configfs(struct se_session *se_sess)
{
struct se_node_acl *se_nacl;
@@ -333,7 +360,8 @@ void transport_deregister_session_configfs(struct se_session *se_sess)
se_nacl = se_sess->se_node_acl;
if (se_nacl) {
spin_lock_irqsave(&se_nacl->nacl_sess_lock, flags);
- list_del(&se_sess->sess_acl_list);
+ if (se_nacl->acl_stop == 0)
+ list_del(&se_sess->sess_acl_list);
/*
* If the session list is empty, then clear the pointer.
* Otherwise, set the struct se_session pointer from the tail
@@ -360,13 +388,16 @@ EXPORT_SYMBOL(transport_free_session);
void transport_deregister_session(struct se_session *se_sess)
{
struct se_portal_group *se_tpg = se_sess->se_tpg;
+ struct target_core_fabric_ops *se_tfo;
struct se_node_acl *se_nacl;
unsigned long flags;
+ bool comp_nacl = true;
if (!se_tpg) {
transport_free_session(se_sess);
return;
}
+ se_tfo = se_tpg->se_tpg_tfo;
spin_lock_irqsave(&se_tpg->session_lock, flags);
list_del(&se_sess->sess_list);
@@ -379,29 +410,34 @@ void transport_deregister_session(struct se_session *se_sess)
* struct se_node_acl if it had been previously dynamically generated.
*/
se_nacl = se_sess->se_node_acl;
- if (se_nacl) {
- spin_lock_irqsave(&se_tpg->acl_node_lock, flags);
- if (se_nacl->dynamic_node_acl) {
- if (!se_tpg->se_tpg_tfo->tpg_check_demo_mode_cache(
- se_tpg)) {
- list_del(&se_nacl->acl_list);
- se_tpg->num_node_acls--;
- spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags);
-
- core_tpg_wait_for_nacl_pr_ref(se_nacl);
- core_free_device_list_for_node(se_nacl, se_tpg);
- se_tpg->se_tpg_tfo->tpg_release_fabric_acl(se_tpg,
- se_nacl);
- spin_lock_irqsave(&se_tpg->acl_node_lock, flags);
- }
+
+ spin_lock_irqsave(&se_tpg->acl_node_lock, flags);
+ if (se_nacl && se_nacl->dynamic_node_acl) {
+ if (!se_tfo->tpg_check_demo_mode_cache(se_tpg)) {
+ list_del(&se_nacl->acl_list);
+ se_tpg->num_node_acls--;
+ spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags);
+ core_tpg_wait_for_nacl_pr_ref(se_nacl);
+ core_free_device_list_for_node(se_nacl, se_tpg);
+ se_tfo->tpg_release_fabric_acl(se_tpg, se_nacl);
+
+ comp_nacl = false;
+ spin_lock_irqsave(&se_tpg->acl_node_lock, flags);
}
- spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags);
}
-
- transport_free_session(se_sess);
+ spin_unlock_irqrestore(&se_tpg->acl_node_lock, flags);
pr_debug("TARGET_CORE[%s]: Deregistered fabric_sess\n",
se_tpg->se_tpg_tfo->get_fabric_name());
+ /*
+ * If last kref is dropping now for an explict NodeACL, awake sleeping
+ * ->acl_free_comp caller to wakeup configfs se_node_acl->acl_group
+ * removal context.
+ */
+ if (se_nacl && comp_nacl == true)
+ target_put_nacl(se_nacl);
+
+ transport_free_session(se_sess);
}
EXPORT_SYMBOL(transport_deregister_session);
@@ -437,7 +473,7 @@ static void transport_all_task_dev_remove_state(struct se_cmd *cmd)
/* transport_cmd_check_stop():
*
- * 'transport_off = 1' determines if t_transport_active should be cleared.
+ * 'transport_off = 1' determines if CMD_T_ACTIVE should be cleared.
* 'transport_off = 2' determines if task_dev_state should be removed.
*
* A non-zero u8 t_state sets cmd->t_state.
@@ -455,12 +491,11 @@ static int transport_cmd_check_stop(
* Determine if IOCTL context caller in requesting the stopping of this
* command for LUN shutdown purposes.
*/
- if (atomic_read(&cmd->transport_lun_stop)) {
- pr_debug("%s:%d atomic_read(&cmd->transport_lun_stop)"
- " == TRUE for ITT: 0x%08x\n", __func__, __LINE__,
- cmd->se_tfo->get_task_tag(cmd));
+ if (cmd->transport_state & CMD_T_LUN_STOP) {
+ pr_debug("%s:%d CMD_T_LUN_STOP for ITT: 0x%08x\n",
+ __func__, __LINE__, cmd->se_tfo->get_task_tag(cmd));
- atomic_set(&cmd->t_transport_active, 0);
+ cmd->transport_state &= ~CMD_T_ACTIVE;
if (transport_off == 2)
transport_all_task_dev_remove_state(cmd);
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
@@ -472,9 +507,9 @@ static int transport_cmd_check_stop(
* Determine if frontend context caller is requesting the stopping of
* this command for frontend exceptions.
*/
- if (atomic_read(&cmd->t_transport_stop)) {
- pr_debug("%s:%d atomic_read(&cmd->t_transport_stop) =="
- " TRUE for ITT: 0x%08x\n", __func__, __LINE__,
+ if (cmd->transport_state & CMD_T_STOP) {
+ pr_debug("%s:%d CMD_T_STOP for ITT: 0x%08x\n",
+ __func__, __LINE__,
cmd->se_tfo->get_task_tag(cmd));
if (transport_off == 2)
@@ -492,7 +527,7 @@ static int transport_cmd_check_stop(
return 1;
}
if (transport_off) {
- atomic_set(&cmd->t_transport_active, 0);
+ cmd->transport_state &= ~CMD_T_ACTIVE;
if (transport_off == 2) {
transport_all_task_dev_remove_state(cmd);
/*
@@ -540,31 +575,21 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd)
return;
spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (!atomic_read(&cmd->transport_dev_active)) {
- spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- goto check_lun;
+ if (cmd->transport_state & CMD_T_DEV_ACTIVE) {
+ cmd->transport_state &= ~CMD_T_DEV_ACTIVE;
+ transport_all_task_dev_remove_state(cmd);
}
- atomic_set(&cmd->transport_dev_active, 0);
- transport_all_task_dev_remove_state(cmd);
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
-
-check_lun:
spin_lock_irqsave(&lun->lun_cmd_lock, flags);
- if (atomic_read(&cmd->transport_lun_active)) {
- list_del(&cmd->se_lun_node);
- atomic_set(&cmd->transport_lun_active, 0);
-#if 0
- pr_debug("Removed ITT: 0x%08x from LUN LIST[%d]\n"
- cmd->se_tfo->get_task_tag(cmd), lun->unpacked_lun);
-#endif
- }
+ if (!list_empty(&cmd->se_lun_node))
+ list_del_init(&cmd->se_lun_node);
spin_unlock_irqrestore(&lun->lun_cmd_lock, flags);
}
void transport_cmd_finish_abort(struct se_cmd *cmd, int remove)
{
- if (!cmd->se_tmr_req)
+ if (!(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
transport_lun_remove_cmd(cmd);
if (transport_cmd_check_stop_to_fabric(cmd))
@@ -585,7 +610,7 @@ static void transport_add_cmd_to_queue(struct se_cmd *cmd, int t_state,
if (t_state) {
spin_lock_irqsave(&cmd->t_state_lock, flags);
cmd->t_state = t_state;
- atomic_set(&cmd->t_transport_active, 1);
+ cmd->transport_state |= CMD_T_ACTIVE;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
}
@@ -601,7 +626,7 @@ static void transport_add_cmd_to_queue(struct se_cmd *cmd, int t_state,
list_add(&cmd->se_queue_node, &qobj->qobj_list);
else
list_add_tail(&cmd->se_queue_node, &qobj->qobj_list);
- atomic_set(&cmd->t_transport_queue_active, 1);
+ cmd->transport_state |= CMD_T_QUEUED;
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
wake_up_interruptible(&qobj->thread_wq);
@@ -620,8 +645,7 @@ transport_get_cmd_from_queue(struct se_queue_obj *qobj)
}
cmd = list_first_entry(&qobj->qobj_list, struct se_cmd, se_queue_node);
- atomic_set(&cmd->t_transport_queue_active, 0);
-
+ cmd->transport_state &= ~CMD_T_QUEUED;
list_del_init(&cmd->se_queue_node);
atomic_dec(&qobj->queue_cnt);
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
@@ -635,20 +659,14 @@ static void transport_remove_cmd_from_queue(struct se_cmd *cmd)
unsigned long flags;
spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
- if (!atomic_read(&cmd->t_transport_queue_active)) {
+ if (!(cmd->transport_state & CMD_T_QUEUED)) {
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
return;
}
- atomic_set(&cmd->t_transport_queue_active, 0);
+ cmd->transport_state &= ~CMD_T_QUEUED;
atomic_dec(&qobj->queue_cnt);
list_del_init(&cmd->se_queue_node);
spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
-
- if (atomic_read(&cmd->t_transport_queue_active)) {
- pr_err("ITT: 0x%08x t_transport_queue_active: %d\n",
- cmd->se_tfo->get_task_tag(cmd),
- atomic_read(&cmd->t_transport_queue_active));
- }
}
/*
@@ -719,7 +737,7 @@ void transport_complete_task(struct se_task *task, int success)
}
if (!success)
- cmd->t_tasks_failed = 1;
+ cmd->transport_state |= CMD_T_FAILED;
/*
* Decrement the outstanding t_task_cdbs_left count. The last
@@ -730,17 +748,24 @@ void transport_complete_task(struct se_task *task, int success)
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return;
}
-
- if (cmd->t_tasks_failed) {
+ /*
+ * Check for case where an explict ABORT_TASK has been received
+ * and transport_wait_for_tasks() will be waiting for completion..
+ */
+ if (cmd->transport_state & CMD_T_ABORTED &&
+ cmd->transport_state & CMD_T_STOP) {
+ spin_unlock_irqrestore(&cmd->t_state_lock, flags);
+ complete(&cmd->t_transport_stop_comp);
+ return;
+ } else if (cmd->transport_state & CMD_T_FAILED) {
cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;
INIT_WORK(&cmd->work, target_complete_failure_work);
} else {
- atomic_set(&cmd->t_transport_complete, 1);
INIT_WORK(&cmd->work, target_complete_ok_work);
}
cmd->t_state = TRANSPORT_COMPLETE;
- atomic_set(&cmd->t_transport_active, 1);
+ cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE);
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
queue_work(target_completion_wq, &cmd->work);
@@ -1488,7 +1513,7 @@ void transport_init_se_cmd(
init_completion(&cmd->t_transport_stop_comp);
init_completion(&cmd->cmd_wait_comp);
spin_lock_init(&cmd->t_state_lock);
- atomic_set(&cmd->transport_dev_active, 1);
+ cmd->transport_state = CMD_T_DEV_ACTIVE;
cmd->se_tfo = tfo;
cmd->se_sess = se_sess;
@@ -1618,7 +1643,7 @@ int transport_handle_cdb_direct(
return -EINVAL;
}
/*
- * Set TRANSPORT_NEW_CMD state and cmd->t_transport_active=1 following
+ * Set TRANSPORT_NEW_CMD state and CMD_T_ACTIVE following
* transport_generic_handle_cdb*() -> transport_add_cmd_to_queue()
* in existing usage to ensure that outstanding descriptors are handled
* correctly during shutdown via transport_wait_for_tasks()
@@ -1627,7 +1652,8 @@ int transport_handle_cdb_direct(
* this to be called for initial descriptor submission.
*/
cmd->t_state = TRANSPORT_NEW_CMD;
- atomic_set(&cmd->t_transport_active, 1);
+ cmd->transport_state |= CMD_T_ACTIVE;
+
/*
* transport_generic_new_cmd() is already handling QUEUE_FULL,
* so follow TRANSPORT_NEW_CMD processing thread context usage
@@ -1716,6 +1742,74 @@ void target_submit_cmd(struct se_cmd *se_cmd, struct se_session *se_sess,
}
EXPORT_SYMBOL(target_submit_cmd);
+static void target_complete_tmr_failure(struct work_struct *work)
+{
+ struct se_cmd *se_cmd = container_of(work, struct se_cmd, work);
+
+ se_cmd->se_tmr_req->response = TMR_LUN_DOES_NOT_EXIST;
+ se_cmd->se_tfo->queue_tm_rsp(se_cmd);
+ transport_generic_free_cmd(se_cmd, 0);
+}
+
+/**
+ * target_submit_tmr - lookup unpacked lun and submit uninitialized se_cmd
+ * for TMR CDBs
+ *
+ * @se_cmd: command descriptor to submit
+ * @se_sess: associated se_sess for endpoint
+ * @sense: pointer to SCSI sense buffer
+ * @unpacked_lun: unpacked LUN to reference for struct se_lun
+ * @fabric_context: fabric context for TMR req
+ * @tm_type: Type of TM request
+ * @gfp: gfp type for caller
+ * @tag: referenced task tag for TMR_ABORT_TASK
+ * @flags: submit cmd flags
+ *
+ * Callable from all contexts.
+ **/
+
+int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess,
+ unsigned char *sense, u32 unpacked_lun,
+ void *fabric_tmr_ptr, unsigned char tm_type,
+ gfp_t gfp, unsigned int tag, int flags)
+{
+ struct se_portal_group *se_tpg;
+ int ret;
+
+ se_tpg = se_sess->se_tpg;
+ BUG_ON(!se_tpg);
+
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess,
+ 0, DMA_NONE, MSG_SIMPLE_TAG, sense);
+ /*
+ * FIXME: Currently expect caller to handle se_cmd->se_tmr_req
+ * allocation failure.
+ */
+ ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp);
+ if (ret < 0)
+ return -ENOMEM;
+
+ if (tm_type == TMR_ABORT_TASK)
+ se_cmd->se_tmr_req->ref_task_tag = tag;
+
+ /* See target_submit_cmd for commentary */
+ target_get_sess_cmd(se_sess, se_cmd, (flags & TARGET_SCF_ACK_KREF));
+
+ ret = transport_lookup_tmr_lun(se_cmd, unpacked_lun);
+ if (ret) {
+ /*
+ * For callback during failure handling, push this work off
+ * to process context with TMR_LUN_DOES_NOT_EXIST status.
+ */
+ INIT_WORK(&se_cmd->work, target_complete_tmr_failure);
+ schedule_work(&se_cmd->work);
+ return 0;
+ }
+ transport_generic_handle_tmr(se_cmd);
+ return 0;
+}
+EXPORT_SYMBOL(target_submit_tmr);
+
/*
* Used by fabric module frontends defining a TFO->new_cmd_map() caller
* to queue up a newly setup se_cmd w/ TRANSPORT_NEW_CMD_MAP in order to
@@ -1847,7 +1941,7 @@ static int transport_stop_tasks_for_cmd(struct se_cmd *cmd)
/*
* Handle SAM-esque emulation for generic transport request failures.
*/
-static void transport_generic_request_failure(struct se_cmd *cmd)
+void transport_generic_request_failure(struct se_cmd *cmd)
{
int ret = 0;
@@ -1859,14 +1953,14 @@ static void transport_generic_request_failure(struct se_cmd *cmd)
cmd->t_state, cmd->scsi_sense_reason);
pr_debug("-----[ t_tasks: %d t_task_cdbs_left: %d"
" t_task_cdbs_sent: %d t_task_cdbs_ex_left: %d --"
- " t_transport_active: %d t_transport_stop: %d"
- " t_transport_sent: %d\n", cmd->t_task_list_num,
+ " CMD_T_ACTIVE: %d CMD_T_STOP: %d CMD_T_SENT: %d\n",
+ cmd->t_task_list_num,
atomic_read(&cmd->t_task_cdbs_left),
atomic_read(&cmd->t_task_cdbs_sent),
atomic_read(&cmd->t_task_cdbs_ex_left),
- atomic_read(&cmd->t_transport_active),
- atomic_read(&cmd->t_transport_stop),
- atomic_read(&cmd->t_transport_sent));
+ (cmd->transport_state & CMD_T_ACTIVE) != 0,
+ (cmd->transport_state & CMD_T_STOP) != 0,
+ (cmd->transport_state & CMD_T_SENT) != 0);
/*
* For SAM Task Attribute emulation for failed struct se_cmd
@@ -1939,6 +2033,7 @@ queue_full:
cmd->t_state = TRANSPORT_COMPLETE_QF_OK;
transport_handle_queue_full(cmd, cmd->se_dev);
}
+EXPORT_SYMBOL(transport_generic_request_failure);
static inline u32 transport_lba_21(unsigned char *cdb)
{
@@ -2125,7 +2220,7 @@ check_depth:
if (atomic_read(&cmd->t_task_cdbs_sent) ==
cmd->t_task_list_num)
- atomic_set(&cmd->t_transport_sent, 1);
+ cmd->transport_state |= CMD_T_SENT;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
@@ -2136,8 +2231,9 @@ check_depth:
if (error != 0) {
spin_lock_irqsave(&cmd->t_state_lock, flags);
task->task_flags &= ~TF_ACTIVE;
+ cmd->transport_state &= ~CMD_T_SENT;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
- atomic_set(&cmd->t_transport_sent, 0);
+
transport_stop_tasks_for_cmd(cmd);
transport_generic_request_failure(cmd);
}
@@ -2847,7 +2943,7 @@ static int transport_generic_cmd_sequencer(
pr_err("Unsupported SA: 0x%02x\n",
cmd->t_task_cdb[1] & 0x1f);
- goto out_unsupported_cdb;
+ goto out_invalid_cdb_field;
}
/*FALLTHROUGH*/
case ACCESS_CONTROL_IN:
@@ -2929,7 +3025,7 @@ static int transport_generic_cmd_sequencer(
cmd->se_cmd_flags |= SCF_SCSI_NON_DATA_CDB;
break;
case SYNCHRONIZE_CACHE:
- case 0x91: /* SYNCHRONIZE_CACHE_16: */
+ case SYNCHRONIZE_CACHE_16:
/*
* Extract LBA and range to be flushed for emulated SYNCHRONIZE_CACHE
*/
@@ -3081,6 +3177,13 @@ static int transport_generic_cmd_sequencer(
cmd->data_length = size;
}
+ if (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB &&
+ sectors > dev->se_sub_dev->se_dev_attrib.fabric_max_sectors) {
+ printk_ratelimited(KERN_ERR "SCSI OP %02xh with too big sectors %u\n",
+ cdb[0], sectors);
+ goto out_invalid_cdb_field;
+ }
+
/* reject any command that we don't have a handler for */
if (!(passthrough || cmd->execute_task ||
(cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)))
@@ -3384,7 +3487,7 @@ static void transport_release_cmd(struct se_cmd *cmd)
{
BUG_ON(!cmd->se_tfo);
- if (cmd->se_tmr_req)
+ if (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
core_tmr_release_req(cmd->se_tmr_req);
if (cmd->t_task_cdb != cmd->__t_task_cdb)
kfree(cmd->t_task_cdb);
@@ -3421,8 +3524,8 @@ static void transport_put_cmd(struct se_cmd *cmd)
goto out_busy;
}
- if (atomic_read(&cmd->transport_dev_active)) {
- atomic_set(&cmd->transport_dev_active, 0);
+ if (cmd->transport_state & CMD_T_DEV_ACTIVE) {
+ cmd->transport_state &= ~CMD_T_DEV_ACTIVE;
transport_all_task_dev_remove_state(cmd);
free_tasks = 1;
}
@@ -3527,10 +3630,12 @@ EXPORT_SYMBOL(transport_kmap_data_sg);
void transport_kunmap_data_sg(struct se_cmd *cmd)
{
- if (!cmd->t_data_nents)
+ if (!cmd->t_data_nents) {
return;
- else if (cmd->t_data_nents == 1)
+ } else if (cmd->t_data_nents == 1) {
kunmap(sg_page(cmd->t_data_sg));
+ return;
+ }
vunmap(cmd->t_data_vmap);
cmd->t_data_vmap = NULL;
@@ -3860,8 +3965,10 @@ int transport_generic_new_cmd(struct se_cmd *cmd)
if (task_cdbs < 0)
goto out_fail;
else if (!task_cdbs && (cmd->se_cmd_flags & SCF_SCSI_DATA_SG_IO_CDB)) {
+ spin_lock_irq(&cmd->t_state_lock);
cmd->t_state = TRANSPORT_COMPLETE;
- atomic_set(&cmd->t_transport_active, 1);
+ cmd->transport_state |= CMD_T_ACTIVE;
+ spin_unlock_irq(&cmd->t_state_lock);
if (cmd->t_task_cdb[0] == REQUEST_SENSE) {
u8 ua_asc = 0, ua_ascq = 0;
@@ -3942,9 +4049,9 @@ static int transport_generic_write_pending(struct se_cmd *cmd)
/*
* Clear the se_cmd for WRITE_PENDING status in order to set
- * cmd->t_transport_active=0 so that transport_generic_handle_data
- * can be called from HW target mode interrupt code. This is safe
- * to be called with transport_off=1 before the cmd->se_tfo->write_pending
+ * CMD_T_ACTIVE so that transport_generic_handle_data can be called
+ * from HW target mode interrupt code. This is safe to be called
+ * with transport_off=1 before the cmd->se_tfo->write_pending
* because the se_cmd->se_lun pointer is not being cleared.
*/
transport_cmd_check_stop(cmd, 1, 0);
@@ -3971,7 +4078,7 @@ queue_full:
void transport_generic_free_cmd(struct se_cmd *cmd, int wait_for_tasks)
{
if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD)) {
- if (wait_for_tasks && cmd->se_tmr_req)
+ if (wait_for_tasks && (cmd->se_cmd_flags & SCF_SCSI_TMR_CDB))
transport_wait_for_tasks(cmd);
transport_release_cmd(cmd);
@@ -4007,8 +4114,10 @@ void target_get_sess_cmd(struct se_session *se_sess, struct se_cmd *se_cmd,
* fabric acknowledgement that requires two target_put_sess_cmd()
* invocations before se_cmd descriptor release.
*/
- if (ack_kref == true)
+ if (ack_kref == true) {
kref_get(&se_cmd->cmd_kref);
+ se_cmd->se_cmd_flags |= SCF_ACK_KREF;
+ }
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
list_add_tail(&se_cmd->se_cmd_list, &se_sess->sess_cmd_list);
@@ -4026,7 +4135,7 @@ static void target_release_cmd_kref(struct kref *kref)
spin_lock_irqsave(&se_sess->sess_cmd_lock, flags);
if (list_empty(&se_cmd->se_cmd_list)) {
spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags);
- WARN_ON(1);
+ se_cmd->se_tfo->release_cmd(se_cmd);
return;
}
if (se_sess->sess_tearing_down && se_cmd->cmd_wait_set) {
@@ -4130,15 +4239,16 @@ static int transport_lun_wait_for_tasks(struct se_cmd *cmd, struct se_lun *lun)
* be stopped, we can safely ignore this struct se_cmd.
*/
spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (atomic_read(&cmd->t_transport_stop)) {
- atomic_set(&cmd->transport_lun_stop, 0);
- pr_debug("ConfigFS ITT[0x%08x] - t_transport_stop =="
- " TRUE, skipping\n", cmd->se_tfo->get_task_tag(cmd));
+ if (cmd->transport_state & CMD_T_STOP) {
+ cmd->transport_state &= ~CMD_T_LUN_STOP;
+
+ pr_debug("ConfigFS ITT[0x%08x] - CMD_T_STOP, skipping\n",
+ cmd->se_tfo->get_task_tag(cmd));
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
transport_cmd_check_stop(cmd, 1, 0);
return -EPERM;
}
- atomic_set(&cmd->transport_lun_fe_stop, 1);
+ cmd->transport_state |= CMD_T_LUN_FE_STOP;
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
wake_up_interruptible(&cmd->se_dev->dev_queue_obj.thread_wq);
@@ -4171,9 +4281,8 @@ static void __transport_clear_lun_from_sessions(struct se_lun *lun)
while (!list_empty(&lun->lun_cmd_list)) {
cmd = list_first_entry(&lun->lun_cmd_list,
struct se_cmd, se_lun_node);
- list_del(&cmd->se_lun_node);
+ list_del_init(&cmd->se_lun_node);
- atomic_set(&cmd->transport_lun_active, 0);
/*
* This will notify iscsi_target_transport.c:
* transport_cmd_check_stop() that a LUN shutdown is in
@@ -4184,7 +4293,7 @@ static void __transport_clear_lun_from_sessions(struct se_lun *lun)
"_lun_stop for ITT: 0x%08x\n",
cmd->se_lun->unpacked_lun,
cmd->se_tfo->get_task_tag(cmd));
- atomic_set(&cmd->transport_lun_stop, 1);
+ cmd->transport_state |= CMD_T_LUN_STOP;
spin_unlock(&cmd->t_state_lock);
spin_unlock_irqrestore(&lun->lun_cmd_lock, lun_flags);
@@ -4214,11 +4323,11 @@ static void __transport_clear_lun_from_sessions(struct se_lun *lun)
cmd->se_tfo->get_task_tag(cmd));
spin_lock_irqsave(&cmd->t_state_lock, cmd_flags);
- if (!atomic_read(&cmd->transport_dev_active)) {
+ if (!(cmd->transport_state & CMD_T_DEV_ACTIVE)) {
spin_unlock_irqrestore(&cmd->t_state_lock, cmd_flags);
goto check_cond;
}
- atomic_set(&cmd->transport_dev_active, 0);
+ cmd->transport_state &= ~CMD_T_DEV_ACTIVE;
transport_all_task_dev_remove_state(cmd);
spin_unlock_irqrestore(&cmd->t_state_lock, cmd_flags);
@@ -4238,7 +4347,7 @@ check_cond:
* finished accessing it.
*/
spin_lock_irqsave(&cmd->t_state_lock, cmd_flags);
- if (atomic_read(&cmd->transport_lun_fe_stop)) {
+ if (cmd->transport_state & CMD_T_LUN_FE_STOP) {
pr_debug("SE_LUN[%d] - Detected FE stop for"
" struct se_cmd: %p ITT: 0x%08x\n",
lun->unpacked_lun,
@@ -4297,7 +4406,8 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
unsigned long flags;
spin_lock_irqsave(&cmd->t_state_lock, flags);
- if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) && !(cmd->se_tmr_req)) {
+ if (!(cmd->se_cmd_flags & SCF_SE_LUN_CMD) &&
+ !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) {
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return false;
}
@@ -4305,7 +4415,8 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
* Only perform a possible wait_for_tasks if SCF_SUPPORTED_SAM_OPCODE
* has been set in transport_set_supported_SAM_opcode().
*/
- if (!(cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) && !cmd->se_tmr_req) {
+ if (!(cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE) &&
+ !(cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)) {
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return false;
}
@@ -4316,8 +4427,7 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
* transport_clear_lun_from_sessions() once the ConfigFS context caller
* has completed its operation on the struct se_cmd.
*/
- if (atomic_read(&cmd->transport_lun_stop)) {
-
+ if (cmd->transport_state & CMD_T_LUN_STOP) {
pr_debug("wait_for_tasks: Stopping"
" wait_for_completion(&cmd->t_tasktransport_lun_fe"
"_stop_comp); for ITT: 0x%08x\n",
@@ -4345,18 +4455,18 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
"stop_comp); for ITT: 0x%08x\n",
cmd->se_tfo->get_task_tag(cmd));
- atomic_set(&cmd->transport_lun_stop, 0);
+ cmd->transport_state &= ~CMD_T_LUN_STOP;
}
- if (!atomic_read(&cmd->t_transport_active) ||
- atomic_read(&cmd->t_transport_aborted)) {
+
+ if (!(cmd->transport_state & CMD_T_ACTIVE)) {
spin_unlock_irqrestore(&cmd->t_state_lock, flags);
return false;
}
- atomic_set(&cmd->t_transport_stop, 1);
+ cmd->transport_state |= CMD_T_STOP;
pr_debug("wait_for_tasks: Stopping %p ITT: 0x%08x"
- " i_state: %d, t_state: %d, t_transport_stop = TRUE\n",
+ " i_state: %d, t_state: %d, CMD_T_STOP\n",
cmd, cmd->se_tfo->get_task_tag(cmd),
cmd->se_tfo->get_cmd_state(cmd), cmd->t_state);
@@ -4367,8 +4477,7 @@ bool transport_wait_for_tasks(struct se_cmd *cmd)
wait_for_completion(&cmd->t_transport_stop_comp);
spin_lock_irqsave(&cmd->t_state_lock, flags);
- atomic_set(&cmd->t_transport_active, 0);
- atomic_set(&cmd->t_transport_stop, 0);
+ cmd->transport_state &= ~(CMD_T_ACTIVE | CMD_T_STOP);
pr_debug("wait_for_tasks: Stopped wait_for_compltion("
"&cmd->t_transport_stop_comp) for ITT: 0x%08x\n",
@@ -4597,7 +4706,7 @@ int transport_check_aborted_status(struct se_cmd *cmd, int send_status)
{
int ret = 0;
- if (atomic_read(&cmd->t_transport_aborted) != 0) {
+ if (cmd->transport_state & CMD_T_ABORTED) {
if (!send_status ||
(cmd->se_cmd_flags & SCF_SENT_DELAYED_TAS))
return 1;
@@ -4634,7 +4743,7 @@ void transport_send_task_abort(struct se_cmd *cmd)
*/
if (cmd->data_direction == DMA_TO_DEVICE) {
if (cmd->se_tfo->write_pending_status(cmd) != 0) {
- atomic_inc(&cmd->t_transport_aborted);
+ cmd->transport_state |= CMD_T_ABORTED;
smp_mb__after_atomic_inc();
}
}
@@ -4655,7 +4764,7 @@ static int transport_generic_do_tmr(struct se_cmd *cmd)
switch (tmr->function) {
case TMR_ABORT_TASK:
- tmr->response = TMR_FUNCTION_REJECTED;
+ core_tmr_abort_task(dev, tmr, cmd->se_sess);
break;
case TMR_ABORT_TASK_SET:
case TMR_CLEAR_ACA:
diff --git a/drivers/target/target_core_ua.c b/drivers/target/target_core_ua.c
index 3e12f6bcfa10..6666a0c74f60 100644
--- a/drivers/target/target_core_ua.c
+++ b/drivers/target/target_core_ua.c
@@ -53,7 +53,7 @@ int core_scsi3_ua_check(
if (!nacl)
return 0;
- deve = &nacl->device_list[cmd->orig_fe_lun];
+ deve = nacl->device_list[cmd->orig_fe_lun];
if (!atomic_read(&deve->ua_count))
return 0;
/*
@@ -110,7 +110,7 @@ int core_scsi3_ua_allocate(
ua->ua_ascq = ascq;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[unpacked_lun];
+ deve = nacl->device_list[unpacked_lun];
spin_lock(&deve->ua_lock);
list_for_each_entry_safe(ua_p, ua_tmp, &deve->ua_list, ua_nacl_list) {
@@ -220,7 +220,7 @@ void core_scsi3_ua_for_check_condition(
return;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[cmd->orig_fe_lun];
+ deve = nacl->device_list[cmd->orig_fe_lun];
if (!atomic_read(&deve->ua_count)) {
spin_unlock_irq(&nacl->device_list_lock);
return;
@@ -289,7 +289,7 @@ int core_scsi3_ua_clear_for_request_sense(
return -EINVAL;
spin_lock_irq(&nacl->device_list_lock);
- deve = &nacl->device_list[cmd->orig_fe_lun];
+ deve = nacl->device_list[cmd->orig_fe_lun];
if (!atomic_read(&deve->ua_count)) {
spin_unlock_irq(&nacl->device_list_lock);
return -EPERM;
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index e05c55100ec6..830657908db8 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -17,7 +17,7 @@
#ifndef __TCM_FC_H__
#define __TCM_FC_H__
-#define FT_VERSION "0.3"
+#define FT_VERSION "0.4"
#define FT_NAMELEN 32 /* length of ASCII WWPNs including pad */
#define FT_TPG_NAMELEN 32 /* max length of TPG name */
@@ -113,12 +113,10 @@ struct ft_lport_acl {
* Commands
*/
struct ft_cmd {
- u32 lun; /* LUN from request */
struct ft_sess *sess; /* session held for cmd */
struct fc_seq *seq; /* sequence in exchange mgr */
struct se_cmd se_cmd; /* Local TCM I/O descriptor */
struct fc_frame *req_frame;
- unsigned char *cdb; /* pointer to CDB inside frame */
u32 write_data_len; /* data received on writes */
struct work_struct work;
/* Local sense buffer */
@@ -143,11 +141,8 @@ extern struct target_fabric_configfs *ft_configfs;
void ft_sess_put(struct ft_sess *);
int ft_sess_shutdown(struct se_session *);
void ft_sess_close(struct se_session *);
-void ft_sess_stop(struct se_session *, int, int);
-int ft_sess_logged_in(struct se_session *);
u32 ft_sess_get_index(struct se_session *);
u32 ft_sess_get_port_name(struct se_session *, unsigned char *, u32);
-void ft_sess_set_erl0(struct se_session *);
void ft_lport_add(struct fc_lport *, void *);
void ft_lport_del(struct fc_lport *, void *);
@@ -165,7 +160,6 @@ int ft_write_pending_status(struct se_cmd *);
u32 ft_get_task_tag(struct se_cmd *);
int ft_get_cmd_state(struct se_cmd *);
int ft_queue_tm_resp(struct se_cmd *);
-int ft_is_state_remove(struct se_cmd *);
/*
* other internal functions.
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index 9e7e26c74c79..62dec9715ce5 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -59,9 +59,6 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
se_cmd = &cmd->se_cmd;
pr_debug("%s: cmd %p sess %p seq %p se_cmd %p\n",
caller, cmd, cmd->sess, cmd->seq, se_cmd);
- pr_debug("%s: cmd %p cdb %p\n",
- caller, cmd, cmd->cdb);
- pr_debug("%s: cmd %p lun %d\n", caller, cmd, cmd->lun);
pr_debug("%s: cmd %p data_nents %u len %u se_cmd_flags <0x%x>\n",
caller, cmd, se_cmd->t_data_nents,
@@ -81,8 +78,6 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
caller, cmd, ep->sid, ep->did, ep->oxid, ep->rxid,
sp->id, ep->esb_stat);
}
- print_hex_dump(KERN_INFO, "ft_dump_cmd ", DUMP_PREFIX_NONE,
- 16, 4, cmd->cdb, MAX_COMMAND_SIZE, 0);
}
static void ft_free_cmd(struct ft_cmd *cmd)
@@ -249,11 +244,6 @@ int ft_get_cmd_state(struct se_cmd *se_cmd)
return 0;
}
-int ft_is_state_remove(struct se_cmd *se_cmd)
-{
- return 0; /* XXX TBD */
-}
-
/*
* FC sequence response handler for follow-on sequences (data) and aborts.
*/
@@ -325,10 +315,12 @@ static void ft_send_resp_status(struct fc_lport *lport,
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0);
sp = fr_seq(fp);
- if (sp)
+ if (sp) {
lport->tt.seq_send(lport, sp, fp);
- else
+ lport->tt.exch_done(sp);
+ } else {
lport->tt.frame_send(lport, fp);
+ }
}
/*
@@ -358,16 +350,10 @@ static void ft_send_resp_code_and_free(struct ft_cmd *cmd,
*/
static void ft_send_tm(struct ft_cmd *cmd)
{
- struct se_tmr_req *tmr;
struct fcp_cmnd *fcp;
- struct ft_sess *sess;
+ int rc;
u8 tm_func;
- transport_init_se_cmd(&cmd->se_cmd, &ft_configfs->tf_ops,
- cmd->sess->se_sess, 0, DMA_NONE, 0,
- &cmd->ft_sense_buffer[0]);
- target_get_sess_cmd(cmd->sess->se_sess, &cmd->se_cmd, false);
-
fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
switch (fcp->fc_tm_flags) {
@@ -396,44 +382,12 @@ static void ft_send_tm(struct ft_cmd *cmd)
return;
}
- pr_debug("alloc tm cmd fn %d\n", tm_func);
- tmr = core_tmr_alloc_req(&cmd->se_cmd, cmd, tm_func, GFP_KERNEL);
- if (!tmr) {
- pr_debug("alloc failed\n");
+ /* FIXME: Add referenced task tag for ABORT_TASK */
+ rc = target_submit_tmr(&cmd->se_cmd, cmd->sess->se_sess,
+ &cmd->ft_sense_buffer[0], scsilun_to_int(&fcp->fc_lun),
+ cmd, tm_func, GFP_KERNEL, 0, 0);
+ if (rc < 0)
ft_send_resp_code_and_free(cmd, FCP_TMF_FAILED);
- return;
- }
- cmd->se_cmd.se_tmr_req = tmr;
-
- switch (fcp->fc_tm_flags) {
- case FCP_TMF_LUN_RESET:
- cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun);
- if (transport_lookup_tmr_lun(&cmd->se_cmd, cmd->lun) < 0) {
- /*
- * Make sure to clean up newly allocated TMR request
- * since "unable to handle TMR request because failed
- * to get to LUN"
- */
- pr_debug("Failed to get LUN for TMR func %d, "
- "se_cmd %p, unpacked_lun %d\n",
- tm_func, &cmd->se_cmd, cmd->lun);
- ft_dump_cmd(cmd, __func__);
- sess = cmd->sess;
- transport_send_check_condition_and_sense(&cmd->se_cmd,
- cmd->se_cmd.scsi_sense_reason, 0);
- ft_sess_put(sess);
- return;
- }
- break;
- case FCP_TMF_TGT_RESET:
- case FCP_TMF_CLR_TASK_SET:
- case FCP_TMF_ABT_TASK_SET:
- case FCP_TMF_CLR_ACA:
- break;
- default:
- return;
- }
- transport_generic_handle_tmr(&cmd->se_cmd);
}
/*
@@ -538,7 +492,6 @@ static void ft_send_work(struct work_struct *work)
struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame);
struct fcp_cmnd *fcp;
int data_dir = 0;
- u32 data_len;
int task_attr;
fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp));
@@ -548,47 +501,6 @@ static void ft_send_work(struct work_struct *work)
if (fcp->fc_flags & FCP_CFL_LEN_MASK)
goto err; /* not handling longer CDBs yet */
- if (fcp->fc_tm_flags) {
- task_attr = FCP_PTA_SIMPLE;
- data_dir = DMA_NONE;
- data_len = 0;
- } else {
- switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
- case 0:
- data_dir = DMA_NONE;
- break;
- case FCP_CFL_RDDATA:
- data_dir = DMA_FROM_DEVICE;
- break;
- case FCP_CFL_WRDATA:
- data_dir = DMA_TO_DEVICE;
- break;
- case FCP_CFL_WRDATA | FCP_CFL_RDDATA:
- goto err; /* TBD not supported by tcm_fc yet */
- }
- /*
- * Locate the SAM Task Attr from fc_pri_ta
- */
- switch (fcp->fc_pri_ta & FCP_PTA_MASK) {
- case FCP_PTA_HEADQ:
- task_attr = MSG_HEAD_TAG;
- break;
- case FCP_PTA_ORDERED:
- task_attr = MSG_ORDERED_TAG;
- break;
- case FCP_PTA_ACA:
- task_attr = MSG_ACA_TAG;
- break;
- case FCP_PTA_SIMPLE: /* Fallthrough */
- default:
- task_attr = MSG_SIMPLE_TAG;
- }
-
-
- task_attr = fcp->fc_pri_ta & FCP_PTA_MASK;
- data_len = ntohl(fcp->fc_dl);
- cmd->cdb = fcp->fc_cdb;
- }
/*
* Check for FCP task management flags
*/
@@ -596,15 +508,46 @@ static void ft_send_work(struct work_struct *work)
ft_send_tm(cmd);
return;
}
+
+ switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) {
+ case 0:
+ data_dir = DMA_NONE;
+ break;
+ case FCP_CFL_RDDATA:
+ data_dir = DMA_FROM_DEVICE;
+ break;
+ case FCP_CFL_WRDATA:
+ data_dir = DMA_TO_DEVICE;
+ break;
+ case FCP_CFL_WRDATA | FCP_CFL_RDDATA:
+ goto err; /* TBD not supported by tcm_fc yet */
+ }
+ /*
+ * Locate the SAM Task Attr from fc_pri_ta
+ */
+ switch (fcp->fc_pri_ta & FCP_PTA_MASK) {
+ case FCP_PTA_HEADQ:
+ task_attr = MSG_HEAD_TAG;
+ break;
+ case FCP_PTA_ORDERED:
+ task_attr = MSG_ORDERED_TAG;
+ break;
+ case FCP_PTA_ACA:
+ task_attr = MSG_ACA_TAG;
+ break;
+ case FCP_PTA_SIMPLE: /* Fallthrough */
+ default:
+ task_attr = MSG_SIMPLE_TAG;
+ }
+
fc_seq_exch(cmd->seq)->lp->tt.seq_set_resp(cmd->seq, ft_recv_seq, cmd);
- cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun);
/*
* Use a single se_cmd->cmd_kref as we expect to release se_cmd
* directly from ft_check_stop_free callback in response path.
*/
- target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, cmd->cdb,
- &cmd->ft_sense_buffer[0], cmd->lun, data_len,
- task_attr, data_dir, 0);
+ target_submit_cmd(&cmd->se_cmd, cmd->sess->se_sess, fcp->fc_cdb,
+ &cmd->ft_sense_buffer[0], scsilun_to_int(&fcp->fc_lun),
+ ntohl(fcp->fc_dl), task_attr, data_dir, 0);
pr_debug("r_ctl %x alloc target_submit_cmd\n", fh->fh_r_ctl);
return;
diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c
index 73852fbc857b..f357039349ba 100644
--- a/drivers/target/tcm_fc/tfc_conf.c
+++ b/drivers/target/tcm_fc/tfc_conf.c
@@ -529,9 +529,6 @@ static struct target_core_fabric_ops ft_fabric_ops = {
.release_cmd = ft_release_cmd,
.shutdown_session = ft_sess_shutdown,
.close_session = ft_sess_close,
- .stop_session = ft_sess_stop,
- .fall_back_to_erl0 = ft_sess_set_erl0,
- .sess_logged_in = ft_sess_logged_in,
.sess_get_index = ft_sess_get_index,
.sess_get_initiator_sid = NULL,
.write_pending = ft_write_pending,
@@ -544,7 +541,6 @@ static struct target_core_fabric_ops ft_fabric_ops = {
.queue_tm_rsp = ft_queue_tm_resp,
.get_fabric_sense_len = ft_get_fabric_sense_len,
.set_fabric_sense_len = ft_set_fabric_sense_len,
- .is_state_remove = ft_is_state_remove,
/*
* Setup function pointers for generic logic in
* target_core_fabric_configfs.c
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
index eff512b5a2a0..cb99da920068 100644
--- a/drivers/target/tcm_fc/tfc_sess.c
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -309,11 +309,9 @@ int ft_sess_shutdown(struct se_session *se_sess)
void ft_sess_close(struct se_session *se_sess)
{
struct ft_sess *sess = se_sess->fabric_sess_ptr;
- struct fc_lport *lport;
u32 port_id;
mutex_lock(&ft_lport_lock);
- lport = sess->tport->lport;
port_id = sess->port_id;
if (port_id == -1) {
mutex_unlock(&ft_lport_lock);
@@ -328,20 +326,6 @@ void ft_sess_close(struct se_session *se_sess)
synchronize_rcu(); /* let transport deregister happen */
}
-void ft_sess_stop(struct se_session *se_sess, int sess_sleep, int conn_sleep)
-{
- struct ft_sess *sess = se_sess->fabric_sess_ptr;
-
- pr_debug("port_id %x\n", sess->port_id);
-}
-
-int ft_sess_logged_in(struct se_session *se_sess)
-{
- struct ft_sess *sess = se_sess->fabric_sess_ptr;
-
- return sess->port_id != -1;
-}
-
u32 ft_sess_get_index(struct se_session *se_sess)
{
struct ft_sess *sess = se_sess->fabric_sess_ptr;
@@ -357,11 +341,6 @@ u32 ft_sess_get_port_name(struct se_session *se_sess,
return ft_format_wwn(buf, len, sess->port_name);
}
-void ft_sess_set_erl0(struct se_session *se_sess)
-{
- /* XXX TBD called when out of memory */
-}
-
/*
* libfc ops involving sessions.
*/
diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c
index afadcd43d14e..24145c30c9b0 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/Kconfig b/drivers/tty/hvc/Kconfig
index 48cb8d3d1758..0282a83f51fb 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -66,6 +66,14 @@ config HVC_XEN
help
Xen virtual console device driver
+config HVC_XEN_FRONTEND
+ bool "Xen Hypervisor Multiple Consoles support"
+ depends on HVC_XEN
+ select XEN_XENBUS_FRONTEND
+ default y
+ help
+ Xen driver for secondary virtual consoles
+
config HVC_UDBG
bool "udbg based fake hypervisor console"
depends on PPC && EXPERIMENTAL
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index 3a0d53d6368f..ee307799271a 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/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index a1b0a75c3eae..83d5c88e7165 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -23,44 +23,74 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/types.h>
+#include <linux/list.h>
+#include <asm/io.h>
#include <asm/xen/hypervisor.h>
#include <xen/xen.h>
+#include <xen/interface/xen.h>
+#include <xen/hvm.h>
+#include <xen/grant_table.h>
#include <xen/page.h>
#include <xen/events.h>
#include <xen/interface/io/console.h>
#include <xen/hvc-console.h>
+#include <xen/xenbus.h>
#include "hvc_console.h"
#define HVC_COOKIE 0x58656e /* "Xen" in hex */
-static struct hvc_struct *hvc;
-static int xencons_irq;
+struct xencons_info {
+ struct list_head list;
+ struct xenbus_device *xbdev;
+ struct xencons_interface *intf;
+ unsigned int evtchn;
+ struct hvc_struct *hvc;
+ int irq;
+ int vtermno;
+ grant_ref_t gntref;
+};
+
+static LIST_HEAD(xenconsoles);
+static DEFINE_SPINLOCK(xencons_lock);
/* ------------------------------------------------------------------ */
-static unsigned long console_pfn = ~0ul;
+static struct xencons_info *vtermno_to_xencons(int vtermno)
+{
+ struct xencons_info *entry, *n, *ret = NULL;
+
+ if (list_empty(&xenconsoles))
+ return NULL;
+
+ list_for_each_entry_safe(entry, n, &xenconsoles, list) {
+ if (entry->vtermno == vtermno) {
+ ret = entry;
+ break;
+ }
+ }
+
+ return ret;
+}
-static inline struct xencons_interface *xencons_interface(void)
+static inline int xenbus_devid_to_vtermno(int devid)
{
- if (console_pfn == ~0ul)
- return mfn_to_virt(xen_start_info->console.domU.mfn);
- else
- return __va(console_pfn << PAGE_SHIFT);
+ return devid + HVC_COOKIE;
}
-static inline void notify_daemon(void)
+static inline void notify_daemon(struct xencons_info *cons)
{
/* Use evtchn: this is called early, before irq is set up. */
- notify_remote_via_evtchn(xen_start_info->console.domU.evtchn);
+ notify_remote_via_evtchn(cons->evtchn);
}
-static int __write_console(const char *data, int len)
+static int __write_console(struct xencons_info *xencons,
+ const char *data, int len)
{
- struct xencons_interface *intf = xencons_interface();
XENCONS_RING_IDX cons, prod;
+ struct xencons_interface *intf = xencons->intf;
int sent = 0;
cons = intf->out_cons;
@@ -75,13 +105,16 @@ static int __write_console(const char *data, int len)
intf->out_prod = prod;
if (sent)
- notify_daemon();
+ notify_daemon(xencons);
return sent;
}
static int domU_write_console(uint32_t vtermno, const char *data, int len)
{
int ret = len;
+ struct xencons_info *cons = vtermno_to_xencons(vtermno);
+ if (cons == NULL)
+ return -EINVAL;
/*
* Make sure the whole buffer is emitted, polling if
@@ -90,7 +123,7 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len)
* kernel is crippled.
*/
while (len) {
- int sent = __write_console(data, len);
+ int sent = __write_console(cons, data, len);
data += sent;
len -= sent;
@@ -104,9 +137,13 @@ static int domU_write_console(uint32_t vtermno, const char *data, int len)
static int domU_read_console(uint32_t vtermno, char *buf, int len)
{
- struct xencons_interface *intf = xencons_interface();
+ struct xencons_interface *intf;
XENCONS_RING_IDX cons, prod;
int recv = 0;
+ struct xencons_info *xencons = vtermno_to_xencons(vtermno);
+ if (xencons == NULL)
+ return -EINVAL;
+ intf = xencons->intf;
cons = intf->in_cons;
prod = intf->in_prod;
@@ -119,7 +156,7 @@ static int domU_read_console(uint32_t vtermno, char *buf, int len)
mb(); /* read ring before consuming */
intf->in_cons = cons;
- notify_daemon();
+ notify_daemon(xencons);
return recv;
}
@@ -157,68 +194,407 @@ static struct hv_ops dom0_hvc_ops = {
.notifier_hangup = notifier_hangup_irq,
};
-static int __init xen_hvc_init(void)
+static int xen_hvm_console_init(void)
+{
+ int r;
+ uint64_t v = 0;
+ unsigned long mfn;
+ struct xencons_info *info;
+
+ if (!xen_hvm_domain())
+ return -ENODEV;
+
+ info = vtermno_to_xencons(HVC_COOKIE);
+ if (!info) {
+ info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+ if (!info)
+ return -ENOMEM;
+ }
+
+ /* already configured */
+ if (info->intf != NULL)
+ return 0;
+
+ r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v);
+ if (r < 0) {
+ kfree(info);
+ return -ENODEV;
+ }
+ info->evtchn = v;
+ hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v);
+ if (r < 0) {
+ kfree(info);
+ return -ENODEV;
+ }
+ mfn = v;
+ info->intf = ioremap(mfn << PAGE_SHIFT, PAGE_SIZE);
+ if (info->intf == NULL) {
+ kfree(info);
+ return -ENODEV;
+ }
+ info->vtermno = HVC_COOKIE;
+
+ spin_lock(&xencons_lock);
+ list_add_tail(&info->list, &xenconsoles);
+ spin_unlock(&xencons_lock);
+
+ return 0;
+}
+
+static int xen_pv_console_init(void)
{
- struct hvc_struct *hp;
- struct hv_ops *ops;
+ struct xencons_info *info;
if (!xen_pv_domain())
return -ENODEV;
- if (xen_initial_domain()) {
- ops = &dom0_hvc_ops;
- xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0);
- } else {
- if (!xen_start_info->console.domU.evtchn)
- return -ENODEV;
+ if (!xen_start_info->console.domU.evtchn)
+ return -ENODEV;
- ops = &domU_hvc_ops;
- xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn);
+ info = vtermno_to_xencons(HVC_COOKIE);
+ if (!info) {
+ info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+ if (!info)
+ return -ENOMEM;
}
- if (xencons_irq < 0)
- xencons_irq = 0;
- else
- irq_set_noprobe(xencons_irq);
- hp = hvc_alloc(HVC_COOKIE, xencons_irq, ops, 256);
- if (IS_ERR(hp))
- return PTR_ERR(hp);
+ /* already configured */
+ if (info->intf != NULL)
+ return 0;
+
+ info->evtchn = xen_start_info->console.domU.evtchn;
+ info->intf = mfn_to_virt(xen_start_info->console.domU.mfn);
+ info->vtermno = HVC_COOKIE;
+
+ spin_lock(&xencons_lock);
+ list_add_tail(&info->list, &xenconsoles);
+ spin_unlock(&xencons_lock);
+
+ return 0;
+}
+
+static int xen_initial_domain_console_init(void)
+{
+ struct xencons_info *info;
+
+ if (!xen_initial_domain())
+ return -ENODEV;
+
+ info = vtermno_to_xencons(HVC_COOKIE);
+ if (!info) {
+ info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+ if (!info)
+ return -ENOMEM;
+ }
- hvc = hp;
+ info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0);
+ info->vtermno = HVC_COOKIE;
- console_pfn = mfn_to_pfn(xen_start_info->console.domU.mfn);
+ spin_lock(&xencons_lock);
+ list_add_tail(&info->list, &xenconsoles);
+ spin_unlock(&xencons_lock);
return 0;
}
void xen_console_resume(void)
{
- if (xencons_irq)
- rebind_evtchn_irq(xen_start_info->console.domU.evtchn, xencons_irq);
+ struct xencons_info *info = vtermno_to_xencons(HVC_COOKIE);
+ if (info != NULL && info->irq)
+ rebind_evtchn_irq(info->evtchn, info->irq);
+}
+
+static void xencons_disconnect_backend(struct xencons_info *info)
+{
+ if (info->irq > 0)
+ unbind_from_irqhandler(info->irq, NULL);
+ info->irq = 0;
+ if (info->evtchn > 0)
+ xenbus_free_evtchn(info->xbdev, info->evtchn);
+ info->evtchn = 0;
+ if (info->gntref > 0)
+ gnttab_free_grant_references(info->gntref);
+ info->gntref = 0;
+ if (info->hvc != NULL)
+ hvc_remove(info->hvc);
+ info->hvc = NULL;
+}
+
+static void xencons_free(struct xencons_info *info)
+{
+ free_page((unsigned long)info->intf);
+ info->intf = NULL;
+ info->vtermno = 0;
+ kfree(info);
+}
+
+static int xen_console_remove(struct xencons_info *info)
+{
+ xencons_disconnect_backend(info);
+ spin_lock(&xencons_lock);
+ list_del(&info->list);
+ spin_unlock(&xencons_lock);
+ if (info->xbdev != NULL)
+ xencons_free(info);
+ else {
+ if (xen_hvm_domain())
+ iounmap(info->intf);
+ kfree(info);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_HVC_XEN_FRONTEND
+static struct xenbus_driver xencons_driver;
+
+static int xencons_remove(struct xenbus_device *dev)
+{
+ return xen_console_remove(dev_get_drvdata(&dev->dev));
+}
+
+static int xencons_connect_backend(struct xenbus_device *dev,
+ struct xencons_info *info)
+{
+ int ret, evtchn, devid, ref, irq;
+ struct xenbus_transaction xbt;
+ grant_ref_t gref_head;
+ unsigned long mfn;
+
+ ret = xenbus_alloc_evtchn(dev, &evtchn);
+ if (ret)
+ return ret;
+ info->evtchn = evtchn;
+ irq = bind_evtchn_to_irq(evtchn);
+ if (irq < 0)
+ return irq;
+ info->irq = irq;
+ devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
+ info->hvc = hvc_alloc(xenbus_devid_to_vtermno(devid),
+ irq, &domU_hvc_ops, 256);
+ if (IS_ERR(info->hvc))
+ return PTR_ERR(info->hvc);
+ if (xen_pv_domain())
+ mfn = virt_to_mfn(info->intf);
+ else
+ mfn = __pa(info->intf) >> PAGE_SHIFT;
+ ret = gnttab_alloc_grant_references(1, &gref_head);
+ if (ret < 0)
+ return ret;
+ info->gntref = gref_head;
+ ref = gnttab_claim_grant_reference(&gref_head);
+ if (ref < 0)
+ return ref;
+ gnttab_grant_foreign_access_ref(ref, info->xbdev->otherend_id,
+ mfn, 0);
+
+ again:
+ ret = xenbus_transaction_start(&xbt);
+ if (ret) {
+ xenbus_dev_fatal(dev, ret, "starting transaction");
+ return ret;
+ }
+ ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", ref);
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_printf(xbt, dev->nodename, "port", "%u",
+ evtchn);
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_printf(xbt, dev->nodename, "type", "ioemu");
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_transaction_end(xbt, 0);
+ if (ret) {
+ if (ret == -EAGAIN)
+ goto again;
+ xenbus_dev_fatal(dev, ret, "completing transaction");
+ return ret;
+ }
+
+ xenbus_switch_state(dev, XenbusStateInitialised);
+ return 0;
+
+ error_xenbus:
+ xenbus_transaction_end(xbt, 1);
+ xenbus_dev_fatal(dev, ret, "writing xenstore");
+ return ret;
+}
+
+static int __devinit xencons_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int ret, devid;
+ struct xencons_info *info;
+
+ devid = dev->nodename[strlen(dev->nodename) - 1] - '0';
+ if (devid == 0)
+ return -ENODEV;
+
+ info = kzalloc(sizeof(struct xencons_info), GFP_KERNEL | __GFP_ZERO);
+ if (!info)
+ goto error_nomem;
+ dev_set_drvdata(&dev->dev, info);
+ info->xbdev = dev;
+ info->vtermno = xenbus_devid_to_vtermno(devid);
+ info->intf = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!info->intf)
+ goto error_nomem;
+
+ ret = xencons_connect_backend(dev, info);
+ if (ret < 0)
+ goto error;
+ spin_lock(&xencons_lock);
+ list_add_tail(&info->list, &xenconsoles);
+ spin_unlock(&xencons_lock);
+
+ return 0;
+
+ error_nomem:
+ ret = -ENOMEM;
+ xenbus_dev_fatal(dev, ret, "allocating device memory");
+ error:
+ xencons_disconnect_backend(info);
+ xencons_free(info);
+ return ret;
+}
+
+static int xencons_resume(struct xenbus_device *dev)
+{
+ struct xencons_info *info = dev_get_drvdata(&dev->dev);
+
+ xencons_disconnect_backend(info);
+ memset(info->intf, 0, PAGE_SIZE);
+ return xencons_connect_backend(dev, info);
+}
+
+static void xencons_backend_changed(struct xenbus_device *dev,
+ enum xenbus_state backend_state)
+{
+ switch (backend_state) {
+ case XenbusStateReconfiguring:
+ case XenbusStateReconfigured:
+ case XenbusStateInitialising:
+ case XenbusStateInitialised:
+ case XenbusStateUnknown:
+ case XenbusStateClosed:
+ break;
+
+ case XenbusStateInitWait:
+ break;
+
+ case XenbusStateConnected:
+ xenbus_switch_state(dev, XenbusStateConnected);
+ break;
+
+ case XenbusStateClosing:
+ xenbus_frontend_closed(dev);
+ break;
+ }
+}
+
+static const struct xenbus_device_id xencons_ids[] = {
+ { "console" },
+ { "" }
+};
+
+
+static DEFINE_XENBUS_DRIVER(xencons, "xenconsole",
+ .probe = xencons_probe,
+ .remove = xencons_remove,
+ .resume = xencons_resume,
+ .otherend_changed = xencons_backend_changed,
+);
+#endif /* CONFIG_HVC_XEN_FRONTEND */
+
+static int __init xen_hvc_init(void)
+{
+ int r;
+ struct xencons_info *info;
+ const struct hv_ops *ops;
+
+ if (!xen_domain())
+ return -ENODEV;
+
+ if (xen_initial_domain()) {
+ ops = &dom0_hvc_ops;
+ r = xen_initial_domain_console_init();
+ if (r < 0)
+ return r;
+ info = vtermno_to_xencons(HVC_COOKIE);
+ } else {
+ ops = &domU_hvc_ops;
+ if (xen_hvm_domain())
+ r = xen_hvm_console_init();
+ else
+ r = xen_pv_console_init();
+ if (r < 0)
+ return r;
+
+ info = vtermno_to_xencons(HVC_COOKIE);
+ info->irq = bind_evtchn_to_irq(info->evtchn);
+ }
+ if (info->irq < 0)
+ info->irq = 0; /* NO_IRQ */
+ else
+ irq_set_noprobe(info->irq);
+
+ info->hvc = hvc_alloc(HVC_COOKIE, info->irq, ops, 256);
+ if (IS_ERR(info->hvc)) {
+ r = PTR_ERR(info->hvc);
+ spin_lock(&xencons_lock);
+ list_del(&info->list);
+ spin_unlock(&xencons_lock);
+ if (info->irq)
+ unbind_from_irqhandler(info->irq, NULL);
+ kfree(info);
+ return r;
+ }
+
+ r = 0;
+#ifdef CONFIG_HVC_XEN_FRONTEND
+ r = xenbus_register_frontend(&xencons_driver);
+#endif
+ return r;
}
static void __exit xen_hvc_fini(void)
{
- if (hvc)
- hvc_remove(hvc);
+ struct xencons_info *entry, *next;
+
+ if (list_empty(&xenconsoles))
+ return;
+
+ list_for_each_entry_safe(entry, next, &xenconsoles, list) {
+ xen_console_remove(entry);
+ }
}
static int xen_cons_init(void)
{
- struct hv_ops *ops;
+ const struct hv_ops *ops;
- if (!xen_pv_domain())
+ if (!xen_domain())
return 0;
if (xen_initial_domain())
ops = &dom0_hvc_ops;
- else
+ else {
+ int r;
ops = &domU_hvc_ops;
+ if (xen_hvm_domain())
+ r = xen_hvm_console_init();
+ else
+ r = xen_pv_console_init();
+ if (r < 0)
+ return r;
+ }
+
hvc_instantiate(HVC_COOKIE, 0, ops);
return 0;
}
+
module_init(xen_hvc_init);
module_exit(xen_hvc_fini);
console_initcall(xen_cons_init);
@@ -230,6 +606,9 @@ static void xenboot_write_console(struct console *console, const char *string,
unsigned int linelen, off = 0;
const char *pos;
+ if (!xen_pv_domain())
+ return;
+
dom0_write_console(0, string, len);
if (xen_initial_domain())
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index d23759183b47..3436436fe2d7 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 03c14979accf..794ecb40017c 100644
--- a/drivers/tty/isicom.c
+++ b/drivers/tty/isicom.c
@@ -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 8a8d0440bab0..324467d28a54 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 17ff377e4129..c6f372dd5623 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 a09ce3ef5d74..1b2db9a3038c 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 d2256d08ee7e..94b6eda87afd 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 f96ecaec24f8..eeae7fafe9a7 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 f899996b4363..a44345a2dbb4 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 7398390e7e65..5ce782529d65 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 86090605a84e..29b695d041ec 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/atmel_serial.c b/drivers/tty/serial/atmel_serial.c
index 10605ecc99ab..f9a6be7a9bed 100644
--- a/drivers/tty/serial/atmel_serial.c
+++ b/drivers/tty/serial/atmel_serial.c
@@ -1526,6 +1526,8 @@ void __init atmel_register_uart_fns(struct atmel_port_fns *fns)
atmel_pops.set_wake = fns->set_wake;
}
+struct platform_device *atmel_default_console_device; /* the serial console device */
+
#ifdef CONFIG_SERIAL_ATMEL_CONSOLE
static void atmel_console_putchar(struct uart_port *port, int ch)
{
diff --git a/drivers/tty/serial/crisv10.c b/drivers/tty/serial/crisv10.c
index 23d791696879..5b07c0c3a10c 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 e3699a84049f..6491b8644a7f 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 d55709a7a75a..defc4e3393a3 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/imx.c b/drivers/tty/serial/imx.c
index 0b7fed746b27..e7feceeebc2f 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1508,7 +1508,7 @@ static int serial_imx_probe(struct platform_device *pdev)
ret = PTR_ERR(sport->clk);
goto unmap;
}
- clk_enable(sport->clk);
+ clk_prepare_enable(sport->clk);
sport->port.uartclk = clk_get_rate(sport->clk);
@@ -1531,8 +1531,8 @@ deinit:
if (pdata && pdata->exit)
pdata->exit(pdev);
clkput:
+ clk_disable_unprepare(sport->clk);
clk_put(sport->clk);
- clk_disable(sport->clk);
unmap:
iounmap(sport->port.membase);
free:
@@ -1552,11 +1552,10 @@ static int serial_imx_remove(struct platform_device *pdev)
if (sport) {
uart_remove_one_port(&imx_reg, &sport->port);
+ clk_disable_unprepare(sport->clk);
clk_put(sport->clk);
}
- clk_disable(sport->clk);
-
if (pdata && pdata->exit)
pdata->exit(pdev);
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index 5e85e1e14c44..fca13dc73e23 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/pxa.c b/drivers/tty/serial/pxa.c
index e2fd3d8e0ab4..5847a4b855f7 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -36,6 +36,7 @@
#include <linux/circ_buf.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -44,6 +45,8 @@
#include <linux/io.h>
#include <linux/slab.h>
+#define PXA_NAME_LEN 8
+
struct uart_pxa_port {
struct uart_port port;
unsigned char ier;
@@ -51,7 +54,7 @@ struct uart_pxa_port {
unsigned char mcr;
unsigned int lsr_break_flag;
struct clk *clk;
- char *name;
+ char name[PXA_NAME_LEN];
};
static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
@@ -781,6 +784,31 @@ static const struct dev_pm_ops serial_pxa_pm_ops = {
};
#endif
+static struct of_device_id serial_pxa_dt_ids[] = {
+ { .compatible = "mrvl,pxa-uart", },
+ { .compatible = "mrvl,mmp-uart", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, serial_pxa_dt_ids);
+
+static int serial_pxa_probe_dt(struct platform_device *pdev,
+ struct uart_pxa_port *sport)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+
+ if (!np)
+ return 1;
+
+ ret = of_alias_get_id(np, "serial");
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ return ret;
+ }
+ sport->port.line = ret;
+ return 0;
+}
+
static int serial_pxa_probe(struct platform_device *dev)
{
struct uart_pxa_port *sport;
@@ -808,20 +836,16 @@ static int serial_pxa_probe(struct platform_device *dev)
sport->port.irq = irqres->start;
sport->port.fifosize = 64;
sport->port.ops = &serial_pxa_pops;
- sport->port.line = dev->id;
sport->port.dev = &dev->dev;
sport->port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
sport->port.uartclk = clk_get_rate(sport->clk);
- switch (dev->id) {
- case 0: sport->name = "FFUART"; break;
- case 1: sport->name = "BTUART"; break;
- case 2: sport->name = "STUART"; break;
- case 3: sport->name = "HWUART"; break;
- default:
- sport->name = "???";
- break;
- }
+ ret = serial_pxa_probe_dt(dev, sport);
+ if (ret > 0)
+ sport->port.line = dev->id;
+ else if (ret < 0)
+ goto err_clk;
+ snprintf(sport->name, PXA_NAME_LEN - 1, "UART%d", sport->port.line + 1);
sport->port.membase = ioremap(mmres->start, resource_size(mmres));
if (!sport->port.membase) {
@@ -829,7 +853,7 @@ static int serial_pxa_probe(struct platform_device *dev)
goto err_clk;
}
- serial_pxa_ports[dev->id] = sport;
+ serial_pxa_ports[sport->port.line] = sport;
uart_add_one_port(&serial_pxa_reg, &sport->port);
platform_set_drvdata(dev, sport);
@@ -866,6 +890,7 @@ static struct platform_driver serial_pxa_driver = {
#ifdef CONFIG_PM
.pm = &serial_pxa_pm_ops,
#endif
+ .of_match_table = serial_pxa_dt_ids,
},
};
diff --git a/drivers/tty/serial/sa1100.c b/drivers/tty/serial/sa1100.c
index ef7a21a6a01b..2ca5959ec3fa 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/sirfsoc_uart.c b/drivers/tty/serial/sirfsoc_uart.c
index a60523fee11b..5b3eda2024fe 100644
--- a/drivers/tty/serial/sirfsoc_uart.c
+++ b/drivers/tty/serial/sirfsoc_uart.c
@@ -22,7 +22,7 @@
#include <linux/io.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
-#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/consumer.h>
#include "sirfsoc_uart.h"
@@ -673,12 +673,10 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
port->irq = res->start;
if (sirfport->hw_flow_ctrl) {
- sirfport->pmx = pinmux_get(&pdev->dev, NULL);
- ret = IS_ERR(sirfport->pmx);
+ sirfport->p = pinctrl_get_select_default(&pdev->dev);
+ ret = IS_ERR(sirfport->p);
if (ret)
- goto pmx_err;
-
- pinmux_enable(sirfport->pmx);
+ goto pin_err;
}
port->ops = &sirfsoc_uart_ops;
@@ -695,11 +693,9 @@ int sirfsoc_uart_probe(struct platform_device *pdev)
port_err:
platform_set_drvdata(pdev, NULL);
- if (sirfport->hw_flow_ctrl) {
- pinmux_disable(sirfport->pmx);
- pinmux_put(sirfport->pmx);
- }
-pmx_err:
+ if (sirfport->hw_flow_ctrl)
+ pinctrl_put(sirfport->p);
+pin_err:
irq_err:
devm_iounmap(&pdev->dev, port->membase);
err:
@@ -711,10 +707,8 @@ static int sirfsoc_uart_remove(struct platform_device *pdev)
struct sirfsoc_uart_port *sirfport = platform_get_drvdata(pdev);
struct uart_port *port = &sirfport->port;
platform_set_drvdata(pdev, NULL);
- if (sirfport->hw_flow_ctrl) {
- pinmux_disable(sirfport->pmx);
- pinmux_put(sirfport->pmx);
- }
+ if (sirfport->hw_flow_ctrl)
+ pinctrl_put(sirfport->p);
devm_iounmap(&pdev->dev, port->membase);
uart_remove_one_port(&sirfsoc_uart_drv, port);
return 0;
diff --git a/drivers/tty/serial/sirfsoc_uart.h b/drivers/tty/serial/sirfsoc_uart.h
index fc64260fa93c..6e207fdc2fed 100644
--- a/drivers/tty/serial/sirfsoc_uart.h
+++ b/drivers/tty/serial/sirfsoc_uart.h
@@ -162,7 +162,7 @@ struct sirfsoc_uart_port {
unsigned char ms_enabled;
struct uart_port port;
- struct pinmux *pmx;
+ struct pinctrl *p;
};
/* Hardware Flow Control */
diff --git a/drivers/tty/serial/sn_console.c b/drivers/tty/serial/sn_console.c
index 4e1b5515f881..1c6de9f58699 100644
--- a/drivers/tty/serial/sn_console.c
+++ b/drivers/tty/serial/sn_console.c
@@ -743,6 +743,7 @@ static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port)
spin_lock_irqsave(&port->sc_port.lock, flags);
port->sc_port.irq = SGI_UART_VECTOR;
port->sc_ops = &intr_ops;
+ irq_set_handler(port->sc_port.irq, handle_level_irq);
/* turn on receive interrupts */
ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV);
diff --git a/drivers/tty/serial/sunhv.c b/drivers/tty/serial/sunhv.c
index 3ba5d285c2d0..505961cfd934 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 62dacd0ba526..f0d93eb7e6ec 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 d3ca6da129fe..675303b8ed84 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 da4415842a43..b3b70b0bf85b 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 b7455b526080..4001eee6c08d 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 8e518da85fd5..593d40ad0a6b 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 34b1a3c43066..aa1debf97cc7 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 4fb6c4b31b79..a3dddc12d2fe 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 dd8a938510ca..d939bd705c71 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 9314d93c1a20..a1b9a2f68567 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 84c4a7d5603e..3bdd4b19dd06 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 e4405e088589..cbd8f5f80596 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -16,7 +16,7 @@ config USB_ARCH_HAS_OHCI
# ARM:
default y if SA1111
default y if ARCH_OMAP
- default y if ARCH_S3C2410
+ default y if ARCH_S3C24XX
default y if PXA27x
default y if PXA3xx
default y if ARCH_EP93XX
@@ -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 c14a3972953a..2633f7595116 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_AT91CAP9 && !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
@@ -150,7 +150,7 @@ config USB_AT91
config USB_ATMEL_USBA
tristate "Atmel USBA"
select USB_GADGET_DUALSPEED
- depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
+ depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
@@ -284,7 +284,7 @@ config USB_IMX
config USB_S3C2410
tristate "S3C2410 USB Device Controller"
- depends on ARCH_S3C2410
+ depends on ARCH_S3C24XX
help
Samsung's S3C2410 is an ARM-4 processor with an integrated
full speed USB 1.1 device controller. It has 4 configurable
@@ -299,7 +299,7 @@ config USB_S3C2410_DEBUG
config USB_S3C_HSUDC
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
- depends on ARCH_S3C2410
+ depends on ARCH_S3C24XX
select USB_GADGET_DUALSPEED
help
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 2204a4c68d85..77779271f487 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 15a8cdb2ded5..0c935d7c65bd 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -29,17 +29,19 @@
#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>
#include <mach/cpu.h>
#include <mach/at91sam9261_matrix.h>
+#include <mach/at91_matrix.h>
#include "at91_udc.h"
@@ -910,9 +912,9 @@ static void pullup(struct at91_udc *udc, int is_on)
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
u32 usbpucr;
- usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR);
+ usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR);
usbpucr |= AT91_MATRIX_USBPUCR_PUON;
- at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr);
+ at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr);
}
} else {
stop_activity(udc);
@@ -928,9 +930,9 @@ static void pullup(struct at91_udc *udc, int is_on)
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
u32 usbpucr;
- usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR);
+ usbpucr = at91_matrix_read(AT91_MATRIX_USBPUCR);
usbpucr &= ~AT91_MATRIX_USBPUCR_PUON;
- at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr);
+ at91_matrix_write(AT91_MATRIX_USBPUCR, usbpucr);
}
clk_off(udc);
}
@@ -1706,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;
@@ -1741,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);
@@ -1970,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,
@@ -1978,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/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index 5e10f651ad63..9f98508966d1 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -332,12 +332,12 @@ static int vbus_is_present(struct usba_udc *udc)
static void toggle_bias(int is_on)
{
- unsigned int uckr = at91_sys_read(AT91_CKGR_UCKR);
+ unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
if (is_on)
- at91_sys_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+ at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
else
- at91_sys_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+ at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
}
#else
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index e1cd56c5e2a8..a6dfd2164166 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/f_phonet.c b/drivers/usb/gadget/f_phonet.c
index 7cdcb63b21ff..85a5cebe96b3 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -345,7 +345,7 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req)
}
skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page,
- skb->len <= 1, req->actual);
+ skb->len <= 1, req->actual, req->actual);
page = NULL;
if (req->actual < req->length) { /* Last fragment */
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index b30e21fdbb1b..5f94e79cd6b9 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 e1dfd32dc805..e151d6b87dee 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 edd52d963f14..f9cedd52cf20 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 19bbe80c2f8c..a73cf406e2a4 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 01ae56f47174..43ac7482fa91 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 a5ccabc37f30..ac335af154ba 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 b44830df593e..3b4b6dd0f95a 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 d83134b0f78a..4e4dc1f5f388 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 1b33634f2736..41ed69c96d8c 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 d3cdffea9c8a..73a934a170d1 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 ab9c65e2c1d5..195524cde6c3 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 a5a3ef1f0096..19f318ababa2 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 aede6374e4b6..057cdda7a489 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 924880087a74..9e65e3091c8a 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 9e63cdf1ab75..2ed112d3e159 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 77afabc77f9b..db8963f5fbce 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>
@@ -448,10 +450,11 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
/* From the GPIO notifying the over-current situation, find
* out the corresponding port */
- gpio = irq_to_gpio(irq);
for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) {
- if (pdata->overcurrent_pin[port] == gpio)
+ if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
+ gpio = pdata->overcurrent_pin[port];
break;
+ }
}
if (port == ARRAY_SIZE(pdata->overcurrent_pin)) {
@@ -476,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]))
@@ -595,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 cd5e382db89c..235171f29460 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>
@@ -1000,7 +999,7 @@ MODULE_LICENSE ("GPL");
#define SA1111_DRIVER ohci_hcd_sa1111_driver
#endif
-#if defined(CONFIG_ARCH_S3C2410) || defined(CONFIG_ARCH_S3C64XX)
+#if defined(CONFIG_ARCH_S3C24XX) || defined(CONFIG_ARCH_S3C64XX)
#include "ohci-s3c2410.c"
#define PLATFORM_DRIVER ohci_hcd_s3c2410_driver
#endif
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index 4bde4f9821ba..e1004fb37bd9 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 015c7c62ed49..3b38030b02a8 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/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index 7732d69e49e0..11de5f1be981 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -893,4 +893,5 @@ static void __devinit quirk_usb_early_handoff(struct pci_dev *pdev)
quirk_usb_handoff_xhci(pdev);
pci_disable_device(pdev);
}
-DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, quirk_usb_early_handoff);
+DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_ANY_ID, PCI_ANY_ID,
+ PCI_CLASS_SERIAL_USB, 8, quirk_usb_early_handoff);
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 2a2cce2d2fa7..91ce1c02e617 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 16dd6a6abf00..dbbd1ba25224 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 e37dea87bb56..e4db350602b8 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/serial/option.c b/drivers/usb/serial/option.c
index 6815701cf656..836cfa9a515f 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -903,8 +903,10 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0165, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0167, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1008, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
diff --git a/drivers/uwb/allocator.c b/drivers/uwb/allocator.c
index e45e673b8770..6e3e713f0ef7 100644
--- a/drivers/uwb/allocator.c
+++ b/drivers/uwb/allocator.c
@@ -334,10 +334,8 @@ int uwb_rsv_find_best_allocation(struct uwb_rsv *rsv, struct uwb_mas_bm *availab
/* fill the not available vector from the available bm */
- for (bit_index = 0; bit_index < UWB_NUM_MAS; bit_index++) {
- if (!test_bit(bit_index, available->bm))
- ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL;
- }
+ for_each_clear_bit(bit_index, available->bm, UWB_NUM_MAS)
+ ai->bm[bit_index] = UWB_RSV_MAS_NOT_AVAIL;
if (ai->max_interval == 1) {
get_row_descriptors(ai);
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index 9dab1f51dd43..f0da2c32fbde 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -588,7 +588,7 @@ static int vhost_net_release(struct inode *inode, struct file *f)
vhost_net_stop(n, &tx_sock, &rx_sock);
vhost_net_flush(n);
- vhost_dev_cleanup(&n->dev);
+ vhost_dev_cleanup(&n->dev, false);
if (tx_sock)
fput(tx_sock->file);
if (rx_sock)
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index bdb2d6436b2b..947f00d8e091 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -222,6 +222,8 @@ static int vhost_worker(void *data)
if (work) {
__set_current_state(TASK_RUNNING);
work->fn(work);
+ if (need_resched())
+ schedule();
} else
schedule();
@@ -403,7 +405,7 @@ long vhost_dev_reset_owner(struct vhost_dev *dev)
if (!memory)
return -ENOMEM;
- vhost_dev_cleanup(dev);
+ vhost_dev_cleanup(dev, true);
memory->nregions = 0;
RCU_INIT_POINTER(dev->memory, memory);
@@ -434,8 +436,8 @@ int vhost_zerocopy_signal_used(struct vhost_virtqueue *vq)
return j;
}
-/* Caller should have device mutex */
-void vhost_dev_cleanup(struct vhost_dev *dev)
+/* Caller should have device mutex if and only if locked is set */
+void vhost_dev_cleanup(struct vhost_dev *dev, bool locked)
{
int i;
@@ -472,7 +474,8 @@ void vhost_dev_cleanup(struct vhost_dev *dev)
dev->log_file = NULL;
/* No one will access memory at this point */
kfree(rcu_dereference_protected(dev->memory,
- lockdep_is_held(&dev->mutex)));
+ locked ==
+ lockdep_is_held(&dev->mutex)));
RCU_INIT_POINTER(dev->memory, NULL);
WARN_ON(!list_empty(&dev->work_list));
if (dev->worker) {
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index a801e2821d03..8dcf4cca6bf2 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -163,7 +163,7 @@ struct vhost_dev {
long vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue *vqs, int nvqs);
long vhost_dev_check_owner(struct vhost_dev *);
long vhost_dev_reset_owner(struct vhost_dev *);
-void vhost_dev_cleanup(struct vhost_dev *);
+void vhost_dev_cleanup(struct vhost_dev *, bool locked);
long vhost_dev_ioctl(struct vhost_dev *, unsigned int ioctl, unsigned long arg);
int vhost_vq_access_ok(struct vhost_virtqueue *vq);
int vhost_log_access_ok(struct vhost_dev *);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 6ca0c407c144..a290be51a1f4 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1123,6 +1123,18 @@ config FB_RIVA_BACKLIGHT
help
Say Y here if you want to control the backlight of your display.
+config FB_I740
+ tristate "Intel740 support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL && FB && PCI
+ select FB_MODE_HELPERS
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ select VGASTATE
+ select FB_DDC
+ help
+ This driver supports graphics cards based on Intel740 chip.
+
config FB_I810
tristate "Intel 810/815 support (EXPERIMENTAL)"
depends on EXPERIMENTAL && FB && PCI && X86_32 && AGP_INTEL
@@ -2001,18 +2013,6 @@ config FB_SH_MOBILE_HDMI
---help---
Driver for the on-chip SH-Mobile HDMI controller.
-config FB_SH_MOBILE_MERAM
- tristate "SuperH Mobile MERAM read ahead support for LCDC"
- depends on FB_SH_MOBILE_LCDC
- default y
- ---help---
- Enable MERAM support for the SH-Mobile LCD controller.
-
- This will allow for caching of the framebuffer to provide more
- reliable access under heavy main memory bus traffic situations.
- Up to 4 memory channels can be configured, allowing 4 RGB or
- 2 YCbCr framebuffers to be configured.
-
config FB_TMIO
tristate "Toshiba Mobile IO FrameBuffer support"
depends on FB && MFD_CORE
@@ -2061,7 +2061,7 @@ config FB_S3C_DEBUG_REGWRITE
config FB_S3C2410
tristate "S3C2410 LCD framebuffer support"
- depends on FB && ARCH_S3C2410
+ depends on FB && ARCH_S3C24XX
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
@@ -2233,6 +2233,7 @@ config FB_DA8XX
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
+ select FB_CFB_REV_PIXELS_IN_BYTE
---help---
This is the frame buffer device driver for the TI LCD controller
found on DA8xx/OMAP-L1xx SoCs.
@@ -2269,6 +2270,7 @@ config XEN_FBDEV_FRONTEND
select FB_SYS_IMAGEBLIT
select FB_SYS_FOPS
select FB_DEFERRED_IO
+ select INPUT_XEN_KBDDEV_FRONTEND
select XEN_XENBUS_FRONTEND
default y
help
@@ -2411,7 +2413,7 @@ config FB_PUV3_UNIGFX
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
-
+source "drivers/video/exynos/Kconfig"
source "drivers/video/backlight/Kconfig"
if VT
@@ -2422,4 +2424,16 @@ if FB || SGI_NEWPORT_CONSOLE
source "drivers/video/logo/Kconfig"
endif
+config FB_SH_MOBILE_MERAM
+ tristate "SuperH Mobile MERAM read ahead support"
+ depends on (SUPERH || ARCH_SHMOBILE)
+ select GENERIC_ALLOCATOR
+ ---help---
+ Enable MERAM support for the SuperH controller.
+
+ This will allow for caching of the framebuffer to provide more
+ reliable access under heavy main memory bus traffic situations.
+ Up to 4 memory channels can be configured, allowing 4 RGB or
+ 2 YCbCr framebuffers to be configured.
+
endmenu
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 142606814d98..9356add945b3 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -15,6 +15,8 @@ obj-$(CONFIG_VT) += console/
obj-$(CONFIG_LOGO) += logo/
obj-y += backlight/
+obj-$(CONFIG_EXYNOS_VIDEO) += exynos/
+
obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o
obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
@@ -37,6 +39,7 @@ obj-$(CONFIG_FB_GRVGA) += grvga.o
obj-$(CONFIG_FB_PM2) += pm2fb.o
obj-$(CONFIG_FB_PM3) += pm3fb.o
+obj-$(CONFIG_FB_I740) += i740fb.o
obj-$(CONFIG_FB_MATROX) += matrox/
obj-$(CONFIG_FB_RIVA) += riva/
obj-$(CONFIG_FB_NVIDIA) += nvidia/
diff --git a/drivers/video/amifb.c b/drivers/video/amifb.c
index f23cae094f1b..887df9d81422 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/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c
index e40c00f2c2ba..d99505b16374 100644
--- a/drivers/video/atmel_lcdfb.c
+++ b/drivers/video/atmel_lcdfb.c
@@ -421,24 +421,18 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var,
var->red.length = var->green.length = var->blue.length
= var->bits_per_pixel;
break;
- case 15:
case 16:
if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB) {
/* RGB:565 mode */
var->red.offset = 11;
var->blue.offset = 0;
- var->green.length = 6;
- } else if (sinfo->lcd_wiring_mode == ATMEL_LCDC_WIRING_RGB555) {
- var->red.offset = 10;
- var->blue.offset = 0;
- var->green.length = 5;
} else {
- /* BGR:555 mode */
+ /* BGR:565 mode */
var->red.offset = 0;
- var->blue.offset = 10;
- var->green.length = 5;
+ var->blue.offset = 11;
}
var->green.offset = 5;
+ var->green.length = 6;
var->red.length = var->blue.length = 5;
break;
case 32:
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index de9da6774fd9..befcbd8ef019 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -477,7 +477,8 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
u32 sys_clksrc;
/* Allocate new device private */
- fbdev = kzalloc(sizeof(struct au1100fb_device), GFP_KERNEL);
+ fbdev = devm_kzalloc(&dev->dev, sizeof(struct au1100fb_device),
+ GFP_KERNEL);
if (!fbdev) {
print_err("fail to allocate device private record");
return -ENOMEM;
@@ -498,8 +499,9 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
au1100fb_fix.mmio_start = regs_res->start;
au1100fb_fix.mmio_len = resource_size(regs_res);
- if (!request_mem_region(au1100fb_fix.mmio_start, au1100fb_fix.mmio_len,
- DRIVER_NAME)) {
+ if (!devm_request_mem_region(au1100fb_fix.mmio_start,
+ au1100fb_fix.mmio_len,
+ DRIVER_NAME)) {
print_err("fail to lock memory region at 0x%08lx",
au1100fb_fix.mmio_start);
return -EBUSY;
@@ -514,8 +516,9 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
fbdev->fb_len = fbdev->panel->xres * fbdev->panel->yres *
(fbdev->panel->bpp >> 3) * AU1100FB_NBR_VIDEO_BUFFERS;
- fbdev->fb_mem = dma_alloc_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len),
- &fbdev->fb_phys, GFP_KERNEL);
+ fbdev->fb_mem = dmam_alloc_coherent(&dev->dev, &dev->dev,
+ PAGE_ALIGN(fbdev->fb_len),
+ &fbdev->fb_phys, GFP_KERNEL);
if (!fbdev->fb_mem) {
print_err("fail to allocate frambuffer (size: %dK))",
fbdev->fb_len / 1024);
@@ -557,14 +560,14 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
fbdev->info.fbops = &au1100fb_ops;
fbdev->info.fix = au1100fb_fix;
- if (!(fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL))) {
+ fbdev->info.pseudo_palette =
+ devm_kzalloc(&dev->dev, sizeof(u32) * 16, GFP_KERNEL);
+ if (!fbdev->info.pseudo_palette)
return -ENOMEM;
- }
if (fb_alloc_cmap(&fbdev->info.cmap, AU1100_LCD_NBR_PALETTE_ENTRIES, 0) < 0) {
print_err("Fail to allocate colormap (%d entries)",
AU1100_LCD_NBR_PALETTE_ENTRIES);
- kfree(fbdev->info.pseudo_palette);
return -EFAULT;
}
@@ -582,9 +585,6 @@ static int __devinit au1100fb_drv_probe(struct platform_device *dev)
return 0;
failed:
- if (fbdev->regs) {
- release_mem_region(fbdev->regs_phys, fbdev->regs_len);
- }
if (fbdev->fb_mem) {
dma_free_noncoherent(&dev->dev, fbdev->fb_len, fbdev->fb_mem,
fbdev->fb_phys);
@@ -592,10 +592,9 @@ failed:
if (fbdev->info.cmap.len != 0) {
fb_dealloc_cmap(&fbdev->info.cmap);
}
- kfree(fbdev);
platform_set_drvdata(dev, NULL);
- return 0;
+ return -ENODEV;
}
int au1100fb_drv_remove(struct platform_device *dev)
@@ -615,14 +614,7 @@ int au1100fb_drv_remove(struct platform_device *dev)
/* Clean up all probe data */
unregister_framebuffer(&fbdev->info);
- release_mem_region(fbdev->regs_phys, fbdev->regs_len);
-
- dma_free_coherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len), fbdev->fb_mem,
- fbdev->fb_phys);
-
fb_dealloc_cmap(&fbdev->info.cmap);
- kfree(fbdev->info.pseudo_palette);
- kfree((void*)fbdev);
return 0;
}
diff --git a/drivers/video/au1200fb.c b/drivers/video/au1200fb.c
index 04e4479d5afd..3e9a773db09f 100644
--- a/drivers/video/au1200fb.c
+++ b/drivers/video/au1200fb.c
@@ -1724,7 +1724,7 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev)
/* Allocate the framebuffer to the maximum screen size */
fbdev->fb_len = (win->w[plane].xres * win->w[plane].yres * bpp) / 8;
- fbdev->fb_mem = dma_alloc_noncoherent(&dev->dev,
+ fbdev->fb_mem = dmam_alloc_noncoherent(&dev->dev, &dev->dev,
PAGE_ALIGN(fbdev->fb_len),
&fbdev->fb_phys, GFP_KERNEL);
if (!fbdev->fb_mem) {
@@ -1788,9 +1788,6 @@ static int __devinit au1200fb_drv_probe(struct platform_device *dev)
failed:
/* NOTE: This only does the current plane/window that failed; others are still active */
- if (fbdev->fb_mem)
- dma_free_noncoherent(&dev->dev, PAGE_ALIGN(fbdev->fb_len),
- fbdev->fb_mem, fbdev->fb_phys);
if (fbi) {
if (fbi->cmap.len != 0)
fb_dealloc_cmap(&fbi->cmap);
@@ -1817,10 +1814,6 @@ static int __devexit au1200fb_drv_remove(struct platform_device *dev)
/* Clean up all probe data */
unregister_framebuffer(fbi);
- if (fbdev->fb_mem)
- dma_free_noncoherent(&dev->dev,
- PAGE_ALIGN(fbdev->fb_len),
- fbdev->fb_mem, fbdev->fb_phys);
if (fbi->cmap.len != 0)
fb_dealloc_cmap(&fbi->cmap);
kfree(fbi->pseudo_palette);
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index a1376dc73d71..f49181c73113 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;
@@ -187,7 +214,8 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
return -EINVAL;
}
- data = kzalloc(sizeof(struct pm860x_backlight_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_backlight_data),
+ GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(name, res->name, MFD_NAME_SIZE);
@@ -200,7 +228,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
data->port = pdata->flags;
if (data->port < 0) {
dev_err(&pdev->dev, "wrong platform data is assigned");
- kfree(data);
return -EINVAL;
}
@@ -211,33 +238,12 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
&pm860x_backlight_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
- kfree(data);
return PTR_ERR(bl);
}
bl->props.brightness = MAX_BRIGHTNESS;
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)
@@ -247,17 +253,14 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
return 0;
out:
backlight_device_unregister(bl);
- kfree(data);
return ret;
}
static int pm860x_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct pm860x_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
- kfree(data);
return 0;
}
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 681b36929fe4..7ed9991fa747 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -334,6 +334,27 @@ config BACKLIGHT_AAT2870
If you have a AnalogicTech AAT2870 say Y to enable the
backlight driver.
+config BACKLIGHT_LP855X
+ tristate "Backlight driver for TI LP855X"
+ depends on BACKLIGHT_CLASS_DEVICE && I2C
+ help
+ This supports TI LP8550, LP8551, LP8552, LP8553 and LP8556
+ backlight driver.
+
+config BACKLIGHT_OT200
+ tristate "Backlight driver for ot200 visualisation device"
+ depends on BACKLIGHT_CLASS_DEVICE && CS5535_MFGPT && GPIO_CS5535
+ help
+ To compile this driver as a module, choose M here: the module will be
+ called ot200_bl.
+
+config BACKLIGHT_PANDORA
+ tristate "Backlight driver for Pandora console"
+ depends on TWL4030_CORE
+ help
+ If you have a Pandora console, say Y to enable the
+ backlight driver.
+
endif # BACKLIGHT_CLASS_DEVICE
endif # BACKLIGHT_LCD_SUPPORT
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index af5cf654ec7c..8071eb656147 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -22,7 +22,9 @@ obj-$(CONFIG_BACKLIGHT_GENERIC) += generic_bl.o
obj-$(CONFIG_BACKLIGHT_HP700) += jornada720_bl.o
obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
+obj-$(CONFIG_BACKLIGHT_LP855X) += lp855x_bl.o
obj-$(CONFIG_BACKLIGHT_OMAP1) += omap1_bl.o
+obj-$(CONFIG_BACKLIGHT_PANDORA) += pandora_bl.o
obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o
obj-$(CONFIG_BACKLIGHT_CARILLO_RANCH) += cr_bllcd.o
obj-$(CONFIG_BACKLIGHT_PWM) += pwm_bl.o
@@ -38,4 +40,4 @@ obj-$(CONFIG_BACKLIGHT_ADP8870) += adp8870_bl.o
obj-$(CONFIG_BACKLIGHT_88PM860X) += 88pm860x_bl.o
obj-$(CONFIG_BACKLIGHT_PCF50633) += pcf50633-backlight.o
obj-$(CONFIG_BACKLIGHT_AAT2870) += aat2870_bl.o
-
+obj-$(CONFIG_BACKLIGHT_OT200) += ot200_bl.o
diff --git a/drivers/video/backlight/aat2870_bl.c b/drivers/video/backlight/aat2870_bl.c
index 331f1ef1dad5..7ff752288b92 100644
--- a/drivers/video/backlight/aat2870_bl.c
+++ b/drivers/video/backlight/aat2870_bl.c
@@ -145,7 +145,9 @@ static int aat2870_bl_probe(struct platform_device *pdev)
goto out;
}
- aat2870_bl = kzalloc(sizeof(struct aat2870_bl_driver_data), GFP_KERNEL);
+ aat2870_bl = devm_kzalloc(&pdev->dev,
+ sizeof(struct aat2870_bl_driver_data),
+ GFP_KERNEL);
if (!aat2870_bl) {
dev_err(&pdev->dev,
"Failed to allocate memory for aat2870 backlight\n");
@@ -162,7 +164,7 @@ static int aat2870_bl_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Failed allocate memory for backlight device\n");
ret = PTR_ERR(bd);
- goto out_kfree;
+ goto out;
}
aat2870_bl->pdev = pdev;
@@ -199,8 +201,6 @@ static int aat2870_bl_probe(struct platform_device *pdev)
out_bl_dev_unregister:
backlight_device_unregister(bd);
-out_kfree:
- kfree(aat2870_bl);
out:
return ret;
}
@@ -215,7 +215,6 @@ static int aat2870_bl_remove(struct platform_device *pdev)
backlight_update_status(bd);
backlight_device_unregister(bd);
- kfree(aat2870_bl);
return 0;
}
diff --git a/drivers/video/backlight/adp5520_bl.c b/drivers/video/backlight/adp5520_bl.c
index 2e630bf1164c..4911ea7989c8 100644
--- a/drivers/video/backlight/adp5520_bl.c
+++ b/drivers/video/backlight/adp5520_bl.c
@@ -289,7 +289,7 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev)
struct adp5520_bl *data;
int ret = 0;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -298,7 +298,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev)
if (data->pdata == NULL) {
dev_err(&pdev->dev, "missing platform data\n");
- kfree(data);
return -ENODEV;
}
@@ -314,7 +313,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev)
&adp5520_bl_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
- kfree(data);
return PTR_ERR(bl);
}
@@ -326,7 +324,6 @@ static int __devinit adp5520_bl_probe(struct platform_device *pdev)
if (ret) {
dev_err(&pdev->dev, "failed to register sysfs\n");
backlight_device_unregister(bl);
- kfree(data);
}
platform_set_drvdata(pdev, bl);
@@ -348,7 +345,6 @@ static int __devexit adp5520_bl_remove(struct platform_device *pdev)
&adp5520_bl_attr_group);
backlight_device_unregister(bl);
- kfree(data);
return 0;
}
diff --git a/drivers/video/backlight/adp8860_bl.c b/drivers/video/backlight/adp8860_bl.c
index 378276c9d3cf..550dbf0bb896 100644
--- a/drivers/video/backlight/adp8860_bl.c
+++ b/drivers/video/backlight/adp8860_bl.c
@@ -819,17 +819,7 @@ static struct i2c_driver adp8860_driver = {
.id_table = adp8860_id,
};
-static int __init adp8860_init(void)
-{
- return i2c_add_driver(&adp8860_driver);
-}
-module_init(adp8860_init);
-
-static void __exit adp8860_exit(void)
-{
- i2c_del_driver(&adp8860_driver);
-}
-module_exit(adp8860_exit);
+module_i2c_driver(adp8860_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
diff --git a/drivers/video/backlight/adp8870_bl.c b/drivers/video/backlight/adp8870_bl.c
index 6735059376d6..9be58c6f18f1 100644
--- a/drivers/video/backlight/adp8870_bl.c
+++ b/drivers/video/backlight/adp8870_bl.c
@@ -991,17 +991,7 @@ static struct i2c_driver adp8870_driver = {
.id_table = adp8870_id,
};
-static int __init adp8870_init(void)
-{
- return i2c_add_driver(&adp8870_driver);
-}
-module_init(adp8870_init);
-
-static void __exit adp8870_exit(void)
-{
- i2c_del_driver(&adp8870_driver);
-}
-module_exit(adp8870_exit);
+module_i2c_driver(adp8870_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
diff --git a/drivers/video/backlight/ams369fg06.c b/drivers/video/backlight/ams369fg06.c
index 7838a23fbdd1..7bdadc790117 100644
--- a/drivers/video/backlight/ams369fg06.c
+++ b/drivers/video/backlight/ams369fg06.c
@@ -629,18 +629,7 @@ static struct spi_driver ams369fg06_driver = {
.resume = ams369fg06_resume,
};
-static int __init ams369fg06_init(void)
-{
- return spi_register_driver(&ams369fg06_driver);
-}
-
-static void __exit ams369fg06_exit(void)
-{
- spi_unregister_driver(&ams369fg06_driver);
-}
-
-module_init(ams369fg06_init);
-module_exit(ams369fg06_exit);
+module_spi_driver(ams369fg06_driver);
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
MODULE_DESCRIPTION("ams369fg06 LCD Driver");
diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c
index be98d152b7fd..a523b255e124 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/corgi_lcd.c b/drivers/video/backlight/corgi_lcd.c
index c6533bad26f8..6dab13fe562e 100644
--- a/drivers/video/backlight/corgi_lcd.c
+++ b/drivers/video/backlight/corgi_lcd.c
@@ -629,17 +629,7 @@ static struct spi_driver corgi_lcd_driver = {
.resume = corgi_lcd_resume,
};
-static int __init corgi_lcd_init(void)
-{
- return spi_register_driver(&corgi_lcd_driver);
-}
-module_init(corgi_lcd_init);
-
-static void __exit corgi_lcd_exit(void)
-{
- spi_unregister_driver(&corgi_lcd_driver);
-}
-module_exit(corgi_lcd_exit);
+module_spi_driver(corgi_lcd_driver);
MODULE_DESCRIPTION("LCD and backlight driver for SHARP C7x0/Cxx00");
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
diff --git a/drivers/video/backlight/cr_bllcd.c b/drivers/video/backlight/cr_bllcd.c
index 6c8c54041fae..22489eb5f3e0 100644
--- a/drivers/video/backlight/cr_bllcd.c
+++ b/drivers/video/backlight/cr_bllcd.c
@@ -212,7 +212,7 @@ static int cr_backlight_probe(struct platform_device *pdev)
&gpio_bar);
gpio_bar &= ~0x3F;
- crp = kzalloc(sizeof(*crp), GFP_KERNEL);
+ crp = devm_kzalloc(&pdev->dev, sizeof(*crp), GFP_KERNEL);
if (!crp) {
lcd_device_unregister(ldp);
backlight_device_unregister(bdp);
@@ -243,7 +243,6 @@ static int cr_backlight_remove(struct platform_device *pdev)
backlight_device_unregister(crp->cr_backlight_device);
lcd_device_unregister(crp->cr_lcd_device);
pci_dev_put(lpc_dev);
- kfree(crp);
return 0;
}
diff --git a/drivers/video/backlight/da903x_bl.c b/drivers/video/backlight/da903x_bl.c
index abb4a06268f1..30e19681a30b 100644
--- a/drivers/video/backlight/da903x_bl.c
+++ b/drivers/video/backlight/da903x_bl.c
@@ -110,7 +110,7 @@ static int da903x_backlight_probe(struct platform_device *pdev)
struct backlight_properties props;
int max_brightness;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -124,7 +124,6 @@ static int da903x_backlight_probe(struct platform_device *pdev)
default:
dev_err(&pdev->dev, "invalid backlight device ID(%d)\n",
pdev->id);
- kfree(data);
return -EINVAL;
}
@@ -143,7 +142,6 @@ static int da903x_backlight_probe(struct platform_device *pdev)
&da903x_backlight_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
- kfree(data);
return PTR_ERR(bl);
}
@@ -157,10 +155,8 @@ static int da903x_backlight_probe(struct platform_device *pdev)
static int da903x_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct da903x_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
- kfree(data);
return 0;
}
diff --git a/drivers/video/backlight/ep93xx_bl.c b/drivers/video/backlight/ep93xx_bl.c
index b62b8b9063b5..08214e1f0958 100644
--- a/drivers/video/backlight/ep93xx_bl.c
+++ b/drivers/video/backlight/ep93xx_bl.c
@@ -17,11 +17,6 @@
#include <linux/fb.h>
#include <linux/backlight.h>
-#include <mach/hardware.h>
-
-#define EP93XX_RASTER_REG(x) (EP93XX_RASTER_BASE + (x))
-#define EP93XX_RASTER_BRIGHTNESS EP93XX_RASTER_REG(0x20)
-
#define EP93XX_MAX_COUNT 255
#define EP93XX_MAX_BRIGHT 255
#define EP93XX_DEF_BRIGHT 128
@@ -35,7 +30,7 @@ static int ep93xxbl_set(struct backlight_device *bl, int brightness)
{
struct ep93xxbl *ep93xxbl = bl_get_data(bl);
- __raw_writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio);
+ writel((brightness << 8) | EP93XX_MAX_COUNT, ep93xxbl->mmio);
ep93xxbl->brightness = brightness;
@@ -70,21 +65,29 @@ static int __init ep93xxbl_probe(struct platform_device *dev)
struct ep93xxbl *ep93xxbl;
struct backlight_device *bl;
struct backlight_properties props;
+ struct resource *res;
ep93xxbl = devm_kzalloc(&dev->dev, sizeof(*ep93xxbl), GFP_KERNEL);
if (!ep93xxbl)
return -ENOMEM;
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
+
/*
- * This register is located in the range already ioremap'ed by
- * the framebuffer driver. A MFD driver seems a bit of overkill
- * to handle this so use the static I/O mapping; this address
- * is already virtual.
+ * FIXME - We don't do a request_mem_region here because we are
+ * sharing the register space with the framebuffer driver (see
+ * drivers/video/ep93xx-fb.c) and doing so will cause the second
+ * loaded driver to return -EBUSY.
*
* NOTE: No locking is required; the framebuffer does not touch
* this register.
*/
- ep93xxbl->mmio = EP93XX_RASTER_BRIGHTNESS;
+ ep93xxbl->mmio = devm_ioremap(&dev->dev, res->start,
+ resource_size(res));
+ if (!ep93xxbl->mmio)
+ return -ENXIO;
memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW;
diff --git a/drivers/video/backlight/l4f00242t03.c b/drivers/video/backlight/l4f00242t03.c
index 27d1d7a29c77..6022b67285ec 100644
--- a/drivers/video/backlight/l4f00242t03.c
+++ b/drivers/video/backlight/l4f00242t03.c
@@ -274,18 +274,7 @@ static struct spi_driver l4f00242t03_driver = {
.shutdown = l4f00242t03_shutdown,
};
-static __init int l4f00242t03_init(void)
-{
- return spi_register_driver(&l4f00242t03_driver);
-}
-
-static __exit void l4f00242t03_exit(void)
-{
- spi_unregister_driver(&l4f00242t03_driver);
-}
-
-module_init(l4f00242t03_init);
-module_exit(l4f00242t03_exit);
+module_spi_driver(l4f00242t03_driver);
MODULE_AUTHOR("Alberto Panizzo <maramaopercheseimorto@gmail.com>");
MODULE_DESCRIPTION("EPSON L4F00242T03 LCD");
diff --git a/drivers/video/backlight/ld9040.c b/drivers/video/backlight/ld9040.c
index 78dafc0c8fc5..efd352be21ae 100644
--- a/drivers/video/backlight/ld9040.c
+++ b/drivers/video/backlight/ld9040.c
@@ -856,18 +856,7 @@ static struct spi_driver ld9040_driver = {
.resume = ld9040_resume,
};
-static int __init ld9040_init(void)
-{
- return spi_register_driver(&ld9040_driver);
-}
-
-static void __exit ld9040_exit(void)
-{
- spi_unregister_driver(&ld9040_driver);
-}
-
-module_init(ld9040_init);
-module_exit(ld9040_exit);
+module_spi_driver(ld9040_driver);
MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
MODULE_DESCRIPTION("ld9040 LCD Driver");
diff --git a/drivers/video/backlight/lms283gf05.c b/drivers/video/backlight/lms283gf05.c
index 4ec78cfe26ea..4161f9e3982a 100644
--- a/drivers/video/backlight/lms283gf05.c
+++ b/drivers/video/backlight/lms283gf05.c
@@ -226,18 +226,7 @@ static struct spi_driver lms283gf05_driver = {
.remove = __devexit_p(lms283gf05_remove),
};
-static __init int lms283gf05_init(void)
-{
- return spi_register_driver(&lms283gf05_driver);
-}
-
-static __exit void lms283gf05_exit(void)
-{
- spi_unregister_driver(&lms283gf05_driver);
-}
-
-module_init(lms283gf05_init);
-module_exit(lms283gf05_exit);
+module_spi_driver(lms283gf05_driver);
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
MODULE_DESCRIPTION("LCD283GF05 LCD");
diff --git a/drivers/video/backlight/lp855x_bl.c b/drivers/video/backlight/lp855x_bl.c
new file mode 100644
index 000000000000..72a0e0c917cf
--- /dev/null
+++ b/drivers/video/backlight/lp855x_bl.c
@@ -0,0 +1,331 @@
+/*
+ * TI LP855x Backlight Driver
+ *
+ * Copyright (C) 2011 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/backlight.h>
+#include <linux/err.h>
+#include <linux/lp855x.h>
+
+/* Registers */
+#define BRIGHTNESS_CTRL (0x00)
+#define DEVICE_CTRL (0x01)
+
+#define BUF_SIZE 20
+#define DEFAULT_BL_NAME "lcd-backlight"
+#define MAX_BRIGHTNESS 255
+
+struct lp855x {
+ const char *chipname;
+ enum lp855x_chip_id chip_id;
+ struct i2c_client *client;
+ struct backlight_device *bl;
+ struct device *dev;
+ struct mutex xfer_lock;
+ struct lp855x_platform_data *pdata;
+};
+
+static int lp855x_read_byte(struct lp855x *lp, u8 reg, u8 *data)
+{
+ int ret;
+
+ mutex_lock(&lp->xfer_lock);
+ ret = i2c_smbus_read_byte_data(lp->client, reg);
+ if (ret < 0) {
+ mutex_unlock(&lp->xfer_lock);
+ dev_err(lp->dev, "failed to read 0x%.2x\n", reg);
+ return ret;
+ }
+ mutex_unlock(&lp->xfer_lock);
+
+ *data = (u8)ret;
+ return 0;
+}
+
+static int lp855x_write_byte(struct lp855x *lp, u8 reg, u8 data)
+{
+ int ret;
+
+ mutex_lock(&lp->xfer_lock);
+ ret = i2c_smbus_write_byte_data(lp->client, reg, data);
+ mutex_unlock(&lp->xfer_lock);
+
+ return ret;
+}
+
+static bool lp855x_is_valid_rom_area(struct lp855x *lp, u8 addr)
+{
+ u8 start, end;
+
+ switch (lp->chip_id) {
+ case LP8550:
+ case LP8551:
+ case LP8552:
+ case LP8553:
+ start = EEPROM_START;
+ end = EEPROM_END;
+ break;
+ case LP8556:
+ start = EPROM_START;
+ end = EPROM_END;
+ break;
+ default:
+ return false;
+ }
+
+ return (addr >= start && addr <= end);
+}
+
+static int lp855x_init_registers(struct lp855x *lp)
+{
+ u8 val, addr;
+ int i, ret;
+ struct lp855x_platform_data *pd = lp->pdata;
+
+ val = pd->initial_brightness;
+ ret = lp855x_write_byte(lp, BRIGHTNESS_CTRL, val);
+ if (ret)
+ return ret;
+
+ val = pd->device_control;
+ ret = lp855x_write_byte(lp, DEVICE_CTRL, val);
+ if (ret)
+ return ret;
+
+ if (pd->load_new_rom_data && pd->size_program) {
+ for (i = 0; i < pd->size_program; i++) {
+ addr = pd->rom_data[i].addr;
+ val = pd->rom_data[i].val;
+ if (!lp855x_is_valid_rom_area(lp, addr))
+ continue;
+
+ ret = lp855x_write_byte(lp, addr, val);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int lp855x_bl_update_status(struct backlight_device *bl)
+{
+ struct lp855x *lp = bl_get_data(bl);
+ enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
+
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ bl->props.brightness = 0;
+
+ if (mode == PWM_BASED) {
+ struct lp855x_pwm_data *pd = &lp->pdata->pwm_data;
+ int br = bl->props.brightness;
+ int max_br = bl->props.max_brightness;
+
+ if (pd->pwm_set_intensity)
+ pd->pwm_set_intensity(br, max_br);
+
+ } else if (mode == REGISTER_BASED) {
+ u8 val = bl->props.brightness;
+ lp855x_write_byte(lp, BRIGHTNESS_CTRL, val);
+ }
+
+ return 0;
+}
+
+static int lp855x_bl_get_brightness(struct backlight_device *bl)
+{
+ struct lp855x *lp = bl_get_data(bl);
+ enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
+
+ if (mode == PWM_BASED) {
+ struct lp855x_pwm_data *pd = &lp->pdata->pwm_data;
+ int max_br = bl->props.max_brightness;
+
+ if (pd->pwm_get_intensity)
+ bl->props.brightness = pd->pwm_get_intensity(max_br);
+
+ } else if (mode == REGISTER_BASED) {
+ u8 val = 0;
+
+ lp855x_read_byte(lp, BRIGHTNESS_CTRL, &val);
+ bl->props.brightness = val;
+ }
+
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops lp855x_bl_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = lp855x_bl_update_status,
+ .get_brightness = lp855x_bl_get_brightness,
+};
+
+static int lp855x_backlight_register(struct lp855x *lp)
+{
+ struct backlight_device *bl;
+ struct backlight_properties props;
+ struct lp855x_platform_data *pdata = lp->pdata;
+ char *name = pdata->name ? : DEFAULT_BL_NAME;
+
+ props.type = BACKLIGHT_PLATFORM;
+ props.max_brightness = MAX_BRIGHTNESS;
+
+ if (pdata->initial_brightness > props.max_brightness)
+ pdata->initial_brightness = props.max_brightness;
+
+ props.brightness = pdata->initial_brightness;
+
+ bl = backlight_device_register(name, lp->dev, lp,
+ &lp855x_bl_ops, &props);
+ if (IS_ERR(bl))
+ return PTR_ERR(bl);
+
+ lp->bl = bl;
+
+ return 0;
+}
+
+static void lp855x_backlight_unregister(struct lp855x *lp)
+{
+ if (lp->bl)
+ backlight_device_unregister(lp->bl);
+}
+
+static ssize_t lp855x_get_chip_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp855x *lp = dev_get_drvdata(dev);
+ return scnprintf(buf, BUF_SIZE, "%s\n", lp->chipname);
+}
+
+static ssize_t lp855x_get_bl_ctl_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lp855x *lp = dev_get_drvdata(dev);
+ enum lp855x_brightness_ctrl_mode mode = lp->pdata->mode;
+ char *strmode = NULL;
+
+ if (mode == PWM_BASED)
+ strmode = "pwm based";
+ else if (mode == REGISTER_BASED)
+ strmode = "register based";
+
+ return scnprintf(buf, BUF_SIZE, "%s\n", strmode);
+}
+
+static DEVICE_ATTR(chip_id, S_IRUGO, lp855x_get_chip_id, NULL);
+static DEVICE_ATTR(bl_ctl_mode, S_IRUGO, lp855x_get_bl_ctl_mode, NULL);
+
+static struct attribute *lp855x_attributes[] = {
+ &dev_attr_chip_id.attr,
+ &dev_attr_bl_ctl_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group lp855x_attr_group = {
+ .attrs = lp855x_attributes,
+};
+
+static int lp855x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+ struct lp855x *lp;
+ struct lp855x_platform_data *pdata = cl->dev.platform_data;
+ enum lp855x_brightness_ctrl_mode mode;
+ int ret;
+
+ if (!pdata) {
+ dev_err(&cl->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EIO;
+
+ lp = devm_kzalloc(&cl->dev, sizeof(struct lp855x), GFP_KERNEL);
+ if (!lp)
+ return -ENOMEM;
+
+ mode = pdata->mode;
+ lp->client = cl;
+ lp->dev = &cl->dev;
+ lp->pdata = pdata;
+ lp->chipname = id->name;
+ lp->chip_id = id->driver_data;
+ i2c_set_clientdata(cl, lp);
+
+ mutex_init(&lp->xfer_lock);
+
+ ret = lp855x_init_registers(lp);
+ if (ret) {
+ dev_err(lp->dev, "i2c communication err: %d", ret);
+ if (mode == REGISTER_BASED)
+ goto err_dev;
+ }
+
+ ret = lp855x_backlight_register(lp);
+ if (ret) {
+ dev_err(lp->dev,
+ "failed to register backlight. err: %d\n", ret);
+ goto err_dev;
+ }
+
+ ret = sysfs_create_group(&lp->dev->kobj, &lp855x_attr_group);
+ if (ret) {
+ dev_err(lp->dev, "failed to register sysfs. err: %d\n", ret);
+ goto err_sysfs;
+ }
+
+ backlight_update_status(lp->bl);
+ return 0;
+
+err_sysfs:
+ lp855x_backlight_unregister(lp);
+err_dev:
+ return ret;
+}
+
+static int __devexit lp855x_remove(struct i2c_client *cl)
+{
+ struct lp855x *lp = i2c_get_clientdata(cl);
+
+ lp->bl->props.brightness = 0;
+ backlight_update_status(lp->bl);
+ sysfs_remove_group(&lp->dev->kobj, &lp855x_attr_group);
+ lp855x_backlight_unregister(lp);
+
+ return 0;
+}
+
+static const struct i2c_device_id lp855x_ids[] = {
+ {"lp8550", LP8550},
+ {"lp8551", LP8551},
+ {"lp8552", LP8552},
+ {"lp8553", LP8553},
+ {"lp8556", LP8556},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, lp855x_ids);
+
+static struct i2c_driver lp855x_driver = {
+ .driver = {
+ .name = "lp855x",
+ },
+ .probe = lp855x_probe,
+ .remove = __devexit_p(lp855x_remove),
+ .id_table = lp855x_ids,
+};
+
+module_i2c_driver(lp855x_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP855x Backlight driver");
+MODULE_AUTHOR("Milo Kim <milo.kim@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c
index cca43c06d3c8..333949ff3265 100644
--- a/drivers/video/backlight/ltv350qv.c
+++ b/drivers/video/backlight/ltv350qv.c
@@ -321,17 +321,7 @@ static struct spi_driver ltv350qv_driver = {
.resume = ltv350qv_resume,
};
-static int __init ltv350qv_init(void)
-{
- return spi_register_driver(&ltv350qv_driver);
-}
-
-static void __exit ltv350qv_exit(void)
-{
- spi_unregister_driver(&ltv350qv_driver);
-}
-module_init(ltv350qv_init);
-module_exit(ltv350qv_exit);
+module_spi_driver(ltv350qv_driver);
MODULE_AUTHOR("Haavard Skinnemoen (Atmel)");
MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver");
diff --git a/drivers/video/backlight/max8925_bl.c b/drivers/video/backlight/max8925_bl.c
index c915e3b53886..e833ac72e063 100644
--- a/drivers/video/backlight/max8925_bl.c
+++ b/drivers/video/backlight/max8925_bl.c
@@ -129,7 +129,8 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev)
return -EINVAL;
}
- data = kzalloc(sizeof(struct max8925_backlight_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct max8925_backlight_data),
+ GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
strncpy(name, res->name, MAX8925_NAME_SIZE);
@@ -143,7 +144,6 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev)
&max8925_backlight_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
- kfree(data);
return PTR_ERR(bl);
}
bl->props.brightness = MAX_BRIGHTNESS;
@@ -165,17 +165,14 @@ static int __devinit max8925_backlight_probe(struct platform_device *pdev)
return 0;
out:
backlight_device_unregister(bl);
- kfree(data);
return ret;
}
static int __devexit max8925_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct max8925_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
- kfree(data);
return 0;
}
diff --git a/drivers/video/backlight/omap1_bl.c b/drivers/video/backlight/omap1_bl.c
index d8cde277ec83..0175bfb08a1c 100644
--- a/drivers/video/backlight/omap1_bl.c
+++ b/drivers/video/backlight/omap1_bl.c
@@ -141,7 +141,8 @@ static int omapbl_probe(struct platform_device *pdev)
if (!pdata)
return -ENXIO;
- bl = kzalloc(sizeof(struct omap_backlight), GFP_KERNEL);
+ bl = devm_kzalloc(&pdev->dev, sizeof(struct omap_backlight),
+ GFP_KERNEL);
if (unlikely(!bl))
return -ENOMEM;
@@ -150,10 +151,8 @@ static int omapbl_probe(struct platform_device *pdev)
props.max_brightness = OMAPBL_MAX_INTENSITY;
dev = backlight_device_register("omap-bl", &pdev->dev, bl, &omapbl_ops,
&props);
- if (IS_ERR(dev)) {
- kfree(bl);
+ if (IS_ERR(dev))
return PTR_ERR(dev);
- }
bl->powermode = FB_BLANK_POWERDOWN;
bl->current_intensity = 0;
@@ -177,10 +176,8 @@ static int omapbl_probe(struct platform_device *pdev)
static int omapbl_remove(struct platform_device *pdev)
{
struct backlight_device *dev = platform_get_drvdata(pdev);
- struct omap_backlight *bl = dev_get_drvdata(&dev->dev);
backlight_device_unregister(dev);
- kfree(bl);
return 0;
}
diff --git a/drivers/video/backlight/ot200_bl.c b/drivers/video/backlight/ot200_bl.c
new file mode 100644
index 000000000000..f519d55a294c
--- /dev/null
+++ b/drivers/video/backlight/ot200_bl.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 Bachmann electronic GmbH
+ * Christian Gmeiner <christian.gmeiner@gmail.com>
+ *
+ * Backlight driver for ot200 visualisation device from
+ * Bachmann electronic GmbH.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/gpio.h>
+#include <linux/cs5535.h>
+
+static struct cs5535_mfgpt_timer *pwm_timer;
+
+/* this array defines the mapping of brightness in % to pwm frequency */
+static const u8 dim_table[101] = {0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
+ 4, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 9, 9,
+ 10, 10, 11, 11, 12, 12, 13, 14, 15, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28,
+ 30, 31, 33, 35, 37, 39, 41, 43, 45, 47, 50,
+ 53, 55, 58, 61, 65, 68, 72, 75, 79, 84, 88,
+ 93, 97, 103, 108, 114, 120, 126, 133, 140,
+ 147, 155, 163};
+
+struct ot200_backlight_data {
+ int current_brightness;
+};
+
+#define GPIO_DIMM 27
+#define SCALE 1
+#define CMP1MODE 0x2 /* compare on GE; output high on compare
+ * greater than or equal */
+#define PWM_SETUP (SCALE | CMP1MODE << 6 | MFGPT_SETUP_CNTEN)
+#define MAX_COMP2 163
+
+static int ot200_backlight_update_status(struct backlight_device *bl)
+{
+ struct ot200_backlight_data *data = bl_get_data(bl);
+ int brightness = bl->props.brightness;
+
+ if (bl->props.state & BL_CORE_FBBLANK)
+ brightness = 0;
+
+ /* enable or disable PWM timer */
+ if (brightness == 0)
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, 0);
+ else if (data->current_brightness == 0) {
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP,
+ MFGPT_SETUP_CNTEN);
+ }
+
+ /* apply new brightness value */
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1,
+ MAX_COMP2 - dim_table[brightness]);
+ data->current_brightness = brightness;
+
+ return 0;
+}
+
+static int ot200_backlight_get_brightness(struct backlight_device *bl)
+{
+ struct ot200_backlight_data *data = bl_get_data(bl);
+ return data->current_brightness;
+}
+
+static const struct backlight_ops ot200_backlight_ops = {
+ .update_status = ot200_backlight_update_status,
+ .get_brightness = ot200_backlight_get_brightness,
+};
+
+static int ot200_backlight_probe(struct platform_device *pdev)
+{
+ struct backlight_device *bl;
+ struct ot200_backlight_data *data;
+ struct backlight_properties props;
+ int retval = 0;
+
+ /* request gpio */
+ if (gpio_request(GPIO_DIMM, "ot200 backlight dimmer") < 0) {
+ dev_err(&pdev->dev, "failed to request GPIO %d\n", GPIO_DIMM);
+ return -ENODEV;
+ }
+
+ /* request timer */
+ pwm_timer = cs5535_mfgpt_alloc_timer(7, MFGPT_DOMAIN_ANY);
+ if (!pwm_timer) {
+ dev_err(&pdev->dev, "MFGPT 7 not available\n");
+ retval = -ENODEV;
+ goto error_mfgpt_alloc;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ retval = -ENOMEM;
+ goto error_kzalloc;
+ }
+
+ /* setup gpio */
+ cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_ENABLE);
+ cs5535_gpio_set(GPIO_DIMM, GPIO_OUTPUT_AUX1);
+
+ /* setup timer */
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1, 0);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP2, MAX_COMP2);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, PWM_SETUP);
+
+ data->current_brightness = 100;
+ props.max_brightness = 100;
+ props.brightness = 100;
+ props.type = BACKLIGHT_RAW;
+
+ bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, data,
+ &ot200_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ retval = PTR_ERR(bl);
+ goto error_backlight_device_register;
+ }
+
+ platform_set_drvdata(pdev, bl);
+
+ return 0;
+
+error_backlight_device_register:
+ kfree(data);
+error_kzalloc:
+ cs5535_mfgpt_free_timer(pwm_timer);
+error_mfgpt_alloc:
+ gpio_free(GPIO_DIMM);
+ return retval;
+}
+
+static int ot200_backlight_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ struct ot200_backlight_data *data = bl_get_data(bl);
+
+ backlight_device_unregister(bl);
+
+ /* on module unload set brightness to 100% */
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_COUNTER, 0);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);
+ cs5535_mfgpt_write(pwm_timer, MFGPT_REG_CMP1,
+ MAX_COMP2 - dim_table[100]);
+
+ cs5535_mfgpt_free_timer(pwm_timer);
+ gpio_free(GPIO_DIMM);
+
+ kfree(data);
+ return 0;
+}
+
+static struct platform_driver ot200_backlight_driver = {
+ .driver = {
+ .name = "ot200-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = ot200_backlight_probe,
+ .remove = ot200_backlight_remove,
+};
+
+module_platform_driver(ot200_backlight_driver);
+
+MODULE_DESCRIPTION("backlight driver for ot200 visualisation device");
+MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ot200-backlight");
diff --git a/drivers/video/backlight/pandora_bl.c b/drivers/video/backlight/pandora_bl.c
new file mode 100644
index 000000000000..4ec30748b447
--- /dev/null
+++ b/drivers/video/backlight/pandora_bl.c
@@ -0,0 +1,171 @@
+/*
+ * Backlight driver for Pandora handheld.
+ * Pandora uses TWL4030 PWM0 -> TPS61161 combo for control backlight.
+ * Based on pwm_bl.c
+ *
+ * Copyright 2009,2012 Gražvydas Ignotas <notasas@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/i2c/twl.h>
+#include <linux/err.h>
+
+#define TWL_PWM0_ON 0x00
+#define TWL_PWM0_OFF 0x01
+
+#define TWL_INTBR_GPBR1 0x0c
+#define TWL_INTBR_PMBR1 0x0d
+
+#define TWL_PMBR1_PWM0_MUXMASK 0x0c
+#define TWL_PMBR1_PWM0 0x04
+#define PWM0_CLK_ENABLE BIT(0)
+#define PWM0_ENABLE BIT(2)
+
+/* range accepted by hardware */
+#define MIN_VALUE 9
+#define MAX_VALUE 63
+#define MAX_USER_VALUE (MAX_VALUE - MIN_VALUE)
+
+#define PANDORABL_WAS_OFF BL_CORE_DRIVER1
+
+static int pandora_backlight_update_status(struct backlight_device *bl)
+{
+ int brightness = bl->props.brightness;
+ u8 r;
+
+ if (bl->props.power != FB_BLANK_UNBLANK)
+ brightness = 0;
+ if (bl->props.state & BL_CORE_FBBLANK)
+ brightness = 0;
+ if (bl->props.state & BL_CORE_SUSPENDED)
+ brightness = 0;
+
+ if ((unsigned int)brightness > MAX_USER_VALUE)
+ brightness = MAX_USER_VALUE;
+
+ if (brightness == 0) {
+ if (bl->props.state & PANDORABL_WAS_OFF)
+ goto done;
+
+ /* first disable PWM0 output, then clock */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1);
+ r &= ~PWM0_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+ r &= ~PWM0_CLK_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+
+ goto done;
+ }
+
+ if (bl->props.state & PANDORABL_WAS_OFF) {
+ /*
+ * set PWM duty cycle to max. TPS61161 seems to use this
+ * to calibrate it's PWM sensitivity when it starts.
+ */
+ twl_i2c_write_u8(TWL4030_MODULE_PWM0, MAX_VALUE,
+ TWL_PWM0_OFF);
+
+ /* first enable clock, then PWM0 out */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_GPBR1);
+ r &= ~PWM0_ENABLE;
+ r |= PWM0_CLK_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+ r |= PWM0_ENABLE;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_GPBR1);
+
+ /*
+ * TI made it very easy to enable digital control, so easy that
+ * it often triggers unintentionally and disabes PWM control,
+ * so wait until 1 wire mode detection window ends.
+ */
+ usleep_range(2000, 10000);
+ }
+
+ twl_i2c_write_u8(TWL4030_MODULE_PWM0, MIN_VALUE + brightness,
+ TWL_PWM0_OFF);
+
+done:
+ if (brightness != 0)
+ bl->props.state &= ~PANDORABL_WAS_OFF;
+ else
+ bl->props.state |= PANDORABL_WAS_OFF;
+
+ return 0;
+}
+
+static int pandora_backlight_get_brightness(struct backlight_device *bl)
+{
+ return bl->props.brightness;
+}
+
+static const struct backlight_ops pandora_backlight_ops = {
+ .options = BL_CORE_SUSPENDRESUME,
+ .update_status = pandora_backlight_update_status,
+ .get_brightness = pandora_backlight_get_brightness,
+};
+
+static int pandora_backlight_probe(struct platform_device *pdev)
+{
+ struct backlight_properties props;
+ struct backlight_device *bl;
+ u8 r;
+
+ memset(&props, 0, sizeof(props));
+ props.max_brightness = MAX_USER_VALUE;
+ props.type = BACKLIGHT_RAW;
+ bl = backlight_device_register(pdev->name, &pdev->dev,
+ NULL, &pandora_backlight_ops, &props);
+ if (IS_ERR(bl)) {
+ dev_err(&pdev->dev, "failed to register backlight\n");
+ return PTR_ERR(bl);
+ }
+
+ platform_set_drvdata(pdev, bl);
+
+ /* 64 cycle period, ON position 0 */
+ twl_i2c_write_u8(TWL4030_MODULE_PWM0, 0x80, TWL_PWM0_ON);
+
+ bl->props.state |= PANDORABL_WAS_OFF;
+ bl->props.brightness = MAX_USER_VALUE;
+ backlight_update_status(bl);
+
+ /* enable PWM function in pin mux */
+ twl_i2c_read_u8(TWL4030_MODULE_INTBR, &r, TWL_INTBR_PMBR1);
+ r &= ~TWL_PMBR1_PWM0_MUXMASK;
+ r |= TWL_PMBR1_PWM0;
+ twl_i2c_write_u8(TWL4030_MODULE_INTBR, r, TWL_INTBR_PMBR1);
+
+ return 0;
+}
+
+static int pandora_backlight_remove(struct platform_device *pdev)
+{
+ struct backlight_device *bl = platform_get_drvdata(pdev);
+ backlight_device_unregister(bl);
+ return 0;
+}
+
+static struct platform_driver pandora_backlight_driver = {
+ .driver = {
+ .name = "pandora-backlight",
+ .owner = THIS_MODULE,
+ },
+ .probe = pandora_backlight_probe,
+ .remove = pandora_backlight_remove,
+};
+
+module_platform_driver(pandora_backlight_driver);
+
+MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
+MODULE_DESCRIPTION("Pandora Backlight Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pandora-backlight");
diff --git a/drivers/video/backlight/pcf50633-backlight.c b/drivers/video/backlight/pcf50633-backlight.c
index 13e88b71daec..c65853cb9740 100644
--- a/drivers/video/backlight/pcf50633-backlight.c
+++ b/drivers/video/backlight/pcf50633-backlight.c
@@ -101,14 +101,13 @@ static const struct backlight_ops pcf50633_bl_ops = {
static int __devinit pcf50633_bl_probe(struct platform_device *pdev)
{
- int ret;
struct pcf50633_bl *pcf_bl;
struct device *parent = pdev->dev.parent;
struct pcf50633_platform_data *pcf50633_data = parent->platform_data;
struct pcf50633_bl_platform_data *pdata = pcf50633_data->backlight_data;
struct backlight_properties bl_props;
- pcf_bl = kzalloc(sizeof(*pcf_bl), GFP_KERNEL);
+ pcf_bl = devm_kzalloc(&pdev->dev, sizeof(*pcf_bl), GFP_KERNEL);
if (!pcf_bl)
return -ENOMEM;
@@ -129,10 +128,8 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev)
pcf_bl->bl = backlight_device_register(pdev->name, &pdev->dev, pcf_bl,
&pcf50633_bl_ops, &bl_props);
- if (IS_ERR(pcf_bl->bl)) {
- ret = PTR_ERR(pcf_bl->bl);
- goto err_free;
- }
+ if (IS_ERR(pcf_bl->bl))
+ return PTR_ERR(pcf_bl->bl);
platform_set_drvdata(pdev, pcf_bl);
@@ -145,11 +142,6 @@ static int __devinit pcf50633_bl_probe(struct platform_device *pdev)
backlight_update_status(pcf_bl->bl);
return 0;
-
-err_free:
- kfree(pcf_bl);
-
- return ret;
}
static int __devexit pcf50633_bl_remove(struct platform_device *pdev)
@@ -160,8 +152,6 @@ static int __devexit pcf50633_bl_remove(struct platform_device *pdev)
platform_set_drvdata(pdev, NULL);
- kfree(pcf_bl);
-
return 0;
}
diff --git a/drivers/video/backlight/platform_lcd.c b/drivers/video/backlight/platform_lcd.c
index f0bf491ed087..b6672340d6c7 100644
--- a/drivers/video/backlight/platform_lcd.c
+++ b/drivers/video/backlight/platform_lcd.c
@@ -121,9 +121,9 @@ static int __devexit platform_lcd_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st)
+static int platform_lcd_suspend(struct device *dev)
{
- struct platform_lcd *plcd = platform_get_drvdata(pdev);
+ struct platform_lcd *plcd = dev_get_drvdata(dev);
plcd->suspended = 1;
platform_lcd_set_power(plcd->lcd, plcd->power);
@@ -131,29 +131,30 @@ static int platform_lcd_suspend(struct platform_device *pdev, pm_message_t st)
return 0;
}
-static int platform_lcd_resume(struct platform_device *pdev)
+static int platform_lcd_resume(struct device *dev)
{
- struct platform_lcd *plcd = platform_get_drvdata(pdev);
+ struct platform_lcd *plcd = dev_get_drvdata(dev);
plcd->suspended = 0;
platform_lcd_set_power(plcd->lcd, plcd->power);
return 0;
}
-#else
-#define platform_lcd_suspend NULL
-#define platform_lcd_resume NULL
+
+static SIMPLE_DEV_PM_OPS(platform_lcd_pm_ops, platform_lcd_suspend,
+ platform_lcd_resume);
#endif
static struct platform_driver platform_lcd_driver = {
.driver = {
.name = "platform-lcd",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &platform_lcd_pm_ops,
+#endif
},
.probe = platform_lcd_probe,
.remove = __devexit_p(platform_lcd_remove),
- .suspend = platform_lcd_suspend,
- .resume = platform_lcd_resume,
};
module_platform_driver(platform_lcd_driver);
diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c
index 7496d04e1d3c..342b7d7cbb63 100644
--- a/drivers/video/backlight/pwm_bl.c
+++ b/drivers/video/backlight/pwm_bl.c
@@ -102,7 +102,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
return ret;
}
- pb = kzalloc(sizeof(*pb), GFP_KERNEL);
+ pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
if (!pb) {
dev_err(&pdev->dev, "no memory for state\n");
ret = -ENOMEM;
@@ -121,7 +121,7 @@ static int pwm_backlight_probe(struct platform_device *pdev)
if (IS_ERR(pb->pwm)) {
dev_err(&pdev->dev, "unable to request PWM for backlight\n");
ret = PTR_ERR(pb->pwm);
- goto err_pwm;
+ goto err_alloc;
} else
dev_dbg(&pdev->dev, "got pwm for backlight\n");
@@ -144,8 +144,6 @@ static int pwm_backlight_probe(struct platform_device *pdev)
err_bl:
pwm_free(pb->pwm);
-err_pwm:
- kfree(pb);
err_alloc:
if (data->exit)
data->exit(&pdev->dev);
@@ -162,7 +160,6 @@ static int pwm_backlight_remove(struct platform_device *pdev)
pwm_config(pb->pwm, 0, pb->period);
pwm_disable(pb->pwm);
pwm_free(pb->pwm);
- kfree(pb);
if (data->exit)
data->exit(&pdev->dev);
return 0;
diff --git a/drivers/video/backlight/s6e63m0.c b/drivers/video/backlight/s6e63m0.c
index 516db703dd24..e264f55b2574 100644
--- a/drivers/video/backlight/s6e63m0.c
+++ b/drivers/video/backlight/s6e63m0.c
@@ -909,18 +909,7 @@ static struct spi_driver s6e63m0_driver = {
.resume = s6e63m0_resume,
};
-static int __init s6e63m0_init(void)
-{
- return spi_register_driver(&s6e63m0_driver);
-}
-
-static void __exit s6e63m0_exit(void)
-{
- spi_unregister_driver(&s6e63m0_driver);
-}
-
-module_init(s6e63m0_init);
-module_exit(s6e63m0_exit);
+module_spi_driver(s6e63m0_driver);
MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
MODULE_DESCRIPTION("S6E63M0 LCD Driver");
diff --git a/drivers/video/backlight/tdo24m.c b/drivers/video/backlight/tdo24m.c
index 1997e12a1057..2368b8e5f89e 100644
--- a/drivers/video/backlight/tdo24m.c
+++ b/drivers/video/backlight/tdo24m.c
@@ -459,17 +459,7 @@ static struct spi_driver tdo24m_driver = {
.resume = tdo24m_resume,
};
-static int __init tdo24m_init(void)
-{
- return spi_register_driver(&tdo24m_driver);
-}
-module_init(tdo24m_init);
-
-static void __exit tdo24m_exit(void)
-{
- spi_unregister_driver(&tdo24m_driver);
-}
-module_exit(tdo24m_exit);
+module_spi_driver(tdo24m_driver);
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
MODULE_DESCRIPTION("Driver for Toppoly TDO24M LCD Panel");
diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c
index 425a7365470b..2b241abced43 100644
--- a/drivers/video/backlight/tosa_bl.c
+++ b/drivers/video/backlight/tosa_bl.c
@@ -181,18 +181,7 @@ static struct i2c_driver tosa_bl_driver = {
.id_table = tosa_bl_id,
};
-static int __init tosa_bl_init(void)
-{
- return i2c_add_driver(&tosa_bl_driver);
-}
-
-static void __exit tosa_bl_exit(void)
-{
- i2c_del_driver(&tosa_bl_driver);
-}
-
-module_init(tosa_bl_init);
-module_exit(tosa_bl_exit);
+module_i2c_driver(tosa_bl_driver);
MODULE_AUTHOR("Dmitry Baryshkov");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/tosa_lcd.c b/drivers/video/backlight/tosa_lcd.c
index 772f6015219a..2231aec23918 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 = {
@@ -285,18 +285,7 @@ static struct spi_driver tosa_lcd_driver = {
.resume = tosa_lcd_resume,
};
-static int __init tosa_lcd_init(void)
-{
- return spi_register_driver(&tosa_lcd_driver);
-}
-
-static void __exit tosa_lcd_exit(void)
-{
- spi_unregister_driver(&tosa_lcd_driver);
-}
-
-module_init(tosa_lcd_init);
-module_exit(tosa_lcd_exit);
+module_spi_driver(tosa_lcd_driver);
MODULE_AUTHOR("Dmitry Baryshkov");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/backlight/vgg2432a4.c b/drivers/video/backlight/vgg2432a4.c
index b49063c831e7..b617fae9aa26 100644
--- a/drivers/video/backlight/vgg2432a4.c
+++ b/drivers/video/backlight/vgg2432a4.c
@@ -262,20 +262,7 @@ static struct spi_driver vgg2432a4_driver = {
.resume = vgg2432a4_resume,
};
-/* Device driver initialisation */
-
-static int __init vgg2432a4_init(void)
-{
- return spi_register_driver(&vgg2432a4_driver);
-}
-
-static void __exit vgg2432a4_exit(void)
-{
- spi_unregister_driver(&vgg2432a4_driver);
-}
-
-module_init(vgg2432a4_init);
-module_exit(vgg2432a4_exit);
+module_spi_driver(vgg2432a4_driver);
MODULE_AUTHOR("Ben Dooks <ben-linux@fluff.org>");
MODULE_DESCRIPTION("VGG2432A4 LCD Driver");
diff --git a/drivers/video/backlight/wm831x_bl.c b/drivers/video/backlight/wm831x_bl.c
index 4e915f5eca99..5d365deb5f82 100644
--- a/drivers/video/backlight/wm831x_bl.c
+++ b/drivers/video/backlight/wm831x_bl.c
@@ -186,7 +186,7 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (data == NULL)
return -ENOMEM;
@@ -200,7 +200,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
&wm831x_backlight_ops, &props);
if (IS_ERR(bl)) {
dev_err(&pdev->dev, "failed to register backlight\n");
- kfree(data);
return PTR_ERR(bl);
}
@@ -211,7 +210,6 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
/* Disable the DCDC if it was started so we can bootstrap */
wm831x_set_bits(wm831x, WM831X_DCDC_ENABLE, WM831X_DC4_ENA, 0);
-
backlight_update_status(bl);
return 0;
@@ -220,10 +218,8 @@ static int wm831x_backlight_probe(struct platform_device *pdev)
static int wm831x_backlight_remove(struct platform_device *pdev)
{
struct backlight_device *bl = platform_get_drvdata(pdev);
- struct wm831x_backlight_data *data = bl_get_data(bl);
backlight_device_unregister(bl);
- kfree(data);
return 0;
}
diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c
index bea53c1a4950..befbc80d11fc 100644
--- a/drivers/video/bf537-lq035.c
+++ b/drivers/video/bf537-lq035.c
@@ -383,23 +383,19 @@ static int __devinit request_ports(void)
}
#if (defined(UD) && defined(LBR))
- if (gpio_request(UD, KBUILD_MODNAME)) {
+ if (gpio_request_one(UD, GPIOF_OUT_INIT_LOW, KBUILD_MODNAME)) {
pr_err("requesting GPIO %d failed\n", UD);
return -EBUSY;
}
- if (gpio_request(LBR, KBUILD_MODNAME)) {
+ if (gpio_request_one(LBR, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) {
pr_err("requesting GPIO %d failed\n", LBR);
gpio_free(UD);
return -EBUSY;
}
-
- gpio_direction_output(UD, 0);
- gpio_direction_output(LBR, 1);
-
#endif
- if (gpio_request(MOD, KBUILD_MODNAME)) {
+ if (gpio_request_one(MOD, GPIOF_OUT_INIT_HIGH, KBUILD_MODNAME)) {
pr_err("requesting GPIO %d failed\n", MOD);
#if (defined(UD) && defined(LBR))
gpio_free(LBR);
@@ -408,8 +404,6 @@ static int __devinit request_ports(void)
return -EBUSY;
}
- gpio_direction_output(MOD, 1);
-
SSYNC();
return 0;
}
diff --git a/drivers/video/bf54x-lq043fb.c b/drivers/video/bf54x-lq043fb.c
index 46b03f53985f..dc2f0047769b 100644
--- a/drivers/video/bf54x-lq043fb.c
+++ b/drivers/video/bf54x-lq043fb.c
@@ -240,7 +240,7 @@ static int request_ports(struct bfin_bf54xfb_info *fbi)
u16 eppi_req_18[] = EPPI0_18;
u16 disp = fbi->mach_info->disp;
- if (gpio_request(disp, DRIVER_NAME)) {
+ if (gpio_request_one(disp, GPIOF_OUT_INIT_HIGH, DRIVER_NAME)) {
printk(KERN_ERR "Requesting GPIO %d failed\n", disp);
return -EFAULT;
}
@@ -263,8 +263,6 @@ static int request_ports(struct bfin_bf54xfb_info *fbi)
}
}
- gpio_direction_output(disp, 1);
-
return 0;
}
diff --git a/drivers/video/bfin-lq035q1-fb.c b/drivers/video/bfin-lq035q1-fb.c
index c633068372c9..86922ac84412 100644
--- a/drivers/video/bfin-lq035q1-fb.c
+++ b/drivers/video/bfin-lq035q1-fb.c
@@ -365,10 +365,10 @@ static int __devinit bfin_lq035q1_request_ports(struct platform_device *pdev,
* Drive PPI_FS3 Low
*/
if (ANOMALY_05000400) {
- int ret = gpio_request(P_IDENT(P_PPI0_FS3), "PPI_FS3");
+ int ret = gpio_request_one(P_IDENT(P_PPI0_FS3),
+ GPIOF_OUT_INIT_LOW, "PPI_FS3");
if (ret)
return ret;
- gpio_direction_output(P_IDENT(P_PPI0_FS3), 0);
}
if (ppi16)
@@ -716,14 +716,14 @@ static int __devinit bfin_lq035q1_probe(struct platform_device *pdev)
}
if (info->disp_info->use_bl) {
- ret = gpio_request(info->disp_info->gpio_bl, "LQ035 Backlight");
+ ret = gpio_request_one(info->disp_info->gpio_bl,
+ GPIOF_OUT_INIT_LOW, "LQ035 Backlight");
if (ret) {
dev_err(&pdev->dev, "failed to request GPIO %d\n",
info->disp_info->gpio_bl);
goto out9;
}
- gpio_direction_output(info->disp_info->gpio_bl, 0);
}
ret = register_framebuffer(fbinfo);
diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c
index 811dd7f6aa41..1a268a294478 100644
--- a/drivers/video/bfin_adv7393fb.c
+++ b/drivers/video/bfin_adv7393fb.c
@@ -36,9 +36,7 @@
#include <linux/dma-mapping.h>
#include <linux/proc_fs.h>
#include <linux/platform_device.h>
-
#include <linux/i2c.h>
-#include <linux/i2c-dev.h>
#include "bfin_adv7393fb.h"
@@ -411,12 +409,13 @@ static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client,
/* Workaround "PPI Does Not Start Properly In Specific Mode" */
if (ANOMALY_05000400) {
- if (gpio_request(P_IDENT(P_PPI0_FS3), "PPI0_FS3")) {
+ ret = gpio_request_one(P_IDENT(P_PPI0_FS3), GPIOF_OUT_INIT_LOW,
+ "PPI0_FS3")
+ if (ret) {
dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n");
ret = -EBUSY;
goto out_8;
}
- gpio_direction_output(P_IDENT(P_PPI0_FS3), 0);
}
if (peripheral_request_list(ppi_pins, DRIVER_NAME)) {
diff --git a/drivers/video/bt431.h b/drivers/video/bt431.h
index c826f2787bad..04e0cfbba538 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 b7591fea7add..80f61b03e9ae 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 8745637e4b7e..2e471c22abf5 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 a122d9287d16..6d1596629040 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 850380795b05..c1527f5b47ee 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/da8xx-fb.c b/drivers/video/da8xx-fb.c
index 29577bf1f559..47118c75a4c0 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -32,6 +32,7 @@
#include <linux/console.h>
#include <linux/slab.h>
#include <video/da8xx-fb.h>
+#include <asm/div64.h>
#define DRIVER_NAME "da8xx_lcdc"
@@ -161,6 +162,7 @@ struct da8xx_fb_par {
int vsync_timeout;
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
+ unsigned int lcd_fck_rate;
#endif
void (*panel_power_ctrl)(int);
};
@@ -174,7 +176,6 @@ static struct fb_var_screeninfo da8xx_fb_var __devinitdata = {
.activate = 0,
.height = -1,
.width = -1,
- .pixclock = 46666, /* 46us - AUO display */
.accel_flags = 0,
.left_margin = LEFT_MARGIN,
.right_margin = RIGHT_MARGIN,
@@ -238,6 +239,20 @@ static struct da8xx_panel known_lcd_panels[] = {
.pxl_clk = 7833600,
.invert_pxl_clk = 0,
},
+ [2] = {
+ /* Hitachi SP10Q010 */
+ .name = "SP10Q010",
+ .width = 320,
+ .height = 240,
+ .hfp = 10,
+ .hbp = 10,
+ .hsw = 10,
+ .vfp = 10,
+ .vbp = 10,
+ .vsw = 10,
+ .pxl_clk = 7833600,
+ .invert_pxl_clk = 0,
+ },
};
/* Enable the Raster Engine of the LCD Controller */
@@ -546,7 +561,26 @@ static int fb_setcolreg(unsigned regno, unsigned red, unsigned green,
if (info->fix.visual == FB_VISUAL_DIRECTCOLOR)
return 1;
- if (info->var.bits_per_pixel == 8) {
+ if (info->var.bits_per_pixel == 4) {
+ if (regno > 15)
+ return 1;
+
+ if (info->var.grayscale) {
+ pal = regno;
+ } else {
+ red >>= 4;
+ green >>= 8;
+ blue >>= 12;
+
+ pal = (red & 0x0f00);
+ pal |= (green & 0x00f0);
+ pal |= (blue & 0x000f);
+ }
+ if (regno == 0)
+ pal |= 0x2000;
+ palette[regno] = pal;
+
+ } else if (info->var.bits_per_pixel == 8) {
red >>= 4;
green >>= 8;
blue >>= 12;
@@ -801,6 +835,7 @@ static int fb_check_var(struct fb_var_screeninfo *var,
var->blue.length = 8;
var->transp.offset = 0;
var->transp.length = 0;
+ var->nonstd = 0;
break;
case 4:
var->red.offset = 0;
@@ -811,6 +846,7 @@ static int fb_check_var(struct fb_var_screeninfo *var,
var->blue.length = 4;
var->transp.offset = 0;
var->transp.length = 0;
+ var->nonstd = FB_NONSTD_REV_PIX_IN_B;
break;
case 16: /* RGB 565 */
var->red.offset = 11;
@@ -821,6 +857,7 @@ static int fb_check_var(struct fb_var_screeninfo *var,
var->blue.length = 5;
var->transp.offset = 0;
var->transp.length = 0;
+ var->nonstd = 0;
break;
default:
err = -EINVAL;
@@ -840,11 +877,13 @@ static int lcd_da8xx_cpufreq_transition(struct notifier_block *nb,
struct da8xx_fb_par *par;
par = container_of(nb, struct da8xx_fb_par, freq_transition);
- if (val == CPUFREQ_PRECHANGE) {
- lcd_disable_raster();
- } else if (val == CPUFREQ_POSTCHANGE) {
- lcd_calc_clk_divider(par);
- lcd_enable_raster();
+ if (val == CPUFREQ_POSTCHANGE) {
+ if (par->lcd_fck_rate != clk_get_rate(par->lcdc_clk)) {
+ par->lcd_fck_rate = clk_get_rate(par->lcdc_clk);
+ lcd_disable_raster();
+ lcd_calc_clk_divider(par);
+ lcd_enable_raster();
+ }
}
return 0;
@@ -1048,6 +1087,22 @@ static struct fb_ops da8xx_fb_ops = {
.fb_blank = cfb_blank,
};
+/* Calculate and return pixel clock period in pico seconds */
+static unsigned int da8xxfb_pixel_clk_period(struct da8xx_fb_par *par)
+{
+ unsigned int lcd_clk, div;
+ unsigned int configured_pix_clk;
+ unsigned long long pix_clk_period_picosec = 1000000000000ULL;
+
+ lcd_clk = clk_get_rate(par->lcdc_clk);
+ div = lcd_clk / par->pxl_clk;
+ configured_pix_clk = (lcd_clk / div);
+
+ do_div(pix_clk_period_picosec, configured_pix_clk);
+
+ return pix_clk_period_picosec;
+}
+
static int __devinit fb_probe(struct platform_device *device)
{
struct da8xx_lcdc_platform_data *fb_pdata =
@@ -1137,6 +1192,9 @@ static int __devinit fb_probe(struct platform_device *device)
par = da8xx_fb_info->par;
par->lcdc_clk = fb_clk;
+#ifdef CONFIG_CPU_FREQ
+ par->lcd_fck_rate = clk_get_rate(fb_clk);
+#endif
par->pxl_clk = lcdc_info->pxl_clk;
if (fb_pdata->panel_power_ctrl) {
par->panel_power_ctrl = fb_pdata->panel_power_ctrl;
@@ -1209,6 +1267,11 @@ static int __devinit fb_probe(struct platform_device *device)
da8xx_fb_var.hsync_len = lcdc_info->hsw;
da8xx_fb_var.vsync_len = lcdc_info->vsw;
+ da8xx_fb_var.right_margin = lcdc_info->hfp;
+ da8xx_fb_var.left_margin = lcdc_info->hbp;
+ da8xx_fb_var.lower_margin = lcdc_info->vfp;
+ da8xx_fb_var.upper_margin = lcdc_info->vbp;
+ da8xx_fb_var.pixclock = da8xxfb_pixel_clk_period(par);
/* Initialize fbinfo */
da8xx_fb_info->flags = FBINFO_FLAG_DEFAULT;
@@ -1264,8 +1327,8 @@ static int __devinit fb_probe(struct platform_device *device)
irq_freq:
#ifdef CONFIG_CPU_FREQ
lcd_da8xx_cpufreq_deregister(par);
-#endif
err_cpu_freq:
+#endif
unregister_framebuffer(da8xx_fb_info);
err_dealloc_cmap:
diff --git a/drivers/video/dnfb.c b/drivers/video/dnfb.c
index ec56d2544c73..49e3dda1a361 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/ep93xx-fb.c b/drivers/video/ep93xx-fb.c
index 2e830ec52a5a..f8babbeee275 100644
--- a/drivers/video/ep93xx-fb.c
+++ b/drivers/video/ep93xx-fb.c
@@ -519,12 +519,15 @@ static int __devinit ep93xxfb_probe(struct platform_device *pdev)
goto failed;
}
- res = request_mem_region(res->start, resource_size(res), pdev->name);
- if (!res) {
- err = -EBUSY;
- goto failed;
- }
-
+ /*
+ * FIXME - We don't do a request_mem_region here because we are
+ * sharing the register space with the backlight driver (see
+ * drivers/video/backlight/ep93xx_bl.c) and doing so will cause
+ * the second loaded driver to return -EBUSY.
+ *
+ * NOTE: No locking is required; the backlight does not touch
+ * any of the framebuffer registers.
+ */
fbi->res = res;
fbi->mmio_base = ioremap(res->start, resource_size(res));
if (!fbi->mmio_base) {
@@ -586,8 +589,6 @@ failed:
clk_put(fbi->clk);
if (fbi->mmio_base)
iounmap(fbi->mmio_base);
- if (fbi->res)
- release_mem_region(fbi->res->start, resource_size(fbi->res));
ep93xxfb_dealloc_videomem(info);
if (&info->cmap)
fb_dealloc_cmap(&info->cmap);
@@ -608,7 +609,6 @@ static int __devexit ep93xxfb_remove(struct platform_device *pdev)
clk_disable(fbi->clk);
clk_put(fbi->clk);
iounmap(fbi->mmio_base);
- release_mem_region(fbi->res->start, resource_size(fbi->res));
ep93xxfb_dealloc_videomem(info);
fb_dealloc_cmap(&info->cmap);
diff --git a/drivers/video/exynos/Kconfig b/drivers/video/exynos/Kconfig
new file mode 100644
index 000000000000..1b035b2eb6b6
--- /dev/null
+++ b/drivers/video/exynos/Kconfig
@@ -0,0 +1,37 @@
+#
+# Exynos Video configuration
+#
+
+menuconfig EXYNOS_VIDEO
+ bool "Exynos Video driver support"
+ help
+ This enables support for EXYNOS Video device.
+
+if EXYNOS_VIDEO
+
+#
+# MIPI DSI driver
+#
+
+config EXYNOS_MIPI_DSI
+ bool "EXYNOS MIPI DSI driver support."
+ depends on ARCH_S5PV210 || ARCH_EXYNOS
+ help
+ This enables support for MIPI-DSI device.
+
+config EXYNOS_LCD_S6E8AX0
+ bool "S6E8AX0 MIPI AMOLED LCD Driver"
+ depends on (EXYNOS_MIPI_DSI && BACKLIGHT_CLASS_DEVICE && LCD_CLASS_DEVICE)
+ default n
+ help
+ If you have an S6E8AX0 MIPI AMOLED LCD Panel, say Y to enable its
+ LCD control driver.
+
+config EXYNOS_DP
+ bool "EXYNOS DP driver support"
+ depends on ARCH_EXYNOS
+ default n
+ help
+ This enables support for DP device.
+
+endif # EXYNOS_VIDEO
diff --git a/drivers/video/exynos/Makefile b/drivers/video/exynos/Makefile
new file mode 100644
index 000000000000..ec7772e452a9
--- /dev/null
+++ b/drivers/video/exynos/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the exynos video drivers.
+#
+
+obj-$(CONFIG_EXYNOS_MIPI_DSI) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
+ exynos_mipi_dsi_lowlevel.o
+obj-$(CONFIG_EXYNOS_LCD_S6E8AX0) += s6e8ax0.o
+obj-$(CONFIG_EXYNOS_DP) += exynos_dp_core.o exynos_dp_reg.o
diff --git a/drivers/video/exynos/exynos_dp_core.c b/drivers/video/exynos/exynos_dp_core.c
new file mode 100644
index 000000000000..2a4481cf260c
--- /dev/null
+++ b/drivers/video/exynos/exynos_dp_core.c
@@ -0,0 +1,1058 @@
+/*
+ * Samsung SoC DP (Display Port) interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <video/exynos_dp.h>
+
+#include <plat/cpu.h>
+
+#include "exynos_dp_core.h"
+
+static int exynos_dp_init_dp(struct exynos_dp_device *dp)
+{
+ exynos_dp_reset(dp);
+
+ /* SW defined function Normal operation */
+ exynos_dp_enable_sw_function(dp);
+
+ exynos_dp_config_interrupt(dp);
+ exynos_dp_init_analog_func(dp);
+
+ exynos_dp_init_hpd(dp);
+ exynos_dp_init_aux(dp);
+
+ return 0;
+}
+
+static int exynos_dp_detect_hpd(struct exynos_dp_device *dp)
+{
+ int timeout_loop = 0;
+
+ exynos_dp_init_hpd(dp);
+
+ udelay(200);
+
+ while (exynos_dp_get_plug_in_status(dp) != 0) {
+ timeout_loop++;
+ if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+ dev_err(dp->dev, "failed to get hpd plug status\n");
+ return -ETIMEDOUT;
+ }
+ udelay(10);
+ }
+
+ return 0;
+}
+
+static unsigned char exynos_dp_calc_edid_check_sum(unsigned char *edid_data)
+{
+ int i;
+ unsigned char sum = 0;
+
+ for (i = 0; i < EDID_BLOCK_LENGTH; i++)
+ sum = sum + edid_data[i];
+
+ return sum;
+}
+
+static int exynos_dp_read_edid(struct exynos_dp_device *dp)
+{
+ unsigned char edid[EDID_BLOCK_LENGTH * 2];
+ unsigned int extend_block = 0;
+ unsigned char sum;
+ unsigned char test_vector;
+ int retval;
+
+ /*
+ * EDID device address is 0x50.
+ * However, if necessary, you must have set upper address
+ * into E-EDID in I2C device, 0x30.
+ */
+
+ /* Read Extension Flag, Number of 128-byte EDID extension blocks */
+ exynos_dp_read_byte_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+ EDID_EXTENSION_FLAG,
+ &extend_block);
+
+ if (extend_block > 0) {
+ dev_dbg(dp->dev, "EDID data includes a single extension!\n");
+
+ /* Read EDID data */
+ retval = exynos_dp_read_bytes_from_i2c(dp, I2C_EDID_DEVICE_ADDR,
+ EDID_HEADER_PATTERN,
+ EDID_BLOCK_LENGTH,
+ &edid[EDID_HEADER_PATTERN]);
+ if (retval != 0) {
+ dev_err(dp->dev, "EDID Read failed!\n");
+ return -EIO;
+ }
+ sum = exynos_dp_calc_edid_check_sum(edid);
+ if (sum != 0) {
+ dev_err(dp->dev, "EDID bad checksum!\n");
+ return -EIO;
+ }
+
+ /* Read additional EDID data */
+ retval = exynos_dp_read_bytes_from_i2c(dp,
+ I2C_EDID_DEVICE_ADDR,
+ EDID_BLOCK_LENGTH,
+ EDID_BLOCK_LENGTH,
+ &edid[EDID_BLOCK_LENGTH]);
+ if (retval != 0) {
+ dev_err(dp->dev, "EDID Read failed!\n");
+ return -EIO;
+ }
+ sum = exynos_dp_calc_edid_check_sum(&edid[EDID_BLOCK_LENGTH]);
+ if (sum != 0) {
+ dev_err(dp->dev, "EDID bad checksum!\n");
+ return -EIO;
+ }
+
+ exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_TEST_REQUEST,
+ &test_vector);
+ if (test_vector & DPCD_TEST_EDID_READ) {
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TEST_EDID_CHECKSUM,
+ edid[EDID_BLOCK_LENGTH + EDID_CHECKSUM]);
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TEST_RESPONSE,
+ DPCD_TEST_EDID_CHECKSUM_WRITE);
+ }
+ } else {
+ dev_info(dp->dev, "EDID data does not include any extensions.\n");
+
+ /* Read EDID data */
+ retval = exynos_dp_read_bytes_from_i2c(dp,
+ I2C_EDID_DEVICE_ADDR,
+ EDID_HEADER_PATTERN,
+ EDID_BLOCK_LENGTH,
+ &edid[EDID_HEADER_PATTERN]);
+ if (retval != 0) {
+ dev_err(dp->dev, "EDID Read failed!\n");
+ return -EIO;
+ }
+ sum = exynos_dp_calc_edid_check_sum(edid);
+ if (sum != 0) {
+ dev_err(dp->dev, "EDID bad checksum!\n");
+ return -EIO;
+ }
+
+ exynos_dp_read_byte_from_dpcd(dp,
+ DPCD_ADDR_TEST_REQUEST,
+ &test_vector);
+ if (test_vector & DPCD_TEST_EDID_READ) {
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TEST_EDID_CHECKSUM,
+ edid[EDID_CHECKSUM]);
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TEST_RESPONSE,
+ DPCD_TEST_EDID_CHECKSUM_WRITE);
+ }
+ }
+
+ dev_err(dp->dev, "EDID Read success!\n");
+ return 0;
+}
+
+static int exynos_dp_handle_edid(struct exynos_dp_device *dp)
+{
+ u8 buf[12];
+ int i;
+ int retval;
+
+ /* Read DPCD DPCD_ADDR_DPCD_REV~RECEIVE_PORT1_CAP_1 */
+ exynos_dp_read_bytes_from_dpcd(dp,
+ DPCD_ADDR_DPCD_REV,
+ 12, buf);
+
+ /* Read EDID */
+ for (i = 0; i < 3; i++) {
+ retval = exynos_dp_read_edid(dp);
+ if (retval == 0)
+ break;
+ }
+
+ return retval;
+}
+
+static void exynos_dp_enable_rx_to_enhanced_mode(struct exynos_dp_device *dp,
+ bool enable)
+{
+ u8 data;
+
+ exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET, &data);
+
+ if (enable)
+ exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
+ DPCD_ENHANCED_FRAME_EN |
+ DPCD_LANE_COUNT_SET(data));
+ else
+ exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_LANE_COUNT_SET,
+ DPCD_LANE_COUNT_SET(data));
+}
+
+static int exynos_dp_is_enhanced_mode_available(struct exynos_dp_device *dp)
+{
+ u8 data;
+ int retval;
+
+ exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+ retval = DPCD_ENHANCED_FRAME_CAP(data);
+
+ return retval;
+}
+
+static void exynos_dp_set_enhanced_mode(struct exynos_dp_device *dp)
+{
+ u8 data;
+
+ data = exynos_dp_is_enhanced_mode_available(dp);
+ exynos_dp_enable_rx_to_enhanced_mode(dp, data);
+ exynos_dp_enable_enhanced_mode(dp, data);
+}
+
+static void exynos_dp_training_pattern_dis(struct exynos_dp_device *dp)
+{
+ exynos_dp_set_training_pattern(dp, DP_NONE);
+
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ DPCD_TRAINING_PATTERN_DISABLED);
+}
+
+static void exynos_dp_set_lane_lane_pre_emphasis(struct exynos_dp_device *dp,
+ int pre_emphasis, int lane)
+{
+ switch (lane) {
+ case 0:
+ exynos_dp_set_lane0_pre_emphasis(dp, pre_emphasis);
+ break;
+ case 1:
+ exynos_dp_set_lane1_pre_emphasis(dp, pre_emphasis);
+ break;
+
+ case 2:
+ exynos_dp_set_lane2_pre_emphasis(dp, pre_emphasis);
+ break;
+
+ case 3:
+ exynos_dp_set_lane3_pre_emphasis(dp, pre_emphasis);
+ break;
+ }
+}
+
+static void exynos_dp_link_start(struct exynos_dp_device *dp)
+{
+ u8 buf[5];
+ int lane;
+ int lane_count;
+
+ lane_count = dp->link_train.lane_count;
+
+ dp->link_train.lt_state = CLOCK_RECOVERY;
+ dp->link_train.eq_loop = 0;
+
+ for (lane = 0; lane < lane_count; lane++)
+ dp->link_train.cr_loop[lane] = 0;
+
+ /* Set sink to D0 (Sink Not Ready) mode. */
+ exynos_dp_write_byte_to_dpcd(dp, DPCD_ADDR_SINK_POWER_STATE,
+ DPCD_SET_POWER_STATE_D0);
+
+ /* Set link rate and count as you want to establish*/
+ exynos_dp_set_link_bandwidth(dp, dp->link_train.link_rate);
+ exynos_dp_set_lane_count(dp, dp->link_train.lane_count);
+
+ /* Setup RX configuration */
+ buf[0] = dp->link_train.link_rate;
+ buf[1] = dp->link_train.lane_count;
+ exynos_dp_write_bytes_to_dpcd(dp, DPCD_ADDR_LINK_BW_SET,
+ 2, buf);
+
+ /* Set TX pre-emphasis to minimum */
+ for (lane = 0; lane < lane_count; lane++)
+ exynos_dp_set_lane_lane_pre_emphasis(dp,
+ PRE_EMPHASIS_LEVEL_0, lane);
+
+ /* Set training pattern 1 */
+ exynos_dp_set_training_pattern(dp, TRAINING_PTN1);
+
+ /* Set RX training pattern */
+ buf[0] = DPCD_SCRAMBLING_DISABLED |
+ DPCD_TRAINING_PATTERN_1;
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET, buf[0]);
+
+ for (lane = 0; lane < lane_count; lane++)
+ buf[lane] = DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 |
+ DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0;
+ exynos_dp_write_bytes_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ lane_count, buf);
+}
+
+static unsigned char exynos_dp_get_lane_status(u8 link_status[6], int lane)
+{
+ int shift = (lane & 1) * 4;
+ u8 link_value = link_status[lane>>1];
+
+ return (link_value >> shift) & 0xf;
+}
+
+static int exynos_dp_clock_recovery_ok(u8 link_status[6], int lane_count)
+{
+ int lane;
+ u8 lane_status;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ lane_status = exynos_dp_get_lane_status(link_status, lane);
+ if ((lane_status & DPCD_LANE_CR_DONE) == 0)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int exynos_dp_channel_eq_ok(u8 link_status[6], int lane_count)
+{
+ int lane;
+ u8 lane_align;
+ u8 lane_status;
+
+ lane_align = link_status[2];
+ if ((lane_align == DPCD_INTERLANE_ALIGN_DONE) == 0)
+ return -EINVAL;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ lane_status = exynos_dp_get_lane_status(link_status, lane);
+ lane_status &= DPCD_CHANNEL_EQ_BITS;
+ if (lane_status != DPCD_CHANNEL_EQ_BITS)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static unsigned char exynos_dp_get_adjust_request_voltage(u8 adjust_request[2],
+ int lane)
+{
+ int shift = (lane & 1) * 4;
+ u8 link_value = adjust_request[lane>>1];
+
+ return (link_value >> shift) & 0x3;
+}
+
+static unsigned char exynos_dp_get_adjust_request_pre_emphasis(
+ u8 adjust_request[2],
+ int lane)
+{
+ int shift = (lane & 1) * 4;
+ u8 link_value = adjust_request[lane>>1];
+
+ return ((link_value >> shift) & 0xc) >> 2;
+}
+
+static void exynos_dp_set_lane_link_training(struct exynos_dp_device *dp,
+ u8 training_lane_set, int lane)
+{
+ switch (lane) {
+ case 0:
+ exynos_dp_set_lane0_link_training(dp, training_lane_set);
+ break;
+ case 1:
+ exynos_dp_set_lane1_link_training(dp, training_lane_set);
+ break;
+
+ case 2:
+ exynos_dp_set_lane2_link_training(dp, training_lane_set);
+ break;
+
+ case 3:
+ exynos_dp_set_lane3_link_training(dp, training_lane_set);
+ break;
+ }
+}
+
+static unsigned int exynos_dp_get_lane_link_training(
+ struct exynos_dp_device *dp,
+ int lane)
+{
+ u32 reg;
+
+ switch (lane) {
+ case 0:
+ reg = exynos_dp_get_lane0_link_training(dp);
+ break;
+ case 1:
+ reg = exynos_dp_get_lane1_link_training(dp);
+ break;
+ case 2:
+ reg = exynos_dp_get_lane2_link_training(dp);
+ break;
+ case 3:
+ reg = exynos_dp_get_lane3_link_training(dp);
+ break;
+ }
+
+ return reg;
+}
+
+static void exynos_dp_reduce_link_rate(struct exynos_dp_device *dp)
+{
+ if (dp->link_train.link_rate == LINK_RATE_2_70GBPS) {
+ /* set to reduced bit rate */
+ dp->link_train.link_rate = LINK_RATE_1_62GBPS;
+ dev_err(dp->dev, "set to bandwidth %.2x\n",
+ dp->link_train.link_rate);
+ dp->link_train.lt_state = START;
+ } else {
+ exynos_dp_training_pattern_dis(dp);
+ /* set enhanced mode if available */
+ exynos_dp_set_enhanced_mode(dp);
+ dp->link_train.lt_state = FAILED;
+ }
+}
+
+static void exynos_dp_get_adjust_train(struct exynos_dp_device *dp,
+ u8 adjust_request[2])
+{
+ int lane;
+ int lane_count;
+ u8 voltage_swing;
+ u8 pre_emphasis;
+ u8 training_lane;
+
+ lane_count = dp->link_train.lane_count;
+ for (lane = 0; lane < lane_count; lane++) {
+ voltage_swing = exynos_dp_get_adjust_request_voltage(
+ adjust_request, lane);
+ pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+ adjust_request, lane);
+ training_lane = DPCD_VOLTAGE_SWING_SET(voltage_swing) |
+ DPCD_PRE_EMPHASIS_SET(pre_emphasis);
+
+ if (voltage_swing == VOLTAGE_LEVEL_3 ||
+ pre_emphasis == PRE_EMPHASIS_LEVEL_3) {
+ training_lane |= DPCD_MAX_SWING_REACHED;
+ training_lane |= DPCD_MAX_PRE_EMPHASIS_REACHED;
+ }
+ dp->link_train.training_lane[lane] = training_lane;
+ }
+}
+
+static int exynos_dp_check_max_cr_loop(struct exynos_dp_device *dp,
+ u8 voltage_swing)
+{
+ int lane;
+ int lane_count;
+
+ lane_count = dp->link_train.lane_count;
+ for (lane = 0; lane < lane_count; lane++) {
+ if (voltage_swing == VOLTAGE_LEVEL_3 ||
+ dp->link_train.cr_loop[lane] == MAX_CR_LOOP)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int exynos_dp_process_clock_recovery(struct exynos_dp_device *dp)
+{
+ u8 data;
+ u8 link_status[6];
+ int lane;
+ int lane_count;
+ u8 buf[5];
+
+ u8 *adjust_request;
+ u8 voltage_swing;
+ u8 pre_emphasis;
+ u8 training_lane;
+
+ udelay(100);
+
+ exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
+ 6, link_status);
+ lane_count = dp->link_train.lane_count;
+
+ if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
+ /* set training pattern 2 for EQ */
+ exynos_dp_set_training_pattern(dp, TRAINING_PTN2);
+
+ adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1
+ - DPCD_ADDR_LANE0_1_STATUS);
+
+ exynos_dp_get_adjust_train(dp, adjust_request);
+
+ buf[0] = DPCD_SCRAMBLING_DISABLED |
+ DPCD_TRAINING_PATTERN_2;
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_LANE0_SET,
+ buf[0]);
+
+ for (lane = 0; lane < lane_count; lane++) {
+ exynos_dp_set_lane_link_training(dp,
+ dp->link_train.training_lane[lane],
+ lane);
+ buf[lane] = dp->link_train.training_lane[lane];
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_LANE0_SET + lane,
+ buf[lane]);
+ }
+ dp->link_train.lt_state = EQUALIZER_TRAINING;
+ } else {
+ exynos_dp_read_byte_from_dpcd(dp,
+ DPCD_ADDR_ADJUST_REQUEST_LANE0_1,
+ &data);
+ adjust_request[0] = data;
+
+ exynos_dp_read_byte_from_dpcd(dp,
+ DPCD_ADDR_ADJUST_REQUEST_LANE2_3,
+ &data);
+ adjust_request[1] = data;
+
+ for (lane = 0; lane < lane_count; lane++) {
+ training_lane = exynos_dp_get_lane_link_training(
+ dp, lane);
+ voltage_swing = exynos_dp_get_adjust_request_voltage(
+ adjust_request, lane);
+ pre_emphasis = exynos_dp_get_adjust_request_pre_emphasis(
+ adjust_request, lane);
+ if ((DPCD_VOLTAGE_SWING_GET(training_lane) == voltage_swing) &&
+ (DPCD_PRE_EMPHASIS_GET(training_lane) == pre_emphasis))
+ dp->link_train.cr_loop[lane]++;
+ dp->link_train.training_lane[lane] = training_lane;
+ }
+
+ if (exynos_dp_check_max_cr_loop(dp, voltage_swing) != 0) {
+ exynos_dp_reduce_link_rate(dp);
+ } else {
+ exynos_dp_get_adjust_train(dp, adjust_request);
+
+ for (lane = 0; lane < lane_count; lane++) {
+ exynos_dp_set_lane_link_training(dp,
+ dp->link_train.training_lane[lane],
+ lane);
+ buf[lane] = dp->link_train.training_lane[lane];
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_LANE0_SET + lane,
+ buf[lane]);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int exynos_dp_process_equalizer_training(struct exynos_dp_device *dp)
+{
+ u8 link_status[6];
+ int lane;
+ int lane_count;
+ u8 buf[5];
+ u32 reg;
+
+ u8 *adjust_request;
+
+ udelay(400);
+
+ exynos_dp_read_bytes_from_dpcd(dp, DPCD_ADDR_LANE0_1_STATUS,
+ 6, link_status);
+ lane_count = dp->link_train.lane_count;
+
+ if (exynos_dp_clock_recovery_ok(link_status, lane_count) == 0) {
+ adjust_request = link_status + (DPCD_ADDR_ADJUST_REQUEST_LANE0_1
+ - DPCD_ADDR_LANE0_1_STATUS);
+
+ if (exynos_dp_channel_eq_ok(link_status, lane_count) == 0) {
+ /* traing pattern Set to Normal */
+ exynos_dp_training_pattern_dis(dp);
+
+ dev_info(dp->dev, "Link Training success!\n");
+
+ exynos_dp_get_link_bandwidth(dp, &reg);
+ dp->link_train.link_rate = reg;
+ dev_dbg(dp->dev, "final bandwidth = %.2x\n",
+ dp->link_train.link_rate);
+
+ exynos_dp_get_lane_count(dp, &reg);
+ dp->link_train.lane_count = reg;
+ dev_dbg(dp->dev, "final lane count = %.2x\n",
+ dp->link_train.lane_count);
+ /* set enhanced mode if available */
+ exynos_dp_set_enhanced_mode(dp);
+
+ dp->link_train.lt_state = FINISHED;
+ } else {
+ /* not all locked */
+ dp->link_train.eq_loop++;
+
+ if (dp->link_train.eq_loop > MAX_EQ_LOOP) {
+ exynos_dp_reduce_link_rate(dp);
+ } else {
+ exynos_dp_get_adjust_train(dp, adjust_request);
+
+ for (lane = 0; lane < lane_count; lane++) {
+ exynos_dp_set_lane_link_training(dp,
+ dp->link_train.training_lane[lane],
+ lane);
+ buf[lane] = dp->link_train.training_lane[lane];
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_LANE0_SET + lane,
+ buf[lane]);
+ }
+ }
+ }
+ } else {
+ exynos_dp_reduce_link_rate(dp);
+ }
+
+ return 0;
+}
+
+static void exynos_dp_get_max_rx_bandwidth(struct exynos_dp_device *dp,
+ u8 *bandwidth)
+{
+ u8 data;
+
+ /*
+ * For DP rev.1.1, Maximum link rate of Main Link lanes
+ * 0x06 = 1.62 Gbps, 0x0a = 2.7 Gbps
+ */
+ exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LINK_RATE, &data);
+ *bandwidth = data;
+}
+
+static void exynos_dp_get_max_rx_lane_count(struct exynos_dp_device *dp,
+ u8 *lane_count)
+{
+ u8 data;
+
+ /*
+ * For DP rev.1.1, Maximum number of Main Link lanes
+ * 0x01 = 1 lane, 0x02 = 2 lanes, 0x04 = 4 lanes
+ */
+ exynos_dp_read_byte_from_dpcd(dp, DPCD_ADDR_MAX_LANE_COUNT, &data);
+ *lane_count = DPCD_MAX_LANE_COUNT(data);
+}
+
+static void exynos_dp_init_training(struct exynos_dp_device *dp,
+ enum link_lane_count_type max_lane,
+ enum link_rate_type max_rate)
+{
+ /*
+ * MACRO_RST must be applied after the PLL_LOCK to avoid
+ * the DP inter pair skew issue for at least 10 us
+ */
+ exynos_dp_reset_macro(dp);
+
+ /* Initialize by reading RX's DPCD */
+ exynos_dp_get_max_rx_bandwidth(dp, &dp->link_train.link_rate);
+ exynos_dp_get_max_rx_lane_count(dp, &dp->link_train.lane_count);
+
+ if ((dp->link_train.link_rate != LINK_RATE_1_62GBPS) &&
+ (dp->link_train.link_rate != LINK_RATE_2_70GBPS)) {
+ dev_err(dp->dev, "Rx Max Link Rate is abnormal :%x !\n",
+ dp->link_train.link_rate);
+ dp->link_train.link_rate = LINK_RATE_1_62GBPS;
+ }
+
+ if (dp->link_train.lane_count == 0) {
+ dev_err(dp->dev, "Rx Max Lane count is abnormal :%x !\n",
+ dp->link_train.lane_count);
+ dp->link_train.lane_count = (u8)LANE_COUNT1;
+ }
+
+ /* Setup TX lane count & rate */
+ if (dp->link_train.lane_count > max_lane)
+ dp->link_train.lane_count = max_lane;
+ if (dp->link_train.link_rate > max_rate)
+ dp->link_train.link_rate = max_rate;
+
+ /* All DP analog module power up */
+ exynos_dp_set_analog_power_down(dp, POWER_ALL, 0);
+}
+
+static int exynos_dp_sw_link_training(struct exynos_dp_device *dp)
+{
+ int retval = 0;
+ int training_finished;
+
+ /* Turn off unnecessary lane */
+ if (dp->link_train.lane_count == 1)
+ exynos_dp_set_analog_power_down(dp, CH1_BLOCK, 1);
+
+ training_finished = 0;
+
+ dp->link_train.lt_state = START;
+
+ /* Process here */
+ while (!training_finished) {
+ switch (dp->link_train.lt_state) {
+ case START:
+ exynos_dp_link_start(dp);
+ break;
+ case CLOCK_RECOVERY:
+ exynos_dp_process_clock_recovery(dp);
+ break;
+ case EQUALIZER_TRAINING:
+ exynos_dp_process_equalizer_training(dp);
+ break;
+ case FINISHED:
+ training_finished = 1;
+ break;
+ case FAILED:
+ return -EREMOTEIO;
+ }
+ }
+
+ return retval;
+}
+
+static int exynos_dp_set_link_train(struct exynos_dp_device *dp,
+ u32 count,
+ u32 bwtype)
+{
+ int i;
+ int retval;
+
+ for (i = 0; i < DP_TIMEOUT_LOOP_COUNT; i++) {
+ exynos_dp_init_training(dp, count, bwtype);
+ retval = exynos_dp_sw_link_training(dp);
+ if (retval == 0)
+ break;
+
+ udelay(100);
+ }
+
+ return retval;
+}
+
+static int exynos_dp_config_video(struct exynos_dp_device *dp,
+ struct video_info *video_info)
+{
+ int retval = 0;
+ int timeout_loop = 0;
+ int done_count = 0;
+
+ exynos_dp_config_video_slave_mode(dp, video_info);
+
+ exynos_dp_set_video_color_format(dp, video_info->color_depth,
+ video_info->color_space,
+ video_info->dynamic_range,
+ video_info->ycbcr_coeff);
+
+ if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED) {
+ dev_err(dp->dev, "PLL is not locked yet.\n");
+ return -EINVAL;
+ }
+
+ for (;;) {
+ timeout_loop++;
+ if (exynos_dp_is_slave_video_stream_clock_on(dp) == 0)
+ break;
+ if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+ dev_err(dp->dev, "Timeout of video streamclk ok\n");
+ return -ETIMEDOUT;
+ }
+
+ mdelay(100);
+ }
+
+ /* Set to use the register calculated M/N video */
+ exynos_dp_set_video_cr_mn(dp, CALCULATED_M, 0, 0);
+
+ /* For video bist, Video timing must be generated by register */
+ exynos_dp_set_video_timing_mode(dp, VIDEO_TIMING_FROM_CAPTURE);
+
+ /* Disable video mute */
+ exynos_dp_enable_video_mute(dp, 0);
+
+ /* Configure video slave mode */
+ exynos_dp_enable_video_master(dp, 0);
+
+ /* Enable video */
+ exynos_dp_start_video(dp);
+
+ timeout_loop = 0;
+
+ for (;;) {
+ timeout_loop++;
+ if (exynos_dp_is_video_stream_on(dp) == 0) {
+ done_count++;
+ if (done_count > 10)
+ break;
+ } else if (done_count) {
+ done_count = 0;
+ }
+ if (DP_TIMEOUT_LOOP_COUNT < timeout_loop) {
+ dev_err(dp->dev, "Timeout of video streamclk ok\n");
+ return -ETIMEDOUT;
+ }
+
+ mdelay(100);
+ }
+
+ if (retval != 0)
+ dev_err(dp->dev, "Video stream is not detected!\n");
+
+ return retval;
+}
+
+static void exynos_dp_enable_scramble(struct exynos_dp_device *dp, bool enable)
+{
+ u8 data;
+
+ if (enable) {
+ exynos_dp_enable_scrambling(dp);
+
+ exynos_dp_read_byte_from_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ &data);
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ (u8)(data & ~DPCD_SCRAMBLING_DISABLED));
+ } else {
+ exynos_dp_disable_scrambling(dp);
+
+ exynos_dp_read_byte_from_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ &data);
+ exynos_dp_write_byte_to_dpcd(dp,
+ DPCD_ADDR_TRAINING_PATTERN_SET,
+ (u8)(data | DPCD_SCRAMBLING_DISABLED));
+ }
+}
+
+static irqreturn_t exynos_dp_irq_handler(int irq, void *arg)
+{
+ struct exynos_dp_device *dp = arg;
+
+ dev_err(dp->dev, "exynos_dp_irq_handler\n");
+ return IRQ_HANDLED;
+}
+
+static int __devinit exynos_dp_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct exynos_dp_device *dp;
+ struct exynos_dp_platdata *pdata;
+
+ int ret = 0;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ dp = kzalloc(sizeof(struct exynos_dp_device), GFP_KERNEL);
+ if (!dp) {
+ dev_err(&pdev->dev, "no memory for device data\n");
+ return -ENOMEM;
+ }
+
+ dp->dev = &pdev->dev;
+
+ dp->clock = clk_get(&pdev->dev, "dp");
+ if (IS_ERR(dp->clock)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ ret = PTR_ERR(dp->clock);
+ goto err_dp;
+ }
+
+ clk_enable(dp->clock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get registers\n");
+ ret = -EINVAL;
+ goto err_clock;
+ }
+
+ res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!res) {
+ dev_err(&pdev->dev, "failed to request registers region\n");
+ ret = -EINVAL;
+ goto err_clock;
+ }
+
+ dp->res = res;
+
+ dp->reg_base = ioremap(res->start, resource_size(res));
+ if (!dp->reg_base) {
+ dev_err(&pdev->dev, "failed to ioremap\n");
+ ret = -ENOMEM;
+ goto err_req_region;
+ }
+
+ dp->irq = platform_get_irq(pdev, 0);
+ if (!dp->irq) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ ret = -ENODEV;
+ goto err_ioremap;
+ }
+
+ ret = request_irq(dp->irq, exynos_dp_irq_handler, 0,
+ "exynos-dp", dp);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ goto err_ioremap;
+ }
+
+ dp->video_info = pdata->video_info;
+ if (pdata->phy_init)
+ pdata->phy_init();
+
+ exynos_dp_init_dp(dp);
+
+ ret = exynos_dp_detect_hpd(dp);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to detect hpd\n");
+ goto err_irq;
+ }
+
+ exynos_dp_handle_edid(dp);
+
+ ret = exynos_dp_set_link_train(dp, dp->video_info->lane_count,
+ dp->video_info->link_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to do link train\n");
+ goto err_irq;
+ }
+
+ exynos_dp_enable_scramble(dp, 1);
+ exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
+ exynos_dp_enable_enhanced_mode(dp, 1);
+
+ exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
+ exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
+
+ exynos_dp_init_video(dp);
+ ret = exynos_dp_config_video(dp, dp->video_info);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to config video\n");
+ goto err_irq;
+ }
+
+ platform_set_drvdata(pdev, dp);
+
+ return 0;
+
+err_irq:
+ free_irq(dp->irq, dp);
+err_ioremap:
+ iounmap(dp->reg_base);
+err_req_region:
+ release_mem_region(res->start, resource_size(res));
+err_clock:
+ clk_put(dp->clock);
+err_dp:
+ kfree(dp);
+
+ return ret;
+}
+
+static int __devexit exynos_dp_remove(struct platform_device *pdev)
+{
+ struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
+ struct exynos_dp_device *dp = platform_get_drvdata(pdev);
+
+ if (pdata && pdata->phy_exit)
+ pdata->phy_exit();
+
+ free_irq(dp->irq, dp);
+ iounmap(dp->reg_base);
+
+ clk_disable(dp->clock);
+ clk_put(dp->clock);
+
+ release_mem_region(dp->res->start, resource_size(dp->res));
+
+ kfree(dp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int exynos_dp_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
+ struct exynos_dp_device *dp = platform_get_drvdata(pdev);
+
+ if (pdata && pdata->phy_exit)
+ pdata->phy_exit();
+
+ clk_disable(dp->clock);
+
+ return 0;
+}
+
+static int exynos_dp_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
+ struct exynos_dp_device *dp = platform_get_drvdata(pdev);
+
+ if (pdata && pdata->phy_init)
+ pdata->phy_init();
+
+ clk_enable(dp->clock);
+
+ exynos_dp_init_dp(dp);
+
+ exynos_dp_detect_hpd(dp);
+ exynos_dp_handle_edid(dp);
+
+ exynos_dp_set_link_train(dp, dp->video_info->lane_count,
+ dp->video_info->link_rate);
+
+ exynos_dp_enable_scramble(dp, 1);
+ exynos_dp_enable_rx_to_enhanced_mode(dp, 1);
+ exynos_dp_enable_enhanced_mode(dp, 1);
+
+ exynos_dp_set_lane_count(dp, dp->video_info->lane_count);
+ exynos_dp_set_link_bandwidth(dp, dp->video_info->link_rate);
+
+ exynos_dp_init_video(dp);
+ exynos_dp_config_video(dp, dp->video_info);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops exynos_dp_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(exynos_dp_suspend, exynos_dp_resume)
+};
+
+static struct platform_driver exynos_dp_driver = {
+ .probe = exynos_dp_probe,
+ .remove = __devexit_p(exynos_dp_remove),
+ .driver = {
+ .name = "exynos-dp",
+ .owner = THIS_MODULE,
+ .pm = &exynos_dp_pm_ops,
+ },
+};
+
+module_platform_driver(exynos_dp_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung SoC DP Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/exynos/exynos_dp_core.h b/drivers/video/exynos/exynos_dp_core.h
new file mode 100644
index 000000000000..90ceaca0fa24
--- /dev/null
+++ b/drivers/video/exynos/exynos_dp_core.h
@@ -0,0 +1,206 @@
+/*
+ * Header file for Samsung DP (Display Port) interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@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.
+ */
+
+#ifndef _EXYNOS_DP_CORE_H
+#define _EXYNOS_DP_CORE_H
+
+struct link_train {
+ int eq_loop;
+ int cr_loop[4];
+
+ u8 link_rate;
+ u8 lane_count;
+ u8 training_lane[4];
+
+ enum link_training_state lt_state;
+};
+
+struct exynos_dp_device {
+ struct device *dev;
+ struct resource *res;
+ struct clk *clock;
+ unsigned int irq;
+ void __iomem *reg_base;
+
+ struct video_info *video_info;
+ struct link_train link_train;
+};
+
+/* exynos_dp_reg.c */
+void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable);
+void exynos_dp_stop_video(struct exynos_dp_device *dp);
+void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable);
+void exynos_dp_init_interrupt(struct exynos_dp_device *dp);
+void exynos_dp_reset(struct exynos_dp_device *dp);
+void exynos_dp_config_interrupt(struct exynos_dp_device *dp);
+u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp);
+void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable);
+void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
+ enum analog_power_block block,
+ bool enable);
+void exynos_dp_init_analog_func(struct exynos_dp_device *dp);
+void exynos_dp_init_hpd(struct exynos_dp_device *dp);
+void exynos_dp_reset_aux(struct exynos_dp_device *dp);
+void exynos_dp_init_aux(struct exynos_dp_device *dp);
+int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp);
+void exynos_dp_enable_sw_function(struct exynos_dp_device *dp);
+int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp);
+int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned char data);
+int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned char *data);
+int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[]);
+int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[]);
+int exynos_dp_select_i2c_device(struct exynos_dp_device *dp,
+ unsigned int device_addr,
+ unsigned int reg_addr);
+int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
+ unsigned int device_addr,
+ unsigned int reg_addr,
+ unsigned int *data);
+int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
+ unsigned int device_addr,
+ unsigned int reg_addr,
+ unsigned int count,
+ unsigned char edid[]);
+void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
+void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
+void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
+void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
+void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype);
+void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype);
+void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count);
+void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count);
+void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable);
+void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
+ enum pattern_set pattern);
+void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level);
+void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level);
+void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level);
+void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level);
+void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp,
+ u32 training_lane);
+void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp,
+ u32 training_lane);
+void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp,
+ u32 training_lane);
+void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp,
+ u32 training_lane);
+u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp);
+u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp);
+u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp);
+u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp);
+void exynos_dp_reset_macro(struct exynos_dp_device *dp);
+int exynos_dp_init_video(struct exynos_dp_device *dp);
+
+void exynos_dp_set_video_color_format(struct exynos_dp_device *dp,
+ u32 color_depth,
+ u32 color_space,
+ u32 dynamic_range,
+ u32 ycbcr_coeff);
+int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp);
+void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
+ enum clock_recovery_m_value_type type,
+ u32 m_value,
+ u32 n_value);
+void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type);
+void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable);
+void exynos_dp_start_video(struct exynos_dp_device *dp);
+int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp);
+void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp,
+ struct video_info *video_info);
+void exynos_dp_enable_scrambling(struct exynos_dp_device *dp);
+void exynos_dp_disable_scrambling(struct exynos_dp_device *dp);
+
+/* I2C EDID Chip ID, Slave Address */
+#define I2C_EDID_DEVICE_ADDR 0x50
+#define I2C_E_EDID_DEVICE_ADDR 0x30
+
+#define EDID_BLOCK_LENGTH 0x80
+#define EDID_HEADER_PATTERN 0x00
+#define EDID_EXTENSION_FLAG 0x7e
+#define EDID_CHECKSUM 0x7f
+
+/* Definition for DPCD Register */
+#define DPCD_ADDR_DPCD_REV 0x0000
+#define DPCD_ADDR_MAX_LINK_RATE 0x0001
+#define DPCD_ADDR_MAX_LANE_COUNT 0x0002
+#define DPCD_ADDR_LINK_BW_SET 0x0100
+#define DPCD_ADDR_LANE_COUNT_SET 0x0101
+#define DPCD_ADDR_TRAINING_PATTERN_SET 0x0102
+#define DPCD_ADDR_TRAINING_LANE0_SET 0x0103
+#define DPCD_ADDR_LANE0_1_STATUS 0x0202
+#define DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED 0x0204
+#define DPCD_ADDR_ADJUST_REQUEST_LANE0_1 0x0206
+#define DPCD_ADDR_ADJUST_REQUEST_LANE2_3 0x0207
+#define DPCD_ADDR_TEST_REQUEST 0x0218
+#define DPCD_ADDR_TEST_RESPONSE 0x0260
+#define DPCD_ADDR_TEST_EDID_CHECKSUM 0x0261
+#define DPCD_ADDR_SINK_POWER_STATE 0x0600
+
+/* DPCD_ADDR_MAX_LANE_COUNT */
+#define DPCD_ENHANCED_FRAME_CAP(x) (((x) >> 7) & 0x1)
+#define DPCD_MAX_LANE_COUNT(x) ((x) & 0x1f)
+
+/* DPCD_ADDR_LANE_COUNT_SET */
+#define DPCD_ENHANCED_FRAME_EN (0x1 << 7)
+#define DPCD_LANE_COUNT_SET(x) ((x) & 0x1f)
+
+/* DPCD_ADDR_TRAINING_PATTERN_SET */
+#define DPCD_SCRAMBLING_DISABLED (0x1 << 5)
+#define DPCD_SCRAMBLING_ENABLED (0x0 << 5)
+#define DPCD_TRAINING_PATTERN_2 (0x2 << 0)
+#define DPCD_TRAINING_PATTERN_1 (0x1 << 0)
+#define DPCD_TRAINING_PATTERN_DISABLED (0x0 << 0)
+
+/* DPCD_ADDR_TRAINING_LANE0_SET */
+#define DPCD_MAX_PRE_EMPHASIS_REACHED (0x1 << 5)
+#define DPCD_PRE_EMPHASIS_SET(x) (((x) & 0x3) << 3)
+#define DPCD_PRE_EMPHASIS_GET(x) (((x) >> 3) & 0x3)
+#define DPCD_PRE_EMPHASIS_PATTERN2_LEVEL0 (0x0 << 3)
+#define DPCD_MAX_SWING_REACHED (0x1 << 2)
+#define DPCD_VOLTAGE_SWING_SET(x) (((x) & 0x3) << 0)
+#define DPCD_VOLTAGE_SWING_GET(x) (((x) >> 0) & 0x3)
+#define DPCD_VOLTAGE_SWING_PATTERN1_LEVEL0 (0x0 << 0)
+
+/* DPCD_ADDR_LANE0_1_STATUS */
+#define DPCD_LANE_SYMBOL_LOCKED (0x1 << 2)
+#define DPCD_LANE_CHANNEL_EQ_DONE (0x1 << 1)
+#define DPCD_LANE_CR_DONE (0x1 << 0)
+#define DPCD_CHANNEL_EQ_BITS (DPCD_LANE_CR_DONE| \
+ DPCD_LANE_CHANNEL_EQ_DONE|\
+ DPCD_LANE_SYMBOL_LOCKED)
+
+/* DPCD_ADDR_LANE_ALIGN__STATUS_UPDATED */
+#define DPCD_LINK_STATUS_UPDATED (0x1 << 7)
+#define DPCD_DOWNSTREAM_PORT_STATUS_CHANGED (0x1 << 6)
+#define DPCD_INTERLANE_ALIGN_DONE (0x1 << 0)
+
+/* DPCD_ADDR_TEST_REQUEST */
+#define DPCD_TEST_EDID_READ (0x1 << 2)
+
+/* DPCD_ADDR_TEST_RESPONSE */
+#define DPCD_TEST_EDID_CHECKSUM_WRITE (0x1 << 2)
+
+/* DPCD_ADDR_SINK_POWER_STATE */
+#define DPCD_SET_POWER_STATE_D0 (0x1 << 0)
+#define DPCD_SET_POWER_STATE_D4 (0x2 << 0)
+
+#endif /* _EXYNOS_DP_CORE_H */
diff --git a/drivers/video/exynos/exynos_dp_reg.c b/drivers/video/exynos/exynos_dp_reg.c
new file mode 100644
index 000000000000..6548afa0e3d2
--- /dev/null
+++ b/drivers/video/exynos/exynos_dp_reg.c
@@ -0,0 +1,1173 @@
+/*
+ * Samsung DP (Display port) register interface driver.
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include <video/exynos_dp.h>
+
+#include <plat/cpu.h>
+
+#include "exynos_dp_core.h"
+#include "exynos_dp_reg.h"
+
+#define COMMON_INT_MASK_1 (0)
+#define COMMON_INT_MASK_2 (0)
+#define COMMON_INT_MASK_3 (0)
+#define COMMON_INT_MASK_4 (0)
+#define INT_STA_MASK (0)
+
+void exynos_dp_enable_video_mute(struct exynos_dp_device *dp, bool enable)
+{
+ u32 reg;
+
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+ reg |= HDCP_VIDEO_MUTE;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+ reg &= ~HDCP_VIDEO_MUTE;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+ }
+}
+
+void exynos_dp_stop_video(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+ reg &= ~VIDEO_EN;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+}
+
+void exynos_dp_lane_swap(struct exynos_dp_device *dp, bool enable)
+{
+ u32 reg;
+
+ if (enable)
+ reg = LANE3_MAP_LOGIC_LANE_0 | LANE2_MAP_LOGIC_LANE_1 |
+ LANE1_MAP_LOGIC_LANE_2 | LANE0_MAP_LOGIC_LANE_3;
+ else
+ reg = LANE3_MAP_LOGIC_LANE_3 | LANE2_MAP_LOGIC_LANE_2 |
+ LANE1_MAP_LOGIC_LANE_1 | LANE0_MAP_LOGIC_LANE_0;
+
+ writel(reg, dp->reg_base + EXYNOS_DP_LANE_MAP);
+}
+
+void exynos_dp_init_interrupt(struct exynos_dp_device *dp)
+{
+ /* Set interrupt pin assertion polarity as high */
+ writel(INT_POL, dp->reg_base + EXYNOS_DP_INT_CTL);
+
+ /* Clear pending regisers */
+ writel(0xff, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
+ writel(0x4f, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_2);
+ writel(0xe0, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_3);
+ writel(0xe7, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
+ writel(0x63, dp->reg_base + EXYNOS_DP_INT_STA);
+
+ /* 0:mask,1: unmask */
+ writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1);
+ writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2);
+ writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3);
+ writel(0x00, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4);
+ writel(0x00, dp->reg_base + EXYNOS_DP_INT_STA_MASK);
+}
+
+void exynos_dp_reset(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ writel(RESET_DP_TX, dp->reg_base + EXYNOS_DP_TX_SW_RESET);
+
+ exynos_dp_stop_video(dp);
+ exynos_dp_enable_video_mute(dp, 0);
+
+ reg = MASTER_VID_FUNC_EN_N | SLAVE_VID_FUNC_EN_N |
+ AUD_FIFO_FUNC_EN_N | AUD_FUNC_EN_N |
+ HDCP_FUNC_EN_N | SW_FUNC_EN_N;
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
+
+ reg = SSC_FUNC_EN_N | AUX_FUNC_EN_N |
+ SERDES_FIFO_FUNC_EN_N |
+ LS_CLK_DOMAIN_FUNC_EN_N;
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+
+ udelay(20);
+
+ exynos_dp_lane_swap(dp, 0);
+
+ writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
+ writel(0x40, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
+ writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+ writel(0x0, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+
+ writel(0x0, dp->reg_base + EXYNOS_DP_PKT_SEND_CTL);
+ writel(0x0, dp->reg_base + EXYNOS_DP_HDCP_CTL);
+
+ writel(0x5e, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_L);
+ writel(0x1a, dp->reg_base + EXYNOS_DP_HPD_DEGLITCH_H);
+
+ writel(0x10, dp->reg_base + EXYNOS_DP_LINK_DEBUG_CTL);
+
+ writel(0x0, dp->reg_base + EXYNOS_DP_PHY_TEST);
+
+ writel(0x0, dp->reg_base + EXYNOS_DP_VIDEO_FIFO_THRD);
+ writel(0x20, dp->reg_base + EXYNOS_DP_AUDIO_MARGIN);
+
+ writel(0x4, dp->reg_base + EXYNOS_DP_M_VID_GEN_FILTER_TH);
+ writel(0x2, dp->reg_base + EXYNOS_DP_M_AUD_GEN_FILTER_TH);
+
+ writel(0x00000101, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
+
+ exynos_dp_init_interrupt(dp);
+}
+
+void exynos_dp_config_interrupt(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ /* 0: mask, 1: unmask */
+ reg = COMMON_INT_MASK_1;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_1);
+
+ reg = COMMON_INT_MASK_2;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_2);
+
+ reg = COMMON_INT_MASK_3;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_3);
+
+ reg = COMMON_INT_MASK_4;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_MASK_4);
+
+ reg = INT_STA_MASK;
+ writel(reg, dp->reg_base + EXYNOS_DP_INT_STA_MASK);
+}
+
+u32 exynos_dp_get_pll_lock_status(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL);
+ if (reg & PLL_LOCK)
+ return PLL_LOCKED;
+ else
+ return PLL_UNLOCKED;
+}
+
+void exynos_dp_set_pll_power_down(struct exynos_dp_device *dp, bool enable)
+{
+ u32 reg;
+
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL);
+ reg |= DP_PLL_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PLL_CTL);
+ reg &= ~DP_PLL_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PLL_CTL);
+ }
+}
+
+void exynos_dp_set_analog_power_down(struct exynos_dp_device *dp,
+ enum analog_power_block block,
+ bool enable)
+{
+ u32 reg;
+
+ switch (block) {
+ case AUX_BLOCK:
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg |= AUX_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg &= ~AUX_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ case CH0_BLOCK:
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg |= CH0_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg &= ~CH0_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ case CH1_BLOCK:
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg |= CH1_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg &= ~CH1_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ case CH2_BLOCK:
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg |= CH2_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg &= ~CH2_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ case CH3_BLOCK:
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg |= CH3_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg &= ~CH3_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ case ANALOG_TOTAL:
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg |= DP_PHY_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_PD);
+ reg &= ~DP_PHY_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ case POWER_ALL:
+ if (enable) {
+ reg = DP_PHY_PD | AUX_PD | CH3_PD | CH2_PD |
+ CH1_PD | CH0_PD;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_PD);
+ } else {
+ writel(0x00, dp->reg_base + EXYNOS_DP_PHY_PD);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void exynos_dp_init_analog_func(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ exynos_dp_set_analog_power_down(dp, POWER_ALL, 0);
+
+ reg = PLL_LOCK_CHG;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_DEBUG_CTL);
+ reg &= ~(F_PLL_LOCK | PLL_LOCK_CTRL);
+ writel(reg, dp->reg_base + EXYNOS_DP_DEBUG_CTL);
+
+ /* Power up PLL */
+ if (exynos_dp_get_pll_lock_status(dp) == PLL_UNLOCKED)
+ exynos_dp_set_pll_power_down(dp, 0);
+
+ /* Enable Serdes FIFO function and Link symbol clock domain module */
+ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+ reg &= ~(SERDES_FIFO_FUNC_EN_N | LS_CLK_DOMAIN_FUNC_EN_N
+ | AUX_FUNC_EN_N);
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+}
+
+void exynos_dp_init_hpd(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = HOTPLUG_CHG | HPD_LOST | PLUG;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
+
+ reg = INT_HPD;
+ writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+ reg &= ~(F_HPD | HPD_CTRL);
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+}
+
+void exynos_dp_reset_aux(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ /* Disable AUX channel module */
+ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+ reg |= AUX_FUNC_EN_N;
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+}
+
+void exynos_dp_init_aux(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ /* Clear inerrupts related to AUX channel */
+ reg = RPLY_RECEIV | AUX_ERR;
+ writel(reg, dp->reg_base + EXYNOS_DP_INT_STA);
+
+ exynos_dp_reset_aux(dp);
+
+ /* Disable AUX transaction H/W retry */
+ reg = AUX_BIT_PERIOD_EXPECTED_DELAY(3) | AUX_HW_RETRY_COUNT_SEL(0)|
+ AUX_HW_RETRY_INTERVAL_600_MICROSECONDS;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_HW_RETRY_CTL) ;
+
+ /* Receive AUX Channel DEFER commands equal to DEFFER_COUNT*64 */
+ reg = DEFER_CTRL_EN | DEFER_COUNT(1);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_DEFER_CTL);
+
+ /* Enable AUX channel module */
+ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+ reg &= ~AUX_FUNC_EN_N;
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_2);
+}
+
+int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+ if (reg & HPD_STATUS)
+ return 0;
+
+ return -EINVAL;
+}
+
+void exynos_dp_enable_sw_function(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1);
+ reg &= ~SW_FUNC_EN_N;
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
+}
+
+int exynos_dp_start_aux_transaction(struct exynos_dp_device *dp)
+{
+ int reg;
+ int retval = 0;
+
+ /* Enable AUX CH operation */
+ reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
+ reg |= AUX_EN;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
+
+ /* Is AUX CH command reply received? */
+ reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
+ while (!(reg & RPLY_RECEIV))
+ reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
+
+ /* Clear interrupt source for AUX CH command reply */
+ writel(RPLY_RECEIV, dp->reg_base + EXYNOS_DP_INT_STA);
+
+ /* Clear interrupt source for AUX CH access error */
+ reg = readl(dp->reg_base + EXYNOS_DP_INT_STA);
+ if (reg & AUX_ERR) {
+ writel(AUX_ERR, dp->reg_base + EXYNOS_DP_INT_STA);
+ return -EREMOTEIO;
+ }
+
+ /* Check AUX CH error access status */
+ reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_STA);
+ if ((reg & AUX_STATUS_MASK) != 0) {
+ dev_err(dp->dev, "AUX CH error happens: %d\n\n",
+ reg & AUX_STATUS_MASK);
+ return -EREMOTEIO;
+ }
+
+ return retval;
+}
+
+int exynos_dp_write_byte_to_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned char data)
+{
+ u32 reg;
+ int i;
+ int retval;
+
+ for (i = 0; i < 3; i++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
+
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
+ reg = AUX_ADDR_15_8(reg_addr);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
+ reg = AUX_ADDR_19_16(reg_addr);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
+
+ /* Write data buffer */
+ reg = (unsigned int)data;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0);
+
+ /*
+ * Set DisplayPort transaction and write 1 byte
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval == 0)
+ break;
+ else
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+ }
+
+ return retval;
+}
+
+int exynos_dp_read_byte_from_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned char *data)
+{
+ u32 reg;
+ int i;
+ int retval;
+
+ for (i = 0; i < 10; i++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
+
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
+ reg = AUX_ADDR_15_8(reg_addr);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
+ reg = AUX_ADDR_19_16(reg_addr);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
+
+ /*
+ * Set DisplayPort transaction and read 1 byte
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval == 0)
+ break;
+ else
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+ }
+
+ /* Read data buffer */
+ reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0);
+ *data = (unsigned char)(reg & 0xff);
+
+ return retval;
+}
+
+int exynos_dp_write_bytes_to_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[])
+{
+ u32 reg;
+ unsigned int start_offset;
+ unsigned int cur_data_count;
+ unsigned int cur_data_idx;
+ int i;
+ int retval = 0;
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
+
+ start_offset = 0;
+ while (start_offset < count) {
+ /* Buffer size of AUX CH is 16 * 4bytes */
+ if ((count - start_offset) > 16)
+ cur_data_count = 16;
+ else
+ cur_data_count = count - start_offset;
+
+ for (i = 0; i < 10; i++) {
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr + start_offset);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
+ reg = AUX_ADDR_15_8(reg_addr + start_offset);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
+ reg = AUX_ADDR_19_16(reg_addr + start_offset);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
+
+ for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+ cur_data_idx++) {
+ reg = data[start_offset + cur_data_idx];
+ writel(reg, dp->reg_base + EXYNOS_DP_BUF_DATA_0
+ + 4 * cur_data_idx);
+ }
+
+ /*
+ * Set DisplayPort transaction and write
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(cur_data_count) |
+ AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_WRITE;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval == 0)
+ break;
+ else
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+ }
+
+ start_offset += cur_data_count;
+ }
+
+ return retval;
+}
+
+int exynos_dp_read_bytes_from_dpcd(struct exynos_dp_device *dp,
+ unsigned int reg_addr,
+ unsigned int count,
+ unsigned char data[])
+{
+ u32 reg;
+ unsigned int start_offset;
+ unsigned int cur_data_count;
+ unsigned int cur_data_idx;
+ int i;
+ int retval = 0;
+
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
+
+ start_offset = 0;
+ while (start_offset < count) {
+ /* Buffer size of AUX CH is 16 * 4bytes */
+ if ((count - start_offset) > 16)
+ cur_data_count = 16;
+ else
+ cur_data_count = count - start_offset;
+
+ /* AUX CH Request Transaction process */
+ for (i = 0; i < 10; i++) {
+ /* Select DPCD device address */
+ reg = AUX_ADDR_7_0(reg_addr + start_offset);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
+ reg = AUX_ADDR_15_8(reg_addr + start_offset);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
+ reg = AUX_ADDR_19_16(reg_addr + start_offset);
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
+
+ /*
+ * Set DisplayPort transaction and read
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(cur_data_count) |
+ AUX_TX_COMM_DP_TRANSACTION | AUX_TX_COMM_READ;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval == 0)
+ break;
+ else
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+ }
+
+ for (cur_data_idx = 0; cur_data_idx < cur_data_count;
+ cur_data_idx++) {
+ reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0
+ + 4 * cur_data_idx);
+ data[start_offset + cur_data_idx] =
+ (unsigned char)reg;
+ }
+
+ start_offset += cur_data_count;
+ }
+
+ return retval;
+}
+
+int exynos_dp_select_i2c_device(struct exynos_dp_device *dp,
+ unsigned int device_addr,
+ unsigned int reg_addr)
+{
+ u32 reg;
+ int retval;
+
+ /* Set EDID device address */
+ reg = device_addr;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_ADDR_7_0);
+ writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_15_8);
+ writel(0x0, dp->reg_base + EXYNOS_DP_AUX_ADDR_19_16);
+
+ /* Set offset from base address of EDID device */
+ writel(reg_addr, dp->reg_base + EXYNOS_DP_BUF_DATA_0);
+
+ /*
+ * Set I2C transaction and write address
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_I2C_TRANSACTION | AUX_TX_COMM_MOT |
+ AUX_TX_COMM_WRITE;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval != 0)
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+
+ return retval;
+}
+
+int exynos_dp_read_byte_from_i2c(struct exynos_dp_device *dp,
+ unsigned int device_addr,
+ unsigned int reg_addr,
+ unsigned int *data)
+{
+ u32 reg;
+ int i;
+ int retval;
+
+ for (i = 0; i < 10; i++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
+
+ /* Select EDID device */
+ retval = exynos_dp_select_i2c_device(dp, device_addr, reg_addr);
+ if (retval != 0) {
+ dev_err(dp->dev, "Select EDID device fail!\n");
+ continue;
+ }
+
+ /*
+ * Set I2C transaction and read data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval == 0)
+ break;
+ else
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+ }
+
+ /* Read data */
+ if (retval == 0)
+ *data = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0);
+
+ return retval;
+}
+
+int exynos_dp_read_bytes_from_i2c(struct exynos_dp_device *dp,
+ unsigned int device_addr,
+ unsigned int reg_addr,
+ unsigned int count,
+ unsigned char edid[])
+{
+ u32 reg;
+ unsigned int i, j;
+ unsigned int cur_data_idx;
+ unsigned int defer = 0;
+ int retval = 0;
+
+ for (i = 0; i < count; i += 16) {
+ for (j = 0; j < 100; j++) {
+ /* Clear AUX CH data buffer */
+ reg = BUF_CLR;
+ writel(reg, dp->reg_base + EXYNOS_DP_BUFFER_DATA_CTL);
+
+ /* Set normal AUX CH command */
+ reg = readl(dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
+ reg &= ~ADDR_ONLY;
+ writel(reg, dp->reg_base + EXYNOS_DP_AUX_CH_CTL_2);
+
+ /*
+ * If Rx sends defer, Tx sends only reads
+ * request without sending addres
+ */
+ if (!defer)
+ retval = exynos_dp_select_i2c_device(dp,
+ device_addr, reg_addr + i);
+ else
+ defer = 0;
+
+ if (retval == 0) {
+ /*
+ * Set I2C transaction and write data
+ * If bit 3 is 1, DisplayPort transaction.
+ * If Bit 3 is 0, I2C transaction.
+ */
+ reg = AUX_LENGTH(16) |
+ AUX_TX_COMM_I2C_TRANSACTION |
+ AUX_TX_COMM_READ;
+ writel(reg, dp->reg_base +
+ EXYNOS_DP_AUX_CH_CTL_1);
+
+ /* Start AUX transaction */
+ retval = exynos_dp_start_aux_transaction(dp);
+ if (retval == 0)
+ break;
+ else
+ dev_err(dp->dev, "Aux Transaction fail!\n");
+ }
+ /* Check if Rx sends defer */
+ reg = readl(dp->reg_base + EXYNOS_DP_AUX_RX_COMM);
+ if (reg == AUX_RX_COMM_AUX_DEFER ||
+ reg == AUX_RX_COMM_I2C_DEFER) {
+ dev_err(dp->dev, "Defer: %d\n\n", reg);
+ defer = 1;
+ }
+ }
+
+ for (cur_data_idx = 0; cur_data_idx < 16; cur_data_idx++) {
+ reg = readl(dp->reg_base + EXYNOS_DP_BUF_DATA_0
+ + 4 * cur_data_idx);
+ edid[i + cur_data_idx] = (unsigned char)reg;
+ }
+ }
+
+ return retval;
+}
+
+void exynos_dp_set_link_bandwidth(struct exynos_dp_device *dp, u32 bwtype)
+{
+ u32 reg;
+
+ reg = bwtype;
+ if ((bwtype == LINK_RATE_2_70GBPS) || (bwtype == LINK_RATE_1_62GBPS))
+ writel(reg, dp->reg_base + EXYNOS_DP_LINK_BW_SET);
+}
+
+void exynos_dp_get_link_bandwidth(struct exynos_dp_device *dp, u32 *bwtype)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_LINK_BW_SET);
+ *bwtype = reg;
+}
+
+void exynos_dp_set_lane_count(struct exynos_dp_device *dp, u32 count)
+{
+ u32 reg;
+
+ reg = count;
+ writel(reg, dp->reg_base + EXYNOS_DP_LANE_COUNT_SET);
+}
+
+void exynos_dp_get_lane_count(struct exynos_dp_device *dp, u32 *count)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_LANE_COUNT_SET);
+ *count = reg;
+}
+
+void exynos_dp_enable_enhanced_mode(struct exynos_dp_device *dp, bool enable)
+{
+ u32 reg;
+
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ reg |= ENHANCED;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ reg &= ~ENHANCED;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ }
+}
+
+void exynos_dp_set_training_pattern(struct exynos_dp_device *dp,
+ enum pattern_set pattern)
+{
+ u32 reg;
+
+ switch (pattern) {
+ case PRBS7:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_PRBS7;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ break;
+ case D10_2:
+ reg = SCRAMBLING_ENABLE | LINK_QUAL_PATTERN_SET_D10_2;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ break;
+ case TRAINING_PTN1:
+ reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN1;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ break;
+ case TRAINING_PTN2:
+ reg = SCRAMBLING_DISABLE | SW_TRAINING_PATTERN_SET_PTN2;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ break;
+ case DP_NONE:
+ reg = SCRAMBLING_ENABLE |
+ LINK_QUAL_PATTERN_SET_DISABLE |
+ SW_TRAINING_PATTERN_SET_NORMAL;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ break;
+ default:
+ break;
+ }
+}
+
+void exynos_dp_set_lane0_pre_emphasis(struct exynos_dp_device *dp, u32 level)
+{
+ u32 reg;
+
+ reg = level << PRE_EMPHASIS_SET_SHIFT;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane1_pre_emphasis(struct exynos_dp_device *dp, u32 level)
+{
+ u32 reg;
+
+ reg = level << PRE_EMPHASIS_SET_SHIFT;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane2_pre_emphasis(struct exynos_dp_device *dp, u32 level)
+{
+ u32 reg;
+
+ reg = level << PRE_EMPHASIS_SET_SHIFT;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane3_pre_emphasis(struct exynos_dp_device *dp, u32 level)
+{
+ u32 reg;
+
+ reg = level << PRE_EMPHASIS_SET_SHIFT;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane0_link_training(struct exynos_dp_device *dp,
+ u32 training_lane)
+{
+ u32 reg;
+
+ reg = training_lane;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane1_link_training(struct exynos_dp_device *dp,
+ u32 training_lane)
+{
+ u32 reg;
+
+ reg = training_lane;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane2_link_training(struct exynos_dp_device *dp,
+ u32 training_lane)
+{
+ u32 reg;
+
+ reg = training_lane;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
+}
+
+void exynos_dp_set_lane3_link_training(struct exynos_dp_device *dp,
+ u32 training_lane)
+{
+ u32 reg;
+
+ reg = training_lane;
+ writel(reg, dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
+}
+
+u32 exynos_dp_get_lane0_link_training(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_LN0_LINK_TRAINING_CTL);
+ return reg;
+}
+
+u32 exynos_dp_get_lane1_link_training(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_LN1_LINK_TRAINING_CTL);
+ return reg;
+}
+
+u32 exynos_dp_get_lane2_link_training(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_LN2_LINK_TRAINING_CTL);
+ return reg;
+}
+
+u32 exynos_dp_get_lane3_link_training(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_LN3_LINK_TRAINING_CTL);
+ return reg;
+}
+
+void exynos_dp_reset_macro(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_PHY_TEST);
+ reg |= MACRO_RST;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
+
+ /* 10 us is the minimum reset time. */
+ udelay(10);
+
+ reg &= ~MACRO_RST;
+ writel(reg, dp->reg_base + EXYNOS_DP_PHY_TEST);
+}
+
+int exynos_dp_init_video(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = VSYNC_DET | VID_FORMAT_CHG | VID_CLK_CHG;
+ writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_1);
+
+ reg = 0x0;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
+
+ reg = CHA_CRI(4) | CHA_CTRL;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
+
+ reg = 0x0;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+
+ reg = VID_HRES_TH(2) | VID_VRES_TH(0);
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_8);
+
+ return 0;
+}
+
+void exynos_dp_set_video_color_format(struct exynos_dp_device *dp,
+ u32 color_depth,
+ u32 color_space,
+ u32 dynamic_range,
+ u32 ycbcr_coeff)
+{
+ u32 reg;
+
+ /* Configure the input color depth, color space, dynamic range */
+ reg = (dynamic_range << IN_D_RANGE_SHIFT) |
+ (color_depth << IN_BPC_SHIFT) |
+ (color_space << IN_COLOR_F_SHIFT);
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_2);
+
+ /* Set Input Color YCbCr Coefficients to ITU601 or ITU709 */
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
+ reg &= ~IN_YC_COEFFI_MASK;
+ if (ycbcr_coeff)
+ reg |= IN_YC_COEFFI_ITU709;
+ else
+ reg |= IN_YC_COEFFI_ITU601;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_3);
+}
+
+int exynos_dp_is_slave_video_stream_clock_on(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1);
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_1);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_1);
+
+ if (!(reg & DET_STA)) {
+ dev_dbg(dp->dev, "Input stream clock not detected.\n");
+ return -EINVAL;
+ }
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2);
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_2);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_2);
+ dev_dbg(dp->dev, "wait SYS_CTL_2.\n");
+
+ if (reg & CHA_STA) {
+ dev_dbg(dp->dev, "Input stream clk is changing\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void exynos_dp_set_video_cr_mn(struct exynos_dp_device *dp,
+ enum clock_recovery_m_value_type type,
+ u32 m_value,
+ u32 n_value)
+{
+ u32 reg;
+
+ if (type == REGISTER_M) {
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ reg |= FIX_M_VID;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ reg = m_value & 0xff;
+ writel(reg, dp->reg_base + EXYNOS_DP_M_VID_0);
+ reg = (m_value >> 8) & 0xff;
+ writel(reg, dp->reg_base + EXYNOS_DP_M_VID_1);
+ reg = (m_value >> 16) & 0xff;
+ writel(reg, dp->reg_base + EXYNOS_DP_M_VID_2);
+
+ reg = n_value & 0xff;
+ writel(reg, dp->reg_base + EXYNOS_DP_N_VID_0);
+ reg = (n_value >> 8) & 0xff;
+ writel(reg, dp->reg_base + EXYNOS_DP_N_VID_1);
+ reg = (n_value >> 16) & 0xff;
+ writel(reg, dp->reg_base + EXYNOS_DP_N_VID_2);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+ reg &= ~FIX_M_VID;
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_4);
+
+ writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_0);
+ writel(0x80, dp->reg_base + EXYNOS_DP_N_VID_1);
+ writel(0x00, dp->reg_base + EXYNOS_DP_N_VID_2);
+ }
+}
+
+void exynos_dp_set_video_timing_mode(struct exynos_dp_device *dp, u32 type)
+{
+ u32 reg;
+
+ if (type == VIDEO_TIMING_FROM_CAPTURE) {
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ reg &= ~FORMAT_SEL;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ reg |= FORMAT_SEL;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ }
+}
+
+void exynos_dp_enable_video_master(struct exynos_dp_device *dp, bool enable)
+{
+ u32 reg;
+
+ if (enable) {
+ reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
+ reg &= ~VIDEO_MODE_MASK;
+ reg |= VIDEO_MASTER_MODE_EN | VIDEO_MODE_MASTER_MODE;
+ writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
+ } else {
+ reg = readl(dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
+ reg &= ~VIDEO_MODE_MASK;
+ reg |= VIDEO_MODE_SLAVE_MODE;
+ writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
+ }
+}
+
+void exynos_dp_start_video(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+ reg |= VIDEO_EN;
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_1);
+}
+
+int exynos_dp_is_video_stream_on(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+ writel(reg, dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
+ if (!(reg & STRM_VALID)) {
+ dev_dbg(dp->dev, "Input video stream is not detected.\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void exynos_dp_config_video_slave_mode(struct exynos_dp_device *dp,
+ struct video_info *video_info)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_FUNC_EN_1);
+ reg &= ~(MASTER_VID_FUNC_EN_N|SLAVE_VID_FUNC_EN_N);
+ reg |= MASTER_VID_FUNC_EN_N;
+ writel(reg, dp->reg_base + EXYNOS_DP_FUNC_EN_1);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ reg &= ~INTERACE_SCAN_CFG;
+ reg |= (video_info->interlaced << 2);
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ reg &= ~VSYNC_POLARITY_CFG;
+ reg |= (video_info->v_sync_polarity << 1);
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+
+ reg = readl(dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+ reg &= ~HSYNC_POLARITY_CFG;
+ reg |= (video_info->h_sync_polarity << 0);
+ writel(reg, dp->reg_base + EXYNOS_DP_VIDEO_CTL_10);
+
+ reg = AUDIO_MODE_SPDIF_MODE | VIDEO_MODE_SLAVE_MODE;
+ writel(reg, dp->reg_base + EXYNOS_DP_SOC_GENERAL_CTL);
+}
+
+void exynos_dp_enable_scrambling(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ reg &= ~SCRAMBLING_DISABLE;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+}
+
+void exynos_dp_disable_scrambling(struct exynos_dp_device *dp)
+{
+ u32 reg;
+
+ reg = readl(dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+ reg |= SCRAMBLING_DISABLE;
+ writel(reg, dp->reg_base + EXYNOS_DP_TRAINING_PTN_SET);
+}
diff --git a/drivers/video/exynos/exynos_dp_reg.h b/drivers/video/exynos/exynos_dp_reg.h
new file mode 100644
index 000000000000..42f608e2a43e
--- /dev/null
+++ b/drivers/video/exynos/exynos_dp_reg.h
@@ -0,0 +1,335 @@
+/*
+ * Register definition file for Samsung DP driver
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef _EXYNOS_DP_REG_H
+#define _EXYNOS_DP_REG_H
+
+#define EXYNOS_DP_TX_SW_RESET 0x14
+#define EXYNOS_DP_FUNC_EN_1 0x18
+#define EXYNOS_DP_FUNC_EN_2 0x1C
+#define EXYNOS_DP_VIDEO_CTL_1 0x20
+#define EXYNOS_DP_VIDEO_CTL_2 0x24
+#define EXYNOS_DP_VIDEO_CTL_3 0x28
+
+#define EXYNOS_DP_VIDEO_CTL_8 0x3C
+#define EXYNOS_DP_VIDEO_CTL_10 0x44
+
+#define EXYNOS_DP_LANE_MAP 0x35C
+
+#define EXYNOS_DP_AUX_HW_RETRY_CTL 0x390
+
+#define EXYNOS_DP_COMMON_INT_STA_1 0x3C4
+#define EXYNOS_DP_COMMON_INT_STA_2 0x3C8
+#define EXYNOS_DP_COMMON_INT_STA_3 0x3CC
+#define EXYNOS_DP_COMMON_INT_STA_4 0x3D0
+#define EXYNOS_DP_INT_STA 0x3DC
+#define EXYNOS_DP_COMMON_INT_MASK_1 0x3E0
+#define EXYNOS_DP_COMMON_INT_MASK_2 0x3E4
+#define EXYNOS_DP_COMMON_INT_MASK_3 0x3E8
+#define EXYNOS_DP_COMMON_INT_MASK_4 0x3EC
+#define EXYNOS_DP_INT_STA_MASK 0x3F8
+#define EXYNOS_DP_INT_CTL 0x3FC
+
+#define EXYNOS_DP_SYS_CTL_1 0x600
+#define EXYNOS_DP_SYS_CTL_2 0x604
+#define EXYNOS_DP_SYS_CTL_3 0x608
+#define EXYNOS_DP_SYS_CTL_4 0x60C
+
+#define EXYNOS_DP_PKT_SEND_CTL 0x640
+#define EXYNOS_DP_HDCP_CTL 0x648
+
+#define EXYNOS_DP_LINK_BW_SET 0x680
+#define EXYNOS_DP_LANE_COUNT_SET 0x684
+#define EXYNOS_DP_TRAINING_PTN_SET 0x688
+#define EXYNOS_DP_LN0_LINK_TRAINING_CTL 0x68C
+#define EXYNOS_DP_LN1_LINK_TRAINING_CTL 0x690
+#define EXYNOS_DP_LN2_LINK_TRAINING_CTL 0x694
+#define EXYNOS_DP_LN3_LINK_TRAINING_CTL 0x698
+
+#define EXYNOS_DP_DEBUG_CTL 0x6C0
+#define EXYNOS_DP_HPD_DEGLITCH_L 0x6C4
+#define EXYNOS_DP_HPD_DEGLITCH_H 0x6C8
+#define EXYNOS_DP_LINK_DEBUG_CTL 0x6E0
+
+#define EXYNOS_DP_M_VID_0 0x700
+#define EXYNOS_DP_M_VID_1 0x704
+#define EXYNOS_DP_M_VID_2 0x708
+#define EXYNOS_DP_N_VID_0 0x70C
+#define EXYNOS_DP_N_VID_1 0x710
+#define EXYNOS_DP_N_VID_2 0x714
+
+#define EXYNOS_DP_PLL_CTL 0x71C
+#define EXYNOS_DP_PHY_PD 0x720
+#define EXYNOS_DP_PHY_TEST 0x724
+
+#define EXYNOS_DP_VIDEO_FIFO_THRD 0x730
+#define EXYNOS_DP_AUDIO_MARGIN 0x73C
+
+#define EXYNOS_DP_M_VID_GEN_FILTER_TH 0x764
+#define EXYNOS_DP_M_AUD_GEN_FILTER_TH 0x778
+#define EXYNOS_DP_AUX_CH_STA 0x780
+#define EXYNOS_DP_AUX_CH_DEFER_CTL 0x788
+#define EXYNOS_DP_AUX_RX_COMM 0x78C
+#define EXYNOS_DP_BUFFER_DATA_CTL 0x790
+#define EXYNOS_DP_AUX_CH_CTL_1 0x794
+#define EXYNOS_DP_AUX_ADDR_7_0 0x798
+#define EXYNOS_DP_AUX_ADDR_15_8 0x79C
+#define EXYNOS_DP_AUX_ADDR_19_16 0x7A0
+#define EXYNOS_DP_AUX_CH_CTL_2 0x7A4
+
+#define EXYNOS_DP_BUF_DATA_0 0x7C0
+
+#define EXYNOS_DP_SOC_GENERAL_CTL 0x800
+
+/* EXYNOS_DP_TX_SW_RESET */
+#define RESET_DP_TX (0x1 << 0)
+
+/* EXYNOS_DP_FUNC_EN_1 */
+#define MASTER_VID_FUNC_EN_N (0x1 << 7)
+#define SLAVE_VID_FUNC_EN_N (0x1 << 5)
+#define AUD_FIFO_FUNC_EN_N (0x1 << 4)
+#define AUD_FUNC_EN_N (0x1 << 3)
+#define HDCP_FUNC_EN_N (0x1 << 2)
+#define CRC_FUNC_EN_N (0x1 << 1)
+#define SW_FUNC_EN_N (0x1 << 0)
+
+/* EXYNOS_DP_FUNC_EN_2 */
+#define SSC_FUNC_EN_N (0x1 << 7)
+#define AUX_FUNC_EN_N (0x1 << 2)
+#define SERDES_FIFO_FUNC_EN_N (0x1 << 1)
+#define LS_CLK_DOMAIN_FUNC_EN_N (0x1 << 0)
+
+/* EXYNOS_DP_VIDEO_CTL_1 */
+#define VIDEO_EN (0x1 << 7)
+#define HDCP_VIDEO_MUTE (0x1 << 6)
+
+/* EXYNOS_DP_VIDEO_CTL_1 */
+#define IN_D_RANGE_MASK (0x1 << 7)
+#define IN_D_RANGE_SHIFT (7)
+#define IN_D_RANGE_CEA (0x1 << 7)
+#define IN_D_RANGE_VESA (0x0 << 7)
+#define IN_BPC_MASK (0x7 << 4)
+#define IN_BPC_SHIFT (4)
+#define IN_BPC_12_BITS (0x3 << 4)
+#define IN_BPC_10_BITS (0x2 << 4)
+#define IN_BPC_8_BITS (0x1 << 4)
+#define IN_BPC_6_BITS (0x0 << 4)
+#define IN_COLOR_F_MASK (0x3 << 0)
+#define IN_COLOR_F_SHIFT (0)
+#define IN_COLOR_F_YCBCR444 (0x2 << 0)
+#define IN_COLOR_F_YCBCR422 (0x1 << 0)
+#define IN_COLOR_F_RGB (0x0 << 0)
+
+/* EXYNOS_DP_VIDEO_CTL_3 */
+#define IN_YC_COEFFI_MASK (0x1 << 7)
+#define IN_YC_COEFFI_SHIFT (7)
+#define IN_YC_COEFFI_ITU709 (0x1 << 7)
+#define IN_YC_COEFFI_ITU601 (0x0 << 7)
+#define VID_CHK_UPDATE_TYPE_MASK (0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_SHIFT (4)
+#define VID_CHK_UPDATE_TYPE_1 (0x1 << 4)
+#define VID_CHK_UPDATE_TYPE_0 (0x0 << 4)
+
+/* EXYNOS_DP_VIDEO_CTL_8 */
+#define VID_HRES_TH(x) (((x) & 0xf) << 4)
+#define VID_VRES_TH(x) (((x) & 0xf) << 0)
+
+/* EXYNOS_DP_VIDEO_CTL_10 */
+#define FORMAT_SEL (0x1 << 4)
+#define INTERACE_SCAN_CFG (0x1 << 2)
+#define VSYNC_POLARITY_CFG (0x1 << 1)
+#define HSYNC_POLARITY_CFG (0x1 << 0)
+
+/* EXYNOS_DP_LANE_MAP */
+#define LANE3_MAP_LOGIC_LANE_0 (0x0 << 6)
+#define LANE3_MAP_LOGIC_LANE_1 (0x1 << 6)
+#define LANE3_MAP_LOGIC_LANE_2 (0x2 << 6)
+#define LANE3_MAP_LOGIC_LANE_3 (0x3 << 6)
+#define LANE2_MAP_LOGIC_LANE_0 (0x0 << 4)
+#define LANE2_MAP_LOGIC_LANE_1 (0x1 << 4)
+#define LANE2_MAP_LOGIC_LANE_2 (0x2 << 4)
+#define LANE2_MAP_LOGIC_LANE_3 (0x3 << 4)
+#define LANE1_MAP_LOGIC_LANE_0 (0x0 << 2)
+#define LANE1_MAP_LOGIC_LANE_1 (0x1 << 2)
+#define LANE1_MAP_LOGIC_LANE_2 (0x2 << 2)
+#define LANE1_MAP_LOGIC_LANE_3 (0x3 << 2)
+#define LANE0_MAP_LOGIC_LANE_0 (0x0 << 0)
+#define LANE0_MAP_LOGIC_LANE_1 (0x1 << 0)
+#define LANE0_MAP_LOGIC_LANE_2 (0x2 << 0)
+#define LANE0_MAP_LOGIC_LANE_3 (0x3 << 0)
+
+/* EXYNOS_DP_AUX_HW_RETRY_CTL */
+#define AUX_BIT_PERIOD_EXPECTED_DELAY(x) (((x) & 0x7) << 8)
+#define AUX_HW_RETRY_INTERVAL_MASK (0x3 << 3)
+#define AUX_HW_RETRY_INTERVAL_600_MICROSECONDS (0x0 << 3)
+#define AUX_HW_RETRY_INTERVAL_800_MICROSECONDS (0x1 << 3)
+#define AUX_HW_RETRY_INTERVAL_1000_MICROSECONDS (0x2 << 3)
+#define AUX_HW_RETRY_INTERVAL_1800_MICROSECONDS (0x3 << 3)
+#define AUX_HW_RETRY_COUNT_SEL(x) (((x) & 0x7) << 0)
+
+/* EXYNOS_DP_COMMON_INT_STA_1 */
+#define VSYNC_DET (0x1 << 7)
+#define PLL_LOCK_CHG (0x1 << 6)
+#define SPDIF_ERR (0x1 << 5)
+#define SPDIF_UNSTBL (0x1 << 4)
+#define VID_FORMAT_CHG (0x1 << 3)
+#define AUD_CLK_CHG (0x1 << 2)
+#define VID_CLK_CHG (0x1 << 1)
+#define SW_INT (0x1 << 0)
+
+/* EXYNOS_DP_COMMON_INT_STA_2 */
+#define ENC_EN_CHG (0x1 << 6)
+#define HW_BKSV_RDY (0x1 << 3)
+#define HW_SHA_DONE (0x1 << 2)
+#define HW_AUTH_STATE_CHG (0x1 << 1)
+#define HW_AUTH_DONE (0x1 << 0)
+
+/* EXYNOS_DP_COMMON_INT_STA_3 */
+#define AFIFO_UNDER (0x1 << 7)
+#define AFIFO_OVER (0x1 << 6)
+#define R0_CHK_FLAG (0x1 << 5)
+
+/* EXYNOS_DP_COMMON_INT_STA_4 */
+#define PSR_ACTIVE (0x1 << 7)
+#define PSR_INACTIVE (0x1 << 6)
+#define SPDIF_BI_PHASE_ERR (0x1 << 5)
+#define HOTPLUG_CHG (0x1 << 2)
+#define HPD_LOST (0x1 << 1)
+#define PLUG (0x1 << 0)
+
+/* EXYNOS_DP_INT_STA */
+#define INT_HPD (0x1 << 6)
+#define HW_TRAINING_FINISH (0x1 << 5)
+#define RPLY_RECEIV (0x1 << 1)
+#define AUX_ERR (0x1 << 0)
+
+/* EXYNOS_DP_INT_CTL */
+#define SOFT_INT_CTRL (0x1 << 2)
+#define INT_POL (0x1 << 0)
+
+/* EXYNOS_DP_SYS_CTL_1 */
+#define DET_STA (0x1 << 2)
+#define FORCE_DET (0x1 << 1)
+#define DET_CTRL (0x1 << 0)
+
+/* EXYNOS_DP_SYS_CTL_2 */
+#define CHA_CRI(x) (((x) & 0xf) << 4)
+#define CHA_STA (0x1 << 2)
+#define FORCE_CHA (0x1 << 1)
+#define CHA_CTRL (0x1 << 0)
+
+/* EXYNOS_DP_SYS_CTL_3 */
+#define HPD_STATUS (0x1 << 6)
+#define F_HPD (0x1 << 5)
+#define HPD_CTRL (0x1 << 4)
+#define HDCP_RDY (0x1 << 3)
+#define STRM_VALID (0x1 << 2)
+#define F_VALID (0x1 << 1)
+#define VALID_CTRL (0x1 << 0)
+
+/* EXYNOS_DP_SYS_CTL_4 */
+#define FIX_M_AUD (0x1 << 4)
+#define ENHANCED (0x1 << 3)
+#define FIX_M_VID (0x1 << 2)
+#define M_VID_UPDATE_CTRL (0x3 << 0)
+
+/* EXYNOS_DP_TRAINING_PTN_SET */
+#define SCRAMBLER_TYPE (0x1 << 9)
+#define HW_LINK_TRAINING_PATTERN (0x1 << 8)
+#define SCRAMBLING_DISABLE (0x1 << 5)
+#define SCRAMBLING_ENABLE (0x0 << 5)
+#define LINK_QUAL_PATTERN_SET_MASK (0x3 << 2)
+#define LINK_QUAL_PATTERN_SET_PRBS7 (0x3 << 2)
+#define LINK_QUAL_PATTERN_SET_D10_2 (0x1 << 2)
+#define LINK_QUAL_PATTERN_SET_DISABLE (0x0 << 2)
+#define SW_TRAINING_PATTERN_SET_MASK (0x3 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN2 (0x2 << 0)
+#define SW_TRAINING_PATTERN_SET_PTN1 (0x1 << 0)
+#define SW_TRAINING_PATTERN_SET_NORMAL (0x0 << 0)
+
+/* EXYNOS_DP_LN0_LINK_TRAINING_CTL */
+#define PRE_EMPHASIS_SET_SHIFT (3)
+
+/* EXYNOS_DP_DEBUG_CTL */
+#define PLL_LOCK (0x1 << 4)
+#define F_PLL_LOCK (0x1 << 3)
+#define PLL_LOCK_CTRL (0x1 << 2)
+#define PN_INV (0x1 << 0)
+
+/* EXYNOS_DP_PLL_CTL */
+#define DP_PLL_PD (0x1 << 7)
+#define DP_PLL_RESET (0x1 << 6)
+#define DP_PLL_LOOP_BIT_DEFAULT (0x1 << 4)
+#define DP_PLL_REF_BIT_1_1250V (0x5 << 0)
+#define DP_PLL_REF_BIT_1_2500V (0x7 << 0)
+
+/* EXYNOS_DP_PHY_PD */
+#define DP_PHY_PD (0x1 << 5)
+#define AUX_PD (0x1 << 4)
+#define CH3_PD (0x1 << 3)
+#define CH2_PD (0x1 << 2)
+#define CH1_PD (0x1 << 1)
+#define CH0_PD (0x1 << 0)
+
+/* EXYNOS_DP_PHY_TEST */
+#define MACRO_RST (0x1 << 5)
+#define CH1_TEST (0x1 << 1)
+#define CH0_TEST (0x1 << 0)
+
+/* EXYNOS_DP_AUX_CH_STA */
+#define AUX_BUSY (0x1 << 4)
+#define AUX_STATUS_MASK (0xf << 0)
+
+/* EXYNOS_DP_AUX_CH_DEFER_CTL */
+#define DEFER_CTRL_EN (0x1 << 7)
+#define DEFER_COUNT(x) (((x) & 0x7f) << 0)
+
+/* EXYNOS_DP_AUX_RX_COMM */
+#define AUX_RX_COMM_I2C_DEFER (0x2 << 2)
+#define AUX_RX_COMM_AUX_DEFER (0x2 << 0)
+
+/* EXYNOS_DP_BUFFER_DATA_CTL */
+#define BUF_CLR (0x1 << 7)
+#define BUF_DATA_COUNT(x) (((x) & 0x1f) << 0)
+
+/* EXYNOS_DP_AUX_CH_CTL_1 */
+#define AUX_LENGTH(x) (((x - 1) & 0xf) << 4)
+#define AUX_TX_COMM_MASK (0xf << 0)
+#define AUX_TX_COMM_DP_TRANSACTION (0x1 << 3)
+#define AUX_TX_COMM_I2C_TRANSACTION (0x0 << 3)
+#define AUX_TX_COMM_MOT (0x1 << 2)
+#define AUX_TX_COMM_WRITE (0x0 << 0)
+#define AUX_TX_COMM_READ (0x1 << 0)
+
+/* EXYNOS_DP_AUX_ADDR_7_0 */
+#define AUX_ADDR_7_0(x) (((x) >> 0) & 0xff)
+
+/* EXYNOS_DP_AUX_ADDR_15_8 */
+#define AUX_ADDR_15_8(x) (((x) >> 8) & 0xff)
+
+/* EXYNOS_DP_AUX_ADDR_19_16 */
+#define AUX_ADDR_19_16(x) (((x) >> 16) & 0x0f)
+
+/* EXYNOS_DP_AUX_CH_CTL_2 */
+#define ADDR_ONLY (0x1 << 1)
+#define AUX_EN (0x1 << 0)
+
+/* EXYNOS_DP_SOC_GENERAL_CTL */
+#define AUDIO_MODE_SPDIF_MODE (0x1 << 8)
+#define AUDIO_MODE_MASTER_MODE (0x0 << 8)
+#define MASTER_VIDEO_INTERLACE_EN (0x1 << 4)
+#define VIDEO_MASTER_CLK_SEL (0x1 << 2)
+#define VIDEO_MASTER_MODE_EN (0x1 << 1)
+#define VIDEO_MODE_MASK (0x1 << 0)
+#define VIDEO_MODE_SLAVE_MODE (0x1 << 0)
+#define VIDEO_MODE_MASTER_MODE (0x0 << 0)
+
+#endif /* _EXYNOS_DP_REG_H */
diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c
new file mode 100644
index 000000000000..557091dc0e97
--- /dev/null
+++ b/drivers/video/exynos/exynos_mipi_dsi.c
@@ -0,0 +1,600 @@
+/* linux/drivers/video/exynos/exynos_mipi_dsi.c
+ *
+ * Samsung SoC MIPI-DSIM driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/memory.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/notifier.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+
+#include <video/exynos_mipi_dsim.h>
+
+#include <plat/fb.h>
+
+#include "exynos_mipi_dsi_common.h"
+#include "exynos_mipi_dsi_lowlevel.h"
+
+struct mipi_dsim_ddi {
+ int bus_id;
+ struct list_head list;
+ struct mipi_dsim_lcd_device *dsim_lcd_dev;
+ struct mipi_dsim_lcd_driver *dsim_lcd_drv;
+};
+
+static LIST_HEAD(dsim_ddi_list);
+
+static DEFINE_MUTEX(mipi_dsim_lock);
+
+static struct mipi_dsim_platform_data *to_dsim_plat(struct platform_device
+ *pdev)
+{
+ return pdev->dev.platform_data;
+}
+
+static struct regulator_bulk_data supplies[] = {
+ { .supply = "vdd10", },
+ { .supply = "vdd18", },
+};
+
+static int exynos_mipi_regulator_enable(struct mipi_dsim_device *dsim)
+{
+ int ret;
+
+ mutex_lock(&dsim->lock);
+ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+ mutex_unlock(&dsim->lock);
+
+ return ret;
+}
+
+static int exynos_mipi_regulator_disable(struct mipi_dsim_device *dsim)
+{
+ int ret;
+
+ mutex_lock(&dsim->lock);
+ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+ mutex_unlock(&dsim->lock);
+
+ return ret;
+}
+
+/* update all register settings to MIPI DSI controller. */
+static void exynos_mipi_update_cfg(struct mipi_dsim_device *dsim)
+{
+ /*
+ * data from Display controller(FIMD) is not transferred in video mode
+ * but in case of command mode, all settings is not updated to
+ * registers.
+ */
+ exynos_mipi_dsi_stand_by(dsim, 0);
+
+ exynos_mipi_dsi_init_dsim(dsim);
+ exynos_mipi_dsi_init_link(dsim);
+
+ exynos_mipi_dsi_set_hs_enable(dsim);
+
+ /* set display timing. */
+ exynos_mipi_dsi_set_display_mode(dsim, dsim->dsim_config);
+
+ /*
+ * data from Display controller(FIMD) is transferred in video mode
+ * but in case of command mode, all settigs is updated to registers.
+ */
+ exynos_mipi_dsi_stand_by(dsim, 1);
+}
+
+static int exynos_mipi_dsi_early_blank_mode(struct mipi_dsim_device *dsim,
+ int power)
+{
+ struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
+ struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
+
+ switch (power) {
+ case FB_BLANK_POWERDOWN:
+ if (dsim->suspended)
+ return 0;
+
+ if (client_drv && client_drv->suspend)
+ client_drv->suspend(client_dev);
+
+ clk_disable(dsim->clock);
+
+ exynos_mipi_regulator_disable(dsim);
+
+ dsim->suspended = true;
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
+{
+ struct platform_device *pdev = to_platform_device(dsim->dev);
+ struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
+ struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
+
+ switch (power) {
+ case FB_BLANK_UNBLANK:
+ if (!dsim->suspended)
+ return 0;
+
+ /* lcd panel power on. */
+ if (client_drv && client_drv->power_on)
+ client_drv->power_on(client_dev, 1);
+
+ exynos_mipi_regulator_disable(dsim);
+
+ /* enable MIPI-DSI PHY. */
+ if (dsim->pd->phy_enable)
+ dsim->pd->phy_enable(pdev, true);
+
+ clk_enable(dsim->clock);
+
+ exynos_mipi_update_cfg(dsim);
+
+ /* set lcd panel sequence commands. */
+ if (client_drv && client_drv->set_sequence)
+ client_drv->set_sequence(client_dev);
+
+ dsim->suspended = false;
+
+ break;
+ case FB_BLANK_NORMAL:
+ /* TODO. */
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_register_lcd_device(struct mipi_dsim_lcd_device *lcd_dev)
+{
+ struct mipi_dsim_ddi *dsim_ddi;
+
+ if (!lcd_dev->name) {
+ pr_err("dsim_lcd_device name is NULL.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi = kzalloc(sizeof(struct mipi_dsim_ddi), GFP_KERNEL);
+ if (!dsim_ddi) {
+ pr_err("failed to allocate dsim_ddi object.\n");
+ return -ENOMEM;
+ }
+
+ dsim_ddi->dsim_lcd_dev = lcd_dev;
+
+ mutex_lock(&mipi_dsim_lock);
+ list_add_tail(&dsim_ddi->list, &dsim_ddi_list);
+ mutex_unlock(&mipi_dsim_lock);
+
+ return 0;
+}
+
+struct mipi_dsim_ddi *exynos_mipi_dsi_find_lcd_device(struct mipi_dsim_lcd_driver *lcd_drv)
+{
+ struct mipi_dsim_ddi *dsim_ddi, *next;
+ struct mipi_dsim_lcd_device *lcd_dev;
+
+ mutex_lock(&mipi_dsim_lock);
+
+ list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
+ if (!dsim_ddi)
+ goto out;
+
+ lcd_dev = dsim_ddi->dsim_lcd_dev;
+ if (!lcd_dev)
+ continue;
+
+ if ((strcmp(lcd_drv->name, lcd_dev->name)) == 0) {
+ /**
+ * bus_id would be used to identify
+ * connected bus.
+ */
+ dsim_ddi->bus_id = lcd_dev->bus_id;
+ mutex_unlock(&mipi_dsim_lock);
+
+ return dsim_ddi;
+ }
+
+ list_del(&dsim_ddi->list);
+ kfree(dsim_ddi);
+ }
+
+out:
+ mutex_unlock(&mipi_dsim_lock);
+
+ return NULL;
+}
+
+int exynos_mipi_dsi_register_lcd_driver(struct mipi_dsim_lcd_driver *lcd_drv)
+{
+ struct mipi_dsim_ddi *dsim_ddi;
+
+ if (!lcd_drv->name) {
+ pr_err("dsim_lcd_driver name is NULL.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi = exynos_mipi_dsi_find_lcd_device(lcd_drv);
+ if (!dsim_ddi) {
+ pr_err("mipi_dsim_ddi object not found.\n");
+ return -EFAULT;
+ }
+
+ dsim_ddi->dsim_lcd_drv = lcd_drv;
+
+ pr_info("registered panel driver(%s) to mipi-dsi driver.\n",
+ lcd_drv->name);
+
+ return 0;
+
+}
+
+struct mipi_dsim_ddi *exynos_mipi_dsi_bind_lcd_ddi(struct mipi_dsim_device *dsim,
+ const char *name)
+{
+ struct mipi_dsim_ddi *dsim_ddi, *next;
+ struct mipi_dsim_lcd_driver *lcd_drv;
+ struct mipi_dsim_lcd_device *lcd_dev;
+ int ret;
+
+ mutex_lock(&dsim->lock);
+
+ list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
+ lcd_drv = dsim_ddi->dsim_lcd_drv;
+ lcd_dev = dsim_ddi->dsim_lcd_dev;
+ if (!lcd_drv || !lcd_dev ||
+ (dsim->id != dsim_ddi->bus_id))
+ continue;
+
+ dev_dbg(dsim->dev, "lcd_drv->id = %d, lcd_dev->id = %d\n",
+ lcd_drv->id, lcd_dev->id);
+ dev_dbg(dsim->dev, "lcd_dev->bus_id = %d, dsim->id = %d\n",
+ lcd_dev->bus_id, dsim->id);
+
+ if ((strcmp(lcd_drv->name, name) == 0)) {
+ lcd_dev->master = dsim;
+
+ lcd_dev->dev.parent = dsim->dev;
+ dev_set_name(&lcd_dev->dev, "%s", lcd_drv->name);
+
+ ret = device_register(&lcd_dev->dev);
+ if (ret < 0) {
+ dev_err(dsim->dev,
+ "can't register %s, status %d\n",
+ dev_name(&lcd_dev->dev), ret);
+ mutex_unlock(&dsim->lock);
+
+ return NULL;
+ }
+
+ dsim->dsim_lcd_dev = lcd_dev;
+ dsim->dsim_lcd_drv = lcd_drv;
+
+ mutex_unlock(&dsim->lock);
+
+ return dsim_ddi;
+ }
+ }
+
+ mutex_unlock(&dsim->lock);
+
+ return NULL;
+}
+
+/* define MIPI-DSI Master operations. */
+static struct mipi_dsim_master_ops master_ops = {
+ .cmd_read = exynos_mipi_dsi_rd_data,
+ .cmd_write = exynos_mipi_dsi_wr_data,
+ .get_dsim_frame_done = exynos_mipi_dsi_get_frame_done_status,
+ .clear_dsim_frame_done = exynos_mipi_dsi_clear_frame_done,
+ .set_early_blank_mode = exynos_mipi_dsi_early_blank_mode,
+ .set_blank_mode = exynos_mipi_dsi_blank_mode,
+};
+
+static int exynos_mipi_dsi_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct mipi_dsim_device *dsim;
+ struct mipi_dsim_config *dsim_config;
+ struct mipi_dsim_platform_data *dsim_pd;
+ struct mipi_dsim_ddi *dsim_ddi;
+ int ret = -EINVAL;
+
+ dsim = kzalloc(sizeof(struct mipi_dsim_device), GFP_KERNEL);
+ if (!dsim) {
+ dev_err(&pdev->dev, "failed to allocate dsim object.\n");
+ return -ENOMEM;
+ }
+
+ dsim->pd = to_dsim_plat(pdev);
+ dsim->dev = &pdev->dev;
+ dsim->id = pdev->id;
+
+ /* get mipi_dsim_platform_data. */
+ dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
+ if (dsim_pd == NULL) {
+ dev_err(&pdev->dev, "failed to get platform data for dsim.\n");
+ goto err_clock_get;
+ }
+ /* get mipi_dsim_config. */
+ dsim_config = dsim_pd->dsim_config;
+ if (dsim_config == NULL) {
+ dev_err(&pdev->dev, "failed to get dsim config data.\n");
+ goto err_clock_get;
+ }
+
+ dsim->dsim_config = dsim_config;
+ dsim->master_ops = &master_ops;
+
+ mutex_init(&dsim->lock);
+
+ ret = regulator_bulk_get(&pdev->dev, ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to get regulators: %d\n", ret);
+ goto err_clock_get;
+ }
+
+ dsim->clock = clk_get(&pdev->dev, "dsim0");
+ if (IS_ERR(dsim->clock)) {
+ dev_err(&pdev->dev, "failed to get dsim clock source\n");
+ goto err_clock_get;
+ }
+
+ clk_enable(dsim->clock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "failed to get io memory region\n");
+ goto err_platform_get;
+ }
+
+ dsim->res = request_mem_region(res->start, resource_size(res),
+ dev_name(&pdev->dev));
+ if (!dsim->res) {
+ dev_err(&pdev->dev, "failed to request io memory region\n");
+ ret = -ENOMEM;
+ goto err_mem_region;
+ }
+
+ dsim->reg_base = ioremap(res->start, resource_size(res));
+ if (!dsim->reg_base) {
+ dev_err(&pdev->dev, "failed to remap io region\n");
+ ret = -ENOMEM;
+ goto err_ioremap;
+ }
+
+ mutex_init(&dsim->lock);
+
+ /* bind lcd ddi matched with panel name. */
+ dsim_ddi = exynos_mipi_dsi_bind_lcd_ddi(dsim, dsim_pd->lcd_panel_name);
+ if (!dsim_ddi) {
+ dev_err(&pdev->dev, "mipi_dsim_ddi object not found.\n");
+ goto err_bind;
+ }
+
+ dsim->irq = platform_get_irq(pdev, 0);
+ if (dsim->irq < 0) {
+ dev_err(&pdev->dev, "failed to request dsim irq resource\n");
+ ret = -EINVAL;
+ goto err_platform_get_irq;
+ }
+
+ ret = request_irq(dsim->irq, exynos_mipi_dsi_interrupt_handler,
+ IRQF_SHARED, pdev->name, dsim);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to request dsim irq\n");
+ ret = -EINVAL;
+ goto err_bind;
+ }
+
+ init_completion(&dsim_wr_comp);
+ init_completion(&dsim_rd_comp);
+
+ /* enable interrupt */
+ exynos_mipi_dsi_init_interrupt(dsim);
+
+ /* initialize mipi-dsi client(lcd panel). */
+ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->probe)
+ dsim_ddi->dsim_lcd_drv->probe(dsim_ddi->dsim_lcd_dev);
+
+ /* in case that mipi got enabled at bootloader. */
+ if (dsim_pd->enabled)
+ goto out;
+
+ /* lcd panel power on. */
+ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->power_on)
+ dsim_ddi->dsim_lcd_drv->power_on(dsim_ddi->dsim_lcd_dev, 1);
+
+ exynos_mipi_regulator_enable(dsim);
+
+ /* enable MIPI-DSI PHY. */
+ if (dsim->pd->phy_enable)
+ dsim->pd->phy_enable(pdev, true);
+
+ exynos_mipi_update_cfg(dsim);
+
+ /* set lcd panel sequence commands. */
+ if (dsim_ddi->dsim_lcd_drv && dsim_ddi->dsim_lcd_drv->set_sequence)
+ dsim_ddi->dsim_lcd_drv->set_sequence(dsim_ddi->dsim_lcd_dev);
+
+ dsim->suspended = false;
+
+out:
+ platform_set_drvdata(pdev, dsim);
+
+ dev_dbg(&pdev->dev, "mipi-dsi driver(%s mode) has been probed.\n",
+ (dsim_config->e_interface == DSIM_COMMAND) ?
+ "CPU" : "RGB");
+
+ return 0;
+
+err_bind:
+ iounmap(dsim->reg_base);
+
+err_ioremap:
+ release_mem_region(dsim->res->start, resource_size(dsim->res));
+
+err_mem_region:
+ release_resource(dsim->res);
+
+err_platform_get:
+ clk_disable(dsim->clock);
+ clk_put(dsim->clock);
+err_clock_get:
+ kfree(dsim);
+
+err_platform_get_irq:
+ return ret;
+}
+
+static int __devexit exynos_mipi_dsi_remove(struct platform_device *pdev)
+{
+ struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
+ struct mipi_dsim_ddi *dsim_ddi, *next;
+ struct mipi_dsim_lcd_driver *dsim_lcd_drv;
+
+ iounmap(dsim->reg_base);
+
+ clk_disable(dsim->clock);
+ clk_put(dsim->clock);
+
+ release_resource(dsim->res);
+ release_mem_region(dsim->res->start, resource_size(dsim->res));
+
+ list_for_each_entry_safe(dsim_ddi, next, &dsim_ddi_list, list) {
+ if (dsim_ddi) {
+ if (dsim->id != dsim_ddi->bus_id)
+ continue;
+
+ dsim_lcd_drv = dsim_ddi->dsim_lcd_drv;
+
+ if (dsim_lcd_drv->remove)
+ dsim_lcd_drv->remove(dsim_ddi->dsim_lcd_dev);
+
+ kfree(dsim_ddi);
+ }
+ }
+
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+ kfree(dsim);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int exynos_mipi_dsi_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
+ struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
+ struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
+
+ disable_irq(dsim->irq);
+
+ if (dsim->suspended)
+ return 0;
+
+ if (client_drv && client_drv->suspend)
+ client_drv->suspend(client_dev);
+
+ /* enable MIPI-DSI PHY. */
+ if (dsim->pd->phy_enable)
+ dsim->pd->phy_enable(pdev, false);
+
+ clk_disable(dsim->clock);
+
+ exynos_mipi_regulator_disable(dsim);
+
+ dsim->suspended = true;
+
+ return 0;
+}
+
+static int exynos_mipi_dsi_resume(struct platform_device *pdev)
+{
+ struct mipi_dsim_device *dsim = platform_get_drvdata(pdev);
+ struct mipi_dsim_lcd_driver *client_drv = dsim->dsim_lcd_drv;
+ struct mipi_dsim_lcd_device *client_dev = dsim->dsim_lcd_dev;
+
+ enable_irq(dsim->irq);
+
+ if (!dsim->suspended)
+ return 0;
+
+ /* lcd panel power on. */
+ if (client_drv && client_drv->power_on)
+ client_drv->power_on(client_dev, 1);
+
+ exynos_mipi_regulator_enable(dsim);
+
+ /* enable MIPI-DSI PHY. */
+ if (dsim->pd->phy_enable)
+ dsim->pd->phy_enable(pdev, true);
+
+ clk_enable(dsim->clock);
+
+ exynos_mipi_update_cfg(dsim);
+
+ /* set lcd panel sequence commands. */
+ if (client_drv && client_drv->set_sequence)
+ client_drv->set_sequence(client_dev);
+
+ dsim->suspended = false;
+
+ return 0;
+}
+#else
+#define exynos_mipi_dsi_suspend NULL
+#define exynos_mipi_dsi_resume NULL
+#endif
+
+static struct platform_driver exynos_mipi_dsi_driver = {
+ .probe = exynos_mipi_dsi_probe,
+ .remove = __devexit_p(exynos_mipi_dsi_remove),
+ .suspend = exynos_mipi_dsi_suspend,
+ .resume = exynos_mipi_dsi_resume,
+ .driver = {
+ .name = "exynos-mipi-dsim",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(exynos_mipi_dsi_driver);
+
+MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("Samusung SoC MIPI-DSI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.c b/drivers/video/exynos/exynos_mipi_dsi_common.c
new file mode 100644
index 000000000000..14909c1d3832
--- /dev/null
+++ b/drivers/video/exynos/exynos_mipi_dsi_common.c
@@ -0,0 +1,896 @@
+/* linux/drivers/video/exynos/exynos_mipi_dsi_common.c
+ *
+ * Samsung SoC MIPI-DSI common driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/fb.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/memory.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+
+#include <video/mipi_display.h>
+#include <video/exynos_mipi_dsim.h>
+
+#include <mach/map.h>
+
+#include "exynos_mipi_dsi_regs.h"
+#include "exynos_mipi_dsi_lowlevel.h"
+#include "exynos_mipi_dsi_common.h"
+
+#define MIPI_FIFO_TIMEOUT msecs_to_jiffies(250)
+#define MIPI_RX_FIFO_READ_DONE 0x30800002
+#define MIPI_MAX_RX_FIFO 20
+#define MHZ (1000 * 1000)
+#define FIN_HZ (24 * MHZ)
+
+#define DFIN_PLL_MIN_HZ (6 * MHZ)
+#define DFIN_PLL_MAX_HZ (12 * MHZ)
+
+#define DFVCO_MIN_HZ (500 * MHZ)
+#define DFVCO_MAX_HZ (1000 * MHZ)
+
+#define TRY_GET_FIFO_TIMEOUT (5000 * 2)
+#define TRY_FIFO_CLEAR (10)
+
+/* MIPI-DSIM status types. */
+enum {
+ DSIM_STATE_INIT, /* should be initialized. */
+ DSIM_STATE_STOP, /* CPU and LCDC are LP mode. */
+ DSIM_STATE_HSCLKEN, /* HS clock was enabled. */
+ DSIM_STATE_ULPS
+};
+
+/* define DSI lane types. */
+enum {
+ DSIM_LANE_CLOCK = (1 << 0),
+ DSIM_LANE_DATA0 = (1 << 1),
+ DSIM_LANE_DATA1 = (1 << 2),
+ DSIM_LANE_DATA2 = (1 << 3),
+ DSIM_LANE_DATA3 = (1 << 4)
+};
+
+static unsigned int dpll_table[15] = {
+ 100, 120, 170, 220, 270,
+ 320, 390, 450, 510, 560,
+ 640, 690, 770, 870, 950
+};
+
+irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id)
+{
+ unsigned int intsrc = 0;
+ unsigned int intmsk = 0;
+ struct mipi_dsim_device *dsim = NULL;
+
+ dsim = dev_id;
+ if (!dsim) {
+ dev_dbg(dsim->dev, KERN_ERR "%s:error: wrong parameter\n",
+ __func__);
+ return IRQ_HANDLED;
+ }
+
+ intsrc = exynos_mipi_dsi_read_interrupt(dsim);
+ intmsk = exynos_mipi_dsi_read_interrupt_mask(dsim);
+
+ intmsk = ~(intmsk) & intsrc;
+
+ switch (intmsk) {
+ case INTMSK_RX_DONE:
+ complete(&dsim_rd_comp);
+ dev_dbg(dsim->dev, "MIPI INTMSK_RX_DONE\n");
+ break;
+ case INTMSK_FIFO_EMPTY:
+ complete(&dsim_wr_comp);
+ dev_dbg(dsim->dev, "MIPI INTMSK_FIFO_EMPTY\n");
+ break;
+ default:
+ break;
+ }
+
+ exynos_mipi_dsi_clear_interrupt(dsim, intmsk);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * write long packet to mipi dsi slave
+ * @dsim: mipi dsim device structure.
+ * @data0: packet data to send.
+ * @data1: size of packet data
+ */
+static void exynos_mipi_dsi_long_data_wr(struct mipi_dsim_device *dsim,
+ const unsigned char *data0, unsigned int data_size)
+{
+ unsigned int data_cnt = 0, payload = 0;
+
+ /* in case that data count is more then 4 */
+ for (data_cnt = 0; data_cnt < data_size; data_cnt += 4) {
+ /*
+ * after sending 4bytes per one time,
+ * send remainder data less then 4.
+ */
+ if ((data_size - data_cnt) < 4) {
+ if ((data_size - data_cnt) == 3) {
+ payload = data0[data_cnt] |
+ data0[data_cnt + 1] << 8 |
+ data0[data_cnt + 2] << 16;
+ dev_dbg(dsim->dev, "count = 3 payload = %x, %x %x %x\n",
+ payload, data0[data_cnt],
+ data0[data_cnt + 1],
+ data0[data_cnt + 2]);
+ } else if ((data_size - data_cnt) == 2) {
+ payload = data0[data_cnt] |
+ data0[data_cnt + 1] << 8;
+ dev_dbg(dsim->dev,
+ "count = 2 payload = %x, %x %x\n", payload,
+ data0[data_cnt],
+ data0[data_cnt + 1]);
+ } else if ((data_size - data_cnt) == 1) {
+ payload = data0[data_cnt];
+ }
+
+ exynos_mipi_dsi_wr_tx_data(dsim, payload);
+ /* send 4bytes per one time. */
+ } else {
+ payload = data0[data_cnt] |
+ data0[data_cnt + 1] << 8 |
+ data0[data_cnt + 2] << 16 |
+ data0[data_cnt + 3] << 24;
+
+ dev_dbg(dsim->dev,
+ "count = 4 payload = %x, %x %x %x %x\n",
+ payload, *(u8 *)(data0 + data_cnt),
+ data0[data_cnt + 1],
+ data0[data_cnt + 2],
+ data0[data_cnt + 3]);
+
+ exynos_mipi_dsi_wr_tx_data(dsim, payload);
+ }
+ }
+}
+
+int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
+ const unsigned char *data0, unsigned int data_size)
+{
+ unsigned int check_rx_ack = 0;
+
+ if (dsim->state == DSIM_STATE_ULPS) {
+ dev_err(dsim->dev, "state is ULPS.\n");
+
+ return -EINVAL;
+ }
+
+ /* FIXME!!! why does it need this delay? */
+ msleep(20);
+
+ mutex_lock(&dsim->lock);
+
+ switch (data_id) {
+ /* short packet types of packet types for command. */
+ case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
+ case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
+ case MIPI_DSI_DCS_SHORT_WRITE:
+ case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
+ case MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE:
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
+ if (check_rx_ack) {
+ /* process response func should be implemented */
+ mutex_unlock(&dsim->lock);
+ return 0;
+ } else {
+ mutex_unlock(&dsim->lock);
+ return -EINVAL;
+ }
+
+ /* general command */
+ case MIPI_DSI_COLOR_MODE_OFF:
+ case MIPI_DSI_COLOR_MODE_ON:
+ case MIPI_DSI_SHUTDOWN_PERIPHERAL:
+ case MIPI_DSI_TURN_ON_PERIPHERAL:
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data0[0], data0[1]);
+ if (check_rx_ack) {
+ /* process response func should be implemented. */
+ mutex_unlock(&dsim->lock);
+ return 0;
+ } else {
+ mutex_unlock(&dsim->lock);
+ return -EINVAL;
+ }
+
+ /* packet types for video data */
+ case MIPI_DSI_V_SYNC_START:
+ case MIPI_DSI_V_SYNC_END:
+ case MIPI_DSI_H_SYNC_START:
+ case MIPI_DSI_H_SYNC_END:
+ case MIPI_DSI_END_OF_TRANSMISSION:
+ mutex_unlock(&dsim->lock);
+ return 0;
+
+ /* long packet type and null packet */
+ case MIPI_DSI_NULL_PACKET:
+ case MIPI_DSI_BLANKING_PACKET:
+ mutex_unlock(&dsim->lock);
+ return 0;
+ case MIPI_DSI_GENERIC_LONG_WRITE:
+ case MIPI_DSI_DCS_LONG_WRITE:
+ {
+ unsigned int size, payload = 0;
+ INIT_COMPLETION(dsim_wr_comp);
+
+ size = data_size * 4;
+
+ /* if data count is less then 4, then send 3bytes data. */
+ if (data_size < 4) {
+ payload = data0[0] |
+ data0[1] << 8 |
+ data0[2] << 16;
+
+ exynos_mipi_dsi_wr_tx_data(dsim, payload);
+
+ dev_dbg(dsim->dev, "count = %d payload = %x,%x %x %x\n",
+ data_size, payload, data0[0],
+ data0[1], data0[2]);
+
+ /* in case that data count is more then 4 */
+ } else
+ exynos_mipi_dsi_long_data_wr(dsim, data0, data_size);
+
+ /* put data into header fifo */
+ exynos_mipi_dsi_wr_tx_header(dsim, data_id, data_size & 0xff,
+ (data_size & 0xff00) >> 8);
+
+ if (!wait_for_completion_interruptible_timeout(&dsim_wr_comp,
+ MIPI_FIFO_TIMEOUT)) {
+ dev_warn(dsim->dev, "command write timeout.\n");
+ mutex_unlock(&dsim->lock);
+ return -EAGAIN;
+ }
+
+ if (check_rx_ack) {
+ /* process response func should be implemented. */
+ mutex_unlock(&dsim->lock);
+ return 0;
+ } else {
+ mutex_unlock(&dsim->lock);
+ return -EINVAL;
+ }
+ }
+
+ /* packet typo for video data */
+ case MIPI_DSI_PACKED_PIXEL_STREAM_16:
+ case MIPI_DSI_PACKED_PIXEL_STREAM_18:
+ case MIPI_DSI_PIXEL_STREAM_3BYTE_18:
+ case MIPI_DSI_PACKED_PIXEL_STREAM_24:
+ if (check_rx_ack) {
+ /* process response func should be implemented. */
+ mutex_unlock(&dsim->lock);
+ return 0;
+ } else {
+ mutex_unlock(&dsim->lock);
+ return -EINVAL;
+ }
+ default:
+ dev_warn(dsim->dev,
+ "data id %x is not supported current DSI spec.\n",
+ data_id);
+
+ mutex_unlock(&dsim->lock);
+ return -EINVAL;
+ }
+
+ mutex_unlock(&dsim->lock);
+ return 0;
+}
+
+static unsigned int exynos_mipi_dsi_long_data_rd(struct mipi_dsim_device *dsim,
+ unsigned int req_size, unsigned int rx_data, u8 *rx_buf)
+{
+ unsigned int rcv_pkt, i, j;
+ u16 rxsize;
+
+ /* for long packet */
+ rxsize = (u16)((rx_data & 0x00ffff00) >> 8);
+ dev_dbg(dsim->dev, "mipi dsi rx size : %d\n", rxsize);
+ if (rxsize != req_size) {
+ dev_dbg(dsim->dev,
+ "received size mismatch received: %d, requested: %d\n",
+ rxsize, req_size);
+ goto err;
+ }
+
+ for (i = 0; i < (rxsize >> 2); i++) {
+ rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
+ dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
+ for (j = 0; j < 4; j++) {
+ rx_buf[(i * 4) + j] =
+ (u8)(rcv_pkt >> (j * 8)) & 0xff;
+ dev_dbg(dsim->dev, "received value : %02x\n",
+ (rcv_pkt >> (j * 8)) & 0xff);
+ }
+ }
+ if (rxsize % 4) {
+ rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
+ dev_dbg(dsim->dev, "received pkt : %08x\n", rcv_pkt);
+ for (j = 0; j < (rxsize % 4); j++) {
+ rx_buf[(i * 4) + j] =
+ (u8)(rcv_pkt >> (j * 8)) & 0xff;
+ dev_dbg(dsim->dev, "received value : %02x\n",
+ (rcv_pkt >> (j * 8)) & 0xff);
+ }
+ }
+
+ return rxsize;
+
+err:
+ return -EINVAL;
+}
+
+static unsigned int exynos_mipi_dsi_response_size(unsigned int req_size)
+{
+ switch (req_size) {
+ case 1:
+ return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE;
+ case 2:
+ return MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE;
+ default:
+ return MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE;
+ }
+}
+
+int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
+ unsigned int data0, unsigned int req_size, u8 *rx_buf)
+{
+ unsigned int rx_data, rcv_pkt, i;
+ u8 response = 0;
+ u16 rxsize;
+
+ if (dsim->state == DSIM_STATE_ULPS) {
+ dev_err(dsim->dev, "state is ULPS.\n");
+
+ return -EINVAL;
+ }
+
+ /* FIXME!!! */
+ msleep(20);
+
+ mutex_lock(&dsim->lock);
+ INIT_COMPLETION(dsim_rd_comp);
+ exynos_mipi_dsi_rd_tx_header(dsim,
+ MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, req_size);
+
+ response = exynos_mipi_dsi_response_size(req_size);
+
+ switch (data_id) {
+ case MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM:
+ case MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM:
+ case MIPI_DSI_DCS_READ:
+ exynos_mipi_dsi_rd_tx_header(dsim,
+ data_id, data0);
+ /* process response func should be implemented. */
+ break;
+ default:
+ dev_warn(dsim->dev,
+ "data id %x is not supported current DSI spec.\n",
+ data_id);
+
+ return -EINVAL;
+ }
+
+ if (!wait_for_completion_interruptible_timeout(&dsim_rd_comp,
+ MIPI_FIFO_TIMEOUT)) {
+ pr_err("RX done interrupt timeout\n");
+ mutex_unlock(&dsim->lock);
+ return 0;
+ }
+
+ msleep(20);
+
+ rx_data = exynos_mipi_dsi_rd_rx_fifo(dsim);
+
+ if ((u8)(rx_data & 0xff) != response) {
+ printk(KERN_ERR
+ "mipi dsi wrong response rx_data : %x, response:%x\n",
+ rx_data, response);
+ goto clear_rx_fifo;
+ }
+
+ if (req_size <= 2) {
+ /* for short packet */
+ for (i = 0; i < req_size; i++)
+ rx_buf[i] = (rx_data >> (8 + (i * 8))) & 0xff;
+ rxsize = req_size;
+ } else {
+ /* for long packet */
+ rxsize = exynos_mipi_dsi_long_data_rd(dsim, req_size, rx_data,
+ rx_buf);
+ if (rxsize != req_size)
+ goto clear_rx_fifo;
+ }
+
+ rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
+
+ msleep(20);
+
+ if (rcv_pkt != MIPI_RX_FIFO_READ_DONE) {
+ dev_info(dsim->dev,
+ "Can't found RX FIFO READ DONE FLAG : %x\n", rcv_pkt);
+ goto clear_rx_fifo;
+ }
+
+ mutex_unlock(&dsim->lock);
+
+ return rxsize;
+
+clear_rx_fifo:
+ i = 0;
+ while (1) {
+ rcv_pkt = exynos_mipi_dsi_rd_rx_fifo(dsim);
+ if ((rcv_pkt == MIPI_RX_FIFO_READ_DONE)
+ || (i > MIPI_MAX_RX_FIFO))
+ break;
+ dev_dbg(dsim->dev,
+ "mipi dsi clear rx fifo : %08x\n", rcv_pkt);
+ i++;
+ }
+ dev_info(dsim->dev,
+ "mipi dsi rx done count : %d, rcv_pkt : %08x\n", i, rcv_pkt);
+
+ mutex_unlock(&dsim->lock);
+
+ return 0;
+}
+
+static int exynos_mipi_dsi_pll_on(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ int sw_timeout;
+
+ if (enable) {
+ sw_timeout = 1000;
+
+ exynos_mipi_dsi_enable_pll(dsim, 1);
+ while (1) {
+ sw_timeout--;
+ if (exynos_mipi_dsi_is_pll_stable(dsim))
+ return 0;
+ if (sw_timeout == 0)
+ return -EINVAL;
+ }
+ } else
+ exynos_mipi_dsi_enable_pll(dsim, 0);
+
+ return 0;
+}
+
+static unsigned long exynos_mipi_dsi_change_pll(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler)
+{
+ unsigned long dfin_pll, dfvco, dpll_out;
+ unsigned int i, freq_band = 0xf;
+
+ dfin_pll = (FIN_HZ / pre_divider);
+
+ /******************************************************
+ * Serial Clock(=ByteClk X 8) FreqBand[3:0] *
+ ******************************************************
+ * ~ 99.99 MHz 0000
+ * 100 ~ 119.99 MHz 0001
+ * 120 ~ 159.99 MHz 0010
+ * 160 ~ 199.99 MHz 0011
+ * 200 ~ 239.99 MHz 0100
+ * 140 ~ 319.99 MHz 0101
+ * 320 ~ 389.99 MHz 0110
+ * 390 ~ 449.99 MHz 0111
+ * 450 ~ 509.99 MHz 1000
+ * 510 ~ 559.99 MHz 1001
+ * 560 ~ 639.99 MHz 1010
+ * 640 ~ 689.99 MHz 1011
+ * 690 ~ 769.99 MHz 1100
+ * 770 ~ 869.99 MHz 1101
+ * 870 ~ 949.99 MHz 1110
+ * 950 ~ 1000 MHz 1111
+ ******************************************************/
+ if (dfin_pll < DFIN_PLL_MIN_HZ || dfin_pll > DFIN_PLL_MAX_HZ) {
+ dev_warn(dsim->dev, "fin_pll range should be 6MHz ~ 12MHz\n");
+ exynos_mipi_dsi_enable_afc(dsim, 0, 0);
+ } else {
+ if (dfin_pll < 7 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x1);
+ else if (dfin_pll < 8 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x0);
+ else if (dfin_pll < 9 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x3);
+ else if (dfin_pll < 10 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x2);
+ else if (dfin_pll < 11 * MHZ)
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x5);
+ else
+ exynos_mipi_dsi_enable_afc(dsim, 1, 0x4);
+ }
+
+ dfvco = dfin_pll * main_divider;
+ dev_dbg(dsim->dev, "dfvco = %lu, dfin_pll = %lu, main_divider = %d\n",
+ dfvco, dfin_pll, main_divider);
+ if (dfvco < DFVCO_MIN_HZ || dfvco > DFVCO_MAX_HZ)
+ dev_warn(dsim->dev, "fvco range should be 500MHz ~ 1000MHz\n");
+
+ dpll_out = dfvco / (1 << scaler);
+ dev_dbg(dsim->dev, "dpll_out = %lu, dfvco = %lu, scaler = %d\n",
+ dpll_out, dfvco, scaler);
+
+ for (i = 0; i < ARRAY_SIZE(dpll_table); i++) {
+ if (dpll_out < dpll_table[i] * MHZ) {
+ freq_band = i;
+ break;
+ }
+ }
+
+ dev_dbg(dsim->dev, "freq_band = %d\n", freq_band);
+
+ exynos_mipi_dsi_pll_freq(dsim, pre_divider, main_divider, scaler);
+
+ exynos_mipi_dsi_hs_zero_ctrl(dsim, 0);
+ exynos_mipi_dsi_prep_ctrl(dsim, 0);
+
+ /* Freq Band */
+ exynos_mipi_dsi_pll_freq_band(dsim, freq_band);
+
+ /* Stable time */
+ exynos_mipi_dsi_pll_stable_time(dsim, dsim->dsim_config->pll_stable_time);
+
+ /* Enable PLL */
+ dev_dbg(dsim->dev, "FOUT of mipi dphy pll is %luMHz\n",
+ (dpll_out / MHZ));
+
+ return dpll_out;
+}
+
+static int exynos_mipi_dsi_set_clock(struct mipi_dsim_device *dsim,
+ unsigned int byte_clk_sel, unsigned int enable)
+{
+ unsigned int esc_div;
+ unsigned long esc_clk_error_rate;
+ unsigned long hs_clk = 0, byte_clk = 0, escape_clk = 0;
+
+ if (enable) {
+ dsim->e_clk_src = byte_clk_sel;
+
+ /* Escape mode clock and byte clock source */
+ exynos_mipi_dsi_set_byte_clock_src(dsim, byte_clk_sel);
+
+ /* DPHY, DSIM Link : D-PHY clock out */
+ if (byte_clk_sel == DSIM_PLL_OUT_DIV8) {
+ hs_clk = exynos_mipi_dsi_change_pll(dsim,
+ dsim->dsim_config->p, dsim->dsim_config->m,
+ dsim->dsim_config->s);
+ if (hs_clk == 0) {
+ dev_err(dsim->dev,
+ "failed to get hs clock.\n");
+ return -EINVAL;
+ }
+
+ byte_clk = hs_clk / 8;
+ exynos_mipi_dsi_enable_pll_bypass(dsim, 0);
+ exynos_mipi_dsi_pll_on(dsim, 1);
+ /* DPHY : D-PHY clock out, DSIM link : external clock out */
+ } else if (byte_clk_sel == DSIM_EXT_CLK_DIV8) {
+ dev_warn(dsim->dev, "this project is not support\n");
+ dev_warn(dsim->dev,
+ "external clock source for MIPI DSIM.\n");
+ } else if (byte_clk_sel == DSIM_EXT_CLK_BYPASS) {
+ dev_warn(dsim->dev, "this project is not support\n");
+ dev_warn(dsim->dev,
+ "external clock source for MIPI DSIM\n");
+ }
+
+ /* escape clock divider */
+ esc_div = byte_clk / (dsim->dsim_config->esc_clk);
+ dev_dbg(dsim->dev,
+ "esc_div = %d, byte_clk = %lu, esc_clk = %lu\n",
+ esc_div, byte_clk, dsim->dsim_config->esc_clk);
+ if ((byte_clk / esc_div) >= (20 * MHZ) ||
+ (byte_clk / esc_div) >
+ dsim->dsim_config->esc_clk)
+ esc_div += 1;
+
+ escape_clk = byte_clk / esc_div;
+ dev_dbg(dsim->dev,
+ "escape_clk = %lu, byte_clk = %lu, esc_div = %d\n",
+ escape_clk, byte_clk, esc_div);
+
+ /* enable escape clock. */
+ exynos_mipi_dsi_enable_byte_clock(dsim, 1);
+
+ /* enable byte clk and escape clock */
+ exynos_mipi_dsi_set_esc_clk_prs(dsim, 1, esc_div);
+ /* escape clock on lane */
+ exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
+ (DSIM_LANE_CLOCK | dsim->data_lane), 1);
+
+ dev_dbg(dsim->dev, "byte clock is %luMHz\n",
+ (byte_clk / MHZ));
+ dev_dbg(dsim->dev, "escape clock that user's need is %lu\n",
+ (dsim->dsim_config->esc_clk / MHZ));
+ dev_dbg(dsim->dev, "escape clock divider is %x\n", esc_div);
+ dev_dbg(dsim->dev, "escape clock is %luMHz\n",
+ ((byte_clk / esc_div) / MHZ));
+
+ if ((byte_clk / esc_div) > escape_clk) {
+ esc_clk_error_rate = escape_clk /
+ (byte_clk / esc_div);
+ dev_warn(dsim->dev, "error rate is %lu over.\n",
+ (esc_clk_error_rate / 100));
+ } else if ((byte_clk / esc_div) < (escape_clk)) {
+ esc_clk_error_rate = (byte_clk / esc_div) /
+ escape_clk;
+ dev_warn(dsim->dev, "error rate is %lu under.\n",
+ (esc_clk_error_rate / 100));
+ }
+ } else {
+ exynos_mipi_dsi_enable_esc_clk_on_lane(dsim,
+ (DSIM_LANE_CLOCK | dsim->data_lane), 0);
+ exynos_mipi_dsi_set_esc_clk_prs(dsim, 0, 0);
+
+ /* disable escape clock. */
+ exynos_mipi_dsi_enable_byte_clock(dsim, 0);
+
+ if (byte_clk_sel == DSIM_PLL_OUT_DIV8)
+ exynos_mipi_dsi_pll_on(dsim, 0);
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim)
+{
+ dsim->state = DSIM_STATE_INIT;
+
+ switch (dsim->dsim_config->e_no_data_lane) {
+ case DSIM_DATA_LANE_1:
+ dsim->data_lane = DSIM_LANE_DATA0;
+ break;
+ case DSIM_DATA_LANE_2:
+ dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1;
+ break;
+ case DSIM_DATA_LANE_3:
+ dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
+ DSIM_LANE_DATA2;
+ break;
+ case DSIM_DATA_LANE_4:
+ dsim->data_lane = DSIM_LANE_DATA0 | DSIM_LANE_DATA1 |
+ DSIM_LANE_DATA2 | DSIM_LANE_DATA3;
+ break;
+ default:
+ dev_info(dsim->dev, "data lane is invalid.\n");
+ return -EINVAL;
+ };
+
+ exynos_mipi_dsi_sw_reset(dsim);
+ exynos_mipi_dsi_func_reset(dsim);
+
+ exynos_mipi_dsi_dp_dn_swap(dsim, 0);
+
+ return 0;
+}
+
+void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim)
+{
+ unsigned int src = 0;
+
+ src = (INTSRC_SFR_FIFO_EMPTY | INTSRC_RX_DATA_DONE);
+ exynos_mipi_dsi_set_interrupt(dsim, src, 1);
+
+ src = 0;
+ src = ~(INTMSK_RX_DONE | INTMSK_FIFO_EMPTY);
+ exynos_mipi_dsi_set_interrupt_mask(dsim, src, 1);
+}
+
+int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ /* enable only frame done interrupt */
+ exynos_mipi_dsi_set_interrupt_mask(dsim, INTMSK_FRAME_DONE, enable);
+
+ return 0;
+}
+
+void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+
+ /* consider Main display and Sub display. */
+
+ exynos_mipi_dsi_set_main_stand_by(dsim, enable);
+}
+
+int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_config)
+{
+ struct mipi_dsim_platform_data *dsim_pd;
+ struct fb_videomode *timing;
+
+ dsim_pd = (struct mipi_dsim_platform_data *)dsim->pd;
+ timing = (struct fb_videomode *)dsim_pd->lcd_panel_info;
+
+ /* in case of VIDEO MODE (RGB INTERFACE), it sets polarities. */
+ if (dsim_config->e_interface == (u32) DSIM_VIDEO) {
+ if (dsim_config->auto_vertical_cnt == 0) {
+ exynos_mipi_dsi_set_main_disp_vporch(dsim,
+ dsim_config->cmd_allow,
+ timing->upper_margin,
+ timing->lower_margin);
+ exynos_mipi_dsi_set_main_disp_hporch(dsim,
+ timing->left_margin,
+ timing->right_margin);
+ exynos_mipi_dsi_set_main_disp_sync_area(dsim,
+ timing->vsync_len,
+ timing->hsync_len);
+ }
+ }
+
+ exynos_mipi_dsi_set_main_disp_resol(dsim, timing->xres,
+ timing->yres);
+
+ exynos_mipi_dsi_display_config(dsim, dsim_config);
+
+ dev_info(dsim->dev, "lcd panel ==> width = %d, height = %d\n",
+ timing->xres, timing->yres);
+
+ return 0;
+}
+
+int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim)
+{
+ unsigned int time_out = 100;
+
+ switch (dsim->state) {
+ case DSIM_STATE_INIT:
+ exynos_mipi_dsi_init_fifo_pointer(dsim, 0x1f);
+
+ /* dsi configuration */
+ exynos_mipi_dsi_init_config(dsim);
+ exynos_mipi_dsi_enable_lane(dsim, DSIM_LANE_CLOCK, 1);
+ exynos_mipi_dsi_enable_lane(dsim, dsim->data_lane, 1);
+
+ /* set clock configuration */
+ exynos_mipi_dsi_set_clock(dsim, dsim->dsim_config->e_byte_clk, 1);
+
+ /* check clock and data lane state are stop state */
+ while (!(exynos_mipi_dsi_is_lane_state(dsim))) {
+ time_out--;
+ if (time_out == 0) {
+ dev_err(dsim->dev,
+ "DSI Master is not stop state.\n");
+ dev_err(dsim->dev,
+ "Check initialization process\n");
+
+ return -EINVAL;
+ }
+ }
+ if (time_out != 0) {
+ dev_info(dsim->dev,
+ "DSI Master driver has been completed.\n");
+ dev_info(dsim->dev, "DSI Master state is stop state\n");
+ }
+
+ dsim->state = DSIM_STATE_STOP;
+
+ /* BTA sequence counters */
+ exynos_mipi_dsi_set_stop_state_counter(dsim,
+ dsim->dsim_config->stop_holding_cnt);
+ exynos_mipi_dsi_set_bta_timeout(dsim,
+ dsim->dsim_config->bta_timeout);
+ exynos_mipi_dsi_set_lpdr_timeout(dsim,
+ dsim->dsim_config->rx_timeout);
+
+ return 0;
+ default:
+ dev_info(dsim->dev, "DSI Master is already init.\n");
+ return 0;
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim)
+{
+ if (dsim->state != DSIM_STATE_STOP) {
+ dev_warn(dsim->dev, "DSIM is not in stop state.\n");
+ return 0;
+ }
+
+ if (dsim->e_clk_src == DSIM_EXT_CLK_BYPASS) {
+ dev_warn(dsim->dev, "clock source is external bypass.\n");
+ return 0;
+ }
+
+ dsim->state = DSIM_STATE_HSCLKEN;
+
+ /* set LCDC and CPU transfer mode to HS. */
+ exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
+ exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
+ exynos_mipi_dsi_enable_hs_clock(dsim, 1);
+
+ return 0;
+}
+
+int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int mode)
+{
+ if (mode) {
+ if (dsim->state != DSIM_STATE_HSCLKEN) {
+ dev_err(dsim->dev, "HS Clock lane is not enabled.\n");
+ return -EINVAL;
+ }
+
+ exynos_mipi_dsi_set_lcdc_transfer_mode(dsim, 0);
+ } else {
+ if (dsim->state == DSIM_STATE_INIT || dsim->state ==
+ DSIM_STATE_ULPS) {
+ dev_err(dsim->dev,
+ "DSI Master is not STOP or HSDT state.\n");
+ return -EINVAL;
+ }
+
+ exynos_mipi_dsi_set_cpu_transfer_mode(dsim, 0);
+ }
+
+ return 0;
+}
+
+int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
+{
+ return _exynos_mipi_dsi_get_frame_done_status(dsim);
+}
+
+int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
+{
+ _exynos_mipi_dsi_clear_frame_done(dsim);
+
+ return 0;
+}
+
+int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
+ unsigned int val)
+{
+ int try = TRY_FIFO_CLEAR;
+
+ exynos_mipi_dsi_sw_reset_release(dsim);
+ exynos_mipi_dsi_func_reset(dsim);
+
+ do {
+ if (exynos_mipi_dsi_get_sw_reset_release(dsim)) {
+ exynos_mipi_dsi_init_interrupt(dsim);
+ dev_dbg(dsim->dev, "reset release done.\n");
+ return 0;
+ }
+ } while (--try);
+
+ dev_err(dsim->dev, "failed to clear dsim fifo.\n");
+ return -EAGAIN;
+}
+
+MODULE_AUTHOR("InKi Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("Samusung SoC MIPI-DSI common driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/exynos/exynos_mipi_dsi_common.h b/drivers/video/exynos/exynos_mipi_dsi_common.h
new file mode 100644
index 000000000000..412552274df3
--- /dev/null
+++ b/drivers/video/exynos/exynos_mipi_dsi_common.h
@@ -0,0 +1,46 @@
+/* linux/drivers/video/exynos_mipi_dsi_common.h
+ *
+ * Header file for Samsung SoC MIPI-DSI common driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _EXYNOS_MIPI_DSI_COMMON_H
+#define _EXYNOS_MIPI_DSI_COMMON_H
+
+static DECLARE_COMPLETION(dsim_rd_comp);
+static DECLARE_COMPLETION(dsim_wr_comp);
+
+int exynos_mipi_dsi_wr_data(struct mipi_dsim_device *dsim, unsigned int data_id,
+ const unsigned char *data0, unsigned int data_size);
+int exynos_mipi_dsi_rd_data(struct mipi_dsim_device *dsim, unsigned int data_id,
+ unsigned int data0, unsigned int req_size, u8 *rx_buf);
+irqreturn_t exynos_mipi_dsi_interrupt_handler(int irq, void *dev_id);
+void exynos_mipi_dsi_init_interrupt(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_init_dsim(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_stand_by(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+int exynos_mipi_dsi_set_display_mode(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_info);
+int exynos_mipi_dsi_init_link(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_set_hs_enable(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_set_data_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int mode);
+int exynos_mipi_dsi_enable_frame_done_int(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+int exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
+
+extern struct fb_info *registered_fb[FB_MAX] __read_mostly;
+
+int exynos_mipi_dsi_fifo_clear(struct mipi_dsim_device *dsim,
+ unsigned int val);
+
+#endif /* _EXYNOS_MIPI_DSI_COMMON_H */
diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c
new file mode 100644
index 000000000000..0ef38ce72af6
--- /dev/null
+++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c
@@ -0,0 +1,618 @@
+/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.c
+ *
+ * Samsung SoC MIPI-DSI lowlevel driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+
+#include <video/exynos_mipi_dsim.h>
+
+#include <mach/map.h>
+
+#include "exynos_mipi_dsi_regs.h"
+
+void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST);
+
+ reg |= DSIM_FUNCRST;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST);
+}
+
+void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_SWRST);
+
+ reg |= DSIM_SWRST;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_SWRST);
+}
+
+void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
+
+ reg |= INTSRC_SW_RST_RELEASE;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
+}
+
+int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim)
+{
+ return (readl(dsim->reg_base + EXYNOS_DSIM_INTSRC)) &
+ INTSRC_SW_RST_RELEASE;
+}
+
+unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_INTMSK);
+
+ return reg;
+}
+
+void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
+ unsigned int mode, unsigned int mask)
+{
+ unsigned int reg = 0;
+
+ if (mask)
+ reg |= mode;
+ else
+ reg &= ~mode;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_INTMSK);
+}
+
+void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
+ unsigned int cfg)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
+
+ writel(reg & ~(cfg), dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
+ mdelay(10);
+ reg |= cfg;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_FIFOCTRL);
+}
+
+/*
+ * this function set PLL P, M and S value in D-PHY
+ */
+void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
+ unsigned int value)
+{
+ writel(DSIM_AFC_CTL(value), dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
+}
+
+void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL);
+
+ reg &= ~DSIM_MAIN_STAND_BY;
+
+ if (enable)
+ reg |= DSIM_MAIN_STAND_BY;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
+}
+
+void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int width_resol, unsigned int height_resol)
+{
+ unsigned int reg;
+
+ /* standby should be set after configuration so set to not ready*/
+ reg = (readl(dsim->reg_base + EXYNOS_DSIM_MDRESOL)) &
+ ~(DSIM_MAIN_STAND_BY);
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
+
+ reg &= ~((0x7ff << 16) | (0x7ff << 0));
+ reg |= DSIM_MAIN_VRESOL(height_resol) | DSIM_MAIN_HRESOL(width_resol);
+
+ reg |= DSIM_MAIN_STAND_BY;
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_MDRESOL);
+}
+
+void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
+ unsigned int cmd_allow, unsigned int vfront, unsigned int vback)
+{
+ unsigned int reg;
+
+ reg = (readl(dsim->reg_base + EXYNOS_DSIM_MVPORCH)) &
+ ~((DSIM_CMD_ALLOW_MASK) | (DSIM_STABLE_VFP_MASK) |
+ (DSIM_MAIN_VBP_MASK));
+
+ reg |= (DSIM_CMD_ALLOW_SHIFT(cmd_allow & 0xf) |
+ DSIM_STABLE_VFP_SHIFT(vfront & 0x7ff) |
+ DSIM_MAIN_VBP_SHIFT(vback & 0x7ff));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_MVPORCH);
+}
+
+void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
+ unsigned int front, unsigned int back)
+{
+ unsigned int reg;
+
+ reg = (readl(dsim->reg_base + EXYNOS_DSIM_MHPORCH)) &
+ ~((DSIM_MAIN_HFP_MASK) | (DSIM_MAIN_HBP_MASK));
+
+ reg |= DSIM_MAIN_HFP_SHIFT(front) | DSIM_MAIN_HBP_SHIFT(back);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_MHPORCH);
+}
+
+void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori)
+{
+ unsigned int reg;
+
+ reg = (readl(dsim->reg_base + EXYNOS_DSIM_MSYNC)) &
+ ~((DSIM_MAIN_VSA_MASK) | (DSIM_MAIN_HSA_MASK));
+
+ reg |= (DSIM_MAIN_VSA_SHIFT(vert & 0x3ff) |
+ DSIM_MAIN_HSA_SHIFT(hori));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_MSYNC);
+}
+
+void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori)
+{
+ unsigned int reg;
+
+ reg = (readl(dsim->reg_base + EXYNOS_DSIM_SDRESOL)) &
+ ~(DSIM_SUB_STANDY_MASK);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
+
+ reg &= ~(DSIM_SUB_VRESOL_MASK) | ~(DSIM_SUB_HRESOL_MASK);
+ reg |= (DSIM_SUB_VRESOL_SHIFT(vert & 0x7ff) |
+ DSIM_SUB_HRESOL_SHIFT(hori & 0x7ff));
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
+
+ reg |= DSIM_SUB_STANDY_SHIFT(1);
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_SDRESOL);
+}
+
+void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim)
+{
+ struct mipi_dsim_config *dsim_config = dsim->dsim_config;
+
+ unsigned int cfg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) &
+ ~((1 << 28) | (0x1f << 20) | (0x3 << 5));
+
+ cfg = ((DSIM_AUTO_FLUSH(dsim_config->auto_flush)) |
+ (DSIM_EOT_DISABLE(dsim_config->eot_disable)) |
+ (DSIM_AUTO_MODE_SHIFT(dsim_config->auto_vertical_cnt)) |
+ (DSIM_HSE_MODE_SHIFT(dsim_config->hse)) |
+ (DSIM_HFP_MODE_SHIFT(dsim_config->hfp)) |
+ (DSIM_HBP_MODE_SHIFT(dsim_config->hbp)) |
+ (DSIM_HSA_MODE_SHIFT(dsim_config->hsa)) |
+ (DSIM_NUM_OF_DATALANE_SHIFT(dsim_config->e_no_data_lane)));
+
+ writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
+}
+
+void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_config)
+{
+ u32 reg = (readl(dsim->reg_base + EXYNOS_DSIM_CONFIG)) &
+ ~((0x3 << 26) | (1 << 25) | (0x3 << 18) | (0x7 << 12) |
+ (0x3 << 16) | (0x7 << 8));
+
+ if (dsim_config->e_interface == DSIM_VIDEO)
+ reg |= (1 << 25);
+ else if (dsim_config->e_interface == DSIM_COMMAND)
+ reg &= ~(1 << 25);
+ else {
+ dev_err(dsim->dev, "unknown lcd type.\n");
+ return;
+ }
+
+ /* main lcd */
+ reg |= ((u8) (dsim_config->e_burst_mode) & 0x3) << 26 |
+ ((u8) (dsim_config->e_virtual_ch) & 0x3) << 18 |
+ ((u8) (dsim_config->e_pixel_format) & 0x7) << 12;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
+}
+
+void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane,
+ unsigned int enable)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_CONFIG);
+
+ if (enable)
+ reg |= DSIM_LANE_ENx(lane);
+ else
+ reg &= ~DSIM_LANE_ENx(lane);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
+}
+
+
+void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
+ unsigned int count)
+{
+ unsigned int cfg;
+
+ /* get the data lane number. */
+ cfg = DSIM_NUM_OF_DATALANE_SHIFT(count);
+
+ writel(cfg, dsim->reg_base + EXYNOS_DSIM_CONFIG);
+}
+
+void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable,
+ unsigned int afc_code)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
+
+ if (enable) {
+ reg |= (1 << 14);
+ reg &= ~(0x7 << 5);
+ reg |= (afc_code & 0x7) << 5;
+ } else
+ reg &= ~(1 << 14);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR);
+}
+
+void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
+ ~(DSIM_PLL_BYPASS_SHIFT(0x1));
+
+ reg |= DSIM_PLL_BYPASS_SHIFT(enable);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+}
+
+void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p,
+ unsigned int m, unsigned int s)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+
+ reg |= ((p & 0x3f) << 13) | ((m & 0x1ff) << 4) | ((s & 0x7) << 1);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+}
+
+void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
+ unsigned int freq_band)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
+ ~(DSIM_FREQ_BAND_SHIFT(0x1f));
+
+ reg |= DSIM_FREQ_BAND_SHIFT(freq_band & 0x1f);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+}
+
+void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
+ ~(0x7ffff << 1);
+
+ reg |= (pre_divider & 0x3f) << 13 | (main_divider & 0x1ff) << 4 |
+ (scaler & 0x7) << 1;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+}
+
+void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
+ unsigned int lock_time)
+{
+ writel(lock_time, dsim->reg_base + EXYNOS_DSIM_PLLTMR);
+}
+
+void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim, unsigned int enable)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
+ ~(DSIM_PLL_EN_SHIFT(0x1));
+
+ reg |= DSIM_PLL_EN_SHIFT(enable & 0x1);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+}
+
+void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
+ unsigned int src)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
+ ~(DSIM_BYTE_CLK_SRC_SHIFT(0x3));
+
+ reg |= (DSIM_BYTE_CLK_SRC_SHIFT(src));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+}
+
+void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
+ ~(DSIM_BYTE_CLKEN_SHIFT(0x1));
+
+ reg |= DSIM_BYTE_CLKEN_SHIFT(enable);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+}
+
+void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
+ unsigned int enable, unsigned int prs_val)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
+ ~(DSIM_ESC_CLKEN_SHIFT(0x1) | 0xffff);
+
+ reg |= DSIM_ESC_CLKEN_SHIFT(enable);
+ if (enable)
+ reg |= prs_val;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+}
+
+void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
+ unsigned int lane_sel, unsigned int enable)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+
+ if (enable)
+ reg |= DSIM_LANE_ESC_CLKEN(lane_sel);
+ else
+
+ reg &= ~DSIM_LANE_ESC_CLKEN(lane_sel);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+}
+
+void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) &
+ ~(DSIM_FORCE_STOP_STATE_SHIFT(0x1));
+
+ reg |= (DSIM_FORCE_STOP_STATE_SHIFT(enable & 0x1));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
+}
+
+unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS);
+
+ /**
+ * check clock and data lane states.
+ * if MIPI-DSI controller was enabled at bootloader then
+ * TX_READY_HS_CLK is enabled otherwise STOP_STATE_CLK.
+ * so it should be checked for two case.
+ */
+ if ((reg & DSIM_STOP_STATE_DAT(0xf)) &&
+ ((reg & DSIM_STOP_STATE_CLK) ||
+ (reg & DSIM_TX_READY_HS_CLK)))
+ return 1;
+
+ return 0;
+}
+
+void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
+ unsigned int cnt_val)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE)) &
+ ~(DSIM_STOP_STATE_CNT_SHIFT(0x7ff));
+
+ reg |= (DSIM_STOP_STATE_CNT_SHIFT(cnt_val & 0x7ff));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
+}
+
+void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) &
+ ~(DSIM_BTA_TOUT_SHIFT(0xff));
+
+ reg |= (DSIM_BTA_TOUT_SHIFT(timeout));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT);
+}
+
+void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_TIMEOUT)) &
+ ~(DSIM_LPDR_TOUT_SHIFT(0xffff));
+
+ reg |= (DSIM_LPDR_TOUT_SHIFT(timeout));
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_TIMEOUT);
+}
+
+void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE);
+
+ reg &= ~DSIM_CMD_LPDT_LP;
+
+ if (lp)
+ reg |= DSIM_CMD_LPDT_LP;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
+}
+
+void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_ESCMODE);
+
+ reg &= ~DSIM_TX_LPDT_LP;
+
+ if (lp)
+ reg |= DSIM_TX_LPDT_LP;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_ESCMODE);
+}
+
+void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_CLKCTRL)) &
+ ~(DSIM_TX_REQUEST_HSCLK_SHIFT(0x1));
+
+ reg |= DSIM_TX_REQUEST_HSCLK_SHIFT(enable);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_CLKCTRL);
+}
+
+void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
+ unsigned int swap_en)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_PHYACCHR1);
+
+ reg &= ~(0x3 << 0);
+ reg |= (swap_en & 0x3) << 0;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PHYACCHR1);
+}
+
+void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
+ unsigned int hs_zero)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
+ ~(0xf << 28);
+
+ reg |= ((hs_zero & 0xf) << 28);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+}
+
+void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep)
+{
+ unsigned int reg = (readl(dsim->reg_base + EXYNOS_DSIM_PLLCTRL)) &
+ ~(0x7 << 20);
+
+ reg |= ((prep & 0x7) << 20);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PLLCTRL);
+}
+
+unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim)
+{
+ return readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
+}
+
+void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim,
+ unsigned int src)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
+
+ reg |= src;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
+}
+
+void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim,
+ unsigned int src, unsigned int enable)
+{
+ unsigned int reg = 0;
+
+ if (enable)
+ reg |= src;
+ else
+ reg &= ~src;
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_INTSRC);
+}
+
+unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg;
+
+ reg = readl(dsim->reg_base + EXYNOS_DSIM_STATUS);
+
+ return reg & (1 << 31) ? 1 : 0;
+}
+
+unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim)
+{
+ return readl(dsim->reg_base + EXYNOS_DSIM_FIFOCTRL) & ~(0x1f);
+}
+
+void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim,
+ unsigned int di, unsigned int data0, unsigned int data1)
+{
+ unsigned int reg = (data1 << 16) | (data0 << 8) | ((di & 0x3f) << 0);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR);
+}
+
+void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim,
+ unsigned int di, unsigned int data0)
+{
+ unsigned int reg = (data0 << 8) | (di << 0);
+
+ writel(reg, dsim->reg_base + EXYNOS_DSIM_PKTHDR);
+}
+
+unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim)
+{
+ return readl(dsim->reg_base + EXYNOS_DSIM_RXFIFO);
+}
+
+unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
+
+ return (reg & INTSRC_FRAME_DONE) ? 1 : 0;
+}
+
+void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim)
+{
+ unsigned int reg = readl(dsim->reg_base + EXYNOS_DSIM_INTSRC);
+
+ writel(reg | INTSRC_FRAME_DONE, dsim->reg_base +
+ EXYNOS_DSIM_INTSRC);
+}
+
+void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
+ unsigned int tx_data)
+{
+ writel(tx_data, dsim->reg_base + EXYNOS_DSIM_PAYLOAD);
+}
diff --git a/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h
new file mode 100644
index 000000000000..85460701c7ea
--- /dev/null
+++ b/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h
@@ -0,0 +1,112 @@
+/* linux/drivers/video/exynos/exynos_mipi_dsi_lowlevel.h
+ *
+ * Header file for Samsung SoC MIPI-DSI lowlevel driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _EXYNOS_MIPI_DSI_LOWLEVEL_H
+#define _EXYNOS_MIPI_DSI_LOWLEVEL_H
+
+void exynos_mipi_dsi_func_reset(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_sw_reset(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_sw_reset_release(struct mipi_dsim_device *dsim);
+int exynos_mipi_dsi_get_sw_reset_release(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_set_interrupt_mask(struct mipi_dsim_device *dsim,
+ unsigned int mode, unsigned int mask);
+void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
+ unsigned int count);
+void exynos_mipi_dsi_init_fifo_pointer(struct mipi_dsim_device *dsim,
+ unsigned int cfg);
+void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
+ unsigned int value);
+void exynos_mipi_dsi_set_phy_tunning(struct mipi_dsim_device *dsim,
+ unsigned int value);
+void exynos_mipi_dsi_set_main_stand_by(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_set_main_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int width_resol, unsigned int height_resol);
+void exynos_mipi_dsi_set_main_disp_vporch(struct mipi_dsim_device *dsim,
+ unsigned int cmd_allow, unsigned int vfront, unsigned int vback);
+void exynos_mipi_dsi_set_main_disp_hporch(struct mipi_dsim_device *dsim,
+ unsigned int front, unsigned int back);
+void exynos_mipi_dsi_set_main_disp_sync_area(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori);
+void exynos_mipi_dsi_set_sub_disp_resol(struct mipi_dsim_device *dsim,
+ unsigned int vert, unsigned int hori);
+void exynos_mipi_dsi_init_config(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_display_config(struct mipi_dsim_device *dsim,
+ struct mipi_dsim_config *dsim_config);
+void exynos_mipi_dsi_set_data_lane_number(struct mipi_dsim_device *dsim,
+ unsigned int count);
+void exynos_mipi_dsi_enable_lane(struct mipi_dsim_device *dsim, unsigned int lane,
+ unsigned int enable);
+void exynos_mipi_dsi_enable_afc(struct mipi_dsim_device *dsim, unsigned int enable,
+ unsigned int afc_code);
+void exynos_mipi_dsi_enable_pll_bypass(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_set_pll_pms(struct mipi_dsim_device *dsim, unsigned int p,
+ unsigned int m, unsigned int s);
+void exynos_mipi_dsi_pll_freq_band(struct mipi_dsim_device *dsim,
+ unsigned int freq_band);
+void exynos_mipi_dsi_pll_freq(struct mipi_dsim_device *dsim,
+ unsigned int pre_divider, unsigned int main_divider,
+ unsigned int scaler);
+void exynos_mipi_dsi_pll_stable_time(struct mipi_dsim_device *dsim,
+ unsigned int lock_time);
+void exynos_mipi_dsi_enable_pll(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_set_byte_clock_src(struct mipi_dsim_device *dsim,
+ unsigned int src);
+void exynos_mipi_dsi_enable_byte_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_set_esc_clk_prs(struct mipi_dsim_device *dsim,
+ unsigned int enable, unsigned int prs_val);
+void exynos_mipi_dsi_enable_esc_clk_on_lane(struct mipi_dsim_device *dsim,
+ unsigned int lane_sel, unsigned int enable);
+void exynos_mipi_dsi_force_dphy_stop_state(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+unsigned int exynos_mipi_dsi_is_lane_state(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_set_stop_state_counter(struct mipi_dsim_device *dsim,
+ unsigned int cnt_val);
+void exynos_mipi_dsi_set_bta_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout);
+void exynos_mipi_dsi_set_lpdr_timeout(struct mipi_dsim_device *dsim,
+ unsigned int timeout);
+void exynos_mipi_dsi_set_lcdc_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp);
+void exynos_mipi_dsi_set_cpu_transfer_mode(struct mipi_dsim_device *dsim,
+ unsigned int lp);
+void exynos_mipi_dsi_enable_hs_clock(struct mipi_dsim_device *dsim,
+ unsigned int enable);
+void exynos_mipi_dsi_dp_dn_swap(struct mipi_dsim_device *dsim,
+ unsigned int swap_en);
+void exynos_mipi_dsi_hs_zero_ctrl(struct mipi_dsim_device *dsim,
+ unsigned int hs_zero);
+void exynos_mipi_dsi_prep_ctrl(struct mipi_dsim_device *dsim, unsigned int prep);
+unsigned int exynos_mipi_dsi_read_interrupt(struct mipi_dsim_device *dsim);
+unsigned int exynos_mipi_dsi_read_interrupt_mask(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_clear_interrupt(struct mipi_dsim_device *dsim,
+ unsigned int src);
+void exynos_mipi_dsi_set_interrupt(struct mipi_dsim_device *dsim,
+ unsigned int src, unsigned int enable);
+unsigned int exynos_mipi_dsi_is_pll_stable(struct mipi_dsim_device *dsim);
+unsigned int exynos_mipi_dsi_get_fifo_state(struct mipi_dsim_device *dsim);
+unsigned int _exynos_mipi_dsi_get_frame_done_status(struct mipi_dsim_device *dsim);
+void _exynos_mipi_dsi_clear_frame_done(struct mipi_dsim_device *dsim);
+void exynos_mipi_dsi_wr_tx_header(struct mipi_dsim_device *dsim, unsigned int di,
+ unsigned int data0, unsigned int data1);
+void exynos_mipi_dsi_wr_tx_data(struct mipi_dsim_device *dsim,
+ unsigned int tx_data);
+void exynos_mipi_dsi_rd_tx_header(struct mipi_dsim_device *dsim,
+ unsigned int data0, unsigned int data1);
+unsigned int exynos_mipi_dsi_rd_rx_fifo(struct mipi_dsim_device *dsim);
+
+#endif /* _EXYNOS_MIPI_DSI_LOWLEVEL_H */
diff --git a/drivers/video/exynos/exynos_mipi_dsi_regs.h b/drivers/video/exynos/exynos_mipi_dsi_regs.h
new file mode 100644
index 000000000000..4227106d3fd0
--- /dev/null
+++ b/drivers/video/exynos/exynos_mipi_dsi_regs.h
@@ -0,0 +1,149 @@
+/* linux/driver/video/exynos/exynos_mipi_dsi_regs.h
+ *
+ * Register definition file for Samsung MIPI-DSIM driver
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd
+ *
+ * InKi Dae <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _EXYNOS_MIPI_DSI_REGS_H
+#define _EXYNOS_MIPI_DSI_REGS_H
+
+#define EXYNOS_DSIM_STATUS 0x0 /* Status register */
+#define EXYNOS_DSIM_SWRST 0x4 /* Software reset register */
+#define EXYNOS_DSIM_CLKCTRL 0x8 /* Clock control register */
+#define EXYNOS_DSIM_TIMEOUT 0xc /* Time out register */
+#define EXYNOS_DSIM_CONFIG 0x10 /* Configuration register */
+#define EXYNOS_DSIM_ESCMODE 0x14 /* Escape mode register */
+
+/* Main display image resolution register */
+#define EXYNOS_DSIM_MDRESOL 0x18
+#define EXYNOS_DSIM_MVPORCH 0x1c /* Main display Vporch register */
+#define EXYNOS_DSIM_MHPORCH 0x20 /* Main display Hporch register */
+#define EXYNOS_DSIM_MSYNC 0x24 /* Main display sync area register */
+
+/* Sub display image resolution register */
+#define EXYNOS_DSIM_SDRESOL 0x28
+#define EXYNOS_DSIM_INTSRC 0x2c /* Interrupt source register */
+#define EXYNOS_DSIM_INTMSK 0x30 /* Interrupt mask register */
+#define EXYNOS_DSIM_PKTHDR 0x34 /* Packet Header FIFO register */
+#define EXYNOS_DSIM_PAYLOAD 0x38 /* Payload FIFO register */
+#define EXYNOS_DSIM_RXFIFO 0x3c /* Read FIFO register */
+#define EXYNOS_DSIM_FIFOTHLD 0x40 /* FIFO threshold level register */
+#define EXYNOS_DSIM_FIFOCTRL 0x44 /* FIFO status and control register */
+
+/* FIFO memory AC characteristic register */
+#define EXYNOS_DSIM_PLLCTRL 0x4c /* PLL control register */
+#define EXYNOS_DSIM_PLLTMR 0x50 /* PLL timer register */
+#define EXYNOS_DSIM_PHYACCHR 0x54 /* D-PHY AC characteristic register */
+#define EXYNOS_DSIM_PHYACCHR1 0x58 /* D-PHY AC characteristic register1 */
+
+/* DSIM_STATUS */
+#define DSIM_STOP_STATE_DAT(x) (((x) & 0xf) << 0)
+#define DSIM_STOP_STATE_CLK (1 << 8)
+#define DSIM_TX_READY_HS_CLK (1 << 10)
+
+/* DSIM_SWRST */
+#define DSIM_FUNCRST (1 << 16)
+#define DSIM_SWRST (1 << 0)
+
+/* EXYNOS_DSIM_TIMEOUT */
+#define DSIM_LPDR_TOUT_SHIFT(x) ((x) << 0)
+#define DSIM_BTA_TOUT_SHIFT(x) ((x) << 16)
+
+/* EXYNOS_DSIM_CLKCTRL */
+#define DSIM_LANE_ESC_CLKEN(x) (((x) & 0x1f) << 19)
+#define DSIM_BYTE_CLKEN_SHIFT(x) ((x) << 24)
+#define DSIM_BYTE_CLK_SRC_SHIFT(x) ((x) << 25)
+#define DSIM_PLL_BYPASS_SHIFT(x) ((x) << 27)
+#define DSIM_ESC_CLKEN_SHIFT(x) ((x) << 28)
+#define DSIM_TX_REQUEST_HSCLK_SHIFT(x) ((x) << 31)
+
+/* EXYNOS_DSIM_CONFIG */
+#define DSIM_LANE_ENx(x) (((x) & 0x1f) << 0)
+#define DSIM_NUM_OF_DATALANE_SHIFT(x) ((x) << 5)
+#define DSIM_HSA_MODE_SHIFT(x) ((x) << 20)
+#define DSIM_HBP_MODE_SHIFT(x) ((x) << 21)
+#define DSIM_HFP_MODE_SHIFT(x) ((x) << 22)
+#define DSIM_HSE_MODE_SHIFT(x) ((x) << 23)
+#define DSIM_AUTO_MODE_SHIFT(x) ((x) << 24)
+#define DSIM_EOT_DISABLE(x) ((x) << 28)
+#define DSIM_AUTO_FLUSH(x) ((x) << 29)
+
+#define DSIM_NUM_OF_DATA_LANE(x) ((x) << DSIM_NUM_OF_DATALANE_SHIFT)
+
+/* EXYNOS_DSIM_ESCMODE */
+#define DSIM_TX_LPDT_LP (1 << 6)
+#define DSIM_CMD_LPDT_LP (1 << 7)
+#define DSIM_FORCE_STOP_STATE_SHIFT(x) ((x) << 20)
+#define DSIM_STOP_STATE_CNT_SHIFT(x) ((x) << 21)
+
+/* EXYNOS_DSIM_MDRESOL */
+#define DSIM_MAIN_STAND_BY (1 << 31)
+#define DSIM_MAIN_VRESOL(x) (((x) & 0x7ff) << 16)
+#define DSIM_MAIN_HRESOL(x) (((x) & 0X7ff) << 0)
+
+/* EXYNOS_DSIM_MVPORCH */
+#define DSIM_CMD_ALLOW_SHIFT(x) ((x) << 28)
+#define DSIM_STABLE_VFP_SHIFT(x) ((x) << 16)
+#define DSIM_MAIN_VBP_SHIFT(x) ((x) << 0)
+#define DSIM_CMD_ALLOW_MASK (0xf << 28)
+#define DSIM_STABLE_VFP_MASK (0x7ff << 16)
+#define DSIM_MAIN_VBP_MASK (0x7ff << 0)
+
+/* EXYNOS_DSIM_MHPORCH */
+#define DSIM_MAIN_HFP_SHIFT(x) ((x) << 16)
+#define DSIM_MAIN_HBP_SHIFT(x) ((x) << 0)
+#define DSIM_MAIN_HFP_MASK ((0xffff) << 16)
+#define DSIM_MAIN_HBP_MASK ((0xffff) << 0)
+
+/* EXYNOS_DSIM_MSYNC */
+#define DSIM_MAIN_VSA_SHIFT(x) ((x) << 22)
+#define DSIM_MAIN_HSA_SHIFT(x) ((x) << 0)
+#define DSIM_MAIN_VSA_MASK ((0x3ff) << 22)
+#define DSIM_MAIN_HSA_MASK ((0xffff) << 0)
+
+/* EXYNOS_DSIM_SDRESOL */
+#define DSIM_SUB_STANDY_SHIFT(x) ((x) << 31)
+#define DSIM_SUB_VRESOL_SHIFT(x) ((x) << 16)
+#define DSIM_SUB_HRESOL_SHIFT(x) ((x) << 0)
+#define DSIM_SUB_STANDY_MASK ((0x1) << 31)
+#define DSIM_SUB_VRESOL_MASK ((0x7ff) << 16)
+#define DSIM_SUB_HRESOL_MASK ((0x7ff) << 0)
+
+/* EXYNOS_DSIM_INTSRC */
+#define INTSRC_PLL_STABLE (1 << 31)
+#define INTSRC_SW_RST_RELEASE (1 << 30)
+#define INTSRC_SFR_FIFO_EMPTY (1 << 29)
+#define INTSRC_FRAME_DONE (1 << 24)
+#define INTSRC_RX_DATA_DONE (1 << 18)
+
+/* EXYNOS_DSIM_INTMSK */
+#define INTMSK_FIFO_EMPTY (1 << 29)
+#define INTMSK_BTA (1 << 25)
+#define INTMSK_FRAME_DONE (1 << 24)
+#define INTMSK_RX_TIMEOUT (1 << 21)
+#define INTMSK_BTA_TIMEOUT (1 << 20)
+#define INTMSK_RX_DONE (1 << 18)
+#define INTMSK_RX_TE (1 << 17)
+#define INTMSK_RX_ACK (1 << 16)
+#define INTMSK_RX_ECC_ERR (1 << 15)
+#define INTMSK_RX_CRC_ERR (1 << 14)
+
+/* EXYNOS_DSIM_FIFOCTRL */
+#define SFR_HEADER_EMPTY (1 << 22)
+
+/* EXYNOS_DSIM_PHYACCHR */
+#define DSIM_AFC_CTL(x) (((x) & 0x7) << 5)
+
+/* EXYNOS_DSIM_PLLCTRL */
+#define DSIM_PLL_EN_SHIFT(x) ((x) << 23)
+#define DSIM_FREQ_BAND_SHIFT(x) ((x) << 24)
+
+#endif /* _EXYNOS_MIPI_DSI_REGS_H */
diff --git a/drivers/video/exynos/s6e8ax0.c b/drivers/video/exynos/s6e8ax0.c
new file mode 100644
index 000000000000..4aa9ac6218bf
--- /dev/null
+++ b/drivers/video/exynos/s6e8ax0.c
@@ -0,0 +1,898 @@
+/* linux/drivers/video/exynos/s6e8ax0.c
+ *
+ * MIPI-DSI based s6e8ax0 AMOLED lcd 4.65 inch panel driver.
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee, <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/ctype.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/lcd.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
+#include <linux/regulator/consumer.h>
+
+#include <video/mipi_display.h>
+#include <video/exynos_mipi_dsim.h>
+
+#define LDI_MTP_LENGTH 24
+#define DSIM_PM_STABLE_TIME 10
+#define MIN_BRIGHTNESS 0
+#define MAX_BRIGHTNESS 24
+#define GAMMA_TABLE_COUNT 26
+
+#define POWER_IS_ON(pwr) ((pwr) == FB_BLANK_UNBLANK)
+#define POWER_IS_OFF(pwr) ((pwr) == FB_BLANK_POWERDOWN)
+#define POWER_IS_NRM(pwr) ((pwr) == FB_BLANK_NORMAL)
+
+#define lcd_to_master(a) (a->dsim_dev->master)
+#define lcd_to_master_ops(a) ((lcd_to_master(a))->master_ops)
+
+enum {
+ DSIM_NONE_STATE = 0,
+ DSIM_RESUME_COMPLETE = 1,
+ DSIM_FRAME_DONE = 2,
+};
+
+struct s6e8ax0 {
+ struct device *dev;
+ unsigned int power;
+ unsigned int id;
+ unsigned int gamma;
+ unsigned int acl_enable;
+ unsigned int cur_acl;
+
+ struct lcd_device *ld;
+ struct backlight_device *bd;
+
+ struct mipi_dsim_lcd_device *dsim_dev;
+ struct lcd_platform_data *ddi_pd;
+ struct mutex lock;
+ bool enabled;
+};
+
+
+static struct regulator_bulk_data supplies[] = {
+ { .supply = "vdd3", },
+ { .supply = "vci", },
+};
+
+static void s6e8ax0_regulator_enable(struct s6e8ax0 *lcd)
+{
+ int ret = 0;
+ struct lcd_platform_data *pd = NULL;
+
+ pd = lcd->ddi_pd;
+ mutex_lock(&lcd->lock);
+ if (!lcd->enabled) {
+ ret = regulator_bulk_enable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ goto out;
+
+ lcd->enabled = true;
+ }
+ msleep(pd->power_on_delay);
+out:
+ mutex_unlock(&lcd->lock);
+}
+
+static void s6e8ax0_regulator_disable(struct s6e8ax0 *lcd)
+{
+ int ret = 0;
+
+ mutex_lock(&lcd->lock);
+ if (lcd->enabled) {
+ ret = regulator_bulk_disable(ARRAY_SIZE(supplies), supplies);
+ if (ret)
+ goto out;
+
+ lcd->enabled = false;
+ }
+out:
+ mutex_unlock(&lcd->lock);
+}
+
+static const unsigned char s6e8ax0_22_gamma_30[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xf5, 0x00, 0xff, 0xad, 0xaf,
+ 0xbA, 0xc3, 0xd8, 0xc5, 0x9f, 0xc6, 0x9e, 0xc1, 0xdc, 0xc0,
+ 0x00, 0x61, 0x00, 0x5a, 0x00, 0x74,
+};
+
+static const unsigned char s6e8ax0_22_gamma_50[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xe8, 0x1f, 0xf7, 0xad, 0xc0,
+ 0xb5, 0xc4, 0xdc, 0xc4, 0x9e, 0xc6, 0x9c, 0xbb, 0xd8, 0xbb,
+ 0x00, 0x70, 0x00, 0x68, 0x00, 0x86,
+};
+
+static const unsigned char s6e8ax0_22_gamma_60[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xde, 0x1f, 0xef, 0xad, 0xc4,
+ 0xb3, 0xc3, 0xdd, 0xc4, 0x9e, 0xc6, 0x9c, 0xbc, 0xd6, 0xba,
+ 0x00, 0x75, 0x00, 0x6e, 0x00, 0x8d,
+};
+
+static const unsigned char s6e8ax0_22_gamma_70[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xd8, 0x1f, 0xe7, 0xaf, 0xc8,
+ 0xb4, 0xc4, 0xdd, 0xc3, 0x9d, 0xc6, 0x9c, 0xbb, 0xd6, 0xb9,
+ 0x00, 0x7a, 0x00, 0x72, 0x00, 0x93,
+};
+
+static const unsigned char s6e8ax0_22_gamma_80[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc9, 0x1f, 0xde, 0xae, 0xc9,
+ 0xb1, 0xc3, 0xdd, 0xc2, 0x9d, 0xc5, 0x9b, 0xbc, 0xd6, 0xbb,
+ 0x00, 0x7f, 0x00, 0x77, 0x00, 0x99,
+};
+
+static const unsigned char s6e8ax0_22_gamma_90[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xc7, 0x1f, 0xd9, 0xb0, 0xcc,
+ 0xb2, 0xc3, 0xdc, 0xc1, 0x9c, 0xc6, 0x9c, 0xbc, 0xd4, 0xb9,
+ 0x00, 0x83, 0x00, 0x7b, 0x00, 0x9e,
+};
+
+static const unsigned char s6e8ax0_22_gamma_100[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xbd, 0x80, 0xcd, 0xba, 0xce,
+ 0xb3, 0xc4, 0xde, 0xc3, 0x9c, 0xc4, 0x9, 0xb8, 0xd3, 0xb6,
+ 0x00, 0x88, 0x00, 0x80, 0x00, 0xa5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_120[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb9, 0x95, 0xc8, 0xb1, 0xcf,
+ 0xb2, 0xc6, 0xdf, 0xc5, 0x9b, 0xc3, 0x99, 0xb6, 0xd2, 0xb6,
+ 0x00, 0x8f, 0x00, 0x86, 0x00, 0xac,
+};
+
+static const unsigned char s6e8ax0_22_gamma_130[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc7, 0xb1, 0xd0,
+ 0xb2, 0xc4, 0xdd, 0xc3, 0x9a, 0xc3, 0x98, 0xb6, 0xd0, 0xb4,
+ 0x00, 0x92, 0x00, 0x8a, 0x00, 0xb1,
+};
+
+static const unsigned char s6e8ax0_22_gamma_140[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb7, 0xa0, 0xc5, 0xb2, 0xd0,
+ 0xb3, 0xc3, 0xde, 0xc3, 0x9b, 0xc2, 0x98, 0xb6, 0xd0, 0xb4,
+ 0x00, 0x95, 0x00, 0x8d, 0x00, 0xb5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_150[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xa0, 0xc2, 0xb2, 0xd0,
+ 0xb2, 0xc1, 0xdd, 0xc2, 0x9b, 0xc2, 0x98, 0xb4, 0xcf, 0xb1,
+ 0x00, 0x99, 0x00, 0x90, 0x00, 0xba,
+};
+
+static const unsigned char s6e8ax0_22_gamma_160[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xa5, 0xbf, 0xb0, 0xd0,
+ 0xb1, 0xc3, 0xde, 0xc2, 0x99, 0xc1, 0x97, 0xb4, 0xce, 0xb1,
+ 0x00, 0x9c, 0x00, 0x93, 0x00, 0xbe,
+};
+
+static const unsigned char s6e8ax0_22_gamma_170[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb5, 0xbf, 0xb1, 0xd1,
+ 0xb1, 0xc3, 0xde, 0xc3, 0x99, 0xc0, 0x96, 0xb4, 0xce, 0xb1,
+ 0x00, 0x9f, 0x00, 0x96, 0x00, 0xc2,
+};
+
+static const unsigned char s6e8ax0_22_gamma_180[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb7, 0xbe, 0xb3, 0xd2,
+ 0xb3, 0xc3, 0xde, 0xc2, 0x97, 0xbf, 0x95, 0xb4, 0xcd, 0xb1,
+ 0x00, 0xa2, 0x00, 0x99, 0x00, 0xc5,
+};
+
+static const unsigned char s6e8ax0_22_gamma_190[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbe, 0xb2, 0xd2,
+ 0xb2, 0xc3, 0xdd, 0xc3, 0x98, 0xbf, 0x95, 0xb2, 0xcc, 0xaf,
+ 0x00, 0xa5, 0x00, 0x9c, 0x00, 0xc9,
+};
+
+static const unsigned char s6e8ax0_22_gamma_200[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xaf, 0xb9, 0xbc, 0xb2, 0xd2,
+ 0xb1, 0xc4, 0xdd, 0xc3, 0x97, 0xbe, 0x95, 0xb1, 0xcb, 0xae,
+ 0x00, 0xa8, 0x00, 0x9f, 0x00, 0xcd,
+};
+
+static const unsigned char s6e8ax0_22_gamma_210[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc1, 0xbd, 0xb1, 0xd1,
+ 0xb1, 0xc2, 0xde, 0xc2, 0x97, 0xbe, 0x94, 0xB0, 0xc9, 0xad,
+ 0x00, 0xae, 0x00, 0xa4, 0x00, 0xd4,
+};
+
+static const unsigned char s6e8ax0_22_gamma_220[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc7, 0xbd, 0xb1, 0xd1,
+ 0xb1, 0xc2, 0xdd, 0xc2, 0x97, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
+ 0x00, 0xad, 0x00, 0xa2, 0x00, 0xd3,
+};
+
+static const unsigned char s6e8ax0_22_gamma_230[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xc3, 0xbd, 0xb2, 0xd1,
+ 0xb1, 0xc3, 0xdd, 0xc1, 0x96, 0xbd, 0x94, 0xb0, 0xc9, 0xad,
+ 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xd7,
+};
+
+static const unsigned char s6e8ax0_22_gamma_240[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb1, 0xcb, 0xbd, 0xb1, 0xd2,
+ 0xb1, 0xc3, 0xdD, 0xc2, 0x95, 0xbd, 0x93, 0xaf, 0xc8, 0xab,
+ 0x00, 0xb3, 0x00, 0xa9, 0x00, 0xdb,
+};
+
+static const unsigned char s6e8ax0_22_gamma_250[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xcc, 0xbe, 0xb0, 0xd2,
+ 0xb0, 0xc3, 0xdD, 0xc2, 0x94, 0xbc, 0x92, 0xae, 0xc8, 0xab,
+ 0x00, 0xb6, 0x00, 0xab, 0x00, 0xde,
+};
+
+static const unsigned char s6e8ax0_22_gamma_260[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb3, 0xd0, 0xbe, 0xaf, 0xd1,
+ 0xaf, 0xc2, 0xdd, 0xc1, 0x96, 0xbc, 0x93, 0xaf, 0xc8, 0xac,
+ 0x00, 0xb7, 0x00, 0xad, 0x00, 0xe0,
+};
+
+static const unsigned char s6e8ax0_22_gamma_270[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xcF, 0xbd, 0xb0, 0xd2,
+ 0xaf, 0xc2, 0xdc, 0xc1, 0x95, 0xbd, 0x93, 0xae, 0xc6, 0xaa,
+ 0x00, 0xba, 0x00, 0xb0, 0x00, 0xe4,
+};
+
+static const unsigned char s6e8ax0_22_gamma_280[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb2, 0xd0, 0xbd, 0xaf, 0xd0,
+ 0xad, 0xc4, 0xdd, 0xc3, 0x95, 0xbd, 0x93, 0xac, 0xc5, 0xa9,
+ 0x00, 0xbd, 0x00, 0xb2, 0x00, 0xe7,
+};
+
+static const unsigned char s6e8ax0_22_gamma_300[] = {
+ 0xfa, 0x01, 0x60, 0x10, 0x60, 0xb5, 0xd3, 0xbd, 0xb1, 0xd2,
+ 0xb0, 0xc0, 0xdc, 0xc0, 0x94, 0xba, 0x91, 0xac, 0xc5, 0xa9,
+ 0x00, 0xc2, 0x00, 0xb7, 0x00, 0xed,
+};
+
+static const unsigned char *s6e8ax0_22_gamma_table[] = {
+ s6e8ax0_22_gamma_30,
+ s6e8ax0_22_gamma_50,
+ s6e8ax0_22_gamma_60,
+ s6e8ax0_22_gamma_70,
+ s6e8ax0_22_gamma_80,
+ s6e8ax0_22_gamma_90,
+ s6e8ax0_22_gamma_100,
+ s6e8ax0_22_gamma_120,
+ s6e8ax0_22_gamma_130,
+ s6e8ax0_22_gamma_140,
+ s6e8ax0_22_gamma_150,
+ s6e8ax0_22_gamma_160,
+ s6e8ax0_22_gamma_170,
+ s6e8ax0_22_gamma_180,
+ s6e8ax0_22_gamma_190,
+ s6e8ax0_22_gamma_200,
+ s6e8ax0_22_gamma_210,
+ s6e8ax0_22_gamma_220,
+ s6e8ax0_22_gamma_230,
+ s6e8ax0_22_gamma_240,
+ s6e8ax0_22_gamma_250,
+ s6e8ax0_22_gamma_260,
+ s6e8ax0_22_gamma_270,
+ s6e8ax0_22_gamma_280,
+ s6e8ax0_22_gamma_300,
+};
+
+static void s6e8ax0_panel_cond(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+
+ static const unsigned char data_to_send[] = {
+ 0xf8, 0x3d, 0x35, 0x00, 0x00, 0x00, 0x93, 0x00, 0x3c, 0x7d,
+ 0x08, 0x27, 0x7d, 0x3f, 0x00, 0x00, 0x00, 0x20, 0x04, 0x08,
+ 0x6e, 0x00, 0x00, 0x00, 0x02, 0x08, 0x08, 0x23, 0x23, 0xc0,
+ 0xc8, 0x08, 0x48, 0xc1, 0x00, 0xc1, 0xff, 0xff, 0xc8
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_cond(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xf2, 0x80, 0x03, 0x0d
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+/* Gamma 2.2 Setting (200cd, 7500K, 10MPCD) */
+static void s6e8ax0_gamma_cond(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ unsigned int gamma = lcd->bd->props.brightness;
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ s6e8ax0_22_gamma_table[gamma],
+ GAMMA_TABLE_COUNT);
+}
+
+static void s6e8ax0_gamma_update(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xf7, 0x03
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE_PARAM, data_to_send,
+ ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond1(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xd1, 0xfe, 0x80, 0x00, 0x01, 0x0b, 0x00, 0x00, 0x40,
+ 0x0d, 0x00, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond2(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xb6, 0x0c, 0x02, 0x03, 0x32, 0xff, 0x44, 0x44, 0xc0,
+ 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond3(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xe1, 0x10, 0x1c, 0x17, 0x08, 0x1d
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond4(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xe2, 0xed, 0x07, 0xc3, 0x13, 0x0d, 0x03
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond5(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xf4, 0xcf, 0x0a, 0x12, 0x10, 0x19, 0x33, 0x02
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+static void s6e8ax0_etc_cond6(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xe3, 0x40
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE_PARAM,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_etc_cond7(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xe4, 0x00, 0x00, 0x14, 0x80, 0x00, 0x00, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_set(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xb1, 0x04, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_elvss_nvm_set(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xd9, 0x5c, 0x20, 0x0c, 0x0f, 0x41, 0x00, 0x10, 0x11,
+ 0x12, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x80, 0xcb, 0xed,
+ 0x64, 0xaf
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_sleep_in(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0x10, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_sleep_out(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0x11, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_on(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0x29, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_display_off(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0x28, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_apply_level2_key(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xf0, 0x5a, 0x5a
+ };
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_acl_on(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xc0, 0x01
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+static void s6e8ax0_acl_off(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ static const unsigned char data_to_send[] = {
+ 0xc0, 0x00
+ };
+
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_SHORT_WRITE,
+ data_to_send, ARRAY_SIZE(data_to_send));
+}
+
+/* Full white 50% reducing setting */
+static void s6e8ax0_acl_ctrl_set(struct s6e8ax0 *lcd)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ /* Full white 50% reducing setting */
+ static const unsigned char cutoff_50[] = {
+ 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+ 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x08, 0x0f, 0x16, 0x1d, 0x24, 0x2a, 0x31, 0x38,
+ 0x3f, 0x46
+ };
+ /* Full white 45% reducing setting */
+ static const unsigned char cutoff_45[] = {
+ 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+ 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x07, 0x0d, 0x13, 0x19, 0x1f, 0x25, 0x2b, 0x31,
+ 0x37, 0x3d
+ };
+ /* Full white 40% reducing setting */
+ static const unsigned char cutoff_40[] = {
+ 0xc1, 0x47, 0x53, 0x13, 0x53, 0x00, 0x00, 0x02, 0xcf,
+ 0x00, 0x00, 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x06, 0x0c, 0x11, 0x16, 0x1c, 0x21, 0x26, 0x2b,
+ 0x31, 0x36
+ };
+
+ if (lcd->acl_enable) {
+ if (lcd->cur_acl == 0) {
+ if (lcd->gamma == 0 || lcd->gamma == 1) {
+ s6e8ax0_acl_off(lcd);
+ dev_dbg(&lcd->ld->dev,
+ "cur_acl=%d\n", lcd->cur_acl);
+ } else
+ s6e8ax0_acl_on(lcd);
+ }
+ switch (lcd->gamma) {
+ case 0: /* 30cd */
+ s6e8ax0_acl_off(lcd);
+ lcd->cur_acl = 0;
+ break;
+ case 1 ... 3: /* 50cd ~ 90cd */
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_LONG_WRITE,
+ cutoff_40,
+ ARRAY_SIZE(cutoff_40));
+ lcd->cur_acl = 40;
+ break;
+ case 4 ... 7: /* 120cd ~ 210cd */
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_LONG_WRITE,
+ cutoff_45,
+ ARRAY_SIZE(cutoff_45));
+ lcd->cur_acl = 45;
+ break;
+ case 8 ... 10: /* 220cd ~ 300cd */
+ ops->cmd_write(lcd_to_master(lcd),
+ MIPI_DSI_DCS_LONG_WRITE,
+ cutoff_50,
+ ARRAY_SIZE(cutoff_50));
+ lcd->cur_acl = 50;
+ break;
+ default:
+ break;
+ }
+ } else {
+ s6e8ax0_acl_off(lcd);
+ lcd->cur_acl = 0;
+ dev_dbg(&lcd->ld->dev, "cur_acl = %d\n", lcd->cur_acl);
+ }
+}
+
+static void s6e8ax0_read_id(struct s6e8ax0 *lcd, u8 *mtp_id)
+{
+ unsigned int ret;
+ unsigned int addr = 0xd1; /* MTP ID */
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+
+ ret = ops->cmd_read(lcd_to_master(lcd),
+ MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM,
+ addr, 3, mtp_id);
+}
+
+static int s6e8ax0_panel_init(struct s6e8ax0 *lcd)
+{
+ s6e8ax0_apply_level2_key(lcd);
+ s6e8ax0_sleep_out(lcd);
+ msleep(1);
+ s6e8ax0_panel_cond(lcd);
+ s6e8ax0_display_cond(lcd);
+ s6e8ax0_gamma_cond(lcd);
+ s6e8ax0_gamma_update(lcd);
+
+ s6e8ax0_etc_cond1(lcd);
+ s6e8ax0_etc_cond2(lcd);
+ s6e8ax0_etc_cond3(lcd);
+ s6e8ax0_etc_cond4(lcd);
+ s6e8ax0_etc_cond5(lcd);
+ s6e8ax0_etc_cond6(lcd);
+ s6e8ax0_etc_cond7(lcd);
+
+ s6e8ax0_elvss_nvm_set(lcd);
+ s6e8ax0_elvss_set(lcd);
+
+ s6e8ax0_acl_ctrl_set(lcd);
+ s6e8ax0_acl_on(lcd);
+
+ /* if ID3 value is not 33h, branch private elvss mode */
+ msleep(lcd->ddi_pd->power_on_delay);
+
+ return 0;
+}
+
+static int s6e8ax0_update_gamma_ctrl(struct s6e8ax0 *lcd, int brightness)
+{
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+
+ ops->cmd_write(lcd_to_master(lcd), MIPI_DSI_DCS_LONG_WRITE,
+ s6e8ax0_22_gamma_table[brightness],
+ ARRAY_SIZE(s6e8ax0_22_gamma_table));
+
+ /* update gamma table. */
+ s6e8ax0_gamma_update(lcd);
+ lcd->gamma = brightness;
+
+ return 0;
+}
+
+static int s6e8ax0_gamma_ctrl(struct s6e8ax0 *lcd, int gamma)
+{
+ s6e8ax0_update_gamma_ctrl(lcd, gamma);
+
+ return 0;
+}
+
+static int s6e8ax0_set_power(struct lcd_device *ld, int power)
+{
+ struct s6e8ax0 *lcd = lcd_get_data(ld);
+ struct mipi_dsim_master_ops *ops = lcd_to_master_ops(lcd);
+ int ret = 0;
+
+ if (power != FB_BLANK_UNBLANK && power != FB_BLANK_POWERDOWN &&
+ power != FB_BLANK_NORMAL) {
+ dev_err(lcd->dev, "power value should be 0, 1 or 4.\n");
+ return -EINVAL;
+ }
+
+ if ((power == FB_BLANK_UNBLANK) && ops->set_blank_mode) {
+ /* LCD power on */
+ if ((POWER_IS_ON(power) && POWER_IS_OFF(lcd->power))
+ || (POWER_IS_ON(power) && POWER_IS_NRM(lcd->power))) {
+ ret = ops->set_blank_mode(lcd_to_master(lcd), power);
+ if (!ret && lcd->power != power)
+ lcd->power = power;
+ }
+ } else if ((power == FB_BLANK_POWERDOWN) && ops->set_early_blank_mode) {
+ /* LCD power off */
+ if ((POWER_IS_OFF(power) && POWER_IS_ON(lcd->power)) ||
+ (POWER_IS_ON(lcd->power) && POWER_IS_NRM(power))) {
+ ret = ops->set_early_blank_mode(lcd_to_master(lcd),
+ power);
+ if (!ret && lcd->power != power)
+ lcd->power = power;
+ }
+ }
+
+ return ret;
+}
+
+static int s6e8ax0_get_power(struct lcd_device *ld)
+{
+ struct s6e8ax0 *lcd = lcd_get_data(ld);
+
+ return lcd->power;
+}
+
+static int s6e8ax0_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static int s6e8ax0_set_brightness(struct backlight_device *bd)
+{
+ int ret = 0, brightness = bd->props.brightness;
+ struct s6e8ax0 *lcd = bl_get_data(bd);
+
+ if (brightness < MIN_BRIGHTNESS ||
+ brightness > bd->props.max_brightness) {
+ dev_err(lcd->dev, "lcd brightness should be %d to %d.\n",
+ MIN_BRIGHTNESS, MAX_BRIGHTNESS);
+ return -EINVAL;
+ }
+
+ ret = s6e8ax0_gamma_ctrl(lcd, brightness);
+ if (ret) {
+ dev_err(&bd->dev, "lcd brightness setting failed.\n");
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static struct lcd_ops s6e8ax0_lcd_ops = {
+ .set_power = s6e8ax0_set_power,
+ .get_power = s6e8ax0_get_power,
+};
+
+static const struct backlight_ops s6e8ax0_backlight_ops = {
+ .get_brightness = s6e8ax0_get_brightness,
+ .update_status = s6e8ax0_set_brightness,
+};
+
+static void s6e8ax0_power_on(struct mipi_dsim_lcd_device *dsim_dev, int power)
+{
+ struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
+
+ msleep(lcd->ddi_pd->power_on_delay);
+
+ /* lcd power on */
+ if (power)
+ s6e8ax0_regulator_enable(lcd);
+ else
+ s6e8ax0_regulator_disable(lcd);
+
+ msleep(lcd->ddi_pd->reset_delay);
+
+ /* lcd reset */
+ if (lcd->ddi_pd->reset)
+ lcd->ddi_pd->reset(lcd->ld);
+ msleep(5);
+}
+
+static void s6e8ax0_set_sequence(struct mipi_dsim_lcd_device *dsim_dev)
+{
+ struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
+
+ s6e8ax0_panel_init(lcd);
+ s6e8ax0_display_on(lcd);
+
+ lcd->power = FB_BLANK_UNBLANK;
+}
+
+static int s6e8ax0_probe(struct mipi_dsim_lcd_device *dsim_dev)
+{
+ struct s6e8ax0 *lcd;
+ int ret;
+ u8 mtp_id[3] = {0, };
+
+ lcd = kzalloc(sizeof(struct s6e8ax0), GFP_KERNEL);
+ if (!lcd) {
+ dev_err(&dsim_dev->dev, "failed to allocate s6e8ax0 structure.\n");
+ return -ENOMEM;
+ }
+
+ lcd->dsim_dev = dsim_dev;
+ lcd->ddi_pd = (struct lcd_platform_data *)dsim_dev->platform_data;
+ lcd->dev = &dsim_dev->dev;
+
+ mutex_init(&lcd->lock);
+
+ ret = regulator_bulk_get(lcd->dev, ARRAY_SIZE(supplies), supplies);
+ if (ret) {
+ dev_err(lcd->dev, "Failed to get regulators: %d\n", ret);
+ goto err_lcd_register;
+ }
+
+ lcd->ld = lcd_device_register("s6e8ax0", lcd->dev, lcd,
+ &s6e8ax0_lcd_ops);
+ if (IS_ERR(lcd->ld)) {
+ dev_err(lcd->dev, "failed to register lcd ops.\n");
+ ret = PTR_ERR(lcd->ld);
+ goto err_lcd_register;
+ }
+
+ lcd->bd = backlight_device_register("s6e8ax0-bl", lcd->dev, lcd,
+ &s6e8ax0_backlight_ops, NULL);
+ if (IS_ERR(lcd->bd)) {
+ dev_err(lcd->dev, "failed to register backlight ops.\n");
+ ret = PTR_ERR(lcd->bd);
+ goto err_backlight_register;
+ }
+
+ lcd->bd->props.max_brightness = MAX_BRIGHTNESS;
+ lcd->bd->props.brightness = MAX_BRIGHTNESS;
+
+ s6e8ax0_read_id(lcd, mtp_id);
+ if (mtp_id[0] == 0x00)
+ dev_err(lcd->dev, "read id failed\n");
+
+ dev_info(lcd->dev, "Read ID : %x, %x, %x\n",
+ mtp_id[0], mtp_id[1], mtp_id[2]);
+
+ if (mtp_id[2] == 0x33)
+ dev_info(lcd->dev,
+ "ID-3 is 0xff does not support dynamic elvss\n");
+ else
+ dev_info(lcd->dev,
+ "ID-3 is 0x%x support dynamic elvss\n", mtp_id[2]);
+
+ lcd->acl_enable = 1;
+ lcd->cur_acl = 0;
+
+ dev_set_drvdata(&dsim_dev->dev, lcd);
+
+ dev_dbg(lcd->dev, "probed s6e8ax0 panel driver.\n");
+
+ return 0;
+
+err_backlight_register:
+ lcd_device_unregister(lcd->ld);
+
+err_lcd_register:
+ regulator_bulk_free(ARRAY_SIZE(supplies), supplies);
+ kfree(lcd);
+
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int s6e8ax0_suspend(struct mipi_dsim_lcd_device *dsim_dev)
+{
+ struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
+
+ s6e8ax0_sleep_in(lcd);
+ msleep(lcd->ddi_pd->power_off_delay);
+ s6e8ax0_display_off(lcd);
+
+ s6e8ax0_regulator_disable(lcd);
+
+ return 0;
+}
+
+static int s6e8ax0_resume(struct mipi_dsim_lcd_device *dsim_dev)
+{
+ struct s6e8ax0 *lcd = dev_get_drvdata(&dsim_dev->dev);
+
+ s6e8ax0_sleep_out(lcd);
+ msleep(lcd->ddi_pd->power_on_delay);
+
+ s6e8ax0_regulator_enable(lcd);
+ s6e8ax0_set_sequence(dsim_dev);
+
+ return 0;
+}
+#else
+#define s6e8ax0_suspend NULL
+#define s6e8ax0_resume NULL
+#endif
+
+static struct mipi_dsim_lcd_driver s6e8ax0_dsim_ddi_driver = {
+ .name = "s6e8ax0",
+ .id = -1,
+
+ .power_on = s6e8ax0_power_on,
+ .set_sequence = s6e8ax0_set_sequence,
+ .probe = s6e8ax0_probe,
+ .suspend = s6e8ax0_suspend,
+ .resume = s6e8ax0_resume,
+};
+
+static int s6e8ax0_init(void)
+{
+ exynos_mipi_dsi_register_lcd_driver(&s6e8ax0_dsim_ddi_driver);
+
+ return 0;
+}
+
+static void s6e8ax0_exit(void)
+{
+ return;
+}
+
+module_init(s6e8ax0_init);
+module_exit(s6e8ax0_exit);
+
+MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
+MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
+MODULE_DESCRIPTION("MIPI-DSI based s6e8ax0 AMOLED LCD Panel Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/exynos/s6e8ax0.h b/drivers/video/exynos/s6e8ax0.h
new file mode 100644
index 000000000000..1f1b270484b0
--- /dev/null
+++ b/drivers/video/exynos/s6e8ax0.h
@@ -0,0 +1,21 @@
+/* linux/drivers/video/backlight/s6e8ax0.h
+ *
+ * MIPI-DSI based s6e8ax0 AMOLED LCD Panel definitions.
+ *
+ * Copyright (c) 2011 Samsung Electronics
+ *
+ * Inki Dae, <inki.dae@samsung.com>
+ * Donghwa Lee <dh09.lee@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef _S6E8AX0_H
+#define _S6E8AX0_H
+
+extern void s6e8ax0_init(void);
+
+#endif
+
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index ac9141b85356..c6ce416ab587 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1665,6 +1665,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
if (ret)
return -EINVAL;
+ unlink_framebuffer(fb_info);
if (fb_info->pixmap.addr &&
(fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr);
@@ -1672,7 +1673,6 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
registered_fb[i] = NULL;
num_registered_fb--;
fb_cleanup_device(fb_info);
- device_destroy(fb_class, MKDEV(FB_MAJOR, i));
event.info = fb_info;
fb_notifier_call_chain(FB_EVENT_FB_UNREGISTERED, &event);
@@ -1681,6 +1681,22 @@ static int do_unregister_framebuffer(struct fb_info *fb_info)
return 0;
}
+int unlink_framebuffer(struct fb_info *fb_info)
+{
+ int i;
+
+ i = fb_info->node;
+ if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)
+ return -EINVAL;
+
+ if (fb_info->dev) {
+ device_destroy(fb_class, MKDEV(FB_MAJOR, i));
+ fb_info->dev = NULL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(unlink_framebuffer);
+
void remove_conflicting_framebuffers(struct apertures_struct *a,
const char *name, bool primary)
{
diff --git a/drivers/video/i740_reg.h b/drivers/video/i740_reg.h
new file mode 100644
index 000000000000..91bac76549d7
--- /dev/null
+++ b/drivers/video/i740_reg.h
@@ -0,0 +1,309 @@
+/**************************************************************************
+
+Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sub license, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice (including the
+next paragraph) shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
+ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+/*
+ * Authors:
+ * Kevin E. Martin <kevin@precisioninsight.com>
+ */
+
+/* I/O register offsets */
+#define SRX VGA_SEQ_I
+#define GRX VGA_GFX_I
+#define ARX VGA_ATT_IW
+#define XRX 0x3D6
+#define MRX 0x3D2
+
+/* VGA Color Palette Registers */
+#define DACMASK 0x3C6
+#define DACSTATE 0x3C7
+#define DACRX 0x3C7
+#define DACWX 0x3C8
+#define DACDATA 0x3C9
+
+/* CRT Controller Registers (CRX) */
+#define START_ADDR_HI 0x0C
+#define START_ADDR_LO 0x0D
+#define VERT_SYNC_END 0x11
+#define EXT_VERT_TOTAL 0x30
+#define EXT_VERT_DISPLAY 0x31
+#define EXT_VERT_SYNC_START 0x32
+#define EXT_VERT_BLANK_START 0x33
+#define EXT_HORIZ_TOTAL 0x35
+#define EXT_HORIZ_BLANK 0x39
+#define EXT_START_ADDR 0x40
+#define EXT_START_ADDR_ENABLE 0x80
+#define EXT_OFFSET 0x41
+#define EXT_START_ADDR_HI 0x42
+#define INTERLACE_CNTL 0x70
+#define INTERLACE_ENABLE 0x80
+#define INTERLACE_DISABLE 0x00
+
+/* Miscellaneous Output Register */
+#define MSR_R 0x3CC
+#define MSR_W 0x3C2
+#define IO_ADDR_SELECT 0x01
+
+#define MDA_BASE 0x3B0
+#define CGA_BASE 0x3D0
+
+/* System Configuration Extension Registers (XRX) */
+#define IO_CTNL 0x09
+#define EXTENDED_ATTR_CNTL 0x02
+#define EXTENDED_CRTC_CNTL 0x01
+
+#define ADDRESS_MAPPING 0x0A
+#define PACKED_MODE_ENABLE 0x04
+#define LINEAR_MODE_ENABLE 0x02
+#define PAGE_MAPPING_ENABLE 0x01
+
+#define BITBLT_CNTL 0x20
+#define COLEXP_MODE 0x30
+#define COLEXP_8BPP 0x00
+#define COLEXP_16BPP 0x10
+#define COLEXP_24BPP 0x20
+#define COLEXP_RESERVED 0x30
+#define CHIP_RESET 0x02
+#define BITBLT_STATUS 0x01
+
+#define DISPLAY_CNTL 0x40
+#define VGA_WRAP_MODE 0x02
+#define VGA_WRAP_AT_256KB 0x00
+#define VGA_NO_WRAP 0x02
+#define GUI_MODE 0x01
+#define STANDARD_VGA_MODE 0x00
+#define HIRES_MODE 0x01
+
+#define DRAM_ROW_TYPE 0x50
+#define DRAM_ROW_0 0x07
+#define DRAM_ROW_0_SDRAM 0x00
+#define DRAM_ROW_0_EMPTY 0x07
+#define DRAM_ROW_1 0x38
+#define DRAM_ROW_1_SDRAM 0x00
+#define DRAM_ROW_1_EMPTY 0x38
+#define DRAM_ROW_CNTL_LO 0x51
+#define DRAM_CAS_LATENCY 0x10
+#define DRAM_RAS_TIMING 0x08
+#define DRAM_RAS_PRECHARGE 0x04
+#define DRAM_ROW_CNTL_HI 0x52
+#define DRAM_EXT_CNTL 0x53
+#define DRAM_REFRESH_RATE 0x03
+#define DRAM_REFRESH_DISABLE 0x00
+#define DRAM_REFRESH_60HZ 0x01
+#define DRAM_REFRESH_FAST_TEST 0x02
+#define DRAM_REFRESH_RESERVED 0x03
+#define DRAM_TIMING 0x54
+#define DRAM_ROW_BNDRY_0 0x55
+#define DRAM_ROW_BNDRY_1 0x56
+
+#define DPMS_SYNC_SELECT 0x61
+#define VSYNC_CNTL 0x08
+#define VSYNC_ON 0x00
+#define VSYNC_OFF 0x08
+#define HSYNC_CNTL 0x02
+#define HSYNC_ON 0x00
+#define HSYNC_OFF 0x02
+
+#define PIXPIPE_CONFIG_0 0x80
+#define DAC_8_BIT 0x80
+#define DAC_6_BIT 0x00
+#define HW_CURSOR_ENABLE 0x10
+#define EXTENDED_PALETTE 0x01
+
+#define PIXPIPE_CONFIG_1 0x81
+#define DISPLAY_COLOR_MODE 0x0F
+#define DISPLAY_VGA_MODE 0x00
+#define DISPLAY_8BPP_MODE 0x02
+#define DISPLAY_15BPP_MODE 0x04
+#define DISPLAY_16BPP_MODE 0x05
+#define DISPLAY_24BPP_MODE 0x06
+#define DISPLAY_32BPP_MODE 0x07
+
+#define PIXPIPE_CONFIG_2 0x82
+#define DISPLAY_GAMMA_ENABLE 0x08
+#define DISPLAY_GAMMA_DISABLE 0x00
+#define OVERLAY_GAMMA_ENABLE 0x04
+#define OVERLAY_GAMMA_DISABLE 0x00
+
+#define CURSOR_CONTROL 0xA0
+#define CURSOR_ORIGIN_SCREEN 0x00
+#define CURSOR_ORIGIN_DISPLAY 0x10
+#define CURSOR_MODE 0x07
+#define CURSOR_MODE_DISABLE 0x00
+#define CURSOR_MODE_32_4C_AX 0x01
+#define CURSOR_MODE_128_2C 0x02
+#define CURSOR_MODE_128_1C 0x03
+#define CURSOR_MODE_64_3C 0x04
+#define CURSOR_MODE_64_4C_AX 0x05
+#define CURSOR_MODE_64_4C 0x06
+#define CURSOR_MODE_RESERVED 0x07
+#define CURSOR_BASEADDR_LO 0xA2
+#define CURSOR_BASEADDR_HI 0xA3
+#define CURSOR_X_LO 0xA4
+#define CURSOR_X_HI 0xA5
+#define CURSOR_X_POS 0x00
+#define CURSOR_X_NEG 0x80
+#define CURSOR_Y_LO 0xA6
+#define CURSOR_Y_HI 0xA7
+#define CURSOR_Y_POS 0x00
+#define CURSOR_Y_NEG 0x80
+
+#define VCLK2_VCO_M 0xC8
+#define VCLK2_VCO_N 0xC9
+#define VCLK2_VCO_MN_MSBS 0xCA
+#define VCO_N_MSBS 0x30
+#define VCO_M_MSBS 0x03
+#define VCLK2_VCO_DIV_SEL 0xCB
+#define POST_DIV_SELECT 0x70
+#define POST_DIV_1 0x00
+#define POST_DIV_2 0x10
+#define POST_DIV_4 0x20
+#define POST_DIV_8 0x30
+#define POST_DIV_16 0x40
+#define POST_DIV_32 0x50
+#define VCO_LOOP_DIV_BY_4M 0x00
+#define VCO_LOOP_DIV_BY_16M 0x04
+#define REF_CLK_DIV_BY_5 0x02
+#define REF_DIV_4 0x00
+#define REF_DIV_1 0x01
+
+#define PLL_CNTL 0xCE
+#define PLL_MEMCLK_SEL 0x03
+#define PLL_MEMCLK__66667KHZ 0x00
+#define PLL_MEMCLK__75000KHZ 0x01
+#define PLL_MEMCLK__88889KHZ 0x02
+#define PLL_MEMCLK_100000KHZ 0x03
+
+/* Multimedia Extension Registers (MRX) */
+#define ACQ_CNTL_1 0x02
+#define ACQ_CNTL_2 0x03
+#define FRAME_CAP_MODE 0x01
+#define CONT_CAP_MODE 0x00
+#define SINGLE_CAP_MODE 0x01
+#define ACQ_CNTL_3 0x04
+#define COL_KEY_CNTL_1 0x3C
+#define BLANK_DISP_OVERLAY 0x20
+
+/* FIFOs */
+#define LP_FIFO 0x1000
+#define HP_FIFO 0x2000
+#define INSTPNT 0x3040
+#define LP_FIFO_COUNT 0x3040
+#define HP_FIFO_COUNT 0x3041
+
+/* FIFO Commands */
+#define CLIENT 0xE0000000
+#define CLIENT_2D 0x60000000
+
+/* Command Parser Mode Register */
+#define COMPARS 0x3038
+#define TWO_D_INST_DISABLE 0x08
+#define THREE_D_INST_DISABLE 0x04
+#define STATE_VAR_UPDATE_DISABLE 0x02
+#define PAL_STIP_DISABLE 0x01
+
+/* Interrupt Control Registers */
+#define IER 0x3030
+#define IIR 0x3032
+#define IMR 0x3034
+#define ISR 0x3036
+#define VMIINTB_EVENT 0x2000
+#define GPIO4_INT 0x1000
+#define DISP_FLIP_EVENT 0x0800
+#define DVD_PORT_DMA 0x0400
+#define DISP_VBLANK 0x0200
+#define FIFO_EMPTY_DMA_DONE 0x0100
+#define INST_PARSER_ERROR 0x0080
+#define USER_DEFINED 0x0040
+#define BREAKPOINT 0x0020
+#define DISP_HORIZ_COUNT 0x0010
+#define DISP_VSYNC 0x0008
+#define CAPTURE_HORIZ_COUNT 0x0004
+#define CAPTURE_VSYNC 0x0002
+#define THREE_D_PIPE_FLUSHED 0x0001
+
+/* FIFO Watermark and Burst Length Control Register */
+#define FWATER_BLC 0x00006000
+#define LMI_BURST_LENGTH 0x7F000000
+#define LMI_FIFO_WATERMARK 0x003F0000
+#define AGP_BURST_LENGTH 0x00007F00
+#define AGP_FIFO_WATERMARK 0x0000003F
+
+/* BitBLT Registers */
+#define SRC_DST_PITCH 0x00040000
+#define DST_PITCH 0x1FFF0000
+#define SRC_PITCH 0x00001FFF
+#define COLEXP_BG_COLOR 0x00040004
+#define COLEXP_FG_COLOR 0x00040008
+#define MONO_SRC_CNTL 0x0004000C
+#define MONO_USE_COLEXP 0x00000000
+#define MONO_USE_SRCEXP 0x08000000
+#define MONO_DATA_ALIGN 0x07000000
+#define MONO_BIT_ALIGN 0x01000000
+#define MONO_BYTE_ALIGN 0x02000000
+#define MONO_WORD_ALIGN 0x03000000
+#define MONO_DWORD_ALIGN 0x04000000
+#define MONO_QWORD_ALIGN 0x05000000
+#define MONO_SRC_INIT_DSCRD 0x003F0000
+#define MONO_SRC_RIGHT_CLIP 0x00003F00
+#define MONO_SRC_LEFT_CLIP 0x0000003F
+#define BITBLT_CONTROL 0x00040010
+#define BLTR_STATUS 0x80000000
+#define DYN_DEPTH 0x03000000
+#define DYN_DEPTH_8BPP 0x00000000
+#define DYN_DEPTH_16BPP 0x01000000
+#define DYN_DEPTH_24BPP 0x02000000
+#define DYN_DEPTH_32BPP 0x03000000 /* Unimplemented on the i740 */
+#define DYN_DEPTH_ENABLE 0x00800000
+#define PAT_VERT_ALIGN 0x00700000
+#define SOLID_PAT_SELECT 0x00080000
+#define PAT_IS_IN_COLOR 0x00000000
+#define PAT_IS_MONO 0x00040000
+#define MONO_PAT_TRANSP 0x00020000
+#define COLOR_TRANSP_ROP 0x00000000
+#define COLOR_TRANSP_DST 0x00008000
+#define COLOR_TRANSP_EQ 0x00000000
+#define COLOR_TRANSP_NOT_EQ 0x00010000
+#define COLOR_TRANSP_ENABLE 0x00004000
+#define MONO_SRC_TRANSP 0x00002000
+#define SRC_IS_IN_COLOR 0x00000000
+#define SRC_IS_MONO 0x00001000
+#define SRC_USE_SRC_ADDR 0x00000000
+#define SRC_USE_BLTDATA 0x00000400
+#define BLT_TOP_TO_BOT 0x00000000
+#define BLT_BOT_TO_TOP 0x00000200
+#define BLT_LEFT_TO_RIGHT 0x00000000
+#define BLT_RIGHT_TO_LEFT 0x00000100
+#define BLT_ROP 0x000000FF
+#define BLT_PAT_ADDR 0x00040014
+#define BLT_SRC_ADDR 0x00040018
+#define BLT_DST_ADDR 0x0004001C
+#define BLT_DST_H_W 0x00040020
+#define BLT_DST_HEIGHT 0x1FFF0000
+#define BLT_DST_WIDTH 0x00001FFF
+#define SRCEXP_BG_COLOR 0x00040024
+#define SRCEXP_FG_COLOR 0x00040028
+#define BLTDATA 0x00050000
diff --git a/drivers/video/i740fb.c b/drivers/video/i740fb.c
new file mode 100644
index 000000000000..fe574d84ed99
--- /dev/null
+++ b/drivers/video/i740fb.c
@@ -0,0 +1,1337 @@
+/*
+ * i740fb - framebuffer driver for Intel740
+ * Copyright (c) 2011 Ondrej Zary
+ *
+ * Based on old i740fb driver (c) 2001-2002 Andrey Ulanov <drey@rt.mipt.ru>
+ * which was partially based on:
+ * VGA 16-color framebuffer driver (c) 1999 Ben Pfaff <pfaffben@debian.org>
+ * and Petr Vandrovec <VANDROVE@vc.cvut.cz>
+ * i740 driver from XFree86 (c) 1998-1999 Precision Insight, Inc., Cedar Park,
+ * Texas.
+ * i740fb by Patrick LERDA, v0.9
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <linux/console.h>
+#include <video/vga.h>
+
+#ifdef CONFIG_MTRR
+#include <asm/mtrr.h>
+#endif
+
+#include "i740_reg.h"
+
+static char *mode_option __devinitdata;
+
+#ifdef CONFIG_MTRR
+static int mtrr __devinitdata = 1;
+#endif
+
+struct i740fb_par {
+ unsigned char __iomem *regs;
+ bool has_sgram;
+#ifdef CONFIG_MTRR
+ int mtrr_reg;
+#endif
+ bool ddc_registered;
+ struct i2c_adapter ddc_adapter;
+ struct i2c_algo_bit_data ddc_algo;
+ u32 pseudo_palette[16];
+ struct mutex open_lock;
+ unsigned int ref_count;
+
+ u8 crtc[VGA_CRT_C];
+ u8 atc[VGA_ATT_C];
+ u8 gdc[VGA_GFX_C];
+ u8 seq[VGA_SEQ_C];
+ u8 misc;
+ u8 vss;
+
+ /* i740 specific registers */
+ u8 display_cntl;
+ u8 pixelpipe_cfg0;
+ u8 pixelpipe_cfg1;
+ u8 pixelpipe_cfg2;
+ u8 video_clk2_m;
+ u8 video_clk2_n;
+ u8 video_clk2_mn_msbs;
+ u8 video_clk2_div_sel;
+ u8 pll_cntl;
+ u8 address_mapping;
+ u8 io_cntl;
+ u8 bitblt_cntl;
+ u8 ext_vert_total;
+ u8 ext_vert_disp_end;
+ u8 ext_vert_sync_start;
+ u8 ext_vert_blank_start;
+ u8 ext_horiz_total;
+ u8 ext_horiz_blank;
+ u8 ext_offset;
+ u8 interlace_cntl;
+ u32 lmi_fifo_watermark;
+ u8 ext_start_addr;
+ u8 ext_start_addr_hi;
+};
+
+#define DACSPEED8 203
+#define DACSPEED16 163
+#define DACSPEED24_SG 136
+#define DACSPEED24_SD 128
+#define DACSPEED32 86
+
+static struct fb_fix_screeninfo i740fb_fix __devinitdata = {
+ .id = "i740fb",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_TRUECOLOR,
+ .xpanstep = 8,
+ .ypanstep = 1,
+ .accel = FB_ACCEL_NONE,
+};
+
+static inline void i740outb(struct i740fb_par *par, u16 port, u8 val)
+{
+ vga_mm_w(par->regs, port, val);
+}
+static inline u8 i740inb(struct i740fb_par *par, u16 port)
+{
+ return vga_mm_r(par->regs, port);
+}
+static inline void i740outreg(struct i740fb_par *par, u16 port, u8 reg, u8 val)
+{
+ vga_mm_w_fast(par->regs, port, reg, val);
+}
+static inline u8 i740inreg(struct i740fb_par *par, u16 port, u8 reg)
+{
+ vga_mm_w(par->regs, port, reg);
+ return vga_mm_r(par->regs, port+1);
+}
+static inline void i740outreg_mask(struct i740fb_par *par, u16 port, u8 reg,
+ u8 val, u8 mask)
+{
+ vga_mm_w_fast(par->regs, port, reg, (val & mask)
+ | (i740inreg(par, port, reg) & ~mask));
+}
+
+#define REG_DDC_DRIVE 0x62
+#define REG_DDC_STATE 0x63
+#define DDC_SCL (1 << 3)
+#define DDC_SDA (1 << 2)
+
+static void i740fb_ddc_setscl(void *data, int val)
+{
+ struct i740fb_par *par = data;
+
+ i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SCL, DDC_SCL);
+ i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SCL : 0, DDC_SCL);
+}
+
+static void i740fb_ddc_setsda(void *data, int val)
+{
+ struct i740fb_par *par = data;
+
+ i740outreg_mask(par, XRX, REG_DDC_DRIVE, DDC_SDA, DDC_SDA);
+ i740outreg_mask(par, XRX, REG_DDC_STATE, val ? DDC_SDA : 0, DDC_SDA);
+}
+
+static int i740fb_ddc_getscl(void *data)
+{
+ struct i740fb_par *par = data;
+
+ i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SCL);
+
+ return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SCL);
+}
+
+static int i740fb_ddc_getsda(void *data)
+{
+ struct i740fb_par *par = data;
+
+ i740outreg_mask(par, XRX, REG_DDC_DRIVE, 0, DDC_SDA);
+
+ return !!(i740inreg(par, XRX, REG_DDC_STATE) & DDC_SDA);
+}
+
+static int __devinit i740fb_setup_ddc_bus(struct fb_info *info)
+{
+ struct i740fb_par *par = info->par;
+
+ strlcpy(par->ddc_adapter.name, info->fix.id,
+ sizeof(par->ddc_adapter.name));
+ par->ddc_adapter.owner = THIS_MODULE;
+ par->ddc_adapter.class = I2C_CLASS_DDC;
+ par->ddc_adapter.algo_data = &par->ddc_algo;
+ par->ddc_adapter.dev.parent = info->device;
+ par->ddc_algo.setsda = i740fb_ddc_setsda;
+ par->ddc_algo.setscl = i740fb_ddc_setscl;
+ par->ddc_algo.getsda = i740fb_ddc_getsda;
+ par->ddc_algo.getscl = i740fb_ddc_getscl;
+ par->ddc_algo.udelay = 10;
+ par->ddc_algo.timeout = 20;
+ par->ddc_algo.data = par;
+
+ i2c_set_adapdata(&par->ddc_adapter, par);
+
+ return i2c_bit_add_bus(&par->ddc_adapter);
+}
+
+static int i740fb_open(struct fb_info *info, int user)
+{
+ struct i740fb_par *par = info->par;
+
+ mutex_lock(&(par->open_lock));
+ par->ref_count++;
+ mutex_unlock(&(par->open_lock));
+
+ return 0;
+}
+
+static int i740fb_release(struct fb_info *info, int user)
+{
+ struct i740fb_par *par = info->par;
+
+ mutex_lock(&(par->open_lock));
+ if (par->ref_count == 0) {
+ printk(KERN_ERR "fb%d: release called with zero refcount\n",
+ info->node);
+ mutex_unlock(&(par->open_lock));
+ return -EINVAL;
+ }
+
+ par->ref_count--;
+ mutex_unlock(&(par->open_lock));
+
+ return 0;
+}
+
+static u32 i740_calc_fifo(struct i740fb_par *par, u32 freq, int bpp)
+{
+ /*
+ * Would like to calculate these values automatically, but a generic
+ * algorithm does not seem possible. Note: These FIFO water mark
+ * values were tested on several cards and seem to eliminate the
+ * all of the snow and vertical banding, but fine adjustments will
+ * probably be required for other cards.
+ */
+
+ u32 wm;
+
+ switch (bpp) {
+ case 8:
+ if (freq > 200)
+ wm = 0x18120000;
+ else if (freq > 175)
+ wm = 0x16110000;
+ else if (freq > 135)
+ wm = 0x120E0000;
+ else
+ wm = 0x100D0000;
+ break;
+ case 15:
+ case 16:
+ if (par->has_sgram) {
+ if (freq > 140)
+ wm = 0x2C1D0000;
+ else if (freq > 120)
+ wm = 0x2C180000;
+ else if (freq > 100)
+ wm = 0x24160000;
+ else if (freq > 90)
+ wm = 0x18120000;
+ else if (freq > 50)
+ wm = 0x16110000;
+ else if (freq > 32)
+ wm = 0x13100000;
+ else
+ wm = 0x120E0000;
+ } else {
+ if (freq > 160)
+ wm = 0x28200000;
+ else if (freq > 140)
+ wm = 0x2A1E0000;
+ else if (freq > 130)
+ wm = 0x2B1A0000;
+ else if (freq > 120)
+ wm = 0x2C180000;
+ else if (freq > 100)
+ wm = 0x24180000;
+ else if (freq > 90)
+ wm = 0x18120000;
+ else if (freq > 50)
+ wm = 0x16110000;
+ else if (freq > 32)
+ wm = 0x13100000;
+ else
+ wm = 0x120E0000;
+ }
+ break;
+ case 24:
+ if (par->has_sgram) {
+ if (freq > 130)
+ wm = 0x31200000;
+ else if (freq > 120)
+ wm = 0x2E200000;
+ else if (freq > 100)
+ wm = 0x2C1D0000;
+ else if (freq > 80)
+ wm = 0x25180000;
+ else if (freq > 64)
+ wm = 0x24160000;
+ else if (freq > 49)
+ wm = 0x18120000;
+ else if (freq > 32)
+ wm = 0x16110000;
+ else
+ wm = 0x13100000;
+ } else {
+ if (freq > 120)
+ wm = 0x311F0000;
+ else if (freq > 100)
+ wm = 0x2C1D0000;
+ else if (freq > 80)
+ wm = 0x25180000;
+ else if (freq > 64)
+ wm = 0x24160000;
+ else if (freq > 49)
+ wm = 0x18120000;
+ else if (freq > 32)
+ wm = 0x16110000;
+ else
+ wm = 0x13100000;
+ }
+ break;
+ case 32:
+ if (par->has_sgram) {
+ if (freq > 80)
+ wm = 0x2A200000;
+ else if (freq > 60)
+ wm = 0x281A0000;
+ else if (freq > 49)
+ wm = 0x25180000;
+ else if (freq > 32)
+ wm = 0x18120000;
+ else
+ wm = 0x16110000;
+ } else {
+ if (freq > 80)
+ wm = 0x29200000;
+ else if (freq > 60)
+ wm = 0x281A0000;
+ else if (freq > 49)
+ wm = 0x25180000;
+ else if (freq > 32)
+ wm = 0x18120000;
+ else
+ wm = 0x16110000;
+ }
+ break;
+ }
+
+ return wm;
+}
+
+/* clock calculation from i740fb by Patrick LERDA */
+
+#define I740_RFREQ 1000000
+#define TARGET_MAX_N 30
+#define I740_FFIX (1 << 8)
+#define I740_RFREQ_FIX (I740_RFREQ / I740_FFIX)
+#define I740_REF_FREQ (6667 * I740_FFIX / 100) /* 66.67 MHz */
+#define I740_MAX_VCO_FREQ (450 * I740_FFIX) /* 450 MHz */
+
+static void i740_calc_vclk(u32 freq, struct i740fb_par *par)
+{
+ const u32 err_max = freq / (200 * I740_RFREQ / I740_FFIX);
+ const u32 err_target = freq / (1000 * I740_RFREQ / I740_FFIX);
+ u32 err_best = 512 * I740_FFIX;
+ u32 f_err, f_vco;
+ int m_best = 0, n_best = 0, p_best = 0, d_best = 0;
+ int m, n;
+
+ p_best = min(15, ilog2(I740_MAX_VCO_FREQ / (freq / I740_RFREQ_FIX)));
+ d_best = 0;
+ f_vco = (freq * (1 << p_best)) / I740_RFREQ_FIX;
+ freq = freq / I740_RFREQ_FIX;
+
+ n = 2;
+ do {
+ n++;
+ m = ((f_vco * n) / I740_REF_FREQ + 2) / 4;
+
+ if (m < 3)
+ m = 3;
+
+ {
+ u32 f_out = (((m * I740_REF_FREQ * (4 << 2 * d_best))
+ / n) + ((1 << p_best) / 2)) / (1 << p_best);
+
+ f_err = (freq - f_out);
+
+ if (abs(f_err) < err_max) {
+ m_best = m;
+ n_best = n;
+ err_best = f_err;
+ }
+ }
+ } while ((abs(f_err) >= err_target) &&
+ ((n <= TARGET_MAX_N) || (abs(err_best) > err_max)));
+
+ if (abs(f_err) < err_target) {
+ m_best = m;
+ n_best = n;
+ }
+
+ par->video_clk2_m = (m_best - 2) & 0xFF;
+ par->video_clk2_n = (n_best - 2) & 0xFF;
+ par->video_clk2_mn_msbs = ((((n_best - 2) >> 4) & VCO_N_MSBS)
+ | (((m_best - 2) >> 8) & VCO_M_MSBS));
+ par->video_clk2_div_sel =
+ ((p_best << 4) | (d_best ? 4 : 0) | REF_DIV_1);
+}
+
+static int i740fb_decode_var(const struct fb_var_screeninfo *var,
+ struct i740fb_par *par, struct fb_info *info)
+{
+ /*
+ * Get the video params out of 'var'.
+ * If a value doesn't fit, round it up, if it's too big, return -EINVAL.
+ */
+
+ u32 xres, right, hslen, left, xtotal;
+ u32 yres, lower, vslen, upper, ytotal;
+ u32 vxres, xoffset, vyres, yoffset;
+ u32 bpp, base, dacspeed24, mem;
+ u8 r7;
+ int i;
+
+ dev_dbg(info->device, "decode_var: xres: %i, yres: %i, xres_v: %i, xres_v: %i\n",
+ var->xres, var->yres, var->xres_virtual, var->xres_virtual);
+ dev_dbg(info->device, " xoff: %i, yoff: %i, bpp: %i, graysc: %i\n",
+ var->xoffset, var->yoffset, var->bits_per_pixel,
+ var->grayscale);
+ dev_dbg(info->device, " activate: %i, nonstd: %i, vmode: %i\n",
+ var->activate, var->nonstd, var->vmode);
+ dev_dbg(info->device, " pixclock: %i, hsynclen:%i, vsynclen:%i\n",
+ var->pixclock, var->hsync_len, var->vsync_len);
+ dev_dbg(info->device, " left: %i, right: %i, up:%i, lower:%i\n",
+ var->left_margin, var->right_margin, var->upper_margin,
+ var->lower_margin);
+
+
+ bpp = var->bits_per_pixel;
+ switch (bpp) {
+ case 1 ... 8:
+ bpp = 8;
+ if ((1000000 / var->pixclock) > DACSPEED8) {
+ dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 8bpp)\n",
+ 1000000 / var->pixclock, DACSPEED8);
+ return -EINVAL;
+ }
+ break;
+ case 9 ... 15:
+ bpp = 15;
+ case 16:
+ if ((1000000 / var->pixclock) > DACSPEED16) {
+ dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n",
+ 1000000 / var->pixclock, DACSPEED16);
+ return -EINVAL;
+ }
+ break;
+ case 17 ... 24:
+ bpp = 24;
+ dacspeed24 = par->has_sgram ? DACSPEED24_SG : DACSPEED24_SD;
+ if ((1000000 / var->pixclock) > dacspeed24) {
+ dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 24bpp)\n",
+ 1000000 / var->pixclock, dacspeed24);
+ return -EINVAL;
+ }
+ break;
+ case 25 ... 32:
+ bpp = 32;
+ if ((1000000 / var->pixclock) > DACSPEED32) {
+ dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 32bpp)\n",
+ 1000000 / var->pixclock, DACSPEED32);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ xres = ALIGN(var->xres, 8);
+ vxres = ALIGN(var->xres_virtual, 16);
+ if (vxres < xres)
+ vxres = xres;
+
+ xoffset = ALIGN(var->xoffset, 8);
+ if (xres + xoffset > vxres)
+ xoffset = vxres - xres;
+
+ left = ALIGN(var->left_margin, 8);
+ right = ALIGN(var->right_margin, 8);
+ hslen = ALIGN(var->hsync_len, 8);
+
+ yres = var->yres;
+ vyres = var->yres_virtual;
+ if (yres > vyres)
+ vyres = yres;
+
+ yoffset = var->yoffset;
+ if (yres + yoffset > vyres)
+ yoffset = vyres - yres;
+
+ lower = var->lower_margin;
+ vslen = var->vsync_len;
+ upper = var->upper_margin;
+
+ mem = vxres * vyres * ((bpp + 1) / 8);
+ if (mem > info->screen_size) {
+ dev_err(info->device, "not enough video memory (%d KB requested, %ld KB avaliable)\n",
+ mem >> 10, info->screen_size >> 10);
+ return -ENOMEM;
+ }
+
+ if (yoffset + yres > vyres)
+ yoffset = vyres - yres;
+
+ xtotal = xres + right + hslen + left;
+ ytotal = yres + lower + vslen + upper;
+
+ par->crtc[VGA_CRTC_H_TOTAL] = (xtotal >> 3) - 5;
+ par->crtc[VGA_CRTC_H_DISP] = (xres >> 3) - 1;
+ par->crtc[VGA_CRTC_H_BLANK_START] = ((xres + right) >> 3) - 1;
+ par->crtc[VGA_CRTC_H_SYNC_START] = (xres + right) >> 3;
+ par->crtc[VGA_CRTC_H_SYNC_END] = (((xres + right + hslen) >> 3) & 0x1F)
+ | ((((xres + right + hslen) >> 3) & 0x20) << 2);
+ par->crtc[VGA_CRTC_H_BLANK_END] = ((xres + right + hslen) >> 3 & 0x1F)
+ | 0x80;
+
+ par->crtc[VGA_CRTC_V_TOTAL] = ytotal - 2;
+
+ r7 = 0x10; /* disable linecompare */
+ if (ytotal & 0x100)
+ r7 |= 0x01;
+ if (ytotal & 0x200)
+ r7 |= 0x20;
+
+ par->crtc[VGA_CRTC_PRESET_ROW] = 0;
+ par->crtc[VGA_CRTC_MAX_SCAN] = 0x40; /* 1 scanline, no linecmp */
+ if (var->vmode & FB_VMODE_DOUBLE)
+ par->crtc[VGA_CRTC_MAX_SCAN] |= 0x80;
+ par->crtc[VGA_CRTC_CURSOR_START] = 0x00;
+ par->crtc[VGA_CRTC_CURSOR_END] = 0x00;
+ par->crtc[VGA_CRTC_CURSOR_HI] = 0x00;
+ par->crtc[VGA_CRTC_CURSOR_LO] = 0x00;
+ par->crtc[VGA_CRTC_V_DISP_END] = yres-1;
+ if ((yres-1) & 0x100)
+ r7 |= 0x02;
+ if ((yres-1) & 0x200)
+ r7 |= 0x40;
+
+ par->crtc[VGA_CRTC_V_BLANK_START] = yres + lower - 1;
+ par->crtc[VGA_CRTC_V_SYNC_START] = yres + lower - 1;
+ if ((yres + lower - 1) & 0x100)
+ r7 |= 0x0C;
+ if ((yres + lower - 1) & 0x200) {
+ par->crtc[VGA_CRTC_MAX_SCAN] |= 0x20;
+ r7 |= 0x80;
+ }
+
+ /* disabled IRQ */
+ par->crtc[VGA_CRTC_V_SYNC_END] =
+ ((yres + lower - 1 + vslen) & 0x0F) & ~0x10;
+ /* 0x7F for VGA, but some SVGA chips require all 8 bits to be set */
+ par->crtc[VGA_CRTC_V_BLANK_END] = (yres + lower - 1 + vslen) & 0xFF;
+
+ par->crtc[VGA_CRTC_UNDERLINE] = 0x00;
+ par->crtc[VGA_CRTC_MODE] = 0xC3 ;
+ par->crtc[VGA_CRTC_LINE_COMPARE] = 0xFF;
+ par->crtc[VGA_CRTC_OVERFLOW] = r7;
+
+ par->vss = 0x00; /* 3DA */
+
+ for (i = 0x00; i < 0x10; i++)
+ par->atc[i] = i;
+ par->atc[VGA_ATC_MODE] = 0x81;
+ par->atc[VGA_ATC_OVERSCAN] = 0x00; /* 0 for EGA, 0xFF for VGA */
+ par->atc[VGA_ATC_PLANE_ENABLE] = 0x0F;
+ par->atc[VGA_ATC_COLOR_PAGE] = 0x00;
+
+ par->misc = 0xC3;
+ if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ par->misc &= ~0x40;
+ if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ par->misc &= ~0x80;
+
+ par->seq[VGA_SEQ_CLOCK_MODE] = 0x01;
+ par->seq[VGA_SEQ_PLANE_WRITE] = 0x0F;
+ par->seq[VGA_SEQ_CHARACTER_MAP] = 0x00;
+ par->seq[VGA_SEQ_MEMORY_MODE] = 0x06;
+
+ par->gdc[VGA_GFX_SR_VALUE] = 0x00;
+ par->gdc[VGA_GFX_SR_ENABLE] = 0x00;
+ par->gdc[VGA_GFX_COMPARE_VALUE] = 0x00;
+ par->gdc[VGA_GFX_DATA_ROTATE] = 0x00;
+ par->gdc[VGA_GFX_PLANE_READ] = 0;
+ par->gdc[VGA_GFX_MODE] = 0x02;
+ par->gdc[VGA_GFX_MISC] = 0x05;
+ par->gdc[VGA_GFX_COMPARE_MASK] = 0x0F;
+ par->gdc[VGA_GFX_BIT_MASK] = 0xFF;
+
+ base = (yoffset * vxres + (xoffset & ~7)) >> 2;
+ switch (bpp) {
+ case 8:
+ par->crtc[VGA_CRTC_OFFSET] = vxres >> 3;
+ par->ext_offset = vxres >> 11;
+ par->pixelpipe_cfg1 = DISPLAY_8BPP_MODE;
+ par->bitblt_cntl = COLEXP_8BPP;
+ break;
+ case 15: /* 0rrrrrgg gggbbbbb */
+ case 16: /* rrrrrggg gggbbbbb */
+ par->pixelpipe_cfg1 = (var->green.length == 6) ?
+ DISPLAY_16BPP_MODE : DISPLAY_15BPP_MODE;
+ par->crtc[VGA_CRTC_OFFSET] = vxres >> 2;
+ par->ext_offset = vxres >> 10;
+ par->bitblt_cntl = COLEXP_16BPP;
+ base *= 2;
+ break;
+ case 24:
+ par->crtc[VGA_CRTC_OFFSET] = (vxres * 3) >> 3;
+ par->ext_offset = (vxres * 3) >> 11;
+ par->pixelpipe_cfg1 = DISPLAY_24BPP_MODE;
+ par->bitblt_cntl = COLEXP_24BPP;
+ base &= 0xFFFFFFFE; /* ...ignore the last bit. */
+ base *= 3;
+ break;
+ case 32:
+ par->crtc[VGA_CRTC_OFFSET] = vxres >> 1;
+ par->ext_offset = vxres >> 9;
+ par->pixelpipe_cfg1 = DISPLAY_32BPP_MODE;
+ par->bitblt_cntl = COLEXP_RESERVED; /* Unimplemented on i740 */
+ base *= 4;
+ break;
+ }
+
+ par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
+ par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8;
+ par->ext_start_addr =
+ ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;
+ par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;
+
+ par->pixelpipe_cfg0 = DAC_8_BIT;
+
+ par->pixelpipe_cfg2 = DISPLAY_GAMMA_ENABLE | OVERLAY_GAMMA_ENABLE;
+ par->io_cntl = EXTENDED_CRTC_CNTL;
+ par->address_mapping = LINEAR_MODE_ENABLE | PAGE_MAPPING_ENABLE;
+ par->display_cntl = HIRES_MODE;
+
+ /* Set the MCLK freq */
+ par->pll_cntl = PLL_MEMCLK_100000KHZ; /* 100 MHz -- use as default */
+
+ /* Calculate the extended CRTC regs */
+ par->ext_vert_total = (ytotal - 2) >> 8;
+ par->ext_vert_disp_end = (yres - 1) >> 8;
+ par->ext_vert_sync_start = (yres + lower) >> 8;
+ par->ext_vert_blank_start = (yres + lower) >> 8;
+ par->ext_horiz_total = ((xtotal >> 3) - 5) >> 8;
+ par->ext_horiz_blank = (((xres + right) >> 3) & 0x40) >> 6;
+
+ par->interlace_cntl = INTERLACE_DISABLE;
+
+ /* Set the overscan color to 0. (NOTE: This only affects >8bpp mode) */
+ par->atc[VGA_ATC_OVERSCAN] = 0;
+
+ /* Calculate VCLK that most closely matches the requested dot clock */
+ i740_calc_vclk((((u32)1e9) / var->pixclock) * (u32)(1e3), par);
+
+ /* Since we program the clocks ourselves, always use VCLK2. */
+ par->misc |= 0x0C;
+
+ /* Calculate the FIFO Watermark and Burst Length. */
+ par->lmi_fifo_watermark =
+ i740_calc_fifo(par, 1000000 / var->pixclock, bpp);
+
+ return 0;
+}
+
+static int i740fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.offset = var->green.offset = var->blue.offset = 0;
+ var->red.length = var->green.length = var->blue.length = 8;
+ break;
+ case 16:
+ switch (var->green.length) {
+ default:
+ case 5:
+ var->red.offset = 10;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = 5;
+ var->green.length = 5;
+ var->blue.length = 5;
+ break;
+ case 6:
+ var->red.offset = 11;
+ var->green.offset = 5;
+ var->blue.offset = 0;
+ var->red.length = var->blue.length = 5;
+ break;
+ }
+ break;
+ case 24:
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->red.length = var->green.length = var->blue.length = 8;
+ break;
+ case 32:
+ var->transp.offset = 24;
+ var->red.offset = 16;
+ var->green.offset = 8;
+ var->blue.offset = 0;
+ var->transp.length = 8;
+ var->red.length = var->green.length = var->blue.length = 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (var->xres > var->xres_virtual)
+ var->xres_virtual = var->xres;
+
+ if (var->yres > var->yres_virtual)
+ var->yres_virtual = var->yres;
+
+ if (info->monspecs.hfmax && info->monspecs.vfmax &&
+ info->monspecs.dclkmax && fb_validate_mode(var, info) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static void vga_protect(struct i740fb_par *par)
+{
+ /* disable the display */
+ i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0x20, 0x20);
+
+ i740inb(par, 0x3DA);
+ i740outb(par, VGA_ATT_W, 0x00); /* enable pallete access */
+}
+
+static void vga_unprotect(struct i740fb_par *par)
+{
+ /* reenable display */
+ i740outreg_mask(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE, 0, 0x20);
+
+ i740inb(par, 0x3DA);
+ i740outb(par, VGA_ATT_W, 0x20); /* disable pallete access */
+}
+
+static int i740fb_set_par(struct fb_info *info)
+{
+ struct i740fb_par *par = info->par;
+ u32 itemp;
+ int i;
+
+ i = i740fb_decode_var(&info->var, par, info);
+ if (i)
+ return i;
+
+ memset(info->screen_base, 0, info->screen_size);
+
+ vga_protect(par);
+
+ i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_DISABLE);
+
+ mdelay(1);
+
+ i740outreg(par, XRX, VCLK2_VCO_M, par->video_clk2_m);
+ i740outreg(par, XRX, VCLK2_VCO_N, par->video_clk2_n);
+ i740outreg(par, XRX, VCLK2_VCO_MN_MSBS, par->video_clk2_mn_msbs);
+ i740outreg(par, XRX, VCLK2_VCO_DIV_SEL, par->video_clk2_div_sel);
+
+ i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0,
+ par->pixelpipe_cfg0 & DAC_8_BIT, 0x80);
+
+ i740inb(par, 0x3DA);
+ i740outb(par, 0x3C0, 0x00);
+
+ /* update misc output register */
+ i740outb(par, VGA_MIS_W, par->misc | 0x01);
+
+ /* synchronous reset on */
+ i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x01);
+ /* write sequencer registers */
+ i740outreg(par, VGA_SEQ_I, VGA_SEQ_CLOCK_MODE,
+ par->seq[VGA_SEQ_CLOCK_MODE] | 0x20);
+ for (i = 2; i < VGA_SEQ_C; i++)
+ i740outreg(par, VGA_SEQ_I, i, par->seq[i]);
+
+ /* synchronous reset off */
+ i740outreg(par, VGA_SEQ_I, VGA_SEQ_RESET, 0x03);
+
+ /* deprotect CRT registers 0-7 */
+ i740outreg(par, VGA_CRT_IC, VGA_CRTC_V_SYNC_END,
+ par->crtc[VGA_CRTC_V_SYNC_END]);
+
+ /* write CRT registers */
+ for (i = 0; i < VGA_CRT_C; i++)
+ i740outreg(par, VGA_CRT_IC, i, par->crtc[i]);
+
+ /* write graphics controller registers */
+ for (i = 0; i < VGA_GFX_C; i++)
+ i740outreg(par, VGA_GFX_I, i, par->gdc[i]);
+
+ /* write attribute controller registers */
+ for (i = 0; i < VGA_ATT_C; i++) {
+ i740inb(par, VGA_IS1_RC); /* reset flip-flop */
+ i740outb(par, VGA_ATT_IW, i);
+ i740outb(par, VGA_ATT_IW, par->atc[i]);
+ }
+
+ i740inb(par, VGA_IS1_RC);
+ i740outb(par, VGA_ATT_IW, 0x20);
+
+ i740outreg(par, VGA_CRT_IC, EXT_VERT_TOTAL, par->ext_vert_total);
+ i740outreg(par, VGA_CRT_IC, EXT_VERT_DISPLAY, par->ext_vert_disp_end);
+ i740outreg(par, VGA_CRT_IC, EXT_VERT_SYNC_START,
+ par->ext_vert_sync_start);
+ i740outreg(par, VGA_CRT_IC, EXT_VERT_BLANK_START,
+ par->ext_vert_blank_start);
+ i740outreg(par, VGA_CRT_IC, EXT_HORIZ_TOTAL, par->ext_horiz_total);
+ i740outreg(par, VGA_CRT_IC, EXT_HORIZ_BLANK, par->ext_horiz_blank);
+ i740outreg(par, VGA_CRT_IC, EXT_OFFSET, par->ext_offset);
+ i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI, par->ext_start_addr_hi);
+ i740outreg(par, VGA_CRT_IC, EXT_START_ADDR, par->ext_start_addr);
+
+ i740outreg_mask(par, VGA_CRT_IC, INTERLACE_CNTL,
+ par->interlace_cntl, INTERLACE_ENABLE);
+ i740outreg_mask(par, XRX, ADDRESS_MAPPING, par->address_mapping, 0x1F);
+ i740outreg_mask(par, XRX, BITBLT_CNTL, par->bitblt_cntl, COLEXP_MODE);
+ i740outreg_mask(par, XRX, DISPLAY_CNTL,
+ par->display_cntl, VGA_WRAP_MODE | GUI_MODE);
+ i740outreg_mask(par, XRX, PIXPIPE_CONFIG_0, par->pixelpipe_cfg0, 0x9B);
+ i740outreg_mask(par, XRX, PIXPIPE_CONFIG_2, par->pixelpipe_cfg2, 0x0C);
+
+ i740outreg(par, XRX, PLL_CNTL, par->pll_cntl);
+
+ i740outreg_mask(par, XRX, PIXPIPE_CONFIG_1,
+ par->pixelpipe_cfg1, DISPLAY_COLOR_MODE);
+
+ itemp = readl(par->regs + FWATER_BLC);
+ itemp &= ~(LMI_BURST_LENGTH | LMI_FIFO_WATERMARK);
+ itemp |= par->lmi_fifo_watermark;
+ writel(itemp, par->regs + FWATER_BLC);
+
+ i740outreg(par, XRX, DRAM_EXT_CNTL, DRAM_REFRESH_60HZ);
+
+ i740outreg_mask(par, MRX, COL_KEY_CNTL_1, 0, BLANK_DISP_OVERLAY);
+ i740outreg_mask(par, XRX, IO_CTNL,
+ par->io_cntl, EXTENDED_ATTR_CNTL | EXTENDED_CRTC_CNTL);
+
+ if (par->pixelpipe_cfg1 != DISPLAY_8BPP_MODE) {
+ i740outb(par, VGA_PEL_MSK, 0xFF);
+ i740outb(par, VGA_PEL_IW, 0x00);
+ for (i = 0; i < 256; i++) {
+ itemp = (par->pixelpipe_cfg0 & DAC_8_BIT) ? i : i >> 2;
+ i740outb(par, VGA_PEL_D, itemp);
+ i740outb(par, VGA_PEL_D, itemp);
+ i740outb(par, VGA_PEL_D, itemp);
+ }
+ }
+
+ /* Wait for screen to stabilize. */
+ mdelay(50);
+ vga_unprotect(par);
+
+ info->fix.line_length =
+ info->var.xres_virtual * info->var.bits_per_pixel / 8;
+ if (info->var.bits_per_pixel == 8)
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ return 0;
+}
+
+static int i740fb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ u32 r, g, b;
+
+ dev_dbg(info->device, "setcolreg: regno: %i, red=%d, green=%d, blue=%d, transp=%d, bpp=%d\n",
+ regno, red, green, blue, transp, info->var.bits_per_pixel);
+
+ switch (info->fix.visual) {
+ case FB_VISUAL_PSEUDOCOLOR:
+ if (regno >= 256)
+ return -EINVAL;
+ i740outb(info->par, VGA_PEL_IW, regno);
+ i740outb(info->par, VGA_PEL_D, red >> 8);
+ i740outb(info->par, VGA_PEL_D, green >> 8);
+ i740outb(info->par, VGA_PEL_D, blue >> 8);
+ break;
+ case FB_VISUAL_TRUECOLOR:
+ if (regno >= 16)
+ return -EINVAL;
+ r = (red >> (16 - info->var.red.length))
+ << info->var.red.offset;
+ b = (blue >> (16 - info->var.blue.length))
+ << info->var.blue.offset;
+ g = (green >> (16 - info->var.green.length))
+ << info->var.green.offset;
+ ((u32 *) info->pseudo_palette)[regno] = r | g | b;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int i740fb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct i740fb_par *par = info->par;
+ u32 base = (var->yoffset * info->var.xres_virtual
+ + (var->xoffset & ~7)) >> 2;
+
+ dev_dbg(info->device, "pan_display: xoffset: %i yoffset: %i base: %i\n",
+ var->xoffset, var->yoffset, base);
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ break;
+ case 15:
+ case 16:
+ base *= 2;
+ break;
+ case 24:
+ /*
+ * The last bit does not seem to have any effect on the start
+ * address register in 24bpp mode, so...
+ */
+ base &= 0xFFFFFFFE; /* ...ignore the last bit. */
+ base *= 3;
+ break;
+ case 32:
+ base *= 4;
+ break;
+ }
+
+ par->crtc[VGA_CRTC_START_LO] = base & 0x000000FF;
+ par->crtc[VGA_CRTC_START_HI] = (base & 0x0000FF00) >> 8;
+ par->ext_start_addr_hi = (base & 0x3FC00000) >> 22;
+ par->ext_start_addr =
+ ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE;
+
+ i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_LO, base & 0x000000FF);
+ i740outreg(par, VGA_CRT_IC, VGA_CRTC_START_HI,
+ (base & 0x0000FF00) >> 8);
+ i740outreg(par, VGA_CRT_IC, EXT_START_ADDR_HI,
+ (base & 0x3FC00000) >> 22);
+ i740outreg(par, VGA_CRT_IC, EXT_START_ADDR,
+ ((base & 0x003F0000) >> 16) | EXT_START_ADDR_ENABLE);
+
+ return 0;
+}
+
+static int i740fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct i740fb_par *par = info->par;
+
+ unsigned char SEQ01;
+ int DPMSSyncSelect;
+
+ switch (blank_mode) {
+ case FB_BLANK_UNBLANK:
+ case FB_BLANK_NORMAL:
+ SEQ01 = 0x00;
+ DPMSSyncSelect = HSYNC_ON | VSYNC_ON;
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ SEQ01 = 0x20;
+ DPMSSyncSelect = HSYNC_ON | VSYNC_OFF;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ SEQ01 = 0x20;
+ DPMSSyncSelect = HSYNC_OFF | VSYNC_ON;
+ break;
+ case FB_BLANK_POWERDOWN:
+ SEQ01 = 0x20;
+ DPMSSyncSelect = HSYNC_OFF | VSYNC_OFF;
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* Turn the screen on/off */
+ i740outb(par, SRX, 0x01);
+ SEQ01 |= i740inb(par, SRX + 1) & ~0x20;
+ i740outb(par, SRX, 0x01);
+ i740outb(par, SRX + 1, SEQ01);
+
+ /* Set the DPMS mode */
+ i740outreg(par, XRX, DPMS_SYNC_SELECT, DPMSSyncSelect);
+
+ /* Let fbcon do a soft blank for us */
+ return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
+}
+
+static struct fb_ops i740fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = i740fb_open,
+ .fb_release = i740fb_release,
+ .fb_check_var = i740fb_check_var,
+ .fb_set_par = i740fb_set_par,
+ .fb_setcolreg = i740fb_setcolreg,
+ .fb_blank = i740fb_blank,
+ .fb_pan_display = i740fb_pan_display,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+/* ------------------------------------------------------------------------- */
+
+static int __devinit i740fb_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ struct fb_info *info;
+ struct i740fb_par *par;
+ int ret, tmp;
+ bool found = false;
+ u8 *edid;
+
+ info = framebuffer_alloc(sizeof(struct i740fb_par), &(dev->dev));
+ if (!info) {
+ dev_err(&(dev->dev), "cannot allocate framebuffer\n");
+ return -ENOMEM;
+ }
+
+ par = info->par;
+ mutex_init(&par->open_lock);
+
+ info->var.activate = FB_ACTIVATE_NOW;
+ info->var.bits_per_pixel = 8;
+ info->fbops = &i740fb_ops;
+ info->pseudo_palette = par->pseudo_palette;
+
+ ret = pci_enable_device(dev);
+ if (ret) {
+ dev_err(info->device, "cannot enable PCI device\n");
+ goto err_enable_device;
+ }
+
+ ret = pci_request_regions(dev, info->fix.id);
+ if (ret) {
+ dev_err(info->device, "error requesting regions\n");
+ goto err_request_regions;
+ }
+
+ info->screen_base = pci_ioremap_bar(dev, 0);
+ if (!info->screen_base) {
+ dev_err(info->device, "error remapping base\n");
+ ret = -ENOMEM;
+ goto err_ioremap_1;
+ }
+
+ par->regs = pci_ioremap_bar(dev, 1);
+ if (!par->regs) {
+ dev_err(info->device, "error remapping MMIO\n");
+ ret = -ENOMEM;
+ goto err_ioremap_2;
+ }
+
+ /* detect memory size */
+ if ((i740inreg(par, XRX, DRAM_ROW_TYPE) & DRAM_ROW_1)
+ == DRAM_ROW_1_SDRAM)
+ i740outb(par, XRX, DRAM_ROW_BNDRY_1);
+ else
+ i740outb(par, XRX, DRAM_ROW_BNDRY_0);
+ info->screen_size = i740inb(par, XRX + 1) * 1024 * 1024;
+ /* detect memory type */
+ tmp = i740inreg(par, XRX, DRAM_ROW_CNTL_LO);
+ par->has_sgram = !((tmp & DRAM_RAS_TIMING) ||
+ (tmp & DRAM_RAS_PRECHARGE));
+
+ printk(KERN_INFO "fb%d: Intel740 on %s, %ld KB %s\n", info->node,
+ pci_name(dev), info->screen_size >> 10,
+ par->has_sgram ? "SGRAM" : "SDRAM");
+
+ info->fix = i740fb_fix;
+ info->fix.mmio_start = pci_resource_start(dev, 1);
+ info->fix.mmio_len = pci_resource_len(dev, 1);
+ info->fix.smem_start = pci_resource_start(dev, 0);
+ info->fix.smem_len = info->screen_size;
+ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
+
+ if (i740fb_setup_ddc_bus(info) == 0) {
+ par->ddc_registered = true;
+ edid = fb_ddc_read(&par->ddc_adapter);
+ if (edid) {
+ fb_edid_to_monspecs(edid, &info->monspecs);
+ kfree(edid);
+ if (!info->monspecs.modedb)
+ dev_err(info->device,
+ "error getting mode database\n");
+ else {
+ const struct fb_videomode *m;
+
+ fb_videomode_to_modelist(
+ info->monspecs.modedb,
+ info->monspecs.modedb_len,
+ &info->modelist);
+ m = fb_find_best_display(&info->monspecs,
+ &info->modelist);
+ if (m) {
+ fb_videomode_to_var(&info->var, m);
+ /* fill all other info->var's fields */
+ if (!i740fb_check_var(&info->var, info))
+ found = true;
+ }
+ }
+ }
+ }
+
+ if (!mode_option && !found)
+ mode_option = "640x480-8@60";
+
+ if (mode_option) {
+ ret = fb_find_mode(&info->var, info, mode_option,
+ info->monspecs.modedb,
+ info->monspecs.modedb_len,
+ NULL, info->var.bits_per_pixel);
+ if (!ret || ret == 4) {
+ dev_err(info->device, "mode %s not found\n",
+ mode_option);
+ ret = -EINVAL;
+ }
+ }
+
+ fb_destroy_modedb(info->monspecs.modedb);
+ info->monspecs.modedb = NULL;
+
+ /* maximize virtual vertical size for fast scrolling */
+ info->var.yres_virtual = info->fix.smem_len * 8 /
+ (info->var.bits_per_pixel * info->var.xres_virtual);
+
+ if (ret == -EINVAL)
+ goto err_find_mode;
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ dev_err(info->device, "cannot allocate colormap\n");
+ goto err_alloc_cmap;
+ }
+
+ ret = register_framebuffer(info);
+ if (ret) {
+ dev_err(info->device, "error registering framebuffer\n");
+ goto err_reg_framebuffer;
+ }
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n",
+ info->node, info->fix.id);
+ pci_set_drvdata(dev, info);
+#ifdef CONFIG_MTRR
+ if (mtrr) {
+ par->mtrr_reg = -1;
+ par->mtrr_reg = mtrr_add(info->fix.smem_start,
+ info->fix.smem_len, MTRR_TYPE_WRCOMB, 1);
+ }
+#endif
+ return 0;
+
+err_reg_framebuffer:
+ fb_dealloc_cmap(&info->cmap);
+err_alloc_cmap:
+err_find_mode:
+ if (par->ddc_registered)
+ i2c_del_adapter(&par->ddc_adapter);
+ pci_iounmap(dev, par->regs);
+err_ioremap_2:
+ pci_iounmap(dev, info->screen_base);
+err_ioremap_1:
+ pci_release_regions(dev);
+err_request_regions:
+/* pci_disable_device(dev); */
+err_enable_device:
+ framebuffer_release(info);
+ return ret;
+}
+
+static void __devexit i740fb_remove(struct pci_dev *dev)
+{
+ struct fb_info *info = pci_get_drvdata(dev);
+
+ if (info) {
+ struct i740fb_par *par = info->par;
+
+#ifdef CONFIG_MTRR
+ if (par->mtrr_reg >= 0) {
+ mtrr_del(par->mtrr_reg, 0, 0);
+ par->mtrr_reg = -1;
+ }
+#endif
+ unregister_framebuffer(info);
+ fb_dealloc_cmap(&info->cmap);
+ if (par->ddc_registered)
+ i2c_del_adapter(&par->ddc_adapter);
+ pci_iounmap(dev, par->regs);
+ pci_iounmap(dev, info->screen_base);
+ pci_release_regions(dev);
+/* pci_disable_device(dev); */
+ pci_set_drvdata(dev, NULL);
+ framebuffer_release(info);
+ }
+}
+
+#ifdef CONFIG_PM
+static int i740fb_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ struct fb_info *info = pci_get_drvdata(dev);
+ struct i740fb_par *par = info->par;
+
+ /* don't disable console during hibernation and wakeup from it */
+ if (state.event == PM_EVENT_FREEZE || state.event == PM_EVENT_PRETHAW)
+ return 0;
+
+ console_lock();
+ mutex_lock(&(par->open_lock));
+
+ /* do nothing if framebuffer is not active */
+ if (par->ref_count == 0) {
+ mutex_unlock(&(par->open_lock));
+ console_unlock();
+ return 0;
+ }
+
+ fb_set_suspend(info, 1);
+
+ pci_save_state(dev);
+ pci_disable_device(dev);
+ pci_set_power_state(dev, pci_choose_state(dev, state));
+
+ mutex_unlock(&(par->open_lock));
+ console_unlock();
+
+ return 0;
+}
+
+static int i740fb_resume(struct pci_dev *dev)
+{
+ struct fb_info *info = pci_get_drvdata(dev);
+ struct i740fb_par *par = info->par;
+
+ console_lock();
+ mutex_lock(&(par->open_lock));
+
+ if (par->ref_count == 0)
+ goto fail;
+
+ pci_set_power_state(dev, PCI_D0);
+ pci_restore_state(dev);
+ if (pci_enable_device(dev))
+ goto fail;
+
+ i740fb_set_par(info);
+ fb_set_suspend(info, 0);
+
+fail:
+ mutex_unlock(&(par->open_lock));
+ console_unlock();
+ return 0;
+}
+#else
+#define i740fb_suspend NULL
+#define i740fb_resume NULL
+#endif /* CONFIG_PM */
+
+#define I740_ID_PCI 0x00d1
+#define I740_ID_AGP 0x7800
+
+static DEFINE_PCI_DEVICE_TABLE(i740fb_id_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_PCI) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, I740_ID_AGP) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, i740fb_id_table);
+
+static struct pci_driver i740fb_driver = {
+ .name = "i740fb",
+ .id_table = i740fb_id_table,
+ .probe = i740fb_probe,
+ .remove = __devexit_p(i740fb_remove),
+ .suspend = i740fb_suspend,
+ .resume = i740fb_resume,
+};
+
+#ifndef MODULE
+static int __init i740fb_setup(char *options)
+{
+ char *opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((opt = strsep(&options, ",")) != NULL) {
+ if (!*opt)
+ continue;
+#ifdef CONFIG_MTRR
+ else if (!strncmp(opt, "mtrr:", 5))
+ mtrr = simple_strtoul(opt + 5, NULL, 0);
+#endif
+ else
+ mode_option = opt;
+ }
+
+ return 0;
+}
+#endif
+
+int __init i740fb_init(void)
+{
+#ifndef MODULE
+ char *option = NULL;
+
+ if (fb_get_options("i740fb", &option))
+ return -ENODEV;
+ i740fb_setup(option);
+#endif
+
+ return pci_register_driver(&i740fb_driver);
+}
+
+static void __exit i740fb_exit(void)
+{
+ pci_unregister_driver(&i740fb_driver);
+}
+
+module_init(i740fb_init);
+module_exit(i740fb_exit);
+
+MODULE_AUTHOR("(c) 2011 Ondrej Zary <linux@rainbow-software.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("fbdev driver for Intel740");
+
+module_param(mode_option, charp, 0444);
+MODULE_PARM_DESC(mode_option, "Default video mode ('640x480-8@60', etc)");
+
+#ifdef CONFIG_MTRR
+module_param(mtrr, int, 0444);
+MODULE_PARM_DESC(mtrr, "Enable write-combining with MTRR (1=enable, 0=disable, default=1)");
+#endif
diff --git a/drivers/video/msm/mddi_client_nt35399.c b/drivers/video/msm/mddi_client_nt35399.c
index f239f4a25e01..7fcd67e132bf 100644
--- a/drivers/video/msm/mddi_client_nt35399.c
+++ b/drivers/video/msm/mddi_client_nt35399.c
@@ -155,14 +155,10 @@ static int setup_vsync(struct panel_info *panel, int init)
ret = 0;
goto uninit;
}
- ret = gpio_request(gpio, "vsync");
+ ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
if (ret)
goto err_request_gpio_failed;
- ret = gpio_direction_input(gpio);
- if (ret)
- goto err_gpio_direction_input_failed;
-
ret = irq = gpio_to_irq(gpio);
if (ret < 0)
goto err_get_irq_num_failed;
@@ -180,7 +176,6 @@ uninit:
free_irq(gpio_to_irq(gpio), panel->client_data);
err_request_irq_failed:
err_get_irq_num_failed:
-err_gpio_direction_input_failed:
gpio_free(gpio);
err_request_gpio_failed:
return ret;
diff --git a/drivers/video/msm/mddi_client_toshiba.c b/drivers/video/msm/mddi_client_toshiba.c
index f9bc932ac46b..053eb6877330 100644
--- a/drivers/video/msm/mddi_client_toshiba.c
+++ b/drivers/video/msm/mddi_client_toshiba.c
@@ -186,14 +186,10 @@ static int setup_vsync(struct panel_info *panel,
ret = 0;
goto uninit;
}
- ret = gpio_request(gpio, "vsync");
+ ret = gpio_request_one(gpio, GPIOF_IN, "vsync");
if (ret)
goto err_request_gpio_failed;
- ret = gpio_direction_input(gpio);
- if (ret)
- goto err_gpio_direction_input_failed;
-
ret = irq = gpio_to_irq(gpio);
if (ret < 0)
goto err_get_irq_num_failed;
@@ -210,7 +206,6 @@ uninit:
free_irq(gpio_to_irq(gpio), panel);
err_request_irq_failed:
err_get_irq_num_failed:
-err_gpio_direction_input_failed:
gpio_free(gpio);
err_request_gpio_failed:
return ret;
diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c
index fb3f67391105..afc9521173ef 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/omap/Kconfig b/drivers/video/omap/Kconfig
index 84ff23208c25..1e7536d9a8fc 100644
--- a/drivers/video/omap/Kconfig
+++ b/drivers/video/omap/Kconfig
@@ -1,11 +1,10 @@
config FB_OMAP
tristate "OMAP frame buffer support (EXPERIMENTAL)"
- depends on FB && (OMAP2_DSS = "n")
- depends on ARCH_OMAP1 || ARCH_OMAP2 || ARCH_OMAP3
+ depends on FB
+ depends on ARCH_OMAP1
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
- select TWL4030_CORE if MACH_OMAP_2430SDP
help
Frame buffer driver for OMAP based boards.
@@ -23,13 +22,6 @@ config FB_OMAP_LCDC_HWA742
Say Y here if you want to have support for the external
Epson HWA742 LCD controller.
-config FB_OMAP_LCDC_BLIZZARD
- bool "Epson Blizzard LCD controller support"
- depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
- help
- Say Y here if you want to have support for the external
- Epson Blizzard LCD controller.
-
config FB_OMAP_MANUAL_UPDATE
bool "Default to manual update mode"
depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL
@@ -49,7 +41,7 @@ config FB_OMAP_LCD_MIPID
config FB_OMAP_BOOTLOADER_INIT
bool "Check bootloader initialization"
- depends on FB_OMAP || FB_OMAP2
+ depends on FB_OMAP
help
Say Y here if you want to enable checking if the bootloader has
already initialized the display controller. In this case the
@@ -68,7 +60,7 @@ config FB_OMAP_CONSISTENT_DMA_SIZE
config FB_OMAP_DMA_TUNE
bool "Set DMA SDRAM access priority high"
- depends on FB_OMAP && ARCH_OMAP1
+ depends on FB_OMAP
help
On systems in which video memory is in system memory
(SDRAM) this will speed up graphics DMA operations.
diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile
index ef78550917ff..1927faffb5bc 100644
--- a/drivers/video/omap/Makefile
+++ b/drivers/video/omap/Makefile
@@ -1,20 +1,14 @@
#
-# Makefile for the new OMAP framebuffer device driver
+# Makefile for the OMAP1 framebuffer device driver
#
obj-$(CONFIG_FB_OMAP) += omapfb.o
-objs-yy := omapfb_main.o
+objs-yy := omapfb_main.o lcdc.o
-objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
-objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
-objs-y$(CONFIG_ARCH_OMAP3) += dispc.o
-
-objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
-objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
+objs-y$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
-objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o
objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
diff --git a/drivers/video/omap/blizzard.c b/drivers/video/omap/blizzard.c
deleted file mode 100644
index c0504a8a5079..000000000000
--- a/drivers/video/omap/blizzard.c
+++ /dev/null
@@ -1,1648 +0,0 @@
-/*
- * Epson Blizzard LCD controller driver
- *
- * Copyright (C) 2004-2005 Nokia Corporation
- * Authors: Juha Yrjola <juha.yrjola@nokia.com>
- * Imre Deak <imre.deak@nokia.com>
- * YUV support: Jussi Laako <jussi.laako@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-#include <linux/module.h>
-#include <linux/mm.h>
-#include <linux/fb.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-
-#include <plat/dma.h>
-#include <plat/blizzard.h>
-
-#include "omapfb.h"
-#include "dispc.h"
-
-#define MODULE_NAME "blizzard"
-
-#define BLIZZARD_REV_CODE 0x00
-#define BLIZZARD_CONFIG 0x02
-#define BLIZZARD_PLL_DIV 0x04
-#define BLIZZARD_PLL_LOCK_RANGE 0x06
-#define BLIZZARD_PLL_CLOCK_SYNTH_0 0x08
-#define BLIZZARD_PLL_CLOCK_SYNTH_1 0x0a
-#define BLIZZARD_PLL_MODE 0x0c
-#define BLIZZARD_CLK_SRC 0x0e
-#define BLIZZARD_MEM_BANK0_ACTIVATE 0x10
-#define BLIZZARD_MEM_BANK0_STATUS 0x14
-#define BLIZZARD_PANEL_CONFIGURATION 0x28
-#define BLIZZARD_HDISP 0x2a
-#define BLIZZARD_HNDP 0x2c
-#define BLIZZARD_VDISP0 0x2e
-#define BLIZZARD_VDISP1 0x30
-#define BLIZZARD_VNDP 0x32
-#define BLIZZARD_HSW 0x34
-#define BLIZZARD_VSW 0x38
-#define BLIZZARD_DISPLAY_MODE 0x68
-#define BLIZZARD_INPUT_WIN_X_START_0 0x6c
-#define BLIZZARD_DATA_SOURCE_SELECT 0x8e
-#define BLIZZARD_DISP_MEM_DATA_PORT 0x90
-#define BLIZZARD_DISP_MEM_READ_ADDR0 0x92
-#define BLIZZARD_POWER_SAVE 0xE6
-#define BLIZZARD_NDISP_CTRL_STATUS 0xE8
-
-/* Data source select */
-/* For S1D13745 */
-#define BLIZZARD_SRC_WRITE_LCD_BACKGROUND 0x00
-#define BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE 0x01
-#define BLIZZARD_SRC_WRITE_OVERLAY_ENABLE 0x04
-#define BLIZZARD_SRC_DISABLE_OVERLAY 0x05
-/* For S1D13744 */
-#define BLIZZARD_SRC_WRITE_LCD 0x00
-#define BLIZZARD_SRC_BLT_LCD 0x06
-
-#define BLIZZARD_COLOR_RGB565 0x01
-#define BLIZZARD_COLOR_YUV420 0x09
-
-#define BLIZZARD_VERSION_S1D13745 0x01 /* Hailstorm */
-#define BLIZZARD_VERSION_S1D13744 0x02 /* Blizzard */
-
-#define BLIZZARD_AUTO_UPDATE_TIME (HZ / 20)
-
-/* Reserve 4 request slots for requests in irq context */
-#define REQ_POOL_SIZE 24
-#define IRQ_REQ_POOL_SIZE 4
-
-#define REQ_FROM_IRQ_POOL 0x01
-
-#define REQ_COMPLETE 0
-#define REQ_PENDING 1
-
-struct blizzard_reg_list {
- int start;
- int end;
-};
-
-/* These need to be saved / restored separately from the rest. */
-static const struct blizzard_reg_list blizzard_pll_regs[] = {
- {
- .start = 0x04, /* Don't save PLL ctrl (0x0C) */
- .end = 0x0a,
- },
- {
- .start = 0x0e, /* Clock configuration */
- .end = 0x0e,
- },
-};
-
-static const struct blizzard_reg_list blizzard_gen_regs[] = {
- {
- .start = 0x18, /* SDRAM control */
- .end = 0x20,
- },
- {
- .start = 0x28, /* LCD Panel configuration */
- .end = 0x5a, /* HSSI interface, TV configuration */
- },
-};
-
-static u8 blizzard_reg_cache[0x5a / 2];
-
-struct update_param {
- int plane;
- int x, y, width, height;
- int out_x, out_y;
- int out_width, out_height;
- int color_mode;
- int bpp;
- int flags;
-};
-
-struct blizzard_request {
- struct list_head entry;
- unsigned int flags;
-
- int (*handler)(struct blizzard_request *req);
- void (*complete)(void *data);
- void *complete_data;
-
- union {
- struct update_param update;
- struct completion *sync;
- } par;
-};
-
-struct plane_info {
- unsigned long offset;
- int pos_x, pos_y;
- int width, height;
- int out_width, out_height;
- int scr_width;
- int color_mode;
- int bpp;
-};
-
-struct blizzard_struct {
- enum omapfb_update_mode update_mode;
- enum omapfb_update_mode update_mode_before_suspend;
-
- struct timer_list auto_update_timer;
- int stop_auto_update;
- struct omapfb_update_window auto_update_window;
- int enabled_planes;
- int vid_nonstd_color;
- int vid_scaled;
- int last_color_mode;
- int zoom_on;
- int zoom_area_gx1;
- int zoom_area_gx2;
- int zoom_area_gy1;
- int zoom_area_gy2;
- int screen_width;
- int screen_height;
- unsigned te_connected:1;
- unsigned vsync_only:1;
-
- struct plane_info plane[OMAPFB_PLANE_NUM];
-
- struct blizzard_request req_pool[REQ_POOL_SIZE];
- struct list_head pending_req_list;
- struct list_head free_req_list;
- struct semaphore req_sema;
- spinlock_t req_lock;
-
- unsigned long sys_ck_rate;
- struct extif_timings reg_timings, lut_timings;
-
- u32 max_transmit_size;
- u32 extif_clk_period;
- int extif_clk_div;
- unsigned long pix_tx_time;
- unsigned long line_upd_time;
-
- struct omapfb_device *fbdev;
- struct lcd_ctrl_extif *extif;
- const struct lcd_ctrl *int_ctrl;
-
- void (*power_up)(struct device *dev);
- void (*power_down)(struct device *dev);
-
- int version;
-} blizzard;
-
-struct lcd_ctrl blizzard_ctrl;
-
-static u8 blizzard_read_reg(u8 reg)
-{
- u8 data;
-
- blizzard.extif->set_bits_per_cycle(8);
- blizzard.extif->write_command(&reg, 1);
- blizzard.extif->read_data(&data, 1);
-
- return data;
-}
-
-static void blizzard_write_reg(u8 reg, u8 val)
-{
- blizzard.extif->set_bits_per_cycle(8);
- blizzard.extif->write_command(&reg, 1);
- blizzard.extif->write_data(&val, 1);
-}
-
-static void blizzard_restart_sdram(void)
-{
- unsigned long tmo;
-
- blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0);
- udelay(50);
- blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 1);
- tmo = jiffies + msecs_to_jiffies(200);
- while (!(blizzard_read_reg(BLIZZARD_MEM_BANK0_STATUS) & 0x01)) {
- if (time_after(jiffies, tmo)) {
- dev_err(blizzard.fbdev->dev,
- "s1d1374x: SDRAM not ready\n");
- break;
- }
- msleep(1);
- }
-}
-
-static void blizzard_stop_sdram(void)
-{
- blizzard_write_reg(BLIZZARD_MEM_BANK0_ACTIVATE, 0);
-}
-
-/* Wait until the last window was completely written into the controllers
- * SDRAM and we can start transferring the next window.
- */
-static void blizzard_wait_line_buffer(void)
-{
- unsigned long tmo = jiffies + msecs_to_jiffies(30);
-
- while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 7)) {
- if (time_after(jiffies, tmo)) {
- if (printk_ratelimit())
- dev_err(blizzard.fbdev->dev,
- "s1d1374x: line buffer not ready\n");
- break;
- }
- }
-}
-
-/* Wait until the YYC color space converter is idle. */
-static void blizzard_wait_yyc(void)
-{
- unsigned long tmo = jiffies + msecs_to_jiffies(30);
-
- while (blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS) & (1 << 4)) {
- if (time_after(jiffies, tmo)) {
- if (printk_ratelimit())
- dev_err(blizzard.fbdev->dev,
- "s1d1374x: YYC not ready\n");
- break;
- }
- }
-}
-
-static void disable_overlay(void)
-{
- blizzard_write_reg(BLIZZARD_DATA_SOURCE_SELECT,
- BLIZZARD_SRC_DISABLE_OVERLAY);
-}
-
-static void set_window_regs(int x_start, int y_start, int x_end, int y_end,
- int x_out_start, int y_out_start,
- int x_out_end, int y_out_end, int color_mode,
- int zoom_off, int flags)
-{
- u8 tmp[18];
- u8 cmd;
-
- x_end--;
- y_end--;
- tmp[0] = x_start;
- tmp[1] = x_start >> 8;
- tmp[2] = y_start;
- tmp[3] = y_start >> 8;
- tmp[4] = x_end;
- tmp[5] = x_end >> 8;
- tmp[6] = y_end;
- tmp[7] = y_end >> 8;
-
- x_out_end--;
- y_out_end--;
- tmp[8] = x_out_start;
- tmp[9] = x_out_start >> 8;
- tmp[10] = y_out_start;
- tmp[11] = y_out_start >> 8;
- tmp[12] = x_out_end;
- tmp[13] = x_out_end >> 8;
- tmp[14] = y_out_end;
- tmp[15] = y_out_end >> 8;
-
- tmp[16] = color_mode;
- if (zoom_off && blizzard.version == BLIZZARD_VERSION_S1D13745)
- tmp[17] = BLIZZARD_SRC_WRITE_LCD_BACKGROUND;
- else if (flags & OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY)
- tmp[17] = BLIZZARD_SRC_WRITE_OVERLAY_ENABLE;
- else
- tmp[17] = blizzard.version == BLIZZARD_VERSION_S1D13744 ?
- BLIZZARD_SRC_WRITE_LCD :
- BLIZZARD_SRC_WRITE_LCD_DESTRUCTIVE;
-
- blizzard.extif->set_bits_per_cycle(8);
- cmd = BLIZZARD_INPUT_WIN_X_START_0;
- blizzard.extif->write_command(&cmd, 1);
- blizzard.extif->write_data(tmp, 18);
-}
-
-static void enable_tearsync(int y, int width, int height, int screen_height,
- int out_height, int force_vsync)
-{
- u8 b;
-
- b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
- b |= 1 << 3;
- blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
-
- if (likely(blizzard.vsync_only || force_vsync)) {
- blizzard.extif->enable_tearsync(1, 0);
- return;
- }
-
- if (width * blizzard.pix_tx_time < blizzard.line_upd_time) {
- blizzard.extif->enable_tearsync(1, 0);
- return;
- }
-
- if ((width * blizzard.pix_tx_time / 1000) * height <
- (y + out_height) * (blizzard.line_upd_time / 1000)) {
- blizzard.extif->enable_tearsync(1, 0);
- return;
- }
-
- blizzard.extif->enable_tearsync(1, y + 1);
-}
-
-static void disable_tearsync(void)
-{
- u8 b;
-
- blizzard.extif->enable_tearsync(0, 0);
- b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
- b &= ~(1 << 3);
- blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
- b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
-}
-
-static inline void set_extif_timings(const struct extif_timings *t);
-
-static inline struct blizzard_request *alloc_req(void)
-{
- unsigned long flags;
- struct blizzard_request *req;
- int req_flags = 0;
-
- if (!in_interrupt())
- down(&blizzard.req_sema);
- else
- req_flags = REQ_FROM_IRQ_POOL;
-
- spin_lock_irqsave(&blizzard.req_lock, flags);
- BUG_ON(list_empty(&blizzard.free_req_list));
- req = list_entry(blizzard.free_req_list.next,
- struct blizzard_request, entry);
- list_del(&req->entry);
- spin_unlock_irqrestore(&blizzard.req_lock, flags);
-
- INIT_LIST_HEAD(&req->entry);
- req->flags = req_flags;
-
- return req;
-}
-
-static inline void free_req(struct blizzard_request *req)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&blizzard.req_lock, flags);
-
- list_move(&req->entry, &blizzard.free_req_list);
- if (!(req->flags & REQ_FROM_IRQ_POOL))
- up(&blizzard.req_sema);
-
- spin_unlock_irqrestore(&blizzard.req_lock, flags);
-}
-
-static void process_pending_requests(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&blizzard.req_lock, flags);
-
- while (!list_empty(&blizzard.pending_req_list)) {
- struct blizzard_request *req;
- void (*complete)(void *);
- void *complete_data;
-
- req = list_entry(blizzard.pending_req_list.next,
- struct blizzard_request, entry);
- spin_unlock_irqrestore(&blizzard.req_lock, flags);
-
- if (req->handler(req) == REQ_PENDING)
- return;
-
- complete = req->complete;
- complete_data = req->complete_data;
- free_req(req);
-
- if (complete)
- complete(complete_data);
-
- spin_lock_irqsave(&blizzard.req_lock, flags);
- }
-
- spin_unlock_irqrestore(&blizzard.req_lock, flags);
-}
-
-static void submit_req_list(struct list_head *head)
-{
- unsigned long flags;
- int process = 1;
-
- spin_lock_irqsave(&blizzard.req_lock, flags);
- if (likely(!list_empty(&blizzard.pending_req_list)))
- process = 0;
- list_splice_init(head, blizzard.pending_req_list.prev);
- spin_unlock_irqrestore(&blizzard.req_lock, flags);
-
- if (process)
- process_pending_requests();
-}
-
-static void request_complete(void *data)
-{
- struct blizzard_request *req = (struct blizzard_request *)data;
- void (*complete)(void *);
- void *complete_data;
-
- complete = req->complete;
- complete_data = req->complete_data;
-
- free_req(req);
-
- if (complete)
- complete(complete_data);
-
- process_pending_requests();
-}
-
-
-static int do_full_screen_update(struct blizzard_request *req)
-{
- int i;
- int flags;
-
- for (i = 0; i < 3; i++) {
- struct plane_info *p = &blizzard.plane[i];
- if (!(blizzard.enabled_planes & (1 << i))) {
- blizzard.int_ctrl->enable_plane(i, 0);
- continue;
- }
- dev_dbg(blizzard.fbdev->dev, "pw %d ph %d\n",
- p->width, p->height);
- blizzard.int_ctrl->setup_plane(i,
- OMAPFB_CHANNEL_OUT_LCD, p->offset,
- p->scr_width, p->pos_x, p->pos_y,
- p->width, p->height,
- p->color_mode);
- blizzard.int_ctrl->enable_plane(i, 1);
- }
-
- dev_dbg(blizzard.fbdev->dev, "sw %d sh %d\n",
- blizzard.screen_width, blizzard.screen_height);
- blizzard_wait_line_buffer();
- flags = req->par.update.flags;
- if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC)
- enable_tearsync(0, blizzard.screen_width,
- blizzard.screen_height,
- blizzard.screen_height,
- blizzard.screen_height,
- flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC);
- else
- disable_tearsync();
-
- set_window_regs(0, 0, blizzard.screen_width, blizzard.screen_height,
- 0, 0, blizzard.screen_width, blizzard.screen_height,
- BLIZZARD_COLOR_RGB565, blizzard.zoom_on, flags);
- blizzard.zoom_on = 0;
-
- blizzard.extif->set_bits_per_cycle(16);
- /* set_window_regs has left the register index at the right
- * place, so no need to set it here.
- */
- blizzard.extif->transfer_area(blizzard.screen_width,
- blizzard.screen_height,
- request_complete, req);
- return REQ_PENDING;
-}
-
-static int check_1d_intersect(int a1, int a2, int b1, int b2)
-{
- if (a2 <= b1 || b2 <= a1)
- return 0;
- return 1;
-}
-
-/* Setup all planes with an overlapping area with the update window. */
-static int do_partial_update(struct blizzard_request *req, int plane,
- int x, int y, int w, int h,
- int x_out, int y_out, int w_out, int h_out,
- int wnd_color_mode, int bpp)
-{
- int i;
- int gx1, gy1, gx2, gy2;
- int gx1_out, gy1_out, gx2_out, gy2_out;
- int color_mode;
- int flags;
- int zoom_off;
- int have_zoom_for_this_update = 0;
-
- /* Global coordinates, relative to pixel 0,0 of the LCD */
- gx1 = x + blizzard.plane[plane].pos_x;
- gy1 = y + blizzard.plane[plane].pos_y;
- gx2 = gx1 + w;
- gy2 = gy1 + h;
-
- flags = req->par.update.flags;
- if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) {
- gx1_out = gx1;
- gy1_out = gy1;
- gx2_out = gx1 + w * 2;
- gy2_out = gy1 + h * 2;
- } else {
- gx1_out = x_out + blizzard.plane[plane].pos_x;
- gy1_out = y_out + blizzard.plane[plane].pos_y;
- gx2_out = gx1_out + w_out;
- gy2_out = gy1_out + h_out;
- }
-
- for (i = 0; i < OMAPFB_PLANE_NUM; i++) {
- struct plane_info *p = &blizzard.plane[i];
- int px1, py1;
- int px2, py2;
- int pw, ph;
- int pposx, pposy;
- unsigned long offset;
-
- if (!(blizzard.enabled_planes & (1 << i)) ||
- (wnd_color_mode && i != plane)) {
- blizzard.int_ctrl->enable_plane(i, 0);
- continue;
- }
- /* Plane coordinates */
- if (i == plane) {
- /* Plane in which we are doing the update.
- * Local coordinates are the one in the update
- * request.
- */
- px1 = x;
- py1 = y;
- px2 = x + w;
- py2 = y + h;
- pposx = 0;
- pposy = 0;
- } else {
- /* Check if this plane has an overlapping part */
- px1 = gx1 - p->pos_x;
- py1 = gy1 - p->pos_y;
- px2 = gx2 - p->pos_x;
- py2 = gy2 - p->pos_y;
- if (px1 >= p->width || py1 >= p->height ||
- px2 <= 0 || py2 <= 0) {
- blizzard.int_ctrl->enable_plane(i, 0);
- continue;
- }
- /* Calculate the coordinates for the overlapping
- * part in the plane's local coordinates.
- */
- pposx = -px1;
- pposy = -py1;
- if (px1 < 0)
- px1 = 0;
- if (py1 < 0)
- py1 = 0;
- if (px2 > p->width)
- px2 = p->width;
- if (py2 > p->height)
- py2 = p->height;
- if (pposx < 0)
- pposx = 0;
- if (pposy < 0)
- pposy = 0;
- }
- pw = px2 - px1;
- ph = py2 - py1;
- offset = p->offset + (p->scr_width * py1 + px1) * p->bpp / 8;
- if (wnd_color_mode)
- /* Window embedded in the plane with a differing
- * color mode / bpp. Calculate the number of DMA
- * transfer elements in terms of the plane's bpp.
- */
- pw = (pw + 1) * bpp / p->bpp;
-#ifdef VERBOSE
- dev_dbg(blizzard.fbdev->dev,
- "plane %d offset %#08lx pposx %d pposy %d "
- "px1 %d py1 %d pw %d ph %d\n",
- i, offset, pposx, pposy, px1, py1, pw, ph);
-#endif
- blizzard.int_ctrl->setup_plane(i,
- OMAPFB_CHANNEL_OUT_LCD, offset,
- p->scr_width,
- pposx, pposy, pw, ph,
- p->color_mode);
-
- blizzard.int_ctrl->enable_plane(i, 1);
- }
-
- switch (wnd_color_mode) {
- case OMAPFB_COLOR_YUV420:
- color_mode = BLIZZARD_COLOR_YUV420;
- /* Currently only the 16 bits/pixel cycle format is
- * supported on the external interface. Adjust the number
- * of transfer elements per line for 12bpp format.
- */
- w = (w + 1) * 3 / 4;
- break;
- default:
- color_mode = BLIZZARD_COLOR_RGB565;
- break;
- }
-
- blizzard_wait_line_buffer();
- if (blizzard.last_color_mode == BLIZZARD_COLOR_YUV420)
- blizzard_wait_yyc();
- blizzard.last_color_mode = color_mode;
- if (flags & OMAPFB_FORMAT_FLAG_TEARSYNC)
- enable_tearsync(gy1, w, h,
- blizzard.screen_height,
- h_out,
- flags & OMAPFB_FORMAT_FLAG_FORCE_VSYNC);
- else
- disable_tearsync();
-
- if ((gx2_out - gx1_out) != (gx2 - gx1) ||
- (gy2_out - gy1_out) != (gy2 - gy1))
- have_zoom_for_this_update = 1;
-
- /* 'background' type of screen update (as opposed to 'destructive')
- can be used to disable scaling if scaling is active */
- zoom_off = blizzard.zoom_on && !have_zoom_for_this_update &&
- (gx1_out == 0) && (gx2_out == blizzard.screen_width) &&
- (gy1_out == 0) && (gy2_out == blizzard.screen_height) &&
- (gx1 == 0) && (gy1 == 0);
-
- if (blizzard.zoom_on && !have_zoom_for_this_update && !zoom_off &&
- check_1d_intersect(blizzard.zoom_area_gx1, blizzard.zoom_area_gx2,
- gx1_out, gx2_out) &&
- check_1d_intersect(blizzard.zoom_area_gy1, blizzard.zoom_area_gy2,
- gy1_out, gy2_out)) {
- /* Previous screen update was using scaling, current update
- * is not using it. Additionally, current screen update is
- * going to overlap with the scaled area. Scaling needs to be
- * disabled in order to avoid 'magnifying glass' effect.
- * Dummy setup of background window can be used for this.
- */
- set_window_regs(0, 0, blizzard.screen_width,
- blizzard.screen_height,
- 0, 0, blizzard.screen_width,
- blizzard.screen_height,
- BLIZZARD_COLOR_RGB565, 1, flags);
- blizzard.zoom_on = 0;
- }
-
- /* remember scaling settings if we have scaled update */
- if (have_zoom_for_this_update) {
- blizzard.zoom_on = 1;
- blizzard.zoom_area_gx1 = gx1_out;
- blizzard.zoom_area_gx2 = gx2_out;
- blizzard.zoom_area_gy1 = gy1_out;
- blizzard.zoom_area_gy2 = gy2_out;
- }
-
- set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out,
- color_mode, zoom_off, flags);
- if (zoom_off)
- blizzard.zoom_on = 0;
-
- blizzard.extif->set_bits_per_cycle(16);
- /* set_window_regs has left the register index at the right
- * place, so no need to set it here.
- */
- blizzard.extif->transfer_area(w, h, request_complete, req);
-
- return REQ_PENDING;
-}
-
-static int send_frame_handler(struct blizzard_request *req)
-{
- struct update_param *par = &req->par.update;
- int plane = par->plane;
-
-#ifdef VERBOSE
- dev_dbg(blizzard.fbdev->dev,
- "send_frame: x %d y %d w %d h %d "
- "x_out %d y_out %d w_out %d h_out %d "
- "color_mode %04x flags %04x planes %01x\n",
- par->x, par->y, par->width, par->height,
- par->out_x, par->out_y, par->out_width, par->out_height,
- par->color_mode, par->flags, blizzard.enabled_planes);
-#endif
- if (par->flags & OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY)
- disable_overlay();
-
- if ((blizzard.enabled_planes & blizzard.vid_nonstd_color) ||
- (blizzard.enabled_planes & blizzard.vid_scaled))
- return do_full_screen_update(req);
-
- return do_partial_update(req, plane, par->x, par->y,
- par->width, par->height,
- par->out_x, par->out_y,
- par->out_width, par->out_height,
- par->color_mode, par->bpp);
-}
-
-static void send_frame_complete(void *data)
-{
-}
-
-#define ADD_PREQ(_x, _y, _w, _h, _x_out, _y_out, _w_out, _h_out) do { \
- req = alloc_req(); \
- req->handler = send_frame_handler; \
- req->complete = send_frame_complete; \
- req->par.update.plane = plane_idx; \
- req->par.update.x = _x; \
- req->par.update.y = _y; \
- req->par.update.width = _w; \
- req->par.update.height = _h; \
- req->par.update.out_x = _x_out; \
- req->par.update.out_y = _y_out; \
- req->par.update.out_width = _w_out; \
- req->par.update.out_height = _h_out; \
- req->par.update.bpp = bpp; \
- req->par.update.color_mode = color_mode;\
- req->par.update.flags = flags; \
- list_add_tail(&req->entry, req_head); \
-} while(0)
-
-static void create_req_list(int plane_idx,
- struct omapfb_update_window *win,
- struct list_head *req_head)
-{
- struct blizzard_request *req;
- int x = win->x;
- int y = win->y;
- int width = win->width;
- int height = win->height;
- int x_out = win->out_x;
- int y_out = win->out_y;
- int width_out = win->out_width;
- int height_out = win->out_height;
- int color_mode;
- int bpp;
- int flags;
- unsigned int ystart = y;
- unsigned int yspan = height;
- unsigned int ystart_out = y_out;
- unsigned int yspan_out = height_out;
-
- flags = win->format & ~OMAPFB_FORMAT_MASK;
- color_mode = win->format & OMAPFB_FORMAT_MASK;
- switch (color_mode) {
- case OMAPFB_COLOR_YUV420:
- /* Embedded window with different color mode */
- bpp = 12;
- /* X, Y, height must be aligned at 2, width at 4 pixels */
- x &= ~1;
- y &= ~1;
- height = yspan = height & ~1;
- width = width & ~3;
- break;
- default:
- /* Same as the plane color mode */
- bpp = blizzard.plane[plane_idx].bpp;
- break;
- }
- if (width * height * bpp / 8 > blizzard.max_transmit_size) {
- yspan = blizzard.max_transmit_size / (width * bpp / 8);
- yspan_out = yspan * height_out / height;
- ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out,
- width_out, yspan_out);
- ystart += yspan;
- ystart_out += yspan_out;
- yspan = height - yspan;
- yspan_out = height_out - yspan_out;
- flags &= ~OMAPFB_FORMAT_FLAG_TEARSYNC;
- }
-
- ADD_PREQ(x, ystart, width, yspan, x_out, ystart_out,
- width_out, yspan_out);
-}
-
-static void auto_update_complete(void *data)
-{
- if (!blizzard.stop_auto_update)
- mod_timer(&blizzard.auto_update_timer,
- jiffies + BLIZZARD_AUTO_UPDATE_TIME);
-}
-
-static void blizzard_update_window_auto(unsigned long arg)
-{
- LIST_HEAD(req_list);
- struct blizzard_request *last;
- struct omapfb_plane_struct *plane;
-
- plane = blizzard.fbdev->fb_info[0]->par;
- create_req_list(plane->idx,
- &blizzard.auto_update_window, &req_list);
- last = list_entry(req_list.prev, struct blizzard_request, entry);
-
- last->complete = auto_update_complete;
- last->complete_data = NULL;
-
- submit_req_list(&req_list);
-}
-
-int blizzard_update_window_async(struct fb_info *fbi,
- struct omapfb_update_window *win,
- void (*complete_callback)(void *arg),
- void *complete_callback_data)
-{
- LIST_HEAD(req_list);
- struct blizzard_request *last;
- struct omapfb_plane_struct *plane = fbi->par;
-
- if (unlikely(blizzard.update_mode != OMAPFB_MANUAL_UPDATE))
- return -EINVAL;
- if (unlikely(!blizzard.te_connected &&
- (win->format & OMAPFB_FORMAT_FLAG_TEARSYNC)))
- return -EINVAL;
-
- create_req_list(plane->idx, win, &req_list);
- last = list_entry(req_list.prev, struct blizzard_request, entry);
-
- last->complete = complete_callback;
- last->complete_data = (void *)complete_callback_data;
-
- submit_req_list(&req_list);
-
- return 0;
-}
-EXPORT_SYMBOL(blizzard_update_window_async);
-
-static int update_full_screen(void)
-{
- return blizzard_update_window_async(blizzard.fbdev->fb_info[0],
- &blizzard.auto_update_window, NULL, NULL);
-
-}
-
-static int blizzard_setup_plane(int plane, int channel_out,
- unsigned long offset, int screen_width,
- int pos_x, int pos_y, int width, int height,
- int color_mode)
-{
- struct plane_info *p;
-
-#ifdef VERBOSE
- dev_dbg(blizzard.fbdev->dev,
- "plane %d ch_out %d offset %#08lx scr_width %d "
- "pos_x %d pos_y %d width %d height %d color_mode %d\n",
- plane, channel_out, offset, screen_width,
- pos_x, pos_y, width, height, color_mode);
-#endif
- if ((unsigned)plane > OMAPFB_PLANE_NUM)
- return -EINVAL;
- p = &blizzard.plane[plane];
-
- switch (color_mode) {
- case OMAPFB_COLOR_YUV422:
- case OMAPFB_COLOR_YUY422:
- p->bpp = 16;
- blizzard.vid_nonstd_color &= ~(1 << plane);
- break;
- case OMAPFB_COLOR_YUV420:
- p->bpp = 12;
- blizzard.vid_nonstd_color |= 1 << plane;
- break;
- case OMAPFB_COLOR_RGB565:
- p->bpp = 16;
- blizzard.vid_nonstd_color &= ~(1 << plane);
- break;
- default:
- return -EINVAL;
- }
-
- p->offset = offset;
- p->pos_x = pos_x;
- p->pos_y = pos_y;
- p->width = width;
- p->height = height;
- p->scr_width = screen_width;
- if (!p->out_width)
- p->out_width = width;
- if (!p->out_height)
- p->out_height = height;
-
- p->color_mode = color_mode;
-
- return 0;
-}
-
-static int blizzard_set_scale(int plane, int orig_w, int orig_h,
- int out_w, int out_h)
-{
- struct plane_info *p = &blizzard.plane[plane];
- int r;
-
- dev_dbg(blizzard.fbdev->dev,
- "plane %d orig_w %d orig_h %d out_w %d out_h %d\n",
- plane, orig_w, orig_h, out_w, out_h);
- if ((unsigned)plane > OMAPFB_PLANE_NUM)
- return -ENODEV;
-
- r = blizzard.int_ctrl->set_scale(plane, orig_w, orig_h, out_w, out_h);
- if (r < 0)
- return r;
-
- p->width = orig_w;
- p->height = orig_h;
- p->out_width = out_w;
- p->out_height = out_h;
- if (orig_w == out_w && orig_h == out_h)
- blizzard.vid_scaled &= ~(1 << plane);
- else
- blizzard.vid_scaled |= 1 << plane;
-
- return 0;
-}
-
-static int blizzard_set_rotate(int angle)
-{
- u32 l;
-
- l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION);
- l &= ~0x03;
-
- switch (angle) {
- case 0:
- l = l | 0x00;
- break;
- case 90:
- l = l | 0x03;
- break;
- case 180:
- l = l | 0x02;
- break;
- case 270:
- l = l | 0x01;
- break;
- default:
- return -EINVAL;
- }
-
- blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l);
-
- return 0;
-}
-
-static int blizzard_enable_plane(int plane, int enable)
-{
- if (enable)
- blizzard.enabled_planes |= 1 << plane;
- else
- blizzard.enabled_planes &= ~(1 << plane);
-
- return 0;
-}
-
-static int sync_handler(struct blizzard_request *req)
-{
- complete(req->par.sync);
- return REQ_COMPLETE;
-}
-
-static void blizzard_sync(void)
-{
- LIST_HEAD(req_list);
- struct blizzard_request *req;
- struct completion comp;
-
- req = alloc_req();
-
- req->handler = sync_handler;
- req->complete = NULL;
- init_completion(&comp);
- req->par.sync = &comp;
-
- list_add(&req->entry, &req_list);
- submit_req_list(&req_list);
-
- wait_for_completion(&comp);
-}
-
-
-static void blizzard_bind_client(struct omapfb_notifier_block *nb)
-{
- if (blizzard.update_mode == OMAPFB_MANUAL_UPDATE) {
- omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY);
- }
-}
-
-static int blizzard_set_update_mode(enum omapfb_update_mode mode)
-{
- if (unlikely(mode != OMAPFB_MANUAL_UPDATE &&
- mode != OMAPFB_AUTO_UPDATE &&
- mode != OMAPFB_UPDATE_DISABLED))
- return -EINVAL;
-
- if (mode == blizzard.update_mode)
- return 0;
-
- dev_info(blizzard.fbdev->dev, "s1d1374x: setting update mode to %s\n",
- mode == OMAPFB_UPDATE_DISABLED ? "disabled" :
- (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual"));
-
- switch (blizzard.update_mode) {
- case OMAPFB_MANUAL_UPDATE:
- omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_DISABLED);
- break;
- case OMAPFB_AUTO_UPDATE:
- blizzard.stop_auto_update = 1;
- del_timer_sync(&blizzard.auto_update_timer);
- break;
- case OMAPFB_UPDATE_DISABLED:
- break;
- }
-
- blizzard.update_mode = mode;
- blizzard_sync();
- blizzard.stop_auto_update = 0;
-
- switch (mode) {
- case OMAPFB_MANUAL_UPDATE:
- omapfb_notify_clients(blizzard.fbdev, OMAPFB_EVENT_READY);
- break;
- case OMAPFB_AUTO_UPDATE:
- blizzard_update_window_auto(0);
- break;
- case OMAPFB_UPDATE_DISABLED:
- break;
- }
-
- return 0;
-}
-
-static enum omapfb_update_mode blizzard_get_update_mode(void)
-{
- return blizzard.update_mode;
-}
-
-static inline void set_extif_timings(const struct extif_timings *t)
-{
- blizzard.extif->set_timings(t);
-}
-
-static inline unsigned long round_to_extif_ticks(unsigned long ps, int div)
-{
- int bus_tick = blizzard.extif_clk_period * div;
- return (ps + bus_tick - 1) / bus_tick * bus_tick;
-}
-
-static int calc_reg_timing(unsigned long sysclk, int div)
-{
- struct extif_timings *t;
- unsigned long systim;
-
- /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
- * AccessTime 2 ns + 12.2 ns (regs),
- * WEOffTime = WEOnTime + 1 ns,
- * REOffTime = REOnTime + 12 ns (regs),
- * CSOffTime = REOffTime + 1 ns
- * ReadCycle = 2ns + 2*SYSCLK (regs),
- * WriteCycle = 2*SYSCLK + 2 ns,
- * CSPulseWidth = 10 ns */
-
- systim = 1000000000 / (sysclk / 1000);
- dev_dbg(blizzard.fbdev->dev,
- "Blizzard systim %lu ps extif_clk_period %u div %d\n",
- systim, blizzard.extif_clk_period, div);
-
- t = &blizzard.reg_timings;
- memset(t, 0, sizeof(*t));
-
- t->clk_div = div;
-
- t->cs_on_time = 0;
- t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
- t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
- t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div);
- t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
- t->re_off_time = round_to_extif_ticks(t->re_on_time + 13000, div);
- t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
- t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
- if (t->we_cycle_time < t->we_off_time)
- t->we_cycle_time = t->we_off_time;
- t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
- if (t->re_cycle_time < t->re_off_time)
- t->re_cycle_time = t->re_off_time;
- t->cs_pulse_width = 0;
-
- dev_dbg(blizzard.fbdev->dev, "[reg]cson %d csoff %d reon %d reoff %d\n",
- t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
- dev_dbg(blizzard.fbdev->dev, "[reg]weon %d weoff %d recyc %d wecyc %d\n",
- t->we_on_time, t->we_off_time, t->re_cycle_time,
- t->we_cycle_time);
- dev_dbg(blizzard.fbdev->dev, "[reg]rdaccess %d cspulse %d\n",
- t->access_time, t->cs_pulse_width);
-
- return blizzard.extif->convert_timings(t);
-}
-
-static int calc_lut_timing(unsigned long sysclk, int div)
-{
- struct extif_timings *t;
- unsigned long systim;
-
- /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns,
- * AccessTime 2 ns + 4 * SYSCLK + 26 (lut),
- * WEOffTime = WEOnTime + 1 ns,
- * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut),
- * CSOffTime = REOffTime + 1 ns
- * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut),
- * WriteCycle = 2*SYSCLK + 2 ns,
- * CSPulseWidth = 10 ns */
-
- systim = 1000000000 / (sysclk / 1000);
- dev_dbg(blizzard.fbdev->dev,
- "Blizzard systim %lu ps extif_clk_period %u div %d\n",
- systim, blizzard.extif_clk_period, div);
-
- t = &blizzard.lut_timings;
- memset(t, 0, sizeof(*t));
-
- t->clk_div = div;
-
- t->cs_on_time = 0;
- t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
- t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div);
- t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
- 26000, div);
- t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div);
- t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim +
- 26000, div);
- t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div);
- t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div);
- if (t->we_cycle_time < t->we_off_time)
- t->we_cycle_time = t->we_off_time;
- t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div);
- if (t->re_cycle_time < t->re_off_time)
- t->re_cycle_time = t->re_off_time;
- t->cs_pulse_width = 0;
-
- dev_dbg(blizzard.fbdev->dev,
- "[lut]cson %d csoff %d reon %d reoff %d\n",
- t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time);
- dev_dbg(blizzard.fbdev->dev,
- "[lut]weon %d weoff %d recyc %d wecyc %d\n",
- t->we_on_time, t->we_off_time, t->re_cycle_time,
- t->we_cycle_time);
- dev_dbg(blizzard.fbdev->dev, "[lut]rdaccess %d cspulse %d\n",
- t->access_time, t->cs_pulse_width);
-
- return blizzard.extif->convert_timings(t);
-}
-
-static int calc_extif_timings(unsigned long sysclk, int *extif_mem_div)
-{
- int max_clk_div;
- int div;
-
- blizzard.extif->get_clk_info(&blizzard.extif_clk_period, &max_clk_div);
- for (div = 1; div <= max_clk_div; div++) {
- if (calc_reg_timing(sysclk, div) == 0)
- break;
- }
- if (div > max_clk_div) {
- dev_dbg(blizzard.fbdev->dev, "reg timing failed\n");
- goto err;
- }
- *extif_mem_div = div;
-
- for (div = 1; div <= max_clk_div; div++) {
- if (calc_lut_timing(sysclk, div) == 0)
- break;
- }
-
- if (div > max_clk_div)
- goto err;
-
- blizzard.extif_clk_div = div;
-
- return 0;
-err:
- dev_err(blizzard.fbdev->dev, "can't setup timings\n");
- return -1;
-}
-
-static void calc_blizzard_clk_rates(unsigned long ext_clk,
- unsigned long *sys_clk, unsigned long *pix_clk)
-{
- int pix_clk_src;
- int sys_div = 0, sys_mul = 0;
- int pix_div;
-
- pix_clk_src = blizzard_read_reg(BLIZZARD_CLK_SRC);
- pix_div = ((pix_clk_src >> 3) & 0x1f) + 1;
- if ((pix_clk_src & (0x3 << 1)) == 0) {
- /* Source is the PLL */
- sys_div = (blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x3f) + 1;
- sys_mul = blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_0);
- sys_mul |= ((blizzard_read_reg(BLIZZARD_PLL_CLOCK_SYNTH_1)
- & 0x0f) << 11);
- *sys_clk = ext_clk * sys_mul / sys_div;
- } else /* else source is ext clk, or oscillator */
- *sys_clk = ext_clk;
-
- *pix_clk = *sys_clk / pix_div; /* HZ */
- dev_dbg(blizzard.fbdev->dev,
- "ext_clk %ld pix_src %d pix_div %d sys_div %d sys_mul %d\n",
- ext_clk, pix_clk_src & (0x3 << 1), pix_div, sys_div, sys_mul);
- dev_dbg(blizzard.fbdev->dev, "sys_clk %ld pix_clk %ld\n",
- *sys_clk, *pix_clk);
-}
-
-static int setup_tearsync(unsigned long pix_clk, int extif_div)
-{
- int hdisp, vdisp;
- int hndp, vndp;
- int hsw, vsw;
- int hs, vs;
- int hs_pol_inv, vs_pol_inv;
- int use_hsvs, use_ndp;
- u8 b;
-
- hsw = blizzard_read_reg(BLIZZARD_HSW);
- vsw = blizzard_read_reg(BLIZZARD_VSW);
- hs_pol_inv = !(hsw & 0x80);
- vs_pol_inv = !(vsw & 0x80);
- hsw = hsw & 0x7f;
- vsw = vsw & 0x3f;
-
- hdisp = blizzard_read_reg(BLIZZARD_HDISP) * 8;
- vdisp = blizzard_read_reg(BLIZZARD_VDISP0) +
- ((blizzard_read_reg(BLIZZARD_VDISP1) & 0x3) << 8);
-
- hndp = blizzard_read_reg(BLIZZARD_HNDP) & 0x3f;
- vndp = blizzard_read_reg(BLIZZARD_VNDP);
-
- /* time to transfer one pixel (16bpp) in ps */
- blizzard.pix_tx_time = blizzard.reg_timings.we_cycle_time;
- if (blizzard.extif->get_max_tx_rate != NULL) {
- /* The external interface might have a rate limitation,
- * if so, we have to maximize our transfer rate.
- */
- unsigned long min_tx_time;
- unsigned long max_tx_rate = blizzard.extif->get_max_tx_rate();
-
- dev_dbg(blizzard.fbdev->dev, "max_tx_rate %ld HZ\n",
- max_tx_rate);
- min_tx_time = 1000000000 / (max_tx_rate / 1000); /* ps */
- if (blizzard.pix_tx_time < min_tx_time)
- blizzard.pix_tx_time = min_tx_time;
- }
-
- /* time to update one line in ps */
- blizzard.line_upd_time = (hdisp + hndp) * 1000000 / (pix_clk / 1000);
- blizzard.line_upd_time *= 1000;
- if (hdisp * blizzard.pix_tx_time > blizzard.line_upd_time)
- /* transfer speed too low, we might have to use both
- * HS and VS */
- use_hsvs = 1;
- else
- /* decent transfer speed, we'll always use only VS */
- use_hsvs = 0;
-
- if (use_hsvs && (hs_pol_inv || vs_pol_inv)) {
- /* HS or'ed with VS doesn't work, use the active high
- * TE signal based on HNDP / VNDP */
- use_ndp = 1;
- hs_pol_inv = 0;
- vs_pol_inv = 0;
- hs = hndp;
- vs = vndp;
- } else {
- /* Use HS or'ed with VS as a TE signal if both are needed
- * or VNDP if only vsync is needed. */
- use_ndp = 0;
- hs = hsw;
- vs = vsw;
- if (!use_hsvs) {
- hs_pol_inv = 0;
- vs_pol_inv = 0;
- }
- }
-
- hs = hs * 1000000 / (pix_clk / 1000); /* ps */
- hs *= 1000;
-
- vs = vs * (hdisp + hndp) * 1000000 / (pix_clk / 1000); /* ps */
- vs *= 1000;
-
- if (vs <= hs)
- return -EDOM;
- /* set VS to 120% of HS to minimize VS detection time */
- vs = hs * 12 / 10;
- /* minimize HS too */
- if (hs > 10000)
- hs = 10000;
-
- b = blizzard_read_reg(BLIZZARD_NDISP_CTRL_STATUS);
- b &= ~0x3;
- b |= use_hsvs ? 1 : 0;
- b |= (use_ndp && use_hsvs) ? 0 : 2;
- blizzard_write_reg(BLIZZARD_NDISP_CTRL_STATUS, b);
-
- blizzard.vsync_only = !use_hsvs;
-
- dev_dbg(blizzard.fbdev->dev,
- "pix_clk %ld HZ pix_tx_time %ld ps line_upd_time %ld ps\n",
- pix_clk, blizzard.pix_tx_time, blizzard.line_upd_time);
- dev_dbg(blizzard.fbdev->dev,
- "hs %d ps vs %d ps mode %d vsync_only %d\n",
- hs, vs, b & 0x3, !use_hsvs);
-
- return blizzard.extif->setup_tearsync(1, hs, vs,
- hs_pol_inv, vs_pol_inv,
- extif_div);
-}
-
-static void blizzard_get_caps(int plane, struct omapfb_caps *caps)
-{
- blizzard.int_ctrl->get_caps(plane, caps);
- caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE |
- OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE |
- OMAPFB_CAPS_WINDOW_SCALE |
- OMAPFB_CAPS_WINDOW_OVERLAY |
- OMAPFB_CAPS_WINDOW_ROTATE;
- if (blizzard.te_connected)
- caps->ctrl |= OMAPFB_CAPS_TEARSYNC;
- caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) |
- (1 << OMAPFB_COLOR_YUV420);
-}
-
-static void _save_regs(const struct blizzard_reg_list *list, int cnt)
-{
- int i;
-
- for (i = 0; i < cnt; i++, list++) {
- int reg;
- for (reg = list->start; reg <= list->end; reg += 2)
- blizzard_reg_cache[reg / 2] = blizzard_read_reg(reg);
- }
-}
-
-static void _restore_regs(const struct blizzard_reg_list *list, int cnt)
-{
- int i;
-
- for (i = 0; i < cnt; i++, list++) {
- int reg;
- for (reg = list->start; reg <= list->end; reg += 2)
- blizzard_write_reg(reg, blizzard_reg_cache[reg / 2]);
- }
-}
-
-static void blizzard_save_all_regs(void)
-{
- _save_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs));
- _save_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs));
-}
-
-static void blizzard_restore_pll_regs(void)
-{
- _restore_regs(blizzard_pll_regs, ARRAY_SIZE(blizzard_pll_regs));
-}
-
-static void blizzard_restore_gen_regs(void)
-{
- _restore_regs(blizzard_gen_regs, ARRAY_SIZE(blizzard_gen_regs));
-}
-
-static void blizzard_suspend(void)
-{
- u32 l;
- unsigned long tmo;
-
- if (blizzard.last_color_mode) {
- update_full_screen();
- blizzard_sync();
- }
- blizzard.update_mode_before_suspend = blizzard.update_mode;
- /* the following will disable clocks as well */
- blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED);
-
- blizzard_save_all_regs();
-
- blizzard_stop_sdram();
-
- l = blizzard_read_reg(BLIZZARD_POWER_SAVE);
- /* Standby, Sleep. We assume we use an external clock. */
- l |= 0x03;
- blizzard_write_reg(BLIZZARD_POWER_SAVE, l);
-
- tmo = jiffies + msecs_to_jiffies(100);
- while (!(blizzard_read_reg(BLIZZARD_PLL_MODE) & (1 << 1))) {
- if (time_after(jiffies, tmo)) {
- dev_err(blizzard.fbdev->dev,
- "s1d1374x: sleep timeout, stopping PLL manually\n");
- l = blizzard_read_reg(BLIZZARD_PLL_MODE);
- l &= ~0x03;
- /* Disable PLL, counter function */
- l |= 0x2;
- blizzard_write_reg(BLIZZARD_PLL_MODE, l);
- break;
- }
- msleep(1);
- }
-
- if (blizzard.power_down != NULL)
- blizzard.power_down(blizzard.fbdev->dev);
-}
-
-static void blizzard_resume(void)
-{
- u32 l;
-
- if (blizzard.power_up != NULL)
- blizzard.power_up(blizzard.fbdev->dev);
-
- l = blizzard_read_reg(BLIZZARD_POWER_SAVE);
- /* Standby, Sleep */
- l &= ~0x03;
- blizzard_write_reg(BLIZZARD_POWER_SAVE, l);
-
- blizzard_restore_pll_regs();
- l = blizzard_read_reg(BLIZZARD_PLL_MODE);
- l &= ~0x03;
- /* Enable PLL, counter function */
- l |= 0x1;
- blizzard_write_reg(BLIZZARD_PLL_MODE, l);
-
- while (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & (1 << 7)))
- msleep(1);
-
- blizzard_restart_sdram();
-
- blizzard_restore_gen_regs();
-
- /* Enable display */
- blizzard_write_reg(BLIZZARD_DISPLAY_MODE, 0x01);
-
- /* the following will enable clocks as necessary */
- blizzard_set_update_mode(blizzard.update_mode_before_suspend);
-
- /* Force a background update */
- blizzard.zoom_on = 1;
- update_full_screen();
- blizzard_sync();
-}
-
-static int blizzard_init(struct omapfb_device *fbdev, int ext_mode,
- struct omapfb_mem_desc *req_vram)
-{
- int r = 0, i;
- u8 rev, conf;
- unsigned long ext_clk;
- int extif_div;
- unsigned long sys_clk, pix_clk;
- struct omapfb_platform_data *omapfb_conf;
- struct blizzard_platform_data *ctrl_conf;
-
- blizzard.fbdev = fbdev;
-
- BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl);
-
- blizzard.fbdev = fbdev;
- blizzard.extif = fbdev->ext_if;
- blizzard.int_ctrl = fbdev->int_ctrl;
-
- omapfb_conf = fbdev->dev->platform_data;
- ctrl_conf = omapfb_conf->ctrl_platform_data;
- if (ctrl_conf == NULL || ctrl_conf->get_clock_rate == NULL) {
- dev_err(fbdev->dev, "s1d1374x: missing platform data\n");
- r = -ENOENT;
- goto err1;
- }
-
- blizzard.power_down = ctrl_conf->power_down;
- blizzard.power_up = ctrl_conf->power_up;
-
- spin_lock_init(&blizzard.req_lock);
-
- if ((r = blizzard.int_ctrl->init(fbdev, 1, req_vram)) < 0)
- goto err1;
-
- if ((r = blizzard.extif->init(fbdev)) < 0)
- goto err2;
-
- blizzard_ctrl.set_color_key = blizzard.int_ctrl->set_color_key;
- blizzard_ctrl.get_color_key = blizzard.int_ctrl->get_color_key;
- blizzard_ctrl.setup_mem = blizzard.int_ctrl->setup_mem;
- blizzard_ctrl.mmap = blizzard.int_ctrl->mmap;
-
- ext_clk = ctrl_conf->get_clock_rate(fbdev->dev);
- if ((r = calc_extif_timings(ext_clk, &extif_div)) < 0)
- goto err3;
-
- set_extif_timings(&blizzard.reg_timings);
-
- if (blizzard.power_up != NULL)
- blizzard.power_up(fbdev->dev);
-
- calc_blizzard_clk_rates(ext_clk, &sys_clk, &pix_clk);
-
- if ((r = calc_extif_timings(sys_clk, &extif_div)) < 0)
- goto err3;
- set_extif_timings(&blizzard.reg_timings);
-
- if (!(blizzard_read_reg(BLIZZARD_PLL_DIV) & 0x80)) {
- dev_err(fbdev->dev,
- "controller not initialized by the bootloader\n");
- r = -ENODEV;
- goto err3;
- }
-
- if (ctrl_conf->te_connected) {
- if ((r = setup_tearsync(pix_clk, extif_div)) < 0)
- goto err3;
- blizzard.te_connected = 1;
- }
-
- rev = blizzard_read_reg(BLIZZARD_REV_CODE);
- conf = blizzard_read_reg(BLIZZARD_CONFIG);
-
- switch (rev & 0xfc) {
- case 0x9c:
- blizzard.version = BLIZZARD_VERSION_S1D13744;
- pr_info("omapfb: s1d13744 LCD controller rev %d "
- "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
- break;
- case 0xa4:
- blizzard.version = BLIZZARD_VERSION_S1D13745;
- pr_info("omapfb: s1d13745 LCD controller rev %d "
- "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07);
- break;
- default:
- dev_err(fbdev->dev, "invalid s1d1374x revision %02x\n",
- rev);
- r = -ENODEV;
- goto err3;
- }
-
- blizzard.max_transmit_size = blizzard.extif->max_transmit_size;
-
- blizzard.update_mode = OMAPFB_UPDATE_DISABLED;
-
- blizzard.auto_update_window.x = 0;
- blizzard.auto_update_window.y = 0;
- blizzard.auto_update_window.width = fbdev->panel->x_res;
- blizzard.auto_update_window.height = fbdev->panel->y_res;
- blizzard.auto_update_window.out_x = 0;
- blizzard.auto_update_window.out_y = 0;
- blizzard.auto_update_window.out_width = fbdev->panel->x_res;
- blizzard.auto_update_window.out_height = fbdev->panel->y_res;
- blizzard.auto_update_window.format = 0;
-
- blizzard.screen_width = fbdev->panel->x_res;
- blizzard.screen_height = fbdev->panel->y_res;
-
- init_timer(&blizzard.auto_update_timer);
- blizzard.auto_update_timer.function = blizzard_update_window_auto;
- blizzard.auto_update_timer.data = 0;
-
- INIT_LIST_HEAD(&blizzard.free_req_list);
- INIT_LIST_HEAD(&blizzard.pending_req_list);
- for (i = 0; i < ARRAY_SIZE(blizzard.req_pool); i++)
- list_add(&blizzard.req_pool[i].entry, &blizzard.free_req_list);
- BUG_ON(i <= IRQ_REQ_POOL_SIZE);
- sema_init(&blizzard.req_sema, i - IRQ_REQ_POOL_SIZE);
-
- return 0;
-err3:
- if (blizzard.power_down != NULL)
- blizzard.power_down(fbdev->dev);
- blizzard.extif->cleanup();
-err2:
- blizzard.int_ctrl->cleanup();
-err1:
- return r;
-}
-
-static void blizzard_cleanup(void)
-{
- blizzard_set_update_mode(OMAPFB_UPDATE_DISABLED);
- blizzard.extif->cleanup();
- blizzard.int_ctrl->cleanup();
- if (blizzard.power_down != NULL)
- blizzard.power_down(blizzard.fbdev->dev);
-}
-
-struct lcd_ctrl blizzard_ctrl = {
- .name = "blizzard",
- .init = blizzard_init,
- .cleanup = blizzard_cleanup,
- .bind_client = blizzard_bind_client,
- .get_caps = blizzard_get_caps,
- .set_update_mode = blizzard_set_update_mode,
- .get_update_mode = blizzard_get_update_mode,
- .setup_plane = blizzard_setup_plane,
- .set_scale = blizzard_set_scale,
- .enable_plane = blizzard_enable_plane,
- .set_rotate = blizzard_set_rotate,
- .update_window = blizzard_update_window_async,
- .sync = blizzard_sync,
- .suspend = blizzard_suspend,
- .resume = blizzard_resume,
-};
-
diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c
deleted file mode 100644
index 6f61e781f15a..000000000000
--- a/drivers/video/omap/dispc.c
+++ /dev/null
@@ -1,1547 +0,0 @@
-/*
- * OMAP2 display controller support
- *
- * Copyright (C) 2005 Nokia Corporation
- * Author: Imre Deak <imre.deak@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/dma-mapping.h>
-#include <linux/mm.h>
-#include <linux/vmalloc.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-
-#include <plat/sram.h>
-#include <plat/board.h>
-
-#include "omapfb.h"
-#include "dispc.h"
-
-#define MODULE_NAME "dispc"
-
-#define DSS_BASE 0x48050000
-#define DSS_SYSCONFIG 0x0010
-
-#define DISPC_BASE 0x48050400
-
-/* DISPC common */
-#define DISPC_REVISION 0x0000
-#define DISPC_SYSCONFIG 0x0010
-#define DISPC_SYSSTATUS 0x0014
-#define DISPC_IRQSTATUS 0x0018
-#define DISPC_IRQENABLE 0x001C
-#define DISPC_CONTROL 0x0040
-#define DISPC_CONFIG 0x0044
-#define DISPC_CAPABLE 0x0048
-#define DISPC_DEFAULT_COLOR0 0x004C
-#define DISPC_DEFAULT_COLOR1 0x0050
-#define DISPC_TRANS_COLOR0 0x0054
-#define DISPC_TRANS_COLOR1 0x0058
-#define DISPC_LINE_STATUS 0x005C
-#define DISPC_LINE_NUMBER 0x0060
-#define DISPC_TIMING_H 0x0064
-#define DISPC_TIMING_V 0x0068
-#define DISPC_POL_FREQ 0x006C
-#define DISPC_DIVISOR 0x0070
-#define DISPC_SIZE_DIG 0x0078
-#define DISPC_SIZE_LCD 0x007C
-
-#define DISPC_DATA_CYCLE1 0x01D4
-#define DISPC_DATA_CYCLE2 0x01D8
-#define DISPC_DATA_CYCLE3 0x01DC
-
-/* DISPC GFX plane */
-#define DISPC_GFX_BA0 0x0080
-#define DISPC_GFX_BA1 0x0084
-#define DISPC_GFX_POSITION 0x0088
-#define DISPC_GFX_SIZE 0x008C
-#define DISPC_GFX_ATTRIBUTES 0x00A0
-#define DISPC_GFX_FIFO_THRESHOLD 0x00A4
-#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8
-#define DISPC_GFX_ROW_INC 0x00AC
-#define DISPC_GFX_PIXEL_INC 0x00B0
-#define DISPC_GFX_WINDOW_SKIP 0x00B4
-#define DISPC_GFX_TABLE_BA 0x00B8
-
-/* DISPC Video plane 1/2 */
-#define DISPC_VID1_BASE 0x00BC
-#define DISPC_VID2_BASE 0x014C
-
-/* Offsets into DISPC_VID1/2_BASE */
-#define DISPC_VID_BA0 0x0000
-#define DISPC_VID_BA1 0x0004
-#define DISPC_VID_POSITION 0x0008
-#define DISPC_VID_SIZE 0x000C
-#define DISPC_VID_ATTRIBUTES 0x0010
-#define DISPC_VID_FIFO_THRESHOLD 0x0014
-#define DISPC_VID_FIFO_SIZE_STATUS 0x0018
-#define DISPC_VID_ROW_INC 0x001C
-#define DISPC_VID_PIXEL_INC 0x0020
-#define DISPC_VID_FIR 0x0024
-#define DISPC_VID_PICTURE_SIZE 0x0028
-#define DISPC_VID_ACCU0 0x002C
-#define DISPC_VID_ACCU1 0x0030
-
-/* 8 elements in 8 byte increments */
-#define DISPC_VID_FIR_COEF_H0 0x0034
-/* 8 elements in 8 byte increments */
-#define DISPC_VID_FIR_COEF_HV0 0x0038
-/* 5 elements in 4 byte increments */
-#define DISPC_VID_CONV_COEF0 0x0074
-
-#define DISPC_IRQ_FRAMEMASK 0x0001
-#define DISPC_IRQ_VSYNC 0x0002
-#define DISPC_IRQ_EVSYNC_EVEN 0x0004
-#define DISPC_IRQ_EVSYNC_ODD 0x0008
-#define DISPC_IRQ_ACBIAS_COUNT_STAT 0x0010
-#define DISPC_IRQ_PROG_LINE_NUM 0x0020
-#define DISPC_IRQ_GFX_FIFO_UNDERFLOW 0x0040
-#define DISPC_IRQ_GFX_END_WIN 0x0080
-#define DISPC_IRQ_PAL_GAMMA_MASK 0x0100
-#define DISPC_IRQ_OCP_ERR 0x0200
-#define DISPC_IRQ_VID1_FIFO_UNDERFLOW 0x0400
-#define DISPC_IRQ_VID1_END_WIN 0x0800
-#define DISPC_IRQ_VID2_FIFO_UNDERFLOW 0x1000
-#define DISPC_IRQ_VID2_END_WIN 0x2000
-#define DISPC_IRQ_SYNC_LOST 0x4000
-
-#define DISPC_IRQ_MASK_ALL 0x7fff
-
-#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
- DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
- DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
- DISPC_IRQ_SYNC_LOST)
-
-#define RFBI_CONTROL 0x48050040
-
-#define MAX_PALETTE_SIZE (256 * 16)
-
-#define FLD_MASK(pos, len) (((1 << len) - 1) << pos)
-
-#define MOD_REG_FLD(reg, mask, val) \
- dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val));
-
-#define OMAP2_SRAM_START 0x40200000
-/* Maximum size, in reality this is smaller if SRAM is partially locked. */
-#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */
-
-/* We support the SDRAM / SRAM types. See OMAPFB_PLANE_MEMTYPE_* in omapfb.h */
-#define DISPC_MEMTYPE_NUM 2
-
-#define RESMAP_SIZE(_page_cnt) \
- ((_page_cnt + (sizeof(unsigned long) * 8) - 1) / 8)
-#define RESMAP_PTR(_res_map, _page_nr) \
- (((_res_map)->map) + (_page_nr) / (sizeof(unsigned long) * 8))
-#define RESMAP_MASK(_page_nr) \
- (1 << ((_page_nr) & (sizeof(unsigned long) * 8 - 1)))
-
-struct resmap {
- unsigned long start;
- unsigned page_cnt;
- unsigned long *map;
-};
-
-#define MAX_IRQ_HANDLERS 4
-
-static struct {
- void __iomem *base;
-
- struct omapfb_mem_desc mem_desc;
- struct resmap *res_map[DISPC_MEMTYPE_NUM];
- atomic_t map_count[OMAPFB_PLANE_NUM];
-
- dma_addr_t palette_paddr;
- void *palette_vaddr;
-
- int ext_mode;
-
- struct {
- u32 irq_mask;
- void (*callback)(void *);
- void *data;
- } irq_handlers[MAX_IRQ_HANDLERS];
- struct completion frame_done;
-
- int fir_hinc[OMAPFB_PLANE_NUM];
- int fir_vinc[OMAPFB_PLANE_NUM];
-
- struct clk *dss_ick, *dss1_fck;
- struct clk *dss_54m_fck;
-
- enum omapfb_update_mode update_mode;
- struct omapfb_device *fbdev;
-
- struct omapfb_color_key color_key;
-} dispc;
-
-static void enable_lcd_clocks(int enable);
-
-static void inline dispc_write_reg(int idx, u32 val)
-{
- __raw_writel(val, dispc.base + idx);
-}
-
-static u32 inline dispc_read_reg(int idx)
-{
- u32 l = __raw_readl(dispc.base + idx);
- return l;
-}
-
-/* Select RFBI or bypass mode */
-static void enable_rfbi_mode(int enable)
-{
- void __iomem *rfbi_control;
- u32 l;
-
- l = dispc_read_reg(DISPC_CONTROL);
- /* Enable RFBI, GPIO0/1 */
- l &= ~((1 << 11) | (1 << 15) | (1 << 16));
- l |= enable ? (1 << 11) : 0;
- /* RFBI En: GPIO0/1=10 RFBI Dis: GPIO0/1=11 */
- l |= 1 << 15;
- l |= enable ? 0 : (1 << 16);
- dispc_write_reg(DISPC_CONTROL, l);
-
- /* Set bypass mode in RFBI module */
- rfbi_control = ioremap(RFBI_CONTROL, SZ_1K);
- if (!rfbi_control) {
- pr_err("Unable to ioremap rfbi_control\n");
- return;
- }
- l = __raw_readl(rfbi_control);
- l |= enable ? 0 : (1 << 1);
- __raw_writel(l, rfbi_control);
- iounmap(rfbi_control);
-}
-
-static void set_lcd_data_lines(int data_lines)
-{
- u32 l;
- int code = 0;
-
- switch (data_lines) {
- case 12:
- code = 0;
- break;
- case 16:
- code = 1;
- break;
- case 18:
- code = 2;
- break;
- case 24:
- code = 3;
- break;
- default:
- BUG();
- }
-
- l = dispc_read_reg(DISPC_CONTROL);
- l &= ~(0x03 << 8);
- l |= code << 8;
- dispc_write_reg(DISPC_CONTROL, l);
-}
-
-static void set_load_mode(int mode)
-{
- BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY |
- DISPC_LOAD_CLUT_ONCE_FRAME));
- MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1);
-}
-
-void omap_dispc_set_lcd_size(int x, int y)
-{
- BUG_ON((x > (1 << 11)) || (y > (1 << 11)));
- enable_lcd_clocks(1);
- MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11),
- ((y - 1) << 16) | (x - 1));
- enable_lcd_clocks(0);
-}
-EXPORT_SYMBOL(omap_dispc_set_lcd_size);
-
-void omap_dispc_set_digit_size(int x, int y)
-{
- BUG_ON((x > (1 << 11)) || (y > (1 << 11)));
- enable_lcd_clocks(1);
- MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11),
- ((y - 1) << 16) | (x - 1));
- enable_lcd_clocks(0);
-}
-EXPORT_SYMBOL(omap_dispc_set_digit_size);
-
-static void setup_plane_fifo(int plane, int ext_mode)
-{
- const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD,
- DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD,
- DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD };
- const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS,
- DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS,
- DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS };
- int low, high;
- u32 l;
-
- BUG_ON(plane > 2);
-
- l = dispc_read_reg(fsz_reg[plane]);
- l &= FLD_MASK(0, 11);
- if (ext_mode) {
- low = l * 3 / 4;
- high = l;
- } else {
- low = l / 4;
- high = l * 3 / 4;
- }
- MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12),
- (high << 16) | low);
-}
-
-void omap_dispc_enable_lcd_out(int enable)
-{
- enable_lcd_clocks(1);
- MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0);
- enable_lcd_clocks(0);
-}
-EXPORT_SYMBOL(omap_dispc_enable_lcd_out);
-
-void omap_dispc_enable_digit_out(int enable)
-{
- enable_lcd_clocks(1);
- MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0);
- enable_lcd_clocks(0);
-}
-EXPORT_SYMBOL(omap_dispc_enable_digit_out);
-
-static inline int _setup_plane(int plane, int channel_out,
- u32 paddr, int screen_width,
- int pos_x, int pos_y, int width, int height,
- int color_mode)
-{
- const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
- DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
- DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
- const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0,
- DISPC_VID2_BASE + DISPC_VID_BA0 };
- const u32 ps_reg[] = { DISPC_GFX_POSITION,
- DISPC_VID1_BASE + DISPC_VID_POSITION,
- DISPC_VID2_BASE + DISPC_VID_POSITION };
- const u32 sz_reg[] = { DISPC_GFX_SIZE,
- DISPC_VID1_BASE + DISPC_VID_PICTURE_SIZE,
- DISPC_VID2_BASE + DISPC_VID_PICTURE_SIZE };
- const u32 ri_reg[] = { DISPC_GFX_ROW_INC,
- DISPC_VID1_BASE + DISPC_VID_ROW_INC,
- DISPC_VID2_BASE + DISPC_VID_ROW_INC };
- const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE,
- DISPC_VID2_BASE + DISPC_VID_SIZE };
-
- int chout_shift, burst_shift;
- int chout_val;
- int color_code;
- int bpp;
- int cconv_en;
- int set_vsize;
- u32 l;
-
-#ifdef VERBOSE
- dev_dbg(dispc.fbdev->dev, "plane %d channel %d paddr %#08x scr_width %d"
- " pos_x %d pos_y %d width %d height %d color_mode %d\n",
- plane, channel_out, paddr, screen_width, pos_x, pos_y,
- width, height, color_mode);
-#endif
-
- set_vsize = 0;
- switch (plane) {
- case OMAPFB_PLANE_GFX:
- burst_shift = 6;
- chout_shift = 8;
- break;
- case OMAPFB_PLANE_VID1:
- case OMAPFB_PLANE_VID2:
- burst_shift = 14;
- chout_shift = 16;
- set_vsize = 1;
- break;
- default:
- return -EINVAL;
- }
-
- switch (channel_out) {
- case OMAPFB_CHANNEL_OUT_LCD:
- chout_val = 0;
- break;
- case OMAPFB_CHANNEL_OUT_DIGIT:
- chout_val = 1;
- break;
- default:
- return -EINVAL;
- }
-
- cconv_en = 0;
- switch (color_mode) {
- case OMAPFB_COLOR_RGB565:
- color_code = DISPC_RGB_16_BPP;
- bpp = 16;
- break;
- case OMAPFB_COLOR_YUV422:
- if (plane == 0)
- return -EINVAL;
- color_code = DISPC_UYVY_422;
- cconv_en = 1;
- bpp = 16;
- break;
- case OMAPFB_COLOR_YUY422:
- if (plane == 0)
- return -EINVAL;
- color_code = DISPC_YUV2_422;
- cconv_en = 1;
- bpp = 16;
- break;
- default:
- return -EINVAL;
- }
-
- l = dispc_read_reg(at_reg[plane]);
-
- l &= ~(0x0f << 1);
- l |= color_code << 1;
- l &= ~(1 << 9);
- l |= cconv_en << 9;
-
- l &= ~(0x03 << burst_shift);
- l |= DISPC_BURST_8x32 << burst_shift;
-
- l &= ~(1 << chout_shift);
- l |= chout_val << chout_shift;
-
- dispc_write_reg(at_reg[plane], l);
-
- dispc_write_reg(ba_reg[plane], paddr);
- MOD_REG_FLD(ps_reg[plane],
- FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x);
-
- MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11),
- ((height - 1) << 16) | (width - 1));
-
- if (set_vsize) {
- /* Set video size if set_scale hasn't set it */
- if (!dispc.fir_vinc[plane])
- MOD_REG_FLD(vs_reg[plane],
- FLD_MASK(16, 11), (height - 1) << 16);
- if (!dispc.fir_hinc[plane])
- MOD_REG_FLD(vs_reg[plane],
- FLD_MASK(0, 11), width - 1);
- }
-
- dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1);
-
- return height * screen_width * bpp / 8;
-}
-
-static int omap_dispc_setup_plane(int plane, int channel_out,
- unsigned long offset,
- int screen_width,
- int pos_x, int pos_y, int width, int height,
- int color_mode)
-{
- u32 paddr;
- int r;
-
- if ((unsigned)plane > dispc.mem_desc.region_cnt)
- return -EINVAL;
- paddr = dispc.mem_desc.region[plane].paddr + offset;
- enable_lcd_clocks(1);
- r = _setup_plane(plane, channel_out, paddr,
- screen_width,
- pos_x, pos_y, width, height, color_mode);
- enable_lcd_clocks(0);
- return r;
-}
-
-static void write_firh_reg(int plane, int reg, u32 value)
-{
- u32 base;
-
- if (plane == 1)
- base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_H0;
- else
- base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_H0;
- dispc_write_reg(base + reg * 8, value);
-}
-
-static void write_firhv_reg(int plane, int reg, u32 value)
-{
- u32 base;
-
- if (plane == 1)
- base = DISPC_VID1_BASE + DISPC_VID_FIR_COEF_HV0;
- else
- base = DISPC_VID2_BASE + DISPC_VID_FIR_COEF_HV0;
- dispc_write_reg(base + reg * 8, value);
-}
-
-static void set_upsampling_coef_table(int plane)
-{
- const u32 coef[][2] = {
- { 0x00800000, 0x00800000 },
- { 0x0D7CF800, 0x037B02FF },
- { 0x1E70F5FF, 0x0C6F05FE },
- { 0x335FF5FE, 0x205907FB },
- { 0xF74949F7, 0x00404000 },
- { 0xF55F33FB, 0x075920FE },
- { 0xF5701EFE, 0x056F0CFF },
- { 0xF87C0DFF, 0x027B0300 },
- };
- int i;
-
- for (i = 0; i < 8; i++) {
- write_firh_reg(plane, i, coef[i][0]);
- write_firhv_reg(plane, i, coef[i][1]);
- }
-}
-
-static int omap_dispc_set_scale(int plane,
- int orig_width, int orig_height,
- int out_width, int out_height)
-{
- const u32 at_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
- DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
- const u32 vs_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_SIZE,
- DISPC_VID2_BASE + DISPC_VID_SIZE };
- const u32 fir_reg[] = { 0, DISPC_VID1_BASE + DISPC_VID_FIR,
- DISPC_VID2_BASE + DISPC_VID_FIR };
-
- u32 l;
- int fir_hinc;
- int fir_vinc;
-
- if ((unsigned)plane > OMAPFB_PLANE_NUM)
- return -ENODEV;
-
- if (plane == OMAPFB_PLANE_GFX &&
- (out_width != orig_width || out_height != orig_height))
- return -EINVAL;
-
- enable_lcd_clocks(1);
- if (orig_width < out_width) {
- /*
- * Upsampling.
- * Currently you can only scale both dimensions in one way.
- */
- if (orig_height > out_height ||
- orig_width * 8 < out_width ||
- orig_height * 8 < out_height) {
- enable_lcd_clocks(0);
- return -EINVAL;
- }
- set_upsampling_coef_table(plane);
- } else if (orig_width > out_width) {
- /* Downsampling not yet supported
- */
-
- enable_lcd_clocks(0);
- return -EINVAL;
- }
- if (!orig_width || orig_width == out_width)
- fir_hinc = 0;
- else
- fir_hinc = 1024 * orig_width / out_width;
- if (!orig_height || orig_height == out_height)
- fir_vinc = 0;
- else
- fir_vinc = 1024 * orig_height / out_height;
- dispc.fir_hinc[plane] = fir_hinc;
- dispc.fir_vinc[plane] = fir_vinc;
-
- MOD_REG_FLD(fir_reg[plane],
- FLD_MASK(16, 12) | FLD_MASK(0, 12),
- ((fir_vinc & 4095) << 16) |
- (fir_hinc & 4095));
-
- dev_dbg(dispc.fbdev->dev, "out_width %d out_height %d orig_width %d "
- "orig_height %d fir_hinc %d fir_vinc %d\n",
- out_width, out_height, orig_width, orig_height,
- fir_hinc, fir_vinc);
-
- MOD_REG_FLD(vs_reg[plane],
- FLD_MASK(16, 11) | FLD_MASK(0, 11),
- ((out_height - 1) << 16) | (out_width - 1));
-
- l = dispc_read_reg(at_reg[plane]);
- l &= ~(0x03 << 5);
- l |= fir_hinc ? (1 << 5) : 0;
- l |= fir_vinc ? (1 << 6) : 0;
- dispc_write_reg(at_reg[plane], l);
-
- enable_lcd_clocks(0);
- return 0;
-}
-
-static int omap_dispc_enable_plane(int plane, int enable)
-{
- const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES,
- DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES,
- DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES };
- if ((unsigned int)plane > dispc.mem_desc.region_cnt)
- return -EINVAL;
-
- enable_lcd_clocks(1);
- MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0);
- enable_lcd_clocks(0);
-
- return 0;
-}
-
-static int omap_dispc_set_color_key(struct omapfb_color_key *ck)
-{
- u32 df_reg, tr_reg;
- int shift, val;
-
- switch (ck->channel_out) {
- case OMAPFB_CHANNEL_OUT_LCD:
- df_reg = DISPC_DEFAULT_COLOR0;
- tr_reg = DISPC_TRANS_COLOR0;
- shift = 10;
- break;
- case OMAPFB_CHANNEL_OUT_DIGIT:
- df_reg = DISPC_DEFAULT_COLOR1;
- tr_reg = DISPC_TRANS_COLOR1;
- shift = 12;
- break;
- default:
- return -EINVAL;
- }
- switch (ck->key_type) {
- case OMAPFB_COLOR_KEY_DISABLED:
- val = 0;
- break;
- case OMAPFB_COLOR_KEY_GFX_DST:
- val = 1;
- break;
- case OMAPFB_COLOR_KEY_VID_SRC:
- val = 3;
- break;
- default:
- return -EINVAL;
- }
- enable_lcd_clocks(1);
- MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift);
-
- if (val != 0)
- dispc_write_reg(tr_reg, ck->trans_key);
- dispc_write_reg(df_reg, ck->background);
- enable_lcd_clocks(0);
-
- dispc.color_key = *ck;
-
- return 0;
-}
-
-static int omap_dispc_get_color_key(struct omapfb_color_key *ck)
-{
- *ck = dispc.color_key;
- return 0;
-}
-
-static void load_palette(void)
-{
-}
-
-static int omap_dispc_set_update_mode(enum omapfb_update_mode mode)
-{
- int r = 0;
-
- if (mode != dispc.update_mode) {
- switch (mode) {
- case OMAPFB_AUTO_UPDATE:
- case OMAPFB_MANUAL_UPDATE:
- enable_lcd_clocks(1);
- omap_dispc_enable_lcd_out(1);
- dispc.update_mode = mode;
- break;
- case OMAPFB_UPDATE_DISABLED:
- init_completion(&dispc.frame_done);
- omap_dispc_enable_lcd_out(0);
- if (!wait_for_completion_timeout(&dispc.frame_done,
- msecs_to_jiffies(500))) {
- dev_err(dispc.fbdev->dev,
- "timeout waiting for FRAME DONE\n");
- }
- dispc.update_mode = mode;
- enable_lcd_clocks(0);
- break;
- default:
- r = -EINVAL;
- }
- }
-
- return r;
-}
-
-static void omap_dispc_get_caps(int plane, struct omapfb_caps *caps)
-{
- caps->ctrl |= OMAPFB_CAPS_PLANE_RELOCATE_MEM;
- if (plane > 0)
- caps->ctrl |= OMAPFB_CAPS_PLANE_SCALE;
- caps->plane_color |= (1 << OMAPFB_COLOR_RGB565) |
- (1 << OMAPFB_COLOR_YUV422) |
- (1 << OMAPFB_COLOR_YUY422);
- if (plane == 0)
- caps->plane_color |= (1 << OMAPFB_COLOR_CLUT_8BPP) |
- (1 << OMAPFB_COLOR_CLUT_4BPP) |
- (1 << OMAPFB_COLOR_CLUT_2BPP) |
- (1 << OMAPFB_COLOR_CLUT_1BPP) |
- (1 << OMAPFB_COLOR_RGB444);
-}
-
-static enum omapfb_update_mode omap_dispc_get_update_mode(void)
-{
- return dispc.update_mode;
-}
-
-static void setup_color_conv_coef(void)
-{
- u32 mask = FLD_MASK(16, 11) | FLD_MASK(0, 11);
- int cf1_reg = DISPC_VID1_BASE + DISPC_VID_CONV_COEF0;
- int cf2_reg = DISPC_VID2_BASE + DISPC_VID_CONV_COEF0;
- int at1_reg = DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES;
- int at2_reg = DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES;
- const struct color_conv_coef {
- int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
- int full_range;
- } ctbl_bt601_5 = {
- 298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
- };
- const struct color_conv_coef *ct;
-#define CVAL(x, y) (((x & 2047) << 16) | (y & 2047))
-
- ct = &ctbl_bt601_5;
-
- MOD_REG_FLD(cf1_reg, mask, CVAL(ct->rcr, ct->ry));
- MOD_REG_FLD(cf1_reg + 4, mask, CVAL(ct->gy, ct->rcb));
- MOD_REG_FLD(cf1_reg + 8, mask, CVAL(ct->gcb, ct->gcr));
- MOD_REG_FLD(cf1_reg + 12, mask, CVAL(ct->bcr, ct->by));
- MOD_REG_FLD(cf1_reg + 16, mask, CVAL(0, ct->bcb));
-
- MOD_REG_FLD(cf2_reg, mask, CVAL(ct->rcr, ct->ry));
- MOD_REG_FLD(cf2_reg + 4, mask, CVAL(ct->gy, ct->rcb));
- MOD_REG_FLD(cf2_reg + 8, mask, CVAL(ct->gcb, ct->gcr));
- MOD_REG_FLD(cf2_reg + 12, mask, CVAL(ct->bcr, ct->by));
- MOD_REG_FLD(cf2_reg + 16, mask, CVAL(0, ct->bcb));
-#undef CVAL
-
- MOD_REG_FLD(at1_reg, (1 << 11), ct->full_range);
- MOD_REG_FLD(at2_reg, (1 << 11), ct->full_range);
-}
-
-static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div)
-{
- unsigned long fck, lck;
-
- *lck_div = 1;
- pck = max(1, pck);
- fck = clk_get_rate(dispc.dss1_fck);
- lck = fck;
- *pck_div = (lck + pck - 1) / pck;
- if (is_tft)
- *pck_div = max(2, *pck_div);
- else
- *pck_div = max(3, *pck_div);
- if (*pck_div > 255) {
- *pck_div = 255;
- lck = pck * *pck_div;
- *lck_div = fck / lck;
- BUG_ON(*lck_div < 1);
- if (*lck_div > 255) {
- *lck_div = 255;
- dev_warn(dispc.fbdev->dev, "pixclock %d kHz too low.\n",
- pck / 1000);
- }
- }
-}
-
-static void set_lcd_tft_mode(int enable)
-{
- u32 mask;
-
- mask = 1 << 3;
- MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0);
-}
-
-static void set_lcd_timings(void)
-{
- u32 l;
- int lck_div, pck_div;
- struct lcd_panel *panel = dispc.fbdev->panel;
- int is_tft = panel->config & OMAP_LCDC_PANEL_TFT;
- unsigned long fck;
-
- l = dispc_read_reg(DISPC_TIMING_H);
- l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
- l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0;
- l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8;
- l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20;
- dispc_write_reg(DISPC_TIMING_H, l);
-
- l = dispc_read_reg(DISPC_TIMING_V);
- l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8));
- l |= ( max(1, (min(64, panel->vsw))) - 1 ) << 0;
- l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8;
- l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20;
- dispc_write_reg(DISPC_TIMING_V, l);
-
- l = dispc_read_reg(DISPC_POL_FREQ);
- l &= ~FLD_MASK(12, 6);
- l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12;
- l |= panel->acb & 0xff;
- dispc_write_reg(DISPC_POL_FREQ, l);
-
- calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div);
-
- l = dispc_read_reg(DISPC_DIVISOR);
- l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8));
- l |= (lck_div << 16) | (pck_div << 0);
- dispc_write_reg(DISPC_DIVISOR, l);
-
- /* update panel info with the exact clock */
- fck = clk_get_rate(dispc.dss1_fck);
- panel->pixel_clock = fck / lck_div / pck_div / 1000;
-}
-
-static void recalc_irq_mask(void)
-{
- int i;
- unsigned long irq_mask = DISPC_IRQ_MASK_ERROR;
-
- for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
- if (!dispc.irq_handlers[i].callback)
- continue;
-
- irq_mask |= dispc.irq_handlers[i].irq_mask;
- }
-
- enable_lcd_clocks(1);
- MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
- enable_lcd_clocks(0);
-}
-
-int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data),
- void *data)
-{
- int i;
-
- BUG_ON(callback == NULL);
-
- for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
- if (dispc.irq_handlers[i].callback)
- continue;
-
- dispc.irq_handlers[i].irq_mask = irq_mask;
- dispc.irq_handlers[i].callback = callback;
- dispc.irq_handlers[i].data = data;
- recalc_irq_mask();
-
- return 0;
- }
-
- return -EBUSY;
-}
-EXPORT_SYMBOL(omap_dispc_request_irq);
-
-void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data),
- void *data)
-{
- int i;
-
- for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
- if (dispc.irq_handlers[i].callback == callback &&
- dispc.irq_handlers[i].data == data) {
- dispc.irq_handlers[i].irq_mask = 0;
- dispc.irq_handlers[i].callback = NULL;
- dispc.irq_handlers[i].data = NULL;
- recalc_irq_mask();
- return;
- }
- }
-
- BUG();
-}
-EXPORT_SYMBOL(omap_dispc_free_irq);
-
-static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
-{
- u32 stat;
- int i = 0;
-
- enable_lcd_clocks(1);
-
- stat = dispc_read_reg(DISPC_IRQSTATUS);
- if (stat & DISPC_IRQ_FRAMEMASK)
- complete(&dispc.frame_done);
-
- if (stat & DISPC_IRQ_MASK_ERROR) {
- if (printk_ratelimit()) {
- dev_err(dispc.fbdev->dev, "irq error status %04x\n",
- stat & 0x7fff);
- }
- }
-
- for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
- if (unlikely(dispc.irq_handlers[i].callback &&
- (stat & dispc.irq_handlers[i].irq_mask)))
- dispc.irq_handlers[i].callback(
- dispc.irq_handlers[i].data);
- }
-
- dispc_write_reg(DISPC_IRQSTATUS, stat);
-
- enable_lcd_clocks(0);
-
- return IRQ_HANDLED;
-}
-
-static int get_dss_clocks(void)
-{
- dispc.dss_ick = clk_get(&dispc.fbdev->dssdev->dev, "ick");
- if (IS_ERR(dispc.dss_ick)) {
- dev_err(dispc.fbdev->dev, "can't get ick\n");
- return PTR_ERR(dispc.dss_ick);
- }
-
- dispc.dss1_fck = clk_get(&dispc.fbdev->dssdev->dev, "fck");
- if (IS_ERR(dispc.dss1_fck)) {
- dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
- clk_put(dispc.dss_ick);
- return PTR_ERR(dispc.dss1_fck);
- }
-
- dispc.dss_54m_fck = clk_get(&dispc.fbdev->dssdev->dev, "tv_clk");
- if (IS_ERR(dispc.dss_54m_fck)) {
- dev_err(dispc.fbdev->dev, "can't get tv_fck\n");
- clk_put(dispc.dss_ick);
- clk_put(dispc.dss1_fck);
- return PTR_ERR(dispc.dss_54m_fck);
- }
-
- return 0;
-}
-
-static void put_dss_clocks(void)
-{
- clk_put(dispc.dss_54m_fck);
- clk_put(dispc.dss1_fck);
- clk_put(dispc.dss_ick);
-}
-
-static void enable_lcd_clocks(int enable)
-{
- if (enable) {
- clk_enable(dispc.dss_ick);
- clk_enable(dispc.dss1_fck);
- } else {
- clk_disable(dispc.dss1_fck);
- clk_disable(dispc.dss_ick);
- }
-}
-
-static void enable_digit_clocks(int enable)
-{
- if (enable)
- clk_enable(dispc.dss_54m_fck);
- else
- clk_disable(dispc.dss_54m_fck);
-}
-
-static void omap_dispc_suspend(void)
-{
- if (dispc.update_mode == OMAPFB_AUTO_UPDATE) {
- init_completion(&dispc.frame_done);
- omap_dispc_enable_lcd_out(0);
- if (!wait_for_completion_timeout(&dispc.frame_done,
- msecs_to_jiffies(500))) {
- dev_err(dispc.fbdev->dev,
- "timeout waiting for FRAME DONE\n");
- }
- enable_lcd_clocks(0);
- }
-}
-
-static void omap_dispc_resume(void)
-{
- if (dispc.update_mode == OMAPFB_AUTO_UPDATE) {
- enable_lcd_clocks(1);
- if (!dispc.ext_mode) {
- set_lcd_timings();
- load_palette();
- }
- omap_dispc_enable_lcd_out(1);
- }
-}
-
-
-static int omap_dispc_update_window(struct fb_info *fbi,
- struct omapfb_update_window *win,
- void (*complete_callback)(void *arg),
- void *complete_callback_data)
-{
- return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0;
-}
-
-static int mmap_kern(struct omapfb_mem_region *region)
-{
- struct vm_struct *kvma;
- struct vm_area_struct vma;
- pgprot_t pgprot;
- unsigned long vaddr;
-
- kvma = get_vm_area(region->size, VM_IOREMAP);
- if (kvma == NULL) {
- dev_err(dispc.fbdev->dev, "can't get kernel vm area\n");
- return -ENOMEM;
- }
- vma.vm_mm = &init_mm;
-
- vaddr = (unsigned long)kvma->addr;
-
- pgprot = pgprot_writecombine(pgprot_kernel);
- vma.vm_start = vaddr;
- vma.vm_end = vaddr + region->size;
- if (io_remap_pfn_range(&vma, vaddr, region->paddr >> PAGE_SHIFT,
- region->size, pgprot) < 0) {
- dev_err(dispc.fbdev->dev, "kernel mmap for FBMEM failed\n");
- return -EAGAIN;
- }
- region->vaddr = (void *)vaddr;
-
- return 0;
-}
-
-static void mmap_user_open(struct vm_area_struct *vma)
-{
- int plane = (int)vma->vm_private_data;
-
- atomic_inc(&dispc.map_count[plane]);
-}
-
-static void mmap_user_close(struct vm_area_struct *vma)
-{
- int plane = (int)vma->vm_private_data;
-
- atomic_dec(&dispc.map_count[plane]);
-}
-
-static const struct vm_operations_struct mmap_user_ops = {
- .open = mmap_user_open,
- .close = mmap_user_close,
-};
-
-static int omap_dispc_mmap_user(struct fb_info *info,
- struct vm_area_struct *vma)
-{
- struct omapfb_plane_struct *plane = info->par;
- unsigned long off;
- unsigned long start;
- u32 len;
-
- if (vma->vm_end - vma->vm_start == 0)
- return 0;
- if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
- return -EINVAL;
- off = vma->vm_pgoff << PAGE_SHIFT;
-
- start = info->fix.smem_start;
- len = info->fix.smem_len;
- if (off >= len)
- return -EINVAL;
- if ((vma->vm_end - vma->vm_start + off) > len)
- return -EINVAL;
- off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
- vma->vm_flags |= VM_IO | VM_RESERVED;
- vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
- vma->vm_ops = &mmap_user_ops;
- vma->vm_private_data = (void *)plane->idx;
- if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
- vma->vm_end - vma->vm_start, vma->vm_page_prot))
- return -EAGAIN;
- /* vm_ops.open won't be called for mmap itself. */
- atomic_inc(&dispc.map_count[plane->idx]);
- return 0;
-}
-
-static void unmap_kern(struct omapfb_mem_region *region)
-{
- vunmap(region->vaddr);
-}
-
-static int alloc_palette_ram(void)
-{
- dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
- MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL);
- if (dispc.palette_vaddr == NULL) {
- dev_err(dispc.fbdev->dev, "failed to alloc palette memory\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void free_palette_ram(void)
-{
- dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE,
- dispc.palette_vaddr, dispc.palette_paddr);
-}
-
-static int alloc_fbmem(struct omapfb_mem_region *region)
-{
- region->vaddr = dma_alloc_writecombine(dispc.fbdev->dev,
- region->size, &region->paddr, GFP_KERNEL);
-
- if (region->vaddr == NULL) {
- dev_err(dispc.fbdev->dev, "unable to allocate FB DMA memory\n");
- return -ENOMEM;
- }
-
- return 0;
-}
-
-static void free_fbmem(struct omapfb_mem_region *region)
-{
- dma_free_writecombine(dispc.fbdev->dev, region->size,
- region->vaddr, region->paddr);
-}
-
-static struct resmap *init_resmap(unsigned long start, size_t size)
-{
- unsigned page_cnt;
- struct resmap *res_map;
-
- page_cnt = PAGE_ALIGN(size) / PAGE_SIZE;
- res_map =
- kzalloc(sizeof(struct resmap) + RESMAP_SIZE(page_cnt), GFP_KERNEL);
- if (res_map == NULL)
- return NULL;
- res_map->start = start;
- res_map->page_cnt = page_cnt;
- res_map->map = (unsigned long *)(res_map + 1);
- return res_map;
-}
-
-static void cleanup_resmap(struct resmap *res_map)
-{
- kfree(res_map);
-}
-
-static inline int resmap_mem_type(unsigned long start)
-{
- if (start >= OMAP2_SRAM_START &&
- start < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
- return OMAPFB_MEMTYPE_SRAM;
- else
- return OMAPFB_MEMTYPE_SDRAM;
-}
-
-static inline int resmap_page_reserved(struct resmap *res_map, unsigned page_nr)
-{
- return *RESMAP_PTR(res_map, page_nr) & RESMAP_MASK(page_nr) ? 1 : 0;
-}
-
-static inline void resmap_reserve_page(struct resmap *res_map, unsigned page_nr)
-{
- BUG_ON(resmap_page_reserved(res_map, page_nr));
- *RESMAP_PTR(res_map, page_nr) |= RESMAP_MASK(page_nr);
-}
-
-static inline void resmap_free_page(struct resmap *res_map, unsigned page_nr)
-{
- BUG_ON(!resmap_page_reserved(res_map, page_nr));
- *RESMAP_PTR(res_map, page_nr) &= ~RESMAP_MASK(page_nr);
-}
-
-static void resmap_reserve_region(unsigned long start, size_t size)
-{
-
- struct resmap *res_map;
- unsigned start_page;
- unsigned end_page;
- int mtype;
- unsigned i;
-
- mtype = resmap_mem_type(start);
- res_map = dispc.res_map[mtype];
- dev_dbg(dispc.fbdev->dev, "reserve mem type %d start %08lx size %d\n",
- mtype, start, size);
- start_page = (start - res_map->start) / PAGE_SIZE;
- end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE;
- for (i = start_page; i < end_page; i++)
- resmap_reserve_page(res_map, i);
-}
-
-static void resmap_free_region(unsigned long start, size_t size)
-{
- struct resmap *res_map;
- unsigned start_page;
- unsigned end_page;
- unsigned i;
- int mtype;
-
- mtype = resmap_mem_type(start);
- res_map = dispc.res_map[mtype];
- dev_dbg(dispc.fbdev->dev, "free mem type %d start %08lx size %d\n",
- mtype, start, size);
- start_page = (start - res_map->start) / PAGE_SIZE;
- end_page = start_page + PAGE_ALIGN(size) / PAGE_SIZE;
- for (i = start_page; i < end_page; i++)
- resmap_free_page(res_map, i);
-}
-
-static unsigned long resmap_alloc_region(int mtype, size_t size)
-{
- unsigned i;
- unsigned total;
- unsigned start_page;
- unsigned long start;
- struct resmap *res_map = dispc.res_map[mtype];
-
- BUG_ON(mtype >= DISPC_MEMTYPE_NUM || res_map == NULL || !size);
-
- size = PAGE_ALIGN(size) / PAGE_SIZE;
- start_page = 0;
- total = 0;
- for (i = 0; i < res_map->page_cnt; i++) {
- if (resmap_page_reserved(res_map, i)) {
- start_page = i + 1;
- total = 0;
- } else if (++total == size)
- break;
- }
- if (total < size)
- return 0;
-
- start = res_map->start + start_page * PAGE_SIZE;
- resmap_reserve_region(start, size * PAGE_SIZE);
-
- return start;
-}
-
-/* Note that this will only work for user mappings, we don't deal with
- * kernel mappings here, so fbcon will keep using the old region.
- */
-static int omap_dispc_setup_mem(int plane, size_t size, int mem_type,
- unsigned long *paddr)
-{
- struct omapfb_mem_region *rg;
- unsigned long new_addr = 0;
-
- if ((unsigned)plane > dispc.mem_desc.region_cnt)
- return -EINVAL;
- if (mem_type >= DISPC_MEMTYPE_NUM)
- return -EINVAL;
- if (dispc.res_map[mem_type] == NULL)
- return -ENOMEM;
- rg = &dispc.mem_desc.region[plane];
- if (size == rg->size && mem_type == rg->type)
- return 0;
- if (atomic_read(&dispc.map_count[plane]))
- return -EBUSY;
- if (rg->size != 0)
- resmap_free_region(rg->paddr, rg->size);
- if (size != 0) {
- new_addr = resmap_alloc_region(mem_type, size);
- if (!new_addr) {
- /* Reallocate old region. */
- resmap_reserve_region(rg->paddr, rg->size);
- return -ENOMEM;
- }
- }
- rg->paddr = new_addr;
- rg->size = size;
- rg->type = mem_type;
-
- *paddr = new_addr;
-
- return 0;
-}
-
-static int setup_fbmem(struct omapfb_mem_desc *req_md)
-{
- struct omapfb_mem_region *rg;
- int i;
- int r;
- unsigned long mem_start[DISPC_MEMTYPE_NUM];
- unsigned long mem_end[DISPC_MEMTYPE_NUM];
-
- if (!req_md->region_cnt) {
- dev_err(dispc.fbdev->dev, "no memory regions defined\n");
- return -ENOENT;
- }
-
- rg = &req_md->region[0];
- memset(mem_start, 0xff, sizeof(mem_start));
- memset(mem_end, 0, sizeof(mem_end));
-
- for (i = 0; i < req_md->region_cnt; i++, rg++) {
- int mtype;
- if (rg->paddr) {
- rg->alloc = 0;
- if (rg->vaddr == NULL) {
- rg->map = 1;
- if ((r = mmap_kern(rg)) < 0)
- return r;
- }
- } else {
- if (rg->type != OMAPFB_MEMTYPE_SDRAM) {
- dev_err(dispc.fbdev->dev,
- "unsupported memory type\n");
- return -EINVAL;
- }
- rg->alloc = rg->map = 1;
- if ((r = alloc_fbmem(rg)) < 0)
- return r;
- }
- mtype = rg->type;
-
- if (rg->paddr < mem_start[mtype])
- mem_start[mtype] = rg->paddr;
- if (rg->paddr + rg->size > mem_end[mtype])
- mem_end[mtype] = rg->paddr + rg->size;
- }
-
- for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
- unsigned long start;
- size_t size;
- if (mem_end[i] == 0)
- continue;
- start = mem_start[i];
- size = mem_end[i] - start;
- dispc.res_map[i] = init_resmap(start, size);
- r = -ENOMEM;
- if (dispc.res_map[i] == NULL)
- goto fail;
- /* Initial state is that everything is reserved. This
- * includes possible holes as well, which will never be
- * freed.
- */
- resmap_reserve_region(start, size);
- }
-
- dispc.mem_desc = *req_md;
-
- return 0;
-fail:
- for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
- if (dispc.res_map[i] != NULL)
- cleanup_resmap(dispc.res_map[i]);
- }
- return r;
-}
-
-static void cleanup_fbmem(void)
-{
- struct omapfb_mem_region *rg;
- int i;
-
- for (i = 0; i < DISPC_MEMTYPE_NUM; i++) {
- if (dispc.res_map[i] != NULL)
- cleanup_resmap(dispc.res_map[i]);
- }
- rg = &dispc.mem_desc.region[0];
- for (i = 0; i < dispc.mem_desc.region_cnt; i++, rg++) {
- if (rg->alloc)
- free_fbmem(rg);
- else {
- if (rg->map)
- unmap_kern(rg);
- }
- }
-}
-
-static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
- struct omapfb_mem_desc *req_vram)
-{
- int r;
- u32 l;
- struct lcd_panel *panel = fbdev->panel;
- void __iomem *ram_fw_base;
- int tmo = 10000;
- int skip_init = 0;
- int i;
-
- memset(&dispc, 0, sizeof(dispc));
-
- dispc.base = ioremap(DISPC_BASE, SZ_1K);
- if (!dispc.base) {
- dev_err(fbdev->dev, "can't ioremap DISPC\n");
- return -ENOMEM;
- }
-
- dispc.fbdev = fbdev;
- dispc.ext_mode = ext_mode;
-
- init_completion(&dispc.frame_done);
-
- if ((r = get_dss_clocks()) < 0)
- goto fail0;
-
- enable_lcd_clocks(1);
-
-#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
- l = dispc_read_reg(DISPC_CONTROL);
- /* LCD enabled ? */
- if (l & 1) {
- pr_info("omapfb: skipping hardware initialization\n");
- skip_init = 1;
- }
-#endif
-
- if (!skip_init) {
- /* Reset monitoring works only w/ the 54M clk */
- enable_digit_clocks(1);
-
- /* Soft reset */
- MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1);
-
- while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) {
- if (!--tmo) {
- dev_err(dispc.fbdev->dev, "soft reset failed\n");
- r = -ENODEV;
- enable_digit_clocks(0);
- goto fail1;
- }
- }
-
- enable_digit_clocks(0);
- }
-
- /* Enable smart standby/idle, autoidle and wakeup */
- l = dispc_read_reg(DISPC_SYSCONFIG);
- l &= ~((3 << 12) | (3 << 3));
- l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0);
- dispc_write_reg(DISPC_SYSCONFIG, l);
- omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG);
-
- /* Set functional clock autogating */
- l = dispc_read_reg(DISPC_CONFIG);
- l |= 1 << 9;
- dispc_write_reg(DISPC_CONFIG, l);
-
- l = dispc_read_reg(DISPC_IRQSTATUS);
- dispc_write_reg(DISPC_IRQSTATUS, l);
-
- recalc_irq_mask();
-
- if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler,
- 0, MODULE_NAME, fbdev)) < 0) {
- dev_err(dispc.fbdev->dev, "can't get DSS IRQ\n");
- goto fail1;
- }
-
- /* L3 firewall setting: enable access to OCM RAM */
- ram_fw_base = ioremap(0x68005000, SZ_1K);
- if (!ram_fw_base) {
- dev_err(dispc.fbdev->dev, "Cannot ioremap to enable OCM RAM\n");
- goto fail1;
- }
- __raw_writel(0x402000b0, ram_fw_base + 0xa0);
- iounmap(ram_fw_base);
-
- if ((r = alloc_palette_ram()) < 0)
- goto fail2;
-
- if ((r = setup_fbmem(req_vram)) < 0)
- goto fail3;
-
- if (!skip_init) {
- for (i = 0; i < dispc.mem_desc.region_cnt; i++) {
- memset(dispc.mem_desc.region[i].vaddr, 0,
- dispc.mem_desc.region[i].size);
- }
-
- /* Set logic clock to fck, pixel clock to fck/2 for now */
- MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16);
- MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0);
-
- setup_plane_fifo(0, ext_mode);
- setup_plane_fifo(1, ext_mode);
- setup_plane_fifo(2, ext_mode);
-
- setup_color_conv_coef();
-
- set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT);
- set_load_mode(DISPC_LOAD_FRAME_ONLY);
-
- if (!ext_mode) {
- set_lcd_data_lines(panel->data_lines);
- omap_dispc_set_lcd_size(panel->x_res, panel->y_res);
- set_lcd_timings();
- } else
- set_lcd_data_lines(panel->bpp);
- enable_rfbi_mode(ext_mode);
- }
-
- l = dispc_read_reg(DISPC_REVISION);
- pr_info("omapfb: DISPC version %d.%d initialized\n",
- l >> 4 & 0x0f, l & 0x0f);
- enable_lcd_clocks(0);
-
- return 0;
-fail3:
- free_palette_ram();
-fail2:
- free_irq(INT_24XX_DSS_IRQ, fbdev);
-fail1:
- enable_lcd_clocks(0);
- put_dss_clocks();
-fail0:
- iounmap(dispc.base);
- return r;
-}
-
-static void omap_dispc_cleanup(void)
-{
- int i;
-
- omap_dispc_set_update_mode(OMAPFB_UPDATE_DISABLED);
- /* This will also disable clocks that are on */
- for (i = 0; i < dispc.mem_desc.region_cnt; i++)
- omap_dispc_enable_plane(i, 0);
- cleanup_fbmem();
- free_palette_ram();
- free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
- put_dss_clocks();
- iounmap(dispc.base);
-}
-
-const struct lcd_ctrl omap2_int_ctrl = {
- .name = "internal",
- .init = omap_dispc_init,
- .cleanup = omap_dispc_cleanup,
- .get_caps = omap_dispc_get_caps,
- .set_update_mode = omap_dispc_set_update_mode,
- .get_update_mode = omap_dispc_get_update_mode,
- .update_window = omap_dispc_update_window,
- .suspend = omap_dispc_suspend,
- .resume = omap_dispc_resume,
- .setup_plane = omap_dispc_setup_plane,
- .setup_mem = omap_dispc_setup_mem,
- .set_scale = omap_dispc_set_scale,
- .enable_plane = omap_dispc_enable_plane,
- .set_color_key = omap_dispc_set_color_key,
- .get_color_key = omap_dispc_get_color_key,
- .mmap = omap_dispc_mmap_user,
-};
diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h
deleted file mode 100644
index c15ea77f0604..000000000000
--- a/drivers/video/omap/dispc.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef _DISPC_H
-#define _DISPC_H
-
-#include <linux/interrupt.h>
-
-#define DISPC_PLANE_GFX 0
-#define DISPC_PLANE_VID1 1
-#define DISPC_PLANE_VID2 2
-
-#define DISPC_RGB_1_BPP 0x00
-#define DISPC_RGB_2_BPP 0x01
-#define DISPC_RGB_4_BPP 0x02
-#define DISPC_RGB_8_BPP 0x03
-#define DISPC_RGB_12_BPP 0x04
-#define DISPC_RGB_16_BPP 0x06
-#define DISPC_RGB_24_BPP 0x08
-#define DISPC_RGB_24_BPP_UNPACK_32 0x09
-#define DISPC_YUV2_422 0x0a
-#define DISPC_UYVY_422 0x0b
-
-#define DISPC_BURST_4x32 0
-#define DISPC_BURST_8x32 1
-#define DISPC_BURST_16x32 2
-
-#define DISPC_LOAD_CLUT_AND_FRAME 0x00
-#define DISPC_LOAD_CLUT_ONLY 0x01
-#define DISPC_LOAD_FRAME_ONLY 0x02
-#define DISPC_LOAD_CLUT_ONCE_FRAME 0x03
-
-#define DISPC_TFT_DATA_LINES_12 0
-#define DISPC_TFT_DATA_LINES_16 1
-#define DISPC_TFT_DATA_LINES_18 2
-#define DISPC_TFT_DATA_LINES_24 3
-
-extern void omap_dispc_set_lcd_size(int width, int height);
-
-extern void omap_dispc_enable_lcd_out(int enable);
-extern void omap_dispc_enable_digit_out(int enable);
-
-extern int omap_dispc_request_irq(unsigned long irq_mask,
- void (*callback)(void *data), void *data);
-extern void omap_dispc_free_irq(unsigned long irq_mask,
- void (*callback)(void *data), void *data);
-
-extern const struct lcd_ctrl omap2_int_ctrl;
-#endif
diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c
index 084aa0ac562b..9f1d23c319cb 100644
--- a/drivers/video/omap/hwa742.c
+++ b/drivers/video/omap/hwa742.c
@@ -28,7 +28,6 @@
#include <linux/interrupt.h>
#include <plat/dma.h>
-#include <plat/hwa742.h>
#include "omapfb.h"
#define HWA742_REV_CODE_REG 0x0
@@ -942,7 +941,6 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
unsigned long sys_clk, pix_clk;
int extif_mem_div;
struct omapfb_platform_data *omapfb_conf;
- struct hwa742_platform_data *ctrl_conf;
BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl);
@@ -951,13 +949,6 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
hwa742.int_ctrl = fbdev->int_ctrl;
omapfb_conf = fbdev->dev->platform_data;
- ctrl_conf = omapfb_conf->ctrl_platform_data;
-
- if (ctrl_conf == NULL) {
- dev_err(fbdev->dev, "HWA742: missing platform data\n");
- r = -ENOENT;
- goto err1;
- }
hwa742.sys_ck = clk_get(NULL, "hwa_sys_ck");
@@ -995,14 +986,12 @@ static int hwa742_init(struct omapfb_device *fbdev, int ext_mode,
goto err4;
}
- if (ctrl_conf->te_connected) {
- if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) {
- dev_err(hwa742.fbdev->dev,
- "HWA742: can't setup tearing synchronization\n");
- goto err4;
- }
- hwa742.te_connected = 1;
+ if ((r = setup_tearsync(pix_clk, extif_mem_div)) < 0) {
+ dev_err(hwa742.fbdev->dev,
+ "HWA742: can't setup tearing synchronization\n");
+ goto err4;
}
+ hwa742.te_connected = 1;
hwa742.max_transmit_size = hwa742.extif->max_transmit_size;
diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c
index 0fdd6f6873bf..d3a311327227 100644
--- a/drivers/video/omap/lcd_ams_delta.c
+++ b/drivers/video/omap/lcd_ams_delta.c
@@ -25,6 +25,7 @@
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/lcd.h>
+#include <linux/gpio.h>
#include <plat/board-ams-delta.h>
#include <mach/hardware.h>
@@ -98,29 +99,41 @@ static struct lcd_ops ams_delta_lcd_ops = {
/* omapfb panel section */
+static const struct gpio _gpios[] = {
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_LCD_VBLEN,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "lcd_vblen",
+ },
+ {
+ .gpio = AMS_DELTA_GPIO_PIN_LCD_NDISP,
+ .flags = GPIOF_OUT_INIT_LOW,
+ .label = "lcd_ndisp",
+ },
+};
+
static int ams_delta_panel_init(struct lcd_panel *panel,
struct omapfb_device *fbdev)
{
- return 0;
+ return gpio_request_array(_gpios, ARRAY_SIZE(_gpios));
}
static void ams_delta_panel_cleanup(struct lcd_panel *panel)
{
+ gpio_free_array(_gpios, ARRAY_SIZE(_gpios));
}
static int ams_delta_panel_enable(struct lcd_panel *panel)
{
- ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP,
- AMS_DELTA_LATCH2_LCD_NDISP);
- ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN,
- AMS_DELTA_LATCH2_LCD_VBLEN);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 1);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 1);
return 0;
}
static void ams_delta_panel_disable(struct lcd_panel *panel)
{
- ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0);
- ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_VBLEN, 0);
+ gpio_set_value(AMS_DELTA_GPIO_PIN_LCD_NDISP, 0);
}
static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel)
diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c
index 7e8bd8e08a98..e3d3d135aa48 100644
--- a/drivers/video/omap/lcd_inn1610.c
+++ b/drivers/video/omap/lcd_inn1610.c
@@ -22,7 +22,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <asm/gpio.h>
+#include <linux/gpio.h>
#include "omapfb.h"
#define MODULE_NAME "omapfb-lcd_h3"
@@ -32,20 +32,18 @@ static int innovator1610_panel_init(struct lcd_panel *panel,
{
int r = 0;
- if (gpio_request(14, "lcd_en0")) {
+ /* configure GPIO(14, 15) as outputs */
+ if (gpio_request_one(14, GPIOF_OUT_INIT_LOW, "lcd_en0")) {
pr_err(MODULE_NAME ": can't request GPIO 14\n");
r = -1;
goto exit;
}
- if (gpio_request(15, "lcd_en1")) {
+ if (gpio_request_one(15, GPIOF_OUT_INIT_LOW, "lcd_en1")) {
pr_err(MODULE_NAME ": can't request GPIO 15\n");
gpio_free(14);
r = -1;
goto exit;
}
- /* configure GPIO(14, 15) as outputs */
- gpio_direction_output(14, 0);
- gpio_direction_output(15, 0);
exit:
return r;
}
diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c
index 8d546dd55e81..e3880c4a0bb1 100644
--- a/drivers/video/omap/lcd_mipid.c
+++ b/drivers/video/omap/lcd_mipid.c
@@ -609,19 +609,7 @@ static struct spi_driver mipid_spi_driver = {
.remove = __devexit_p(mipid_spi_remove),
};
-static int __init mipid_drv_init(void)
-{
- spi_register_driver(&mipid_spi_driver);
-
- return 0;
-}
-module_init(mipid_drv_init);
-
-static void __exit mipid_drv_cleanup(void)
-{
- spi_unregister_driver(&mipid_spi_driver);
-}
-module_exit(mipid_drv_cleanup);
+module_spi_driver(mipid_spi_driver);
MODULE_DESCRIPTION("MIPI display driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap/omapfb.h b/drivers/video/omap/omapfb.h
index af3c9e571ec3..2921d20e4fba 100644
--- a/drivers/video/omap/omapfb.h
+++ b/drivers/video/omap/omapfb.h
@@ -47,6 +47,27 @@
struct omapfb_device;
+#define OMAPFB_PLANE_NUM 1
+
+struct omapfb_mem_region {
+ u32 paddr;
+ void __iomem *vaddr;
+ unsigned long size;
+ u8 type; /* OMAPFB_PLANE_MEM_* */
+ enum omapfb_color_format format;/* OMAPFB_COLOR_* */
+ unsigned format_used:1; /* Must be set when format is set.
+ * Needed b/c of the badly chosen 0
+ * base for OMAPFB_COLOR_* values
+ */
+ unsigned alloc:1; /* allocated by the driver */
+ unsigned map:1; /* kernel mapped by the driver */
+};
+
+struct omapfb_mem_desc {
+ int region_cnt;
+ struct omapfb_mem_region region[OMAPFB_PLANE_NUM];
+};
+
struct lcd_panel {
const char *name;
int config; /* TFT/STN, signal inversion */
@@ -207,11 +228,7 @@ struct omapfb_device {
struct platform_device *dssdev; /* dummy dev for clocks */
};
-#ifdef CONFIG_ARCH_OMAP1
extern struct lcd_ctrl omap1_lcd_ctrl;
-#else
-extern struct lcd_ctrl omap2_disp_ctrl;
-#endif
extern void omapfb_register_panel(struct lcd_panel *panel);
extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval);
diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c
index b291bfaac80e..f54b463709e9 100644
--- a/drivers/video/omap/omapfb_main.c
+++ b/drivers/video/omap/omapfb_main.c
@@ -34,7 +34,6 @@
#include "omapfb.h"
#include "lcdc.h"
-#include "dispc.h"
#define MODULE_NAME "omapfb"
@@ -104,29 +103,17 @@ static struct platform_device omapdss_device = {
* ---------------------------------------------------------------------------
*/
extern struct lcd_ctrl hwa742_ctrl;
-extern struct lcd_ctrl blizzard_ctrl;
static const struct lcd_ctrl *ctrls[] = {
-#ifdef CONFIG_ARCH_OMAP1
&omap1_int_ctrl,
-#else
- &omap2_int_ctrl,
-#endif
#ifdef CONFIG_FB_OMAP_LCDC_HWA742
&hwa742_ctrl,
#endif
-#ifdef CONFIG_FB_OMAP_LCDC_BLIZZARD
- &blizzard_ctrl,
-#endif
};
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
-#ifdef CONFIG_ARCH_OMAP1
extern struct lcd_ctrl_extif omap1_ext_if;
-#else
-extern struct lcd_ctrl_extif omap2_ext_if;
-#endif
#endif
static void omapfb_rqueue_lock(struct omapfb_device *fbdev)
@@ -170,11 +157,6 @@ static int ctrl_init(struct omapfb_device *fbdev)
fbdev->mem_desc.region[i].size =
PAGE_ALIGN(def_vram[i]);
fbdev->mem_desc.region_cnt = i;
- } else {
- struct omapfb_platform_data *conf;
-
- conf = fbdev->dev->platform_data;
- fbdev->mem_desc = conf->mem_desc;
}
if (!fbdev->mem_desc.region_cnt) {
@@ -880,7 +862,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
if (fbdev->ctrl->setup_mem == NULL)
return -ENODEV;
- if (mi->type > OMAPFB_MEMTYPE_MAX)
+ if (mi->type != OMAPFB_MEMTYPE_SDRAM)
return -EINVAL;
size = PAGE_ALIGN(mi->size);
@@ -1721,17 +1703,10 @@ static int omapfb_do_probe(struct platform_device *pdev,
mutex_init(&fbdev->rqueue_mutex);
-#ifdef CONFIG_ARCH_OMAP1
fbdev->int_ctrl = &omap1_int_ctrl;
#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
fbdev->ext_if = &omap1_ext_if;
#endif
-#else /* OMAP2 */
- fbdev->int_ctrl = &omap2_int_ctrl;
-#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL
- fbdev->ext_if = &omap2_ext_if;
-#endif
-#endif
if (omapfb_find_ctrl(fbdev) < 0) {
dev_err(fbdev->dev,
"LCD controller not found, board not supported\n");
@@ -1766,8 +1741,7 @@ static int omapfb_do_probe(struct platform_device *pdev,
#ifdef CONFIG_FB_OMAP_DMA_TUNE
/* Set DMA priority for EMIFF access to highest */
- if (cpu_class_is_omap1())
- omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);
+ omap_set_dma_priority(0, OMAP_DMA_PORT_EMIFF, 15);
#endif
r = ctrl_change_mode(fbdev->fb_info[0]);
diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c
deleted file mode 100644
index 2c1a3402bef0..000000000000
--- a/drivers/video/omap/rfbi.c
+++ /dev/null
@@ -1,598 +0,0 @@
-/*
- * OMAP2 Remote Frame Buffer Interface support
- *
- * Copyright (C) 2005 Nokia Corporation
- * Author: Juha Yrjölä <juha.yrjola@nokia.com>
- * Imre Deak <imre.deak@nokia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/clk.h>
-#include <linux/io.h>
-#include <linux/platform_device.h>
-
-#include "omapfb.h"
-#include "dispc.h"
-
-/* To work around an RFBI transfer rate limitation */
-#define OMAP_RFBI_RATE_LIMIT 1
-
-#define RFBI_BASE 0x48050800
-#define RFBI_REVISION 0x0000
-#define RFBI_SYSCONFIG 0x0010
-#define RFBI_SYSSTATUS 0x0014
-#define RFBI_CONTROL 0x0040
-#define RFBI_PIXEL_CNT 0x0044
-#define RFBI_LINE_NUMBER 0x0048
-#define RFBI_CMD 0x004c
-#define RFBI_PARAM 0x0050
-#define RFBI_DATA 0x0054
-#define RFBI_READ 0x0058
-#define RFBI_STATUS 0x005c
-#define RFBI_CONFIG0 0x0060
-#define RFBI_ONOFF_TIME0 0x0064
-#define RFBI_CYCLE_TIME0 0x0068
-#define RFBI_DATA_CYCLE1_0 0x006c
-#define RFBI_DATA_CYCLE2_0 0x0070
-#define RFBI_DATA_CYCLE3_0 0x0074
-#define RFBI_VSYNC_WIDTH 0x0090
-#define RFBI_HSYNC_WIDTH 0x0094
-
-#define DISPC_BASE 0x48050400
-#define DISPC_CONTROL 0x0040
-#define DISPC_IRQ_FRAMEMASK 0x0001
-
-static struct {
- void __iomem *base;
- void (*lcdc_callback)(void *data);
- void *lcdc_callback_data;
- unsigned long l4_khz;
- int bits_per_cycle;
- struct omapfb_device *fbdev;
- struct clk *dss_ick;
- struct clk *dss1_fck;
- unsigned tearsync_pin_cnt;
- unsigned tearsync_mode;
-} rfbi;
-
-static inline void rfbi_write_reg(int idx, u32 val)
-{
- __raw_writel(val, rfbi.base + idx);
-}
-
-static inline u32 rfbi_read_reg(int idx)
-{
- return __raw_readl(rfbi.base + idx);
-}
-
-static int rfbi_get_clocks(void)
-{
- rfbi.dss_ick = clk_get(&rfbi.fbdev->dssdev->dev, "ick");
- if (IS_ERR(rfbi.dss_ick)) {
- dev_err(rfbi.fbdev->dev, "can't get ick\n");
- return PTR_ERR(rfbi.dss_ick);
- }
-
- rfbi.dss1_fck = clk_get(&rfbi.fbdev->dssdev->dev, "fck");
- if (IS_ERR(rfbi.dss1_fck)) {
- dev_err(rfbi.fbdev->dev, "can't get dss1_fck\n");
- clk_put(rfbi.dss_ick);
- return PTR_ERR(rfbi.dss1_fck);
- }
-
- return 0;
-}
-
-static void rfbi_put_clocks(void)
-{
- clk_put(rfbi.dss1_fck);
- clk_put(rfbi.dss_ick);
-}
-
-static void rfbi_enable_clocks(int enable)
-{
- if (enable) {
- clk_enable(rfbi.dss_ick);
- clk_enable(rfbi.dss1_fck);
- } else {
- clk_disable(rfbi.dss1_fck);
- clk_disable(rfbi.dss_ick);
- }
-}
-
-
-#ifdef VERBOSE
-static void rfbi_print_timings(void)
-{
- u32 l;
- u32 time;
-
- l = rfbi_read_reg(RFBI_CONFIG0);
- time = 1000000000 / rfbi.l4_khz;
- if (l & (1 << 4))
- time *= 2;
-
- dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time);
- l = rfbi_read_reg(RFBI_ONOFF_TIME0);
- dev_dbg(rfbi.fbdev->dev,
- "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
- "REONTIME %d, REOFFTIME %d\n",
- l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
- (l >> 20) & 0x0f, (l >> 24) & 0x3f);
-
- l = rfbi_read_reg(RFBI_CYCLE_TIME0);
- dev_dbg(rfbi.fbdev->dev,
- "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
- "ACCESSTIME %d\n",
- (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f,
- (l >> 22) & 0x3f);
-}
-#else
-static void rfbi_print_timings(void) {}
-#endif
-
-static void rfbi_set_timings(const struct extif_timings *t)
-{
- u32 l;
-
- BUG_ON(!t->converted);
-
- rfbi_enable_clocks(1);
- rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]);
- rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]);
-
- l = rfbi_read_reg(RFBI_CONFIG0);
- l &= ~(1 << 4);
- l |= (t->tim[2] ? 1 : 0) << 4;
- rfbi_write_reg(RFBI_CONFIG0, l);
-
- rfbi_print_timings();
- rfbi_enable_clocks(0);
-}
-
-static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
-{
- *clk_period = 1000000000 / rfbi.l4_khz;
- *max_clk_div = 2;
-}
-
-static int ps_to_rfbi_ticks(int time, int div)
-{
- unsigned long tick_ps;
- int ret;
-
- /* Calculate in picosecs to yield more exact results */
- tick_ps = 1000000000 / (rfbi.l4_khz) * div;
-
- ret = (time + tick_ps - 1) / tick_ps;
-
- return ret;
-}
-
-#ifdef OMAP_RFBI_RATE_LIMIT
-static unsigned long rfbi_get_max_tx_rate(void)
-{
- unsigned long l4_rate, dss1_rate;
- int min_l4_ticks = 0;
- int i;
-
- /* According to TI this can't be calculated so make the
- * adjustments for a couple of known frequencies and warn for
- * others.
- */
- static const struct {
- unsigned long l4_clk; /* HZ */
- unsigned long dss1_clk; /* HZ */
- unsigned long min_l4_ticks;
- } ftab[] = {
- { 55, 132, 7, }, /* 7.86 MPix/s */
- { 110, 110, 12, }, /* 9.16 MPix/s */
- { 110, 132, 10, }, /* 11 Mpix/s */
- { 120, 120, 10, }, /* 12 Mpix/s */
- { 133, 133, 10, }, /* 13.3 Mpix/s */
- };
-
- l4_rate = rfbi.l4_khz / 1000;
- dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000;
-
- for (i = 0; i < ARRAY_SIZE(ftab); i++) {
- /* Use a window instead of an exact match, to account
- * for different DPLL multiplier / divider pairs.
- */
- if (abs(ftab[i].l4_clk - l4_rate) < 3 &&
- abs(ftab[i].dss1_clk - dss1_rate) < 3) {
- min_l4_ticks = ftab[i].min_l4_ticks;
- break;
- }
- }
- if (i == ARRAY_SIZE(ftab)) {
- /* Can't be sure, return anyway the maximum not
- * rate-limited. This might cause a problem only for the
- * tearing synchronisation.
- */
- dev_err(rfbi.fbdev->dev,
- "can't determine maximum RFBI transfer rate\n");
- return rfbi.l4_khz * 1000;
- }
- return rfbi.l4_khz * 1000 / min_l4_ticks;
-}
-#else
-static int rfbi_get_max_tx_rate(void)
-{
- return rfbi.l4_khz * 1000;
-}
-#endif
-
-
-static int rfbi_convert_timings(struct extif_timings *t)
-{
- u32 l;
- int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
- int actim, recyc, wecyc;
- int div = t->clk_div;
-
- if (div <= 0 || div > 2)
- return -1;
-
- /* Make sure that after conversion it still holds that:
- * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
- * csoff > cson, csoff >= max(weoff, reoff), actim > reon
- */
- weon = ps_to_rfbi_ticks(t->we_on_time, div);
- weoff = ps_to_rfbi_ticks(t->we_off_time, div);
- if (weoff <= weon)
- weoff = weon + 1;
- if (weon > 0x0f)
- return -1;
- if (weoff > 0x3f)
- return -1;
-
- reon = ps_to_rfbi_ticks(t->re_on_time, div);
- reoff = ps_to_rfbi_ticks(t->re_off_time, div);
- if (reoff <= reon)
- reoff = reon + 1;
- if (reon > 0x0f)
- return -1;
- if (reoff > 0x3f)
- return -1;
-
- cson = ps_to_rfbi_ticks(t->cs_on_time, div);
- csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
- if (csoff <= cson)
- csoff = cson + 1;
- if (csoff < max(weoff, reoff))
- csoff = max(weoff, reoff);
- if (cson > 0x0f)
- return -1;
- if (csoff > 0x3f)
- return -1;
-
- l = cson;
- l |= csoff << 4;
- l |= weon << 10;
- l |= weoff << 14;
- l |= reon << 20;
- l |= reoff << 24;
-
- t->tim[0] = l;
-
- actim = ps_to_rfbi_ticks(t->access_time, div);
- if (actim <= reon)
- actim = reon + 1;
- if (actim > 0x3f)
- return -1;
-
- wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
- if (wecyc < weoff)
- wecyc = weoff;
- if (wecyc > 0x3f)
- return -1;
-
- recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
- if (recyc < reoff)
- recyc = reoff;
- if (recyc > 0x3f)
- return -1;
-
- cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
- if (cs_pulse > 0x3f)
- return -1;
-
- l = wecyc;
- l |= recyc << 6;
- l |= cs_pulse << 12;
- l |= actim << 22;
-
- t->tim[1] = l;
-
- t->tim[2] = div - 1;
-
- t->converted = 1;
-
- return 0;
-}
-
-static int rfbi_setup_tearsync(unsigned pin_cnt,
- unsigned hs_pulse_time, unsigned vs_pulse_time,
- int hs_pol_inv, int vs_pol_inv, int extif_div)
-{
- int hs, vs;
- int min;
- u32 l;
-
- if (pin_cnt != 1 && pin_cnt != 2)
- return -EINVAL;
-
- hs = ps_to_rfbi_ticks(hs_pulse_time, 1);
- vs = ps_to_rfbi_ticks(vs_pulse_time, 1);
- if (hs < 2)
- return -EDOM;
- if (pin_cnt == 2)
- min = 2;
- else
- min = 4;
- if (vs < min)
- return -EDOM;
- if (vs == hs)
- return -EINVAL;
- rfbi.tearsync_pin_cnt = pin_cnt;
- dev_dbg(rfbi.fbdev->dev,
- "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n",
- pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv);
-
- rfbi_enable_clocks(1);
- rfbi_write_reg(RFBI_HSYNC_WIDTH, hs);
- rfbi_write_reg(RFBI_VSYNC_WIDTH, vs);
-
- l = rfbi_read_reg(RFBI_CONFIG0);
- if (hs_pol_inv)
- l &= ~(1 << 21);
- else
- l |= 1 << 21;
- if (vs_pol_inv)
- l &= ~(1 << 20);
- else
- l |= 1 << 20;
- rfbi_enable_clocks(0);
-
- return 0;
-}
-
-static int rfbi_enable_tearsync(int enable, unsigned line)
-{
- u32 l;
-
- dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n",
- enable, line, rfbi.tearsync_mode);
- if (line > (1 << 11) - 1)
- return -EINVAL;
-
- rfbi_enable_clocks(1);
- l = rfbi_read_reg(RFBI_CONFIG0);
- l &= ~(0x3 << 2);
- if (enable) {
- rfbi.tearsync_mode = rfbi.tearsync_pin_cnt;
- l |= rfbi.tearsync_mode << 2;
- } else
- rfbi.tearsync_mode = 0;
- rfbi_write_reg(RFBI_CONFIG0, l);
- rfbi_write_reg(RFBI_LINE_NUMBER, line);
- rfbi_enable_clocks(0);
-
- return 0;
-}
-
-static void rfbi_write_command(const void *buf, unsigned int len)
-{
- rfbi_enable_clocks(1);
- if (rfbi.bits_per_cycle == 16) {
- const u16 *w = buf;
- BUG_ON(len & 1);
- for (; len; len -= 2)
- rfbi_write_reg(RFBI_CMD, *w++);
- } else {
- const u8 *b = buf;
- BUG_ON(rfbi.bits_per_cycle != 8);
- for (; len; len--)
- rfbi_write_reg(RFBI_CMD, *b++);
- }
- rfbi_enable_clocks(0);
-}
-
-static void rfbi_read_data(void *buf, unsigned int len)
-{
- rfbi_enable_clocks(1);
- if (rfbi.bits_per_cycle == 16) {
- u16 *w = buf;
- BUG_ON(len & ~1);
- for (; len; len -= 2) {
- rfbi_write_reg(RFBI_READ, 0);
- *w++ = rfbi_read_reg(RFBI_READ);
- }
- } else {
- u8 *b = buf;
- BUG_ON(rfbi.bits_per_cycle != 8);
- for (; len; len--) {
- rfbi_write_reg(RFBI_READ, 0);
- *b++ = rfbi_read_reg(RFBI_READ);
- }
- }
- rfbi_enable_clocks(0);
-}
-
-static void rfbi_write_data(const void *buf, unsigned int len)
-{
- rfbi_enable_clocks(1);
- if (rfbi.bits_per_cycle == 16) {
- const u16 *w = buf;
- BUG_ON(len & 1);
- for (; len; len -= 2)
- rfbi_write_reg(RFBI_PARAM, *w++);
- } else {
- const u8 *b = buf;
- BUG_ON(rfbi.bits_per_cycle != 8);
- for (; len; len--)
- rfbi_write_reg(RFBI_PARAM, *b++);
- }
- rfbi_enable_clocks(0);
-}
-
-static void rfbi_transfer_area(int width, int height,
- void (callback)(void * data), void *data)
-{
- u32 w;
-
- BUG_ON(callback == NULL);
-
- rfbi_enable_clocks(1);
- omap_dispc_set_lcd_size(width, height);
-
- rfbi.lcdc_callback = callback;
- rfbi.lcdc_callback_data = data;
-
- rfbi_write_reg(RFBI_PIXEL_CNT, width * height);
-
- w = rfbi_read_reg(RFBI_CONTROL);
- w |= 1; /* enable */
- if (!rfbi.tearsync_mode)
- w |= 1 << 4; /* internal trigger, reset by HW */
- rfbi_write_reg(RFBI_CONTROL, w);
-
- omap_dispc_enable_lcd_out(1);
-}
-
-static inline void _stop_transfer(void)
-{
- u32 w;
-
- w = rfbi_read_reg(RFBI_CONTROL);
- rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0));
- rfbi_enable_clocks(0);
-}
-
-static void rfbi_dma_callback(void *data)
-{
- _stop_transfer();
- rfbi.lcdc_callback(rfbi.lcdc_callback_data);
-}
-
-static void rfbi_set_bits_per_cycle(int bpc)
-{
- u32 l;
-
- rfbi_enable_clocks(1);
- l = rfbi_read_reg(RFBI_CONFIG0);
- l &= ~(0x03 << 0);
-
- switch (bpc) {
- case 8:
- break;
- case 16:
- l |= 3;
- break;
- default:
- BUG();
- }
- rfbi_write_reg(RFBI_CONFIG0, l);
- rfbi.bits_per_cycle = bpc;
- rfbi_enable_clocks(0);
-}
-
-static int rfbi_init(struct omapfb_device *fbdev)
-{
- u32 l;
- int r;
-
- rfbi.fbdev = fbdev;
- rfbi.base = ioremap(RFBI_BASE, SZ_1K);
- if (!rfbi.base) {
- dev_err(fbdev->dev, "can't ioremap RFBI\n");
- return -ENOMEM;
- }
-
- if ((r = rfbi_get_clocks()) < 0)
- return r;
- rfbi_enable_clocks(1);
-
- rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000;
-
- /* Reset */
- rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1);
- while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0)));
-
- l = rfbi_read_reg(RFBI_SYSCONFIG);
- /* Enable autoidle and smart-idle */
- l |= (1 << 0) | (2 << 3);
- rfbi_write_reg(RFBI_SYSCONFIG, l);
-
- /* 16-bit interface, ITE trigger mode, 16-bit data */
- l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7);
- l |= (0 << 9) | (1 << 20) | (1 << 21);
- rfbi_write_reg(RFBI_CONFIG0, l);
-
- rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010);
-
- l = rfbi_read_reg(RFBI_CONTROL);
- /* Select CS0, clear bypass mode */
- l = (0x01 << 2);
- rfbi_write_reg(RFBI_CONTROL, l);
-
- r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback,
- NULL);
- if (r < 0) {
- dev_err(fbdev->dev, "can't get DISPC irq\n");
- rfbi_enable_clocks(0);
- return r;
- }
-
- l = rfbi_read_reg(RFBI_REVISION);
- pr_info("omapfb: RFBI version %d.%d initialized\n",
- (l >> 4) & 0x0f, l & 0x0f);
-
- rfbi_enable_clocks(0);
-
- return 0;
-}
-
-static void rfbi_cleanup(void)
-{
- omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL);
- rfbi_put_clocks();
- iounmap(rfbi.base);
-}
-
-const struct lcd_ctrl_extif omap2_ext_if = {
- .init = rfbi_init,
- .cleanup = rfbi_cleanup,
- .get_clk_info = rfbi_get_clk_info,
- .get_max_tx_rate = rfbi_get_max_tx_rate,
- .set_bits_per_cycle = rfbi_set_bits_per_cycle,
- .convert_timings = rfbi_convert_timings,
- .set_timings = rfbi_set_timings,
- .write_command = rfbi_write_command,
- .read_data = rfbi_read_data,
- .write_data = rfbi_write_data,
- .transfer_area = rfbi_transfer_area,
- .setup_tearsync = rfbi_setup_tearsync,
- .enable_tearsync = rfbi_enable_tearsync,
-
- .max_transmit_size = (u32) ~0,
-};
-
diff --git a/drivers/video/omap2/displays/panel-acx565akm.c b/drivers/video/omap2/displays/panel-acx565akm.c
index 51a87e149e24..d26f37ac69d8 100644
--- a/drivers/video/omap2/displays/panel-acx565akm.c
+++ b/drivers/video/omap2/displays/panel-acx565akm.c
@@ -809,18 +809,7 @@ static struct spi_driver acx565akm_spi_driver = {
.remove = __devexit_p(acx565akm_spi_remove),
};
-static int __init acx565akm_init(void)
-{
- return spi_register_driver(&acx565akm_spi_driver);
-}
-
-static void __exit acx565akm_exit(void)
-{
- spi_unregister_driver(&acx565akm_spi_driver);
-}
-
-module_init(acx565akm_init);
-module_exit(acx565akm_exit);
+module_spi_driver(acx565akm_spi_driver);
MODULE_AUTHOR("Nokia Corporation");
MODULE_DESCRIPTION("acx565akm LCD Driver");
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index 28b9a6d61b0f..30fe4dfeb227 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -363,6 +363,29 @@ static struct panel_config generic_dpi_panels[] = {
.name = "ortustech_com43h4m10xtc",
},
+
+ /* Innolux AT080TN52 */
+ {
+ {
+ .x_res = 800,
+ .y_res = 600,
+
+ .pixel_clock = 41142,
+
+ .hsw = 20,
+ .hfp = 210,
+ .hbp = 46,
+
+ .vsw = 10,
+ .vfp = 12,
+ .vbp = 23,
+ },
+ .acb = 0x0,
+ .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
+ OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IEO,
+
+ .name = "innolux_at080tn52",
+ },
};
struct panel_drv_data {
diff --git a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
index e0eb35be303e..0841cc2b3f77 100644
--- a/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
+++ b/drivers/video/omap2/displays/panel-lgphilips-lb035q02.c
@@ -264,16 +264,6 @@ static struct spi_driver lb035q02_spi_driver = {
.remove = __devexit_p(lb035q02_panel_spi_remove),
};
-static int __init lb035q02_panel_drv_init(void)
-{
- return spi_register_driver(&lb035q02_spi_driver);
-}
-
-static void __exit lb035q02_panel_drv_exit(void)
-{
- spi_unregister_driver(&lb035q02_spi_driver);
-}
+module_spi_driver(lb035q02_spi_driver);
-module_init(lb035q02_panel_drv_init);
-module_exit(lb035q02_panel_drv_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
index 0eb31caddca8..8b38b39213f4 100644
--- a/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
+++ b/drivers/video/omap2/displays/panel-nec-nl8048hl11-01b.c
@@ -350,18 +350,8 @@ static struct spi_driver nec_8048_spi_driver = {
},
};
-static int __init nec_8048_lcd_init(void)
-{
- return spi_register_driver(&nec_8048_spi_driver);
-}
-
-static void __exit nec_8048_lcd_exit(void)
-{
- return spi_unregister_driver(&nec_8048_spi_driver);
-}
+module_spi_driver(nec_8048_spi_driver);
-module_init(nec_8048_lcd_init);
-module_exit(nec_8048_lcd_exit);
MODULE_AUTHOR("Erik Gilling <konkers@android.com>");
MODULE_DESCRIPTION("NEC-nl8048hl11-01b Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/panel-taal.c b/drivers/video/omap2/displays/panel-taal.c
index 00c5c615585f..0f21fa5a16ae 100644
--- a/drivers/video/omap2/displays/panel-taal.c
+++ b/drivers/video/omap2/displays/panel-taal.c
@@ -1019,14 +1019,12 @@ static int taal_probe(struct omap_dss_device *dssdev)
if (panel_data->use_ext_te) {
int gpio = panel_data->ext_te_gpio;
- r = gpio_request(gpio, "taal irq");
+ r = gpio_request_one(gpio, GPIOF_IN, "taal irq");
if (r) {
dev_err(&dssdev->dev, "GPIO request failed\n");
goto err_gpio;
}
- gpio_direction_input(gpio);
-
r = request_irq(gpio_to_irq(gpio), taal_te_isr,
IRQF_TRIGGER_RISING,
"taal vsync", dssdev);
diff --git a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
index e6649aa89591..32f3fcd7f0f0 100644
--- a/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
+++ b/drivers/video/omap2/displays/panel-tpo-td043mtea1.c
@@ -47,16 +47,20 @@
TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL)
static const u16 tpo_td043_def_gamma[12] = {
- 106, 200, 289, 375, 460, 543, 625, 705, 785, 864, 942, 1020
+ 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023
};
struct tpo_td043_device {
struct spi_device *spi;
struct regulator *vcc_reg;
+ int nreset_gpio;
u16 gamma[12];
u32 mode;
u32 hmirror:1;
u32 vmirror:1;
+ u32 powered_on:1;
+ u32 spi_suspended:1;
+ u32 power_on_resume:1;
};
static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data)
@@ -265,28 +269,16 @@ static const struct omap_video_timings tpo_td043_timings = {
.vbp = 34,
};
-static int tpo_td043_power_on(struct omap_dss_device *dssdev)
+static int tpo_td043_power_on(struct tpo_td043_device *tpo_td043)
{
- struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
- int nreset_gpio = dssdev->reset_gpio;
- int r;
+ int nreset_gpio = tpo_td043->nreset_gpio;
- if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ if (tpo_td043->powered_on)
return 0;
- r = omapdss_dpi_display_enable(dssdev);
- if (r)
- goto err0;
-
- if (dssdev->platform_enable) {
- r = dssdev->platform_enable(dssdev);
- if (r)
- goto err1;
- }
-
regulator_enable(tpo_td043->vcc_reg);
- /* wait for power up */
+ /* wait for regulator to stabilize */
msleep(160);
if (gpio_is_valid(nreset_gpio))
@@ -301,19 +293,15 @@ static int tpo_td043_power_on(struct omap_dss_device *dssdev)
tpo_td043->vmirror);
tpo_td043_write_gamma(tpo_td043->spi, tpo_td043->gamma);
+ tpo_td043->powered_on = 1;
return 0;
-err1:
- omapdss_dpi_display_disable(dssdev);
-err0:
- return r;
}
-static void tpo_td043_power_off(struct omap_dss_device *dssdev)
+static void tpo_td043_power_off(struct tpo_td043_device *tpo_td043)
{
- struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
- int nreset_gpio = dssdev->reset_gpio;
+ int nreset_gpio = tpo_td043->nreset_gpio;
- if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ if (!tpo_td043->powered_on)
return;
tpo_td043_write(tpo_td043->spi, 3,
@@ -329,54 +317,94 @@ static void tpo_td043_power_off(struct omap_dss_device *dssdev)
regulator_disable(tpo_td043->vcc_reg);
+ tpo_td043->powered_on = 0;
+}
+
+static int tpo_td043_enable_dss(struct omap_dss_device *dssdev)
+{
+ struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ return 0;
+
+ r = omapdss_dpi_display_enable(dssdev);
+ if (r)
+ goto err0;
+
+ if (dssdev->platform_enable) {
+ r = dssdev->platform_enable(dssdev);
+ if (r)
+ goto err1;
+ }
+
+ /*
+ * If we are resuming from system suspend, SPI clocks might not be
+ * enabled yet, so we'll program the LCD from SPI PM resume callback.
+ */
+ if (!tpo_td043->spi_suspended) {
+ r = tpo_td043_power_on(tpo_td043);
+ if (r)
+ goto err1;
+ }
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ return 0;
+err1:
+ omapdss_dpi_display_disable(dssdev);
+err0:
+ return r;
+}
+
+static void tpo_td043_disable_dss(struct omap_dss_device *dssdev)
+{
+ struct tpo_td043_device *tpo_td043 = dev_get_drvdata(&dssdev->dev);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return;
+
if (dssdev->platform_disable)
dssdev->platform_disable(dssdev);
omapdss_dpi_display_disable(dssdev);
+
+ if (!tpo_td043->spi_suspended)
+ tpo_td043_power_off(tpo_td043);
}
static int tpo_td043_enable(struct omap_dss_device *dssdev)
{
- int ret;
-
dev_dbg(&dssdev->dev, "enable\n");
- ret = tpo_td043_power_on(dssdev);
- if (ret)
- return ret;
-
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
- return 0;
+ return tpo_td043_enable_dss(dssdev);
}
static void tpo_td043_disable(struct omap_dss_device *dssdev)
{
dev_dbg(&dssdev->dev, "disable\n");
- tpo_td043_power_off(dssdev);
+ tpo_td043_disable_dss(dssdev);
dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
}
static int tpo_td043_suspend(struct omap_dss_device *dssdev)
{
- tpo_td043_power_off(dssdev);
+ dev_dbg(&dssdev->dev, "suspend\n");
+
+ tpo_td043_disable_dss(dssdev);
+
dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
return 0;
}
static int tpo_td043_resume(struct omap_dss_device *dssdev)
{
- int r = 0;
-
- r = tpo_td043_power_on(dssdev);
- if (r)
- return r;
+ dev_dbg(&dssdev->dev, "resume\n");
- dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
-
- return 0;
+ return tpo_td043_enable_dss(dssdev);
}
static int tpo_td043_probe(struct omap_dss_device *dssdev)
@@ -408,17 +436,12 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
}
if (gpio_is_valid(nreset_gpio)) {
- ret = gpio_request(nreset_gpio, "lcd reset");
+ ret = gpio_request_one(nreset_gpio, GPIOF_OUT_INIT_LOW,
+ "lcd reset");
if (ret < 0) {
dev_err(&dssdev->dev, "couldn't request reset GPIO\n");
goto fail_gpio_req;
}
-
- ret = gpio_direction_output(nreset_gpio, 0);
- if (ret < 0) {
- dev_err(&dssdev->dev, "couldn't set GPIO direction\n");
- goto fail_gpio_direction;
- }
}
ret = sysfs_create_group(&dssdev->dev.kobj, &tpo_td043_attr_group);
@@ -427,8 +450,6 @@ static int tpo_td043_probe(struct omap_dss_device *dssdev)
return 0;
-fail_gpio_direction:
- gpio_free(nreset_gpio);
fail_gpio_req:
regulator_put(tpo_td043->vcc_reg);
fail_regulator:
@@ -491,6 +512,7 @@ static int tpo_td043_spi_probe(struct spi_device *spi)
return -ENOMEM;
tpo_td043->spi = spi;
+ tpo_td043->nreset_gpio = dssdev->reset_gpio;
dev_set_drvdata(&spi->dev, tpo_td043);
dev_set_drvdata(&dssdev->dev, tpo_td043);
@@ -509,27 +531,52 @@ static int __devexit tpo_td043_spi_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int tpo_td043_spi_suspend(struct device *dev)
+{
+ struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", tpo_td043);
+
+ tpo_td043->power_on_resume = tpo_td043->powered_on;
+ tpo_td043_power_off(tpo_td043);
+ tpo_td043->spi_suspended = 1;
+
+ return 0;
+}
+
+static int tpo_td043_spi_resume(struct device *dev)
+{
+ struct tpo_td043_device *tpo_td043 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "tpo_td043_spi_resume\n");
+
+ if (tpo_td043->power_on_resume) {
+ ret = tpo_td043_power_on(tpo_td043);
+ if (ret)
+ return ret;
+ }
+ tpo_td043->spi_suspended = 0;
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm,
+ tpo_td043_spi_suspend, tpo_td043_spi_resume);
+
static struct spi_driver tpo_td043_spi_driver = {
.driver = {
.name = "tpo_td043mtea1_panel_spi",
.owner = THIS_MODULE,
+ .pm = &tpo_td043_spi_pm,
},
.probe = tpo_td043_spi_probe,
.remove = __devexit_p(tpo_td043_spi_remove),
};
-static int __init tpo_td043_init(void)
-{
- return spi_register_driver(&tpo_td043_spi_driver);
-}
-
-static void __exit tpo_td043_exit(void)
-{
- spi_unregister_driver(&tpo_td043_spi_driver);
-}
-
-module_init(tpo_td043_init);
-module_exit(tpo_td043_exit);
+module_spi_driver(tpo_td043_spi_driver);
MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>");
MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver");
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c
index 87b3e25294cf..b10b3bc1931e 100644
--- a/drivers/video/omap2/dss/apply.c
+++ b/drivers/video/omap2/dss/apply.c
@@ -105,6 +105,9 @@ static struct {
struct ovl_priv_data ovl_priv_data_array[MAX_DSS_OVERLAYS];
struct mgr_priv_data mgr_priv_data_array[MAX_DSS_MANAGERS];
+ bool fifo_merge_dirty;
+ bool fifo_merge;
+
bool irq_enabled;
} dss_data;
@@ -351,6 +354,7 @@ static void wait_pending_extra_info_updates(void)
bool updating;
unsigned long flags;
unsigned long t;
+ int r;
spin_lock_irqsave(&data_lock, flags);
@@ -366,11 +370,11 @@ static void wait_pending_extra_info_updates(void)
spin_unlock_irqrestore(&data_lock, flags);
t = msecs_to_jiffies(500);
- wait_for_completion_timeout(&extra_updated_completion, t);
-
- updating = extra_info_update_ongoing();
-
- WARN_ON(updating);
+ r = wait_for_completion_timeout(&extra_updated_completion, t);
+ if (r == 0)
+ DSSWARN("timeout in wait_pending_extra_info_updates\n");
+ else if (r < 0)
+ DSSERR("wait_pending_extra_info_updates failed: %d\n", r);
}
int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
@@ -388,6 +392,10 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
if (mgr_manual_update(mgr))
return 0;
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
irq = dispc_mgr_get_vsync_irq(mgr->id);
mp = get_mgr_priv(mgr);
@@ -428,6 +436,8 @@ int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr)
}
}
+ dispc_runtime_put();
+
return r;
}
@@ -451,6 +461,10 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
if (ovl_manual_update(ovl))
return 0;
+ r = dispc_runtime_get();
+ if (r)
+ return r;
+
irq = dispc_mgr_get_vsync_irq(ovl->manager->id);
op = get_ovl_priv(ovl);
@@ -491,6 +505,8 @@ int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl)
}
}
+ dispc_runtime_put();
+
return r;
}
@@ -585,11 +601,40 @@ static void dss_mgr_write_regs(struct omap_overlay_manager *mgr)
}
}
+static void dss_write_regs_common(void)
+{
+ const int num_mgrs = omap_dss_get_num_overlay_managers();
+ int i;
+
+ if (!dss_data.fifo_merge_dirty)
+ return;
+
+ for (i = 0; i < num_mgrs; ++i) {
+ struct omap_overlay_manager *mgr;
+ struct mgr_priv_data *mp;
+
+ mgr = omap_dss_get_overlay_manager(i);
+ mp = get_mgr_priv(mgr);
+
+ if (mp->enabled) {
+ if (dss_data.fifo_merge_dirty) {
+ dispc_enable_fifomerge(dss_data.fifo_merge);
+ dss_data.fifo_merge_dirty = false;
+ }
+
+ if (mp->updating)
+ mp->shadow_info_dirty = true;
+ }
+ }
+}
+
static void dss_write_regs(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
int i;
+ dss_write_regs_common();
+
for (i = 0; i < num_mgrs; ++i) {
struct omap_overlay_manager *mgr;
struct mgr_priv_data *mp;
@@ -640,6 +685,22 @@ static void dss_set_go_bits(void)
}
+static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
+{
+ struct omap_overlay *ovl;
+ struct mgr_priv_data *mp;
+ struct ovl_priv_data *op;
+
+ mp = get_mgr_priv(mgr);
+ mp->shadow_info_dirty = false;
+
+ list_for_each_entry(ovl, &mgr->overlays, list) {
+ op = get_ovl_priv(ovl);
+ op->shadow_info_dirty = false;
+ op->shadow_extra_info_dirty = false;
+ }
+}
+
void dss_mgr_start_update(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
@@ -659,6 +720,8 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
dss_mgr_write_regs(mgr);
+ dss_write_regs_common();
+
mp->updating = true;
if (!dss_data.irq_enabled && need_isr())
@@ -666,6 +729,8 @@ void dss_mgr_start_update(struct omap_overlay_manager *mgr)
dispc_mgr_enable(mgr->id, true);
+ mgr_clear_shadow_dirty(mgr);
+
spin_unlock_irqrestore(&data_lock, flags);
}
@@ -709,22 +774,6 @@ static void dss_unregister_vsync_isr(void)
dss_data.irq_enabled = false;
}
-static void mgr_clear_shadow_dirty(struct omap_overlay_manager *mgr)
-{
- struct omap_overlay *ovl;
- struct mgr_priv_data *mp;
- struct ovl_priv_data *op;
-
- mp = get_mgr_priv(mgr);
- mp->shadow_info_dirty = false;
-
- list_for_each_entry(ovl, &mgr->overlays, list) {
- op = get_ovl_priv(ovl);
- op->shadow_info_dirty = false;
- op->shadow_extra_info_dirty = false;
- }
-}
-
static void dss_apply_irq_handler(void *data, u32 mask)
{
const int num_mgrs = dss_feat_get_num_mgrs();
@@ -754,9 +803,6 @@ static void dss_apply_irq_handler(void *data, u32 mask)
if (was_busy && !mp->busy)
mgr_clear_shadow_dirty(mgr);
- } else {
- if (was_updating && !mp->updating)
- mgr_clear_shadow_dirty(mgr);
}
}
@@ -859,11 +905,20 @@ static void dss_apply_ovl_fifo_thresholds(struct omap_overlay *ovl,
op->extra_info_dirty = true;
}
-static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
+static void dss_apply_fifo_merge(bool use_fifo_merge)
+{
+ if (dss_data.fifo_merge == use_fifo_merge)
+ return;
+
+ dss_data.fifo_merge = use_fifo_merge;
+ dss_data.fifo_merge_dirty = true;
+}
+
+static void dss_ovl_setup_fifo(struct omap_overlay *ovl,
+ bool use_fifo_merge)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
struct omap_dss_device *dssdev;
- u32 size, burst_size;
u32 fifo_low, fifo_high;
if (!op->enabled && !op->enabling)
@@ -871,33 +926,14 @@ static void dss_ovl_setup_fifo(struct omap_overlay *ovl)
dssdev = ovl->manager->device;
- size = dispc_ovl_get_fifo_size(ovl->id);
-
- burst_size = dispc_ovl_get_burst_size(ovl->id);
-
- switch (dssdev->type) {
- case OMAP_DISPLAY_TYPE_DPI:
- case OMAP_DISPLAY_TYPE_DBI:
- case OMAP_DISPLAY_TYPE_SDI:
- case OMAP_DISPLAY_TYPE_VENC:
- case OMAP_DISPLAY_TYPE_HDMI:
- default_get_overlay_fifo_thresholds(ovl->id, size,
- burst_size, &fifo_low, &fifo_high);
- break;
-#ifdef CONFIG_OMAP2_DSS_DSI
- case OMAP_DISPLAY_TYPE_DSI:
- dsi_get_overlay_fifo_thresholds(ovl->id, size,
- burst_size, &fifo_low, &fifo_high);
- break;
-#endif
- default:
- BUG();
- }
+ dispc_ovl_compute_fifo_thresholds(ovl->id, &fifo_low, &fifo_high,
+ use_fifo_merge);
dss_apply_ovl_fifo_thresholds(ovl, fifo_low, fifo_high);
}
-static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
+static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr,
+ bool use_fifo_merge)
{
struct omap_overlay *ovl;
struct mgr_priv_data *mp;
@@ -908,19 +944,94 @@ static void dss_mgr_setup_fifos(struct omap_overlay_manager *mgr)
return;
list_for_each_entry(ovl, &mgr->overlays, list)
- dss_ovl_setup_fifo(ovl);
+ dss_ovl_setup_fifo(ovl, use_fifo_merge);
+}
+
+static void dss_setup_fifos(bool use_fifo_merge)
+{
+ const int num_mgrs = omap_dss_get_num_overlay_managers();
+ struct omap_overlay_manager *mgr;
+ int i;
+
+ for (i = 0; i < num_mgrs; ++i) {
+ mgr = omap_dss_get_overlay_manager(i);
+ dss_mgr_setup_fifos(mgr, use_fifo_merge);
+ }
}
-static void dss_setup_fifos(void)
+static int get_num_used_managers(void)
{
const int num_mgrs = omap_dss_get_num_overlay_managers();
struct omap_overlay_manager *mgr;
+ struct mgr_priv_data *mp;
int i;
+ int enabled_mgrs;
+
+ enabled_mgrs = 0;
for (i = 0; i < num_mgrs; ++i) {
mgr = omap_dss_get_overlay_manager(i);
- dss_mgr_setup_fifos(mgr);
+ mp = get_mgr_priv(mgr);
+
+ if (!mp->enabled)
+ continue;
+
+ enabled_mgrs++;
+ }
+
+ return enabled_mgrs;
+}
+
+static int get_num_used_overlays(void)
+{
+ const int num_ovls = omap_dss_get_num_overlays();
+ struct omap_overlay *ovl;
+ struct ovl_priv_data *op;
+ struct mgr_priv_data *mp;
+ int i;
+ int enabled_ovls;
+
+ enabled_ovls = 0;
+
+ for (i = 0; i < num_ovls; ++i) {
+ ovl = omap_dss_get_overlay(i);
+ op = get_ovl_priv(ovl);
+
+ if (!op->enabled && !op->enabling)
+ continue;
+
+ mp = get_mgr_priv(ovl->manager);
+
+ if (!mp->enabled)
+ continue;
+
+ enabled_ovls++;
}
+
+ return enabled_ovls;
+}
+
+static bool get_use_fifo_merge(void)
+{
+ int enabled_mgrs = get_num_used_managers();
+ int enabled_ovls = get_num_used_overlays();
+
+ if (!dss_has_feature(FEAT_FIFO_MERGE))
+ return false;
+
+ /*
+ * In theory the only requirement for fifomerge is enabled_ovls <= 1.
+ * However, if we have two managers enabled and set/unset the fifomerge,
+ * we need to set the GO bits in particular sequence for the managers,
+ * and wait in between.
+ *
+ * This is rather difficult as new apply calls can happen at any time,
+ * so we simplify the problem by requiring also that enabled_mgrs <= 1.
+ * In practice this shouldn't matter, because when only one overlay is
+ * enabled, most likely only one output is enabled.
+ */
+
+ return enabled_mgrs <= 1 && enabled_ovls <= 1;
}
int dss_mgr_enable(struct omap_overlay_manager *mgr)
@@ -928,6 +1039,7 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
int r;
+ bool fifo_merge;
mutex_lock(&apply_lock);
@@ -945,11 +1057,23 @@ int dss_mgr_enable(struct omap_overlay_manager *mgr)
goto err;
}
- dss_setup_fifos();
+ /* step 1: setup fifos/fifomerge before enabling the manager */
+
+ fifo_merge = get_use_fifo_merge();
+ dss_setup_fifos(fifo_merge);
+ dss_apply_fifo_merge(fifo_merge);
dss_write_regs();
dss_set_go_bits();
+ spin_unlock_irqrestore(&data_lock, flags);
+
+ /* wait until fifo config is in */
+ wait_pending_extra_info_updates();
+
+ /* step 2: enable the manager */
+ spin_lock_irqsave(&data_lock, flags);
+
if (!mgr_manual_update(mgr))
mp->updating = true;
@@ -974,6 +1098,7 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
{
struct mgr_priv_data *mp = get_mgr_priv(mgr);
unsigned long flags;
+ bool fifo_merge;
mutex_lock(&apply_lock);
@@ -988,8 +1113,16 @@ void dss_mgr_disable(struct omap_overlay_manager *mgr)
mp->updating = false;
mp->enabled = false;
+ fifo_merge = get_use_fifo_merge();
+ dss_setup_fifos(fifo_merge);
+ dss_apply_fifo_merge(fifo_merge);
+
+ dss_write_regs();
+ dss_set_go_bits();
+
spin_unlock_irqrestore(&data_lock, flags);
+ wait_pending_extra_info_updates();
out:
mutex_unlock(&apply_lock);
}
@@ -1241,6 +1374,7 @@ int dss_ovl_enable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
+ bool fifo_merge;
int r;
mutex_lock(&apply_lock);
@@ -1266,7 +1400,22 @@ int dss_ovl_enable(struct omap_overlay *ovl)
goto err2;
}
- dss_setup_fifos();
+ /* step 1: configure fifos/fifomerge for currently enabled ovls */
+
+ fifo_merge = get_use_fifo_merge();
+ dss_setup_fifos(fifo_merge);
+ dss_apply_fifo_merge(fifo_merge);
+
+ dss_write_regs();
+ dss_set_go_bits();
+
+ spin_unlock_irqrestore(&data_lock, flags);
+
+ /* wait for fifo configs to go in */
+ wait_pending_extra_info_updates();
+
+ /* step 2: enable the overlay */
+ spin_lock_irqsave(&data_lock, flags);
op->enabling = false;
dss_apply_ovl_enable(ovl, true);
@@ -1294,6 +1443,7 @@ int dss_ovl_disable(struct omap_overlay *ovl)
{
struct ovl_priv_data *op = get_ovl_priv(ovl);
unsigned long flags;
+ bool fifo_merge;
int r;
mutex_lock(&apply_lock);
@@ -1308,9 +1458,11 @@ int dss_ovl_disable(struct omap_overlay *ovl)
goto err;
}
+ /* step 1: disable the overlay */
spin_lock_irqsave(&data_lock, flags);
dss_apply_ovl_enable(ovl, false);
+
dss_write_regs();
dss_set_go_bits();
@@ -1319,6 +1471,21 @@ int dss_ovl_disable(struct omap_overlay *ovl)
/* wait for the overlay to be disabled */
wait_pending_extra_info_updates();
+ /* step 2: configure fifos/fifomerge */
+ spin_lock_irqsave(&data_lock, flags);
+
+ fifo_merge = get_use_fifo_merge();
+ dss_setup_fifos(fifo_merge);
+ dss_apply_fifo_merge(fifo_merge);
+
+ dss_write_regs();
+ dss_set_go_bits();
+
+ spin_unlock_irqrestore(&data_lock, flags);
+
+ /* wait for fifo config to go in */
+ wait_pending_extra_info_updates();
+
mutex_unlock(&apply_lock);
return 0;
diff --git a/drivers/video/omap2/dss/core.c b/drivers/video/omap2/dss/core.c
index 8613f86fb56d..e8a120771ac6 100644
--- a/drivers/video/omap2/dss/core.c
+++ b/drivers/video/omap2/dss/core.c
@@ -183,42 +183,6 @@ static int omap_dss_probe(struct platform_device *pdev)
dss_init_overlay_managers(pdev);
dss_init_overlays(pdev);
- r = dss_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize DSS platform driver\n");
- goto err_dss;
- }
-
- r = dispc_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize dispc platform driver\n");
- goto err_dispc;
- }
-
- r = rfbi_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize rfbi platform driver\n");
- goto err_rfbi;
- }
-
- r = venc_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize venc platform driver\n");
- goto err_venc;
- }
-
- r = dsi_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize DSI platform driver\n");
- goto err_dsi;
- }
-
- r = hdmi_init_platform_driver();
- if (r) {
- DSSERR("Failed to initialize hdmi\n");
- goto err_hdmi;
- }
-
r = dss_initialize_debugfs();
if (r)
goto err_debugfs;
@@ -246,18 +210,6 @@ static int omap_dss_probe(struct platform_device *pdev)
err_register:
dss_uninitialize_debugfs();
err_debugfs:
- hdmi_uninit_platform_driver();
-err_hdmi:
- dsi_uninit_platform_driver();
-err_dsi:
- venc_uninit_platform_driver();
-err_venc:
- dispc_uninit_platform_driver();
-err_dispc:
- rfbi_uninit_platform_driver();
-err_rfbi:
- dss_uninit_platform_driver();
-err_dss:
return r;
}
@@ -269,13 +221,6 @@ static int omap_dss_remove(struct platform_device *pdev)
dss_uninitialize_debugfs();
- hdmi_uninit_platform_driver();
- dsi_uninit_platform_driver();
- venc_uninit_platform_driver();
- rfbi_uninit_platform_driver();
- dispc_uninit_platform_driver();
- dss_uninit_platform_driver();
-
dss_uninit_overlays(pdev);
dss_uninit_overlay_managers(pdev);
@@ -525,6 +470,80 @@ static int omap_dss_bus_register(void)
/* INIT */
+static int __init omap_dss_register_drivers(void)
+{
+ int r;
+
+ r = platform_driver_register(&omap_dss_driver);
+ if (r)
+ return r;
+
+ r = dss_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize DSS platform driver\n");
+ goto err_dss;
+ }
+
+ r = dispc_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize dispc platform driver\n");
+ goto err_dispc;
+ }
+
+ r = rfbi_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize rfbi platform driver\n");
+ goto err_rfbi;
+ }
+
+ r = venc_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize venc platform driver\n");
+ goto err_venc;
+ }
+
+ r = dsi_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize DSI platform driver\n");
+ goto err_dsi;
+ }
+
+ r = hdmi_init_platform_driver();
+ if (r) {
+ DSSERR("Failed to initialize hdmi\n");
+ goto err_hdmi;
+ }
+
+ return 0;
+
+err_hdmi:
+ dsi_uninit_platform_driver();
+err_dsi:
+ venc_uninit_platform_driver();
+err_venc:
+ rfbi_uninit_platform_driver();
+err_rfbi:
+ dispc_uninit_platform_driver();
+err_dispc:
+ dss_uninit_platform_driver();
+err_dss:
+ platform_driver_unregister(&omap_dss_driver);
+
+ return r;
+}
+
+static void __exit omap_dss_unregister_drivers(void)
+{
+ hdmi_uninit_platform_driver();
+ dsi_uninit_platform_driver();
+ venc_uninit_platform_driver();
+ rfbi_uninit_platform_driver();
+ dispc_uninit_platform_driver();
+ dss_uninit_platform_driver();
+
+ platform_driver_unregister(&omap_dss_driver);
+}
+
#ifdef CONFIG_OMAP2_DSS_MODULE
static void omap_dss_bus_unregister(void)
{
@@ -541,7 +560,7 @@ static int __init omap_dss_init(void)
if (r)
return r;
- r = platform_driver_register(&omap_dss_driver);
+ r = omap_dss_register_drivers();
if (r) {
omap_dss_bus_unregister();
return r;
@@ -562,7 +581,7 @@ static void __exit omap_dss_exit(void)
core.vdds_sdi_reg = NULL;
}
- platform_driver_unregister(&omap_dss_driver);
+ omap_dss_unregister_drivers();
omap_dss_bus_unregister();
}
@@ -577,7 +596,7 @@ static int __init omap_dss_init(void)
static int __init omap_dss_init2(void)
{
- return platform_driver_register(&omap_dss_driver);
+ return omap_dss_register_drivers();
}
core_initcall(omap_dss_init);
diff --git a/drivers/video/omap2/dss/dispc.c b/drivers/video/omap2/dss/dispc.c
index e1626a1d5c45..ee30937482e1 100644
--- a/drivers/video/omap2/dss/dispc.c
+++ b/drivers/video/omap2/dss/dispc.c
@@ -37,7 +37,6 @@
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <plat/sram.h>
#include <plat/clock.h>
#include <video/omapdss.h>
@@ -736,11 +735,11 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane,
switch (color_mode) {
case OMAP_DSS_COLOR_NV12:
m = 0x0; break;
- case OMAP_DSS_COLOR_RGB12U:
+ case OMAP_DSS_COLOR_RGBX16:
m = 0x1; break;
case OMAP_DSS_COLOR_RGBA16:
m = 0x2; break;
- case OMAP_DSS_COLOR_RGBX16:
+ case OMAP_DSS_COLOR_RGB12U:
m = 0x4; break;
case OMAP_DSS_COLOR_ARGB16:
m = 0x5; break;
@@ -789,9 +788,9 @@ static void dispc_ovl_set_color_mode(enum omap_plane plane,
m = 0x8; break;
case OMAP_DSS_COLOR_RGB24P:
m = 0x9; break;
- case OMAP_DSS_COLOR_YUV2:
+ case OMAP_DSS_COLOR_RGBX16:
m = 0xa; break;
- case OMAP_DSS_COLOR_UYVY:
+ case OMAP_DSS_COLOR_RGBA16:
m = 0xb; break;
case OMAP_DSS_COLOR_ARGB32:
m = 0xc; break;
@@ -909,7 +908,7 @@ static void dispc_configure_burst_sizes(void)
dispc_ovl_set_burst_size(i, burst_size);
}
-u32 dispc_ovl_get_burst_size(enum omap_plane plane)
+static u32 dispc_ovl_get_burst_size(enum omap_plane plane)
{
unsigned unit = dss_feat_get_burst_size_unit();
/* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
@@ -1018,7 +1017,7 @@ static void dispc_read_plane_fifo_sizes(void)
}
}
-u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
+static u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
{
return dispc.fifo_size[plane];
}
@@ -1039,13 +1038,13 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);
- DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n",
+ DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n",
plane,
REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
- lo_start, lo_end),
+ lo_start, lo_end) * unit,
REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
- hi_start, hi_end),
- low, high);
+ hi_start, hi_end) * unit,
+ low * unit, high * unit);
dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane),
FLD_VAL(high, hi_start, hi_end) |
@@ -1054,10 +1053,53 @@ void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
void dispc_enable_fifomerge(bool enable)
{
+ if (!dss_has_feature(FEAT_FIFO_MERGE)) {
+ WARN_ON(enable);
+ return;
+ }
+
DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
}
+void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
+ u32 *fifo_low, u32 *fifo_high, bool use_fifomerge)
+{
+ /*
+ * All sizes are in bytes. Both the buffer and burst are made of
+ * buffer_units, and the fifo thresholds must be buffer_unit aligned.
+ */
+
+ unsigned buf_unit = dss_feat_get_buffer_size_unit();
+ unsigned ovl_fifo_size, total_fifo_size, burst_size;
+ int i;
+
+ burst_size = dispc_ovl_get_burst_size(plane);
+ ovl_fifo_size = dispc_ovl_get_fifo_size(plane);
+
+ if (use_fifomerge) {
+ total_fifo_size = 0;
+ for (i = 0; i < omap_dss_get_num_overlays(); ++i)
+ total_fifo_size += dispc_ovl_get_fifo_size(i);
+ } else {
+ total_fifo_size = ovl_fifo_size;
+ }
+
+ /*
+ * We use the same low threshold for both fifomerge and non-fifomerge
+ * cases, but for fifomerge we calculate the high threshold using the
+ * combined fifo size
+ */
+
+ if (dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) {
+ *fifo_low = ovl_fifo_size - burst_size * 2;
+ *fifo_high = total_fifo_size - burst_size;
+ } else {
+ *fifo_low = ovl_fifo_size - burst_size;
+ *fifo_high = total_fifo_size - buf_unit;
+ }
+}
+
static void dispc_ovl_set_fir(enum omap_plane plane,
int hinc, int vinc,
enum omap_color_component color_comp)
@@ -1651,6 +1693,7 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
u16 height, u16 out_width, u16 out_height)
{
unsigned int hf, vf;
+ unsigned long pclk = dispc_mgr_pclk_rate(channel);
/*
* FIXME how to determine the 'A' factor
@@ -1673,13 +1716,16 @@ static unsigned long calc_fclk(enum omap_channel channel, u16 width,
if (cpu_is_omap24xx()) {
if (vf > 1 && hf > 1)
- return dispc_mgr_pclk_rate(channel) * 4;
+ return pclk * 4;
else
- return dispc_mgr_pclk_rate(channel) * 2;
+ return pclk * 2;
} else if (cpu_is_omap34xx()) {
- return dispc_mgr_pclk_rate(channel) * vf * hf;
+ return pclk * vf * hf;
} else {
- return dispc_mgr_pclk_rate(channel) * hf;
+ if (hf > 1)
+ return DIV_ROUND_UP(pclk, out_width) * width;
+ else
+ return pclk;
}
}
@@ -3272,11 +3318,6 @@ static void _omap_dispc_initial_config(void)
if (dss_has_feature(FEAT_FUNCGATED))
REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
- /* L3 firewall setting: enable access to OCM RAM */
- /* XXX this should be somewhere in plat-omap */
- if (cpu_is_omap24xx())
- __raw_writel(0x402000b0, OMAP2_L3_IO_ADDRESS(0x680050a0));
-
_dispc_setup_color_conv_coef();
dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
@@ -3298,15 +3339,6 @@ static int omap_dispchw_probe(struct platform_device *pdev)
dispc.pdev = pdev;
- clk = clk_get(&pdev->dev, "fck");
- if (IS_ERR(clk)) {
- DSSERR("can't get fck\n");
- r = PTR_ERR(clk);
- goto err_get_clk;
- }
-
- dispc.dss_clk = clk;
-
spin_lock_init(&dispc.irq_lock);
#ifdef CONFIG_OMAP2_DSS_COLLECT_IRQ_STATS
@@ -3319,29 +3351,38 @@ static int omap_dispchw_probe(struct platform_device *pdev)
dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0);
if (!dispc_mem) {
DSSERR("can't get IORESOURCE_MEM DISPC\n");
- r = -EINVAL;
- goto err_ioremap;
+ return -EINVAL;
}
- dispc.base = ioremap(dispc_mem->start, resource_size(dispc_mem));
+
+ dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start,
+ resource_size(dispc_mem));
if (!dispc.base) {
DSSERR("can't ioremap DISPC\n");
- r = -ENOMEM;
- goto err_ioremap;
+ return -ENOMEM;
}
+
dispc.irq = platform_get_irq(dispc.pdev, 0);
if (dispc.irq < 0) {
DSSERR("platform_get_irq failed\n");
- r = -ENODEV;
- goto err_irq;
+ return -ENODEV;
}
- r = request_irq(dispc.irq, omap_dispc_irq_handler, IRQF_SHARED,
- "OMAP DISPC", dispc.pdev);
+ r = devm_request_irq(&pdev->dev, dispc.irq, omap_dispc_irq_handler,
+ IRQF_SHARED, "OMAP DISPC", dispc.pdev);
if (r < 0) {
DSSERR("request_irq failed\n");
- goto err_irq;
+ return r;
+ }
+
+ clk = clk_get(&pdev->dev, "fck");
+ if (IS_ERR(clk)) {
+ DSSERR("can't get fck\n");
+ r = PTR_ERR(clk);
+ return r;
}
+ dispc.dss_clk = clk;
+
pm_runtime_enable(&pdev->dev);
r = dispc_runtime_get();
@@ -3362,12 +3403,7 @@ static int omap_dispchw_probe(struct platform_device *pdev)
err_runtime_get:
pm_runtime_disable(&pdev->dev);
- free_irq(dispc.irq, dispc.pdev);
-err_irq:
- iounmap(dispc.base);
-err_ioremap:
clk_put(dispc.dss_clk);
-err_get_clk:
return r;
}
@@ -3377,8 +3413,6 @@ static int omap_dispchw_remove(struct platform_device *pdev)
clk_put(dispc.dss_clk);
- free_irq(dispc.irq, dispc.pdev);
- iounmap(dispc.base);
return 0;
}
diff --git a/drivers/video/omap2/dss/dispc_coefs.c b/drivers/video/omap2/dss/dispc_coefs.c
index 069bccbb3f12..038c15b04215 100644
--- a/drivers/video/omap2/dss/dispc_coefs.c
+++ b/drivers/video/omap2/dss/dispc_coefs.c
@@ -19,14 +19,13 @@
#include <linux/kernel.h>
#include <video/omapdss.h>
-#include "dispc.h"
-#define ARRAY_LEN(array) (sizeof(array) / sizeof(array[0]))
+#include "dispc.h"
static const struct dispc_coef coef3_M8[8] = {
{ 0, 0, 128, 0, 0 },
{ 0, -4, 123, 9, 0 },
- { 0, -4, 108, 87, 0 },
+ { 0, -4, 108, 24, 0 },
{ 0, -2, 87, 43, 0 },
{ 0, 64, 64, 0, 0 },
{ 0, 43, 87, -2, 0 },
@@ -168,7 +167,7 @@ static const struct dispc_coef coef5_M8[8] = {
static const struct dispc_coef coef5_M9[8] = {
{ -3, 10, 114, 10, -3 },
- { -6, 24, 110, 0, -1 },
+ { -6, 24, 111, 0, -1 },
{ -8, 40, 103, -7, 0 },
{ -11, 58, 91, -11, 1 },
{ 0, -12, 76, 76, -12 },
@@ -319,7 +318,7 @@ const struct dispc_coef *dispc_ovl_get_scale_coef(int inc, int five_taps)
};
inc /= 128;
- for (i = 0; i < ARRAY_LEN(coefs); ++i)
+ for (i = 0; i < ARRAY_SIZE(coefs); ++i)
if (inc >= coefs[i].Mmin && inc <= coefs[i].Mmax)
return five_taps ? coefs[i].coef_5 : coefs[i].coef_3;
return NULL;
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index be331dc5a61b..4424c198dbcd 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -279,16 +279,6 @@ void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
}
EXPORT_SYMBOL(omapdss_default_get_resolution);
-void default_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, u32 burst_size,
- u32 *fifo_low, u32 *fifo_high)
-{
- unsigned buf_unit = dss_feat_get_buffer_size_unit();
-
- *fifo_high = fifo_size - buf_unit;
- *fifo_low = fifo_size - burst_size;
-}
-
int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
{
switch (dssdev->type) {
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index 52f36ec1c8bb..662d14f8c2c3 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -4524,14 +4524,6 @@ int omapdss_dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
}
EXPORT_SYMBOL(omapdss_dsi_enable_te);
-void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, u32 burst_size,
- u32 *fifo_low, u32 *fifo_high)
-{
- *fifo_high = fifo_size - burst_size;
- *fifo_low = fifo_size - burst_size * 2;
-}
-
int dsi_init_display(struct omap_dss_device *dssdev)
{
struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev);
@@ -4695,11 +4687,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
struct resource *dsi_mem;
struct dsi_data *dsi;
- dsi = kzalloc(sizeof(*dsi), GFP_KERNEL);
- if (!dsi) {
- r = -ENOMEM;
- goto err_alloc;
- }
+ dsi = devm_kzalloc(&dsidev->dev, sizeof(*dsi), GFP_KERNEL);
+ if (!dsi)
+ return -ENOMEM;
dsi->pdev = dsidev;
dsi_pdev_map[dsi_module] = dsidev;
@@ -4722,12 +4712,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
mutex_init(&dsi->lock);
sema_init(&dsi->bus_lock, 1);
- r = dsi_get_clocks(dsidev);
- if (r)
- goto err_get_clk;
-
- pm_runtime_enable(&dsidev->dev);
-
INIT_DELAYED_WORK_DEFERRABLE(&dsi->framedone_timeout_work,
dsi_framedone_timeout_work_callback);
@@ -4739,27 +4723,27 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
dsi_mem = platform_get_resource(dsi->pdev, IORESOURCE_MEM, 0);
if (!dsi_mem) {
DSSERR("can't get IORESOURCE_MEM DSI\n");
- r = -EINVAL;
- goto err_ioremap;
+ return -EINVAL;
}
- dsi->base = ioremap(dsi_mem->start, resource_size(dsi_mem));
+
+ dsi->base = devm_ioremap(&dsidev->dev, dsi_mem->start,
+ resource_size(dsi_mem));
if (!dsi->base) {
DSSERR("can't ioremap DSI\n");
- r = -ENOMEM;
- goto err_ioremap;
+ return -ENOMEM;
}
+
dsi->irq = platform_get_irq(dsi->pdev, 0);
if (dsi->irq < 0) {
DSSERR("platform_get_irq failed\n");
- r = -ENODEV;
- goto err_get_irq;
+ return -ENODEV;
}
- r = request_irq(dsi->irq, omap_dsi_irq_handler, IRQF_SHARED,
- dev_name(&dsidev->dev), dsi->pdev);
+ r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
+ IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
if (r < 0) {
DSSERR("request_irq failed\n");
- goto err_get_irq;
+ return r;
}
/* DSI VCs initialization */
@@ -4771,9 +4755,15 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
dsi_calc_clock_param_ranges(dsidev);
+ r = dsi_get_clocks(dsidev);
+ if (r)
+ return r;
+
+ pm_runtime_enable(&dsidev->dev);
+
r = dsi_runtime_get(dsidev);
if (r)
- goto err_get_dsi;
+ goto err_runtime_get;
rev = dsi_read_reg(dsidev, DSI_REVISION);
dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
@@ -4791,15 +4781,9 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
return 0;
-err_get_dsi:
- free_irq(dsi->irq, dsi->pdev);
-err_get_irq:
- iounmap(dsi->base);
-err_ioremap:
+err_runtime_get:
pm_runtime_disable(&dsidev->dev);
-err_get_clk:
- kfree(dsi);
-err_alloc:
+ dsi_put_clocks(dsidev);
return r;
}
@@ -4823,11 +4807,6 @@ static int omap_dsihw_remove(struct platform_device *dsidev)
dsi->vdds_dsi_reg = NULL;
}
- free_irq(dsi->irq, dsi->pdev);
- iounmap(dsi->base);
-
- kfree(dsi);
-
return 0;
}
diff --git a/drivers/video/omap2/dss/dss.c b/drivers/video/omap2/dss/dss.c
index 77c2b5a32b5d..bd2d5e159463 100644
--- a/drivers/video/omap2/dss/dss.c
+++ b/drivers/video/omap2/dss/dss.c
@@ -33,7 +33,10 @@
#include <linux/pm_runtime.h>
#include <video/omapdss.h>
+
+#include <plat/cpu.h>
#include <plat/clock.h>
+
#include "dss.h"
#include "dss_features.h"
@@ -748,19 +751,19 @@ static int omap_dsshw_probe(struct platform_device *pdev)
dss_mem = platform_get_resource(dss.pdev, IORESOURCE_MEM, 0);
if (!dss_mem) {
DSSERR("can't get IORESOURCE_MEM DSS\n");
- r = -EINVAL;
- goto err_ioremap;
+ return -EINVAL;
}
- dss.base = ioremap(dss_mem->start, resource_size(dss_mem));
+
+ dss.base = devm_ioremap(&pdev->dev, dss_mem->start,
+ resource_size(dss_mem));
if (!dss.base) {
DSSERR("can't ioremap DSS\n");
- r = -ENOMEM;
- goto err_ioremap;
+ return -ENOMEM;
}
r = dss_get_clocks();
if (r)
- goto err_clocks;
+ return r;
pm_runtime_enable(&pdev->dev);
@@ -808,9 +811,6 @@ err_dpi:
err_runtime_get:
pm_runtime_disable(&pdev->dev);
dss_put_clocks();
-err_clocks:
- iounmap(dss.base);
-err_ioremap:
return r;
}
@@ -819,8 +819,6 @@ static int omap_dsshw_remove(struct platform_device *pdev)
dpi_exit();
sdi_exit();
- iounmap(dss.base);
-
pm_runtime_disable(&pdev->dev);
dss_put_clocks();
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h
index 32ff69fb3333..d4b3dff2ead3 100644
--- a/drivers/video/omap2/dss/dss.h
+++ b/drivers/video/omap2/dss/dss.h
@@ -202,9 +202,6 @@ void dss_uninit_device(struct platform_device *pdev,
struct omap_dss_device *dssdev);
bool dss_use_replication(struct omap_dss_device *dssdev,
enum omap_color_mode mode);
-void default_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, u32 burst_size,
- u32 *fifo_low, u32 *fifo_high);
/* manager */
int dss_init_overlay_managers(struct platform_device *pdev);
@@ -313,9 +310,6 @@ int dsi_pll_calc_clock_div_pck(struct platform_device *dsidev, bool is_tft,
int dsi_pll_init(struct platform_device *dsidev, bool enable_hsclk,
bool enable_hsdiv);
void dsi_pll_uninit(struct platform_device *dsidev, bool disconnect_lanes);
-void dsi_get_overlay_fifo_thresholds(enum omap_plane plane,
- u32 fifo_size, u32 burst_size,
- u32 *fifo_low, u32 *fifo_high);
void dsi_wait_pll_hsdiv_dispc_active(struct platform_device *dsidev);
void dsi_wait_pll_hsdiv_dsi_active(struct platform_device *dsidev);
struct platform_device *dsi_get_dsidev_from_id(int module);
@@ -429,8 +423,8 @@ int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high);
-u32 dispc_ovl_get_fifo_size(enum omap_plane plane);
-u32 dispc_ovl_get_burst_size(enum omap_plane plane);
+void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
+ u32 *fifo_low, u32 *fifo_high, bool use_fifomerge);
int dispc_ovl_setup(enum omap_plane plane, struct omap_overlay_info *oi,
bool ilace, bool replication);
int dispc_ovl_enable(enum omap_plane plane, bool enable);
diff --git a/drivers/video/omap2/dss/dss_features.c b/drivers/video/omap2/dss/dss_features.c
index afcb59301c37..ce14aa6dd672 100644
--- a/drivers/video/omap2/dss/dss_features.c
+++ b/drivers/video/omap2/dss/dss_features.c
@@ -41,7 +41,8 @@ struct omap_dss_features {
const struct dss_reg_field *reg_fields;
const int num_reg_fields;
- const u32 has_feature;
+ const enum dss_feat_id *features;
+ const int num_features;
const int num_mgrs;
const int num_ovls;
@@ -189,7 +190,8 @@ static const enum omap_color_mode omap4_dss_supported_color_modes[] = {
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U |
OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 |
OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 |
- OMAP_DSS_COLOR_ARGB16_1555,
+ OMAP_DSS_COLOR_ARGB16_1555 | OMAP_DSS_COLOR_RGBX16 |
+ OMAP_DSS_COLOR_RGBA16 | OMAP_DSS_COLOR_XRGB16_1555,
/* OMAP_DSS_VIDEO1 */
OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB12U |
@@ -337,15 +339,110 @@ static const struct dss_param_range omap4_dss_param_range[] = {
[FEAT_PARAM_LINEWIDTH] = { 1, 2048 },
};
+static const enum dss_feat_id omap2_dss_feat_list[] = {
+ FEAT_LCDENABLEPOL,
+ FEAT_LCDENABLESIGNAL,
+ FEAT_PCKFREEENABLE,
+ FEAT_FUNCGATED,
+ FEAT_ROWREPEATENABLE,
+ FEAT_RESIZECONF,
+};
+
+static const enum dss_feat_id omap3430_dss_feat_list[] = {
+ FEAT_LCDENABLEPOL,
+ FEAT_LCDENABLESIGNAL,
+ FEAT_PCKFREEENABLE,
+ FEAT_FUNCGATED,
+ FEAT_LINEBUFFERSPLIT,
+ FEAT_ROWREPEATENABLE,
+ FEAT_RESIZECONF,
+ FEAT_DSI_PLL_FREQSEL,
+ FEAT_DSI_REVERSE_TXCLKESC,
+ FEAT_VENC_REQUIRES_TV_DAC_CLK,
+ FEAT_CPR,
+ FEAT_PRELOAD,
+ FEAT_FIR_COEF_V,
+ FEAT_ALPHA_FIXED_ZORDER,
+ FEAT_FIFO_MERGE,
+ FEAT_OMAP3_DSI_FIFO_BUG,
+};
+
+static const enum dss_feat_id omap3630_dss_feat_list[] = {
+ FEAT_LCDENABLEPOL,
+ FEAT_LCDENABLESIGNAL,
+ FEAT_PCKFREEENABLE,
+ FEAT_FUNCGATED,
+ FEAT_LINEBUFFERSPLIT,
+ FEAT_ROWREPEATENABLE,
+ FEAT_RESIZECONF,
+ FEAT_DSI_PLL_PWR_BUG,
+ FEAT_DSI_PLL_FREQSEL,
+ FEAT_CPR,
+ FEAT_PRELOAD,
+ FEAT_FIR_COEF_V,
+ FEAT_ALPHA_FIXED_ZORDER,
+ FEAT_FIFO_MERGE,
+ FEAT_OMAP3_DSI_FIFO_BUG,
+};
+
+static const enum dss_feat_id omap4430_es1_0_dss_feat_list[] = {
+ FEAT_MGR_LCD2,
+ FEAT_CORE_CLK_DIV,
+ FEAT_LCD_CLK_SRC,
+ FEAT_DSI_DCS_CMD_CONFIG_VC,
+ FEAT_DSI_VC_OCP_WIDTH,
+ FEAT_DSI_GNQ,
+ FEAT_HANDLE_UV_SEPARATE,
+ FEAT_ATTR2,
+ FEAT_CPR,
+ FEAT_PRELOAD,
+ FEAT_FIR_COEF_V,
+ FEAT_ALPHA_FREE_ZORDER,
+ FEAT_FIFO_MERGE,
+};
+
+static const enum dss_feat_id omap4430_es2_0_1_2_dss_feat_list[] = {
+ FEAT_MGR_LCD2,
+ FEAT_CORE_CLK_DIV,
+ FEAT_LCD_CLK_SRC,
+ FEAT_DSI_DCS_CMD_CONFIG_VC,
+ FEAT_DSI_VC_OCP_WIDTH,
+ FEAT_DSI_GNQ,
+ FEAT_HDMI_CTS_SWMODE,
+ FEAT_HANDLE_UV_SEPARATE,
+ FEAT_ATTR2,
+ FEAT_CPR,
+ FEAT_PRELOAD,
+ FEAT_FIR_COEF_V,
+ FEAT_ALPHA_FREE_ZORDER,
+ FEAT_FIFO_MERGE,
+};
+
+static const enum dss_feat_id omap4_dss_feat_list[] = {
+ FEAT_MGR_LCD2,
+ FEAT_CORE_CLK_DIV,
+ FEAT_LCD_CLK_SRC,
+ FEAT_DSI_DCS_CMD_CONFIG_VC,
+ FEAT_DSI_VC_OCP_WIDTH,
+ FEAT_DSI_GNQ,
+ FEAT_HDMI_CTS_SWMODE,
+ FEAT_HDMI_AUDIO_USE_MCLK,
+ FEAT_HANDLE_UV_SEPARATE,
+ FEAT_ATTR2,
+ FEAT_CPR,
+ FEAT_PRELOAD,
+ FEAT_FIR_COEF_V,
+ FEAT_ALPHA_FREE_ZORDER,
+ FEAT_FIFO_MERGE,
+};
+
/* OMAP2 DSS Features */
static const struct omap_dss_features omap2_dss_features = {
.reg_fields = omap2_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap2_dss_reg_fields),
- .has_feature =
- FEAT_LCDENABLEPOL | FEAT_LCDENABLESIGNAL |
- FEAT_PCKFREEENABLE | FEAT_FUNCGATED |
- FEAT_ROWREPEATENABLE | FEAT_RESIZECONF,
+ .features = omap2_dss_feat_list,
+ .num_features = ARRAY_SIZE(omap2_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
@@ -363,14 +460,8 @@ static const struct omap_dss_features omap3430_dss_features = {
.reg_fields = omap3_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
- .has_feature =
- FEAT_LCDENABLEPOL |
- FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |
- FEAT_FUNCGATED | FEAT_ROWREPEATENABLE |
- FEAT_LINEBUFFERSPLIT | FEAT_RESIZECONF |
- FEAT_DSI_PLL_FREQSEL | FEAT_DSI_REVERSE_TXCLKESC |
- FEAT_VENC_REQUIRES_TV_DAC_CLK | FEAT_CPR | FEAT_PRELOAD |
- FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER,
+ .features = omap3430_dss_feat_list,
+ .num_features = ARRAY_SIZE(omap3430_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
@@ -387,14 +478,8 @@ static const struct omap_dss_features omap3630_dss_features = {
.reg_fields = omap3_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap3_dss_reg_fields),
- .has_feature =
- FEAT_LCDENABLEPOL |
- FEAT_LCDENABLESIGNAL | FEAT_PCKFREEENABLE |
- FEAT_FUNCGATED |
- FEAT_ROWREPEATENABLE | FEAT_LINEBUFFERSPLIT |
- FEAT_RESIZECONF | FEAT_DSI_PLL_PWR_BUG |
- FEAT_DSI_PLL_FREQSEL | FEAT_CPR | FEAT_PRELOAD |
- FEAT_FIR_COEF_V | FEAT_ALPHA_FIXED_ZORDER,
+ .features = omap3630_dss_feat_list,
+ .num_features = ARRAY_SIZE(omap3630_dss_feat_list),
.num_mgrs = 2,
.num_ovls = 3,
@@ -413,13 +498,27 @@ static const struct omap_dss_features omap4430_es1_0_dss_features = {
.reg_fields = omap4_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
- .has_feature =
- FEAT_MGR_LCD2 |
- FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
- FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
- FEAT_DSI_GNQ | FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 |
- FEAT_CPR | FEAT_PRELOAD | FEAT_FIR_COEF_V |
- FEAT_ALPHA_FREE_ZORDER,
+ .features = omap4430_es1_0_dss_feat_list,
+ .num_features = ARRAY_SIZE(omap4430_es1_0_dss_feat_list),
+
+ .num_mgrs = 3,
+ .num_ovls = 4,
+ .supported_displays = omap4_dss_supported_displays,
+ .supported_color_modes = omap4_dss_supported_color_modes,
+ .overlay_caps = omap4_dss_overlay_caps,
+ .clksrc_names = omap4_dss_clk_source_names,
+ .dss_params = omap4_dss_param_range,
+ .buffer_size_unit = 16,
+ .burst_size_unit = 16,
+};
+
+/* For OMAP4430 ES 2.0, 2.1 and 2.2 revisions */
+static const struct omap_dss_features omap4430_es2_0_1_2_dss_features = {
+ .reg_fields = omap4_dss_reg_fields,
+ .num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
+
+ .features = omap4430_es2_0_1_2_dss_feat_list,
+ .num_features = ARRAY_SIZE(omap4430_es2_0_1_2_dss_feat_list),
.num_mgrs = 3,
.num_ovls = 4,
@@ -437,13 +536,8 @@ static const struct omap_dss_features omap4_dss_features = {
.reg_fields = omap4_dss_reg_fields,
.num_reg_fields = ARRAY_SIZE(omap4_dss_reg_fields),
- .has_feature =
- FEAT_MGR_LCD2 |
- FEAT_CORE_CLK_DIV | FEAT_LCD_CLK_SRC |
- FEAT_DSI_DCS_CMD_CONFIG_VC | FEAT_DSI_VC_OCP_WIDTH |
- FEAT_DSI_GNQ | FEAT_HDMI_CTS_SWMODE |
- FEAT_HANDLE_UV_SEPARATE | FEAT_ATTR2 | FEAT_CPR |
- FEAT_PRELOAD | FEAT_FIR_COEF_V | FEAT_ALPHA_FREE_ZORDER,
+ .features = omap4_dss_feat_list,
+ .num_features = ARRAY_SIZE(omap4_dss_feat_list),
.num_mgrs = 3,
.num_ovls = 4,
@@ -547,7 +641,16 @@ u32 dss_feat_get_burst_size_unit(void)
/* DSS has_feature check */
bool dss_has_feature(enum dss_feat_id id)
{
- return omap_current_dss_features->has_feature & id;
+ int i;
+ const enum dss_feat_id *features = omap_current_dss_features->features;
+ const int num_features = omap_current_dss_features->num_features;
+
+ for (i = 0; i < num_features; i++) {
+ if (features[i] == id)
+ return true;
+ }
+
+ return false;
}
void dss_feat_get_reg_field(enum dss_feat_reg_field id, u8 *start, u8 *end)
@@ -569,6 +672,10 @@ void dss_features_init(void)
omap_current_dss_features = &omap3430_dss_features;
else if (omap_rev() == OMAP4430_REV_ES1_0)
omap_current_dss_features = &omap4430_es1_0_dss_features;
+ else if (omap_rev() == OMAP4430_REV_ES2_0 ||
+ omap_rev() == OMAP4430_REV_ES2_1 ||
+ omap_rev() == OMAP4430_REV_ES2_2)
+ omap_current_dss_features = &omap4430_es2_0_1_2_dss_features;
else if (cpu_is_omap44xx())
omap_current_dss_features = &omap4_dss_features;
else
diff --git a/drivers/video/omap2/dss/dss_features.h b/drivers/video/omap2/dss/dss_features.h
index cd833bbaac3d..c332e7ddfce1 100644
--- a/drivers/video/omap2/dss/dss_features.h
+++ b/drivers/video/omap2/dss/dss_features.h
@@ -31,33 +31,37 @@
/* DSS has feature id */
enum dss_feat_id {
- FEAT_LCDENABLEPOL = 1 << 3,
- FEAT_LCDENABLESIGNAL = 1 << 4,
- FEAT_PCKFREEENABLE = 1 << 5,
- FEAT_FUNCGATED = 1 << 6,
- FEAT_MGR_LCD2 = 1 << 7,
- FEAT_LINEBUFFERSPLIT = 1 << 8,
- FEAT_ROWREPEATENABLE = 1 << 9,
- FEAT_RESIZECONF = 1 << 10,
+ FEAT_LCDENABLEPOL,
+ FEAT_LCDENABLESIGNAL,
+ FEAT_PCKFREEENABLE,
+ FEAT_FUNCGATED,
+ FEAT_MGR_LCD2,
+ FEAT_LINEBUFFERSPLIT,
+ FEAT_ROWREPEATENABLE,
+ FEAT_RESIZECONF,
/* Independent core clk divider */
- FEAT_CORE_CLK_DIV = 1 << 11,
- FEAT_LCD_CLK_SRC = 1 << 12,
+ FEAT_CORE_CLK_DIV,
+ FEAT_LCD_CLK_SRC,
/* DSI-PLL power command 0x3 is not working */
- FEAT_DSI_PLL_PWR_BUG = 1 << 13,
- FEAT_DSI_PLL_FREQSEL = 1 << 14,
- FEAT_DSI_DCS_CMD_CONFIG_VC = 1 << 15,
- FEAT_DSI_VC_OCP_WIDTH = 1 << 16,
- FEAT_DSI_REVERSE_TXCLKESC = 1 << 17,
- FEAT_DSI_GNQ = 1 << 18,
- FEAT_HDMI_CTS_SWMODE = 1 << 19,
- FEAT_HANDLE_UV_SEPARATE = 1 << 20,
- FEAT_ATTR2 = 1 << 21,
- FEAT_VENC_REQUIRES_TV_DAC_CLK = 1 << 22,
- FEAT_CPR = 1 << 23,
- FEAT_PRELOAD = 1 << 24,
- FEAT_FIR_COEF_V = 1 << 25,
- FEAT_ALPHA_FIXED_ZORDER = 1 << 26,
- FEAT_ALPHA_FREE_ZORDER = 1 << 27,
+ FEAT_DSI_PLL_PWR_BUG,
+ FEAT_DSI_PLL_FREQSEL,
+ FEAT_DSI_DCS_CMD_CONFIG_VC,
+ FEAT_DSI_VC_OCP_WIDTH,
+ FEAT_DSI_REVERSE_TXCLKESC,
+ FEAT_DSI_GNQ,
+ FEAT_HDMI_CTS_SWMODE,
+ FEAT_HDMI_AUDIO_USE_MCLK,
+ FEAT_HANDLE_UV_SEPARATE,
+ FEAT_ATTR2,
+ FEAT_VENC_REQUIRES_TV_DAC_CLK,
+ FEAT_CPR,
+ FEAT_PRELOAD,
+ FEAT_FIR_COEF_V,
+ FEAT_ALPHA_FIXED_ZORDER,
+ FEAT_ALPHA_FREE_ZORDER,
+ FEAT_FIFO_MERGE,
+ /* An unknown HW bug causing the normal FIFO thresholds not to work */
+ FEAT_OMAP3_DSI_FIFO_BUG,
};
/* DSS register field id */
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c
index a36b934b2db4..c4b4f6950a92 100644
--- a/drivers/video/omap2/dss/hdmi.c
+++ b/drivers/video/omap2/dss/hdmi.c
@@ -58,8 +58,6 @@
#define EDID_SIZE_BLOCK0_TIMING_DESCRIPTOR 4
#define EDID_SIZE_BLOCK1_TIMING_DESCRIPTOR 4
-#define OMAP_HDMI_TIMINGS_NB 34
-
#define HDMI_DEFAULT_REGN 16
#define HDMI_DEFAULT_REGM2 1
@@ -68,8 +66,6 @@ static struct {
struct omap_display_platform_data *pdata;
struct platform_device *pdev;
struct hdmi_ip_data ip_data;
- int code;
- int mode;
struct clk *sys_clk;
} hdmi;
@@ -88,77 +84,46 @@ static struct {
* map it to corresponding CEA or VESA index.
*/
-static const struct hdmi_timings cea_vesa_timings[OMAP_HDMI_TIMINGS_NB] = {
- { {640, 480, 25200, 96, 16, 48, 2, 10, 33} , 0 , 0},
- { {1280, 720, 74250, 40, 440, 220, 5, 5, 20}, 1, 1},
- { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1},
- { {720, 480, 27027, 62, 16, 60, 6, 9, 30}, 0, 0},
- { {2880, 576, 108000, 256, 48, 272, 5, 5, 39}, 0, 0},
- { {1440, 240, 27027, 124, 38, 114, 3, 4, 15}, 0, 0},
- { {1440, 288, 27000, 126, 24, 138, 3, 2, 19}, 0, 0},
- { {1920, 540, 74250, 44, 528, 148, 5, 2, 15}, 1, 1},
- { {1920, 540, 74250, 44, 88, 148, 5, 2, 15}, 1, 1},
- { {1920, 1080, 148500, 44, 88, 148, 5, 4, 36}, 1, 1},
- { {720, 576, 27000, 64, 12, 68, 5, 5, 39}, 0, 0},
- { {1440, 576, 54000, 128, 24, 136, 5, 5, 39}, 0, 0},
- { {1920, 1080, 148500, 44, 528, 148, 5, 4, 36}, 1, 1},
- { {2880, 480, 108108, 248, 64, 240, 6, 9, 30}, 0, 0},
- { {1920, 1080, 74250, 44, 638, 148, 5, 4, 36}, 1, 1},
- /* VESA From Here */
- { {640, 480, 25175, 96, 16, 48, 2 , 11, 31}, 0, 0},
- { {800, 600, 40000, 128, 40, 88, 4 , 1, 23}, 1, 1},
- { {848, 480, 33750, 112, 16, 112, 8 , 6, 23}, 1, 1},
- { {1280, 768, 79500, 128, 64, 192, 7 , 3, 20}, 1, 0},
- { {1280, 800, 83500, 128, 72, 200, 6 , 3, 22}, 1, 0},
- { {1360, 768, 85500, 112, 64, 256, 6 , 3, 18}, 1, 1},
- { {1280, 960, 108000, 112, 96, 312, 3 , 1, 36}, 1, 1},
- { {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38}, 1, 1},
- { {1024, 768, 65000, 136, 24, 160, 6, 3, 29}, 0, 0},
- { {1400, 1050, 121750, 144, 88, 232, 4, 3, 32}, 1, 0},
- { {1440, 900, 106500, 152, 80, 232, 6, 3, 25}, 1, 0},
- { {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30}, 1, 0},
- { {1366, 768, 85500, 143, 70, 213, 3, 3, 24}, 1, 1},
- { {1920, 1080, 148500, 44, 148, 80, 5, 4, 36}, 1, 1},
- { {1280, 768, 68250, 32, 48, 80, 7, 3, 12}, 0, 1},
- { {1400, 1050, 101000, 32, 48, 80, 4, 3, 23}, 0, 1},
- { {1680, 1050, 119000, 32, 48, 80, 6, 3, 21}, 0, 1},
- { {1280, 800, 79500, 32, 48, 80, 6, 3, 14}, 0, 1},
- { {1280, 720, 74250, 40, 110, 220, 5, 5, 20}, 1, 1}
-};
-
-/*
- * This is a static mapping array which maps the timing values
- * with corresponding CEA / VESA code
- */
-static const int code_index[OMAP_HDMI_TIMINGS_NB] = {
- 1, 19, 4, 2, 37, 6, 21, 20, 5, 16, 17, 29, 31, 35, 32,
- /* <--15 CEA 17--> vesa*/
- 4, 9, 0xE, 0x17, 0x1C, 0x27, 0x20, 0x23, 0x10, 0x2A,
- 0X2F, 0x3A, 0X51, 0X52, 0x16, 0x29, 0x39, 0x1B
+static const struct hdmi_config cea_timings[] = {
+{ {640, 480, 25200, 96, 16, 48, 2, 10, 33, 0, 0, 0}, {1, HDMI_HDMI} },
+{ {720, 480, 27027, 62, 16, 60, 6, 9, 30, 0, 0, 0}, {2, HDMI_HDMI} },
+{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {4, HDMI_HDMI} },
+{ {1920, 540, 74250, 44, 88, 148, 5, 2, 15, 1, 1, 1}, {5, HDMI_HDMI} },
+{ {1440, 240, 27027, 124, 38, 114, 3, 4, 15, 0, 0, 1}, {6, HDMI_HDMI} },
+{ {1920, 1080, 148500, 44, 88, 148, 5, 4, 36, 1, 1, 0}, {16, HDMI_HDMI} },
+{ {720, 576, 27000, 64, 12, 68, 5, 5, 39, 0, 0, 0}, {17, HDMI_HDMI} },
+{ {1280, 720, 74250, 40, 440, 220, 5, 5, 20, 1, 1, 0}, {19, HDMI_HDMI} },
+{ {1920, 540, 74250, 44, 528, 148, 5, 2, 15, 1, 1, 1}, {20, HDMI_HDMI} },
+{ {1440, 288, 27000, 126, 24, 138, 3, 2, 19, 0, 0, 1}, {21, HDMI_HDMI} },
+{ {1440, 576, 54000, 128, 24, 136, 5, 5, 39, 0, 0, 0}, {29, HDMI_HDMI} },
+{ {1920, 1080, 148500, 44, 528, 148, 5, 4, 36, 1, 1, 0}, {31, HDMI_HDMI} },
+{ {1920, 1080, 74250, 44, 638, 148, 5, 4, 36, 1, 1, 0}, {32, HDMI_HDMI} },
+{ {2880, 480, 108108, 248, 64, 240, 6, 9, 30, 0, 0, 0}, {35, HDMI_HDMI} },
+{ {2880, 576, 108000, 256, 48, 272, 5, 5, 39, 0, 0, 0}, {37, HDMI_HDMI} },
};
-
-/*
- * This is reverse static mapping which maps the CEA / VESA code
- * to the corresponding timing values
- */
-static const int code_cea[39] = {
- -1, 0, 3, 3, 2, 8, 5, 5, -1, -1,
- -1, -1, -1, -1, -1, -1, 9, 10, 10, 1,
- 7, 6, 6, -1, -1, -1, -1, -1, -1, 11,
- 11, 12, 14, -1, -1, 13, 13, 4, 4
+static const struct hdmi_config vesa_timings[] = {
+/* VESA From Here */
+{ {640, 480, 25175, 96, 16, 48, 2 , 11, 31, 0, 0, 0}, {4, HDMI_DVI} },
+{ {800, 600, 40000, 128, 40, 88, 4 , 1, 23, 1, 1, 0}, {9, HDMI_DVI} },
+{ {848, 480, 33750, 112, 16, 112, 8 , 6, 23, 1, 1, 0}, {0xE, HDMI_DVI} },
+{ {1280, 768, 79500, 128, 64, 192, 7 , 3, 20, 1, 0, 0}, {0x17, HDMI_DVI} },
+{ {1280, 800, 83500, 128, 72, 200, 6 , 3, 22, 1, 0, 0}, {0x1C, HDMI_DVI} },
+{ {1360, 768, 85500, 112, 64, 256, 6 , 3, 18, 1, 1, 0}, {0x27, HDMI_DVI} },
+{ {1280, 960, 108000, 112, 96, 312, 3 , 1, 36, 1, 1, 0}, {0x20, HDMI_DVI} },
+{ {1280, 1024, 108000, 112, 48, 248, 3 , 1, 38, 1, 1, 0}, {0x23, HDMI_DVI} },
+{ {1024, 768, 65000, 136, 24, 160, 6, 3, 29, 0, 0, 0}, {0x10, HDMI_DVI} },
+{ {1400, 1050, 121750, 144, 88, 232, 4, 3, 32, 1, 0, 0}, {0x2A, HDMI_DVI} },
+{ {1440, 900, 106500, 152, 80, 232, 6, 3, 25, 1, 0, 0}, {0x2F, HDMI_DVI} },
+{ {1680, 1050, 146250, 176 , 104, 280, 6, 3, 30, 1, 0, 0}, {0x3A, HDMI_DVI} },
+{ {1366, 768, 85500, 143, 70, 213, 3, 3, 24, 1, 1, 0}, {0x51, HDMI_DVI} },
+{ {1920, 1080, 148500, 44, 148, 80, 5, 4, 36, 1, 1, 0}, {0x52, HDMI_DVI} },
+{ {1280, 768, 68250, 32, 48, 80, 7, 3, 12, 0, 1, 0}, {0x16, HDMI_DVI} },
+{ {1400, 1050, 101000, 32, 48, 80, 4, 3, 23, 0, 1, 0}, {0x29, HDMI_DVI} },
+{ {1680, 1050, 119000, 32, 48, 80, 6, 3, 21, 0, 1, 0}, {0x39, HDMI_DVI} },
+{ {1280, 800, 79500, 32, 48, 80, 6, 3, 14, 0, 1, 0}, {0x1B, HDMI_DVI} },
+{ {1280, 720, 74250, 40, 110, 220, 5, 5, 20, 1, 1, 0}, {0x55, HDMI_DVI} }
};
-static const int code_vesa[85] = {
- -1, -1, -1, -1, 15, -1, -1, -1, -1, 16,
- -1, -1, -1, -1, 17, -1, 23, -1, -1, -1,
- -1, -1, 29, 18, -1, -1, -1, 32, 19, -1,
- -1, -1, 21, -1, -1, 22, -1, -1, -1, 20,
- -1, 30, 24, -1, -1, -1, -1, 25, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, 31, 26, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, 27, 28, -1, 33};
-
static int hdmi_runtime_get(void)
{
int r;
@@ -210,88 +175,89 @@ int hdmi_init_display(struct omap_dss_device *dssdev)
return 0;
}
-static int get_timings_index(void)
+static const struct hdmi_config *hdmi_find_timing(
+ const struct hdmi_config *timings_arr,
+ int len)
{
- int code;
+ int i;
- if (hdmi.mode == 0)
- code = code_vesa[hdmi.code];
- else
- code = code_cea[hdmi.code];
+ for (i = 0; i < len; i++) {
+ if (timings_arr[i].cm.code == hdmi.ip_data.cfg.cm.code)
+ return &timings_arr[i];
+ }
+ return NULL;
+}
- if (code == -1) {
- /* HDMI code 4 corresponds to 640 * 480 VGA */
- hdmi.code = 4;
- /* DVI mode 1 corresponds to HDMI 0 to DVI */
- hdmi.mode = HDMI_DVI;
+static const struct hdmi_config *hdmi_get_timings(void)
+{
+ const struct hdmi_config *arr;
+ int len;
+
+ if (hdmi.ip_data.cfg.cm.mode == HDMI_DVI) {
+ arr = vesa_timings;
+ len = ARRAY_SIZE(vesa_timings);
+ } else {
+ arr = cea_timings;
+ len = ARRAY_SIZE(cea_timings);
+ }
+
+ return hdmi_find_timing(arr, len);
+}
+
+static bool hdmi_timings_compare(struct omap_video_timings *timing1,
+ const struct hdmi_video_timings *timing2)
+{
+ int timing1_vsync, timing1_hsync, timing2_vsync, timing2_hsync;
- code = code_vesa[hdmi.code];
+ if ((timing2->pixel_clock == timing1->pixel_clock) &&
+ (timing2->x_res == timing1->x_res) &&
+ (timing2->y_res == timing1->y_res)) {
+
+ timing2_hsync = timing2->hfp + timing2->hsw + timing2->hbp;
+ timing1_hsync = timing1->hfp + timing1->hsw + timing1->hbp;
+ timing2_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
+ timing1_vsync = timing2->vfp + timing2->vsw + timing2->vbp;
+
+ DSSDBG("timing1_hsync = %d timing1_vsync = %d"\
+ "timing2_hsync = %d timing2_vsync = %d\n",
+ timing1_hsync, timing1_vsync,
+ timing2_hsync, timing2_vsync);
+
+ if ((timing1_hsync == timing2_hsync) &&
+ (timing1_vsync == timing2_vsync)) {
+ return true;
+ }
}
- return code;
+ return false;
}
static struct hdmi_cm hdmi_get_code(struct omap_video_timings *timing)
{
- int i = 0, code = -1, temp_vsync = 0, temp_hsync = 0;
- int timing_vsync = 0, timing_hsync = 0;
- struct hdmi_video_timings temp;
+ int i;
struct hdmi_cm cm = {-1};
DSSDBG("hdmi_get_code\n");
- for (i = 0; i < OMAP_HDMI_TIMINGS_NB; i++) {
- temp = cea_vesa_timings[i].timings;
- if ((temp.pixel_clock == timing->pixel_clock) &&
- (temp.x_res == timing->x_res) &&
- (temp.y_res == timing->y_res)) {
-
- temp_hsync = temp.hfp + temp.hsw + temp.hbp;
- timing_hsync = timing->hfp + timing->hsw + timing->hbp;
- temp_vsync = temp.vfp + temp.vsw + temp.vbp;
- timing_vsync = timing->vfp + timing->vsw + timing->vbp;
-
- DSSDBG("temp_hsync = %d , temp_vsync = %d"
- "timing_hsync = %d, timing_vsync = %d\n",
- temp_hsync, temp_hsync,
- timing_hsync, timing_vsync);
-
- if ((temp_hsync == timing_hsync) &&
- (temp_vsync == timing_vsync)) {
- code = i;
- cm.code = code_index[i];
- if (code < 14)
- cm.mode = HDMI_HDMI;
- else
- cm.mode = HDMI_DVI;
- DSSDBG("Hdmi_code = %d mode = %d\n",
- cm.code, cm.mode);
- break;
- }
+ for (i = 0; i < ARRAY_SIZE(cea_timings); i++) {
+ if (hdmi_timings_compare(timing, &cea_timings[i].timings)) {
+ cm = cea_timings[i].cm;
+ goto end;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vesa_timings); i++) {
+ if (hdmi_timings_compare(timing, &vesa_timings[i].timings)) {
+ cm = vesa_timings[i].cm;
+ goto end;
}
}
- return cm;
-}
+end: return cm;
-static void update_hdmi_timings(struct hdmi_config *cfg,
- struct omap_video_timings *timings, int code)
-{
- cfg->timings.timings.x_res = timings->x_res;
- cfg->timings.timings.y_res = timings->y_res;
- cfg->timings.timings.hbp = timings->hbp;
- cfg->timings.timings.hfp = timings->hfp;
- cfg->timings.timings.hsw = timings->hsw;
- cfg->timings.timings.vbp = timings->vbp;
- cfg->timings.timings.vfp = timings->vfp;
- cfg->timings.timings.vsw = timings->vsw;
- cfg->timings.timings.pixel_clock = timings->pixel_clock;
- cfg->timings.vsync_pol = cea_vesa_timings[code].vsync_pol;
- cfg->timings.hsync_pol = cea_vesa_timings[code].hsync_pol;
}
unsigned long hdmi_get_pixel_clock(void)
{
/* HDMI Pixel Clock in Mhz */
- return hdmi.ip_data.cfg.timings.timings.pixel_clock * 1000;
+ return hdmi.ip_data.cfg.timings.pixel_clock * 1000;
}
static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
@@ -312,24 +278,24 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
refclk = clkin / pi->regn;
- /*
- * multiplier is pixel_clk/ref_clk
- * Multiplying by 100 to avoid fractional part removal
- */
- pi->regm = (phy * 100 / (refclk)) / 100;
-
if (dssdev->clocks.hdmi.regm2 == 0)
pi->regm2 = HDMI_DEFAULT_REGM2;
else
pi->regm2 = dssdev->clocks.hdmi.regm2;
/*
+ * multiplier is pixel_clk/ref_clk
+ * Multiplying by 100 to avoid fractional part removal
+ */
+ pi->regm = phy * pi->regm2 / refclk;
+
+ /*
* fractional multiplier is remainder of the difference between
* multiplier and actual phy(required pixel clock thus should be
* multiplied by 2^18(262144) divided by the reference clock
*/
- mf = (phy - pi->regm * refclk) * 262144;
- pi->regmf = mf / (refclk);
+ mf = (phy - pi->regm / pi->regm2 * refclk) * 262144;
+ pi->regmf = pi->regm2 * mf / refclk;
/*
* Dcofreq should be set to 1 if required pixel clock
@@ -347,7 +313,8 @@ static void hdmi_compute_pll(struct omap_dss_device *dssdev, int phy,
static int hdmi_power_on(struct omap_dss_device *dssdev)
{
- int r, code = 0;
+ int r;
+ const struct hdmi_config *timing;
struct omap_video_timings *p;
unsigned long phy;
@@ -363,9 +330,16 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
dssdev->panel.timings.x_res,
dssdev->panel.timings.y_res);
- code = get_timings_index();
- update_hdmi_timings(&hdmi.ip_data.cfg, p, code);
-
+ timing = hdmi_get_timings();
+ if (timing == NULL) {
+ /* HDMI code 4 corresponds to 640 * 480 VGA */
+ hdmi.ip_data.cfg.cm.code = 4;
+ /* DVI mode 1 corresponds to HDMI 0 to DVI */
+ hdmi.ip_data.cfg.cm.mode = HDMI_DVI;
+ hdmi.ip_data.cfg = vesa_timings[0];
+ } else {
+ hdmi.ip_data.cfg = *timing;
+ }
phy = p->pixel_clock;
hdmi_compute_pll(dssdev, phy, &hdmi.ip_data.pll_data);
@@ -385,8 +359,6 @@ static int hdmi_power_on(struct omap_dss_device *dssdev)
goto err;
}
- hdmi.ip_data.cfg.cm.mode = hdmi.mode;
- hdmi.ip_data.cfg.cm.code = hdmi.code;
hdmi.ip_data.ops->video_configure(&hdmi.ip_data);
/* Make selection of HDMI in DSS */
@@ -453,8 +425,8 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev)
struct hdmi_cm cm;
cm = hdmi_get_code(&dssdev->panel.timings);
- hdmi.code = cm.code;
- hdmi.mode = cm.mode;
+ hdmi.ip_data.cfg.cm.code = cm.code;
+ hdmi.ip_data.cfg.cm.mode = cm.mode;
if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
int r;
@@ -717,13 +689,15 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
if (dss_has_feature(FEAT_HDMI_CTS_SWMODE)) {
core_cfg.aud_par_busclk = 0;
core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_SW;
- core_cfg.use_mclk = false;
+ core_cfg.use_mclk = dss_has_feature(FEAT_HDMI_AUDIO_USE_MCLK);
} else {
core_cfg.aud_par_busclk = (((128 * 31) - 1) << 8);
core_cfg.cts_mode = HDMI_AUDIO_CTS_MODE_HW;
core_cfg.use_mclk = true;
- core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS;
}
+
+ if (core_cfg.use_mclk)
+ core_cfg.mclk_mode = HDMI_AUDIO_MCLK_128FS;
core_cfg.layout = HDMI_AUDIO_LAYOUT_2CH;
core_cfg.en_spdif = false;
/* Use sample frequency from channel status word */
@@ -756,7 +730,7 @@ static int hdmi_audio_hw_params(struct snd_pcm_substream *substream,
static int hdmi_audio_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- if (!hdmi.mode) {
+ if (!hdmi.ip_data.cfg.cm.mode) {
pr_err("Current video settings do not support audio.\n");
return -EIO;
}
diff --git a/drivers/video/omap2/dss/manager.c b/drivers/video/omap2/dss/manager.c
index d1858e71c64e..e7364603f6a1 100644
--- a/drivers/video/omap2/dss/manager.c
+++ b/drivers/video/omap2/dss/manager.c
@@ -494,6 +494,11 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
{
unsigned long timeout = msecs_to_jiffies(500);
u32 irq;
+ int r;
+
+ r = dispc_runtime_get();
+ if (r)
+ return r;
if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) {
irq = DISPC_IRQ_EVSYNC_ODD;
@@ -505,7 +510,12 @@ static int dss_mgr_wait_for_vsync(struct omap_overlay_manager *mgr)
else
irq = DISPC_IRQ_VSYNC2;
}
- return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+
+ r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout);
+
+ dispc_runtime_put();
+
+ return r;
}
int dss_init_overlay_managers(struct platform_device *pdev)
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c
index 55f398014f33..788a0ef6323a 100644
--- a/drivers/video/omap2/dss/rfbi.c
+++ b/drivers/video/omap2/dss/rfbi.c
@@ -922,35 +922,34 @@ static int omap_rfbihw_probe(struct platform_device *pdev)
rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0);
if (!rfbi_mem) {
DSSERR("can't get IORESOURCE_MEM RFBI\n");
- r = -EINVAL;
- goto err_ioremap;
+ return -EINVAL;
}
- rfbi.base = ioremap(rfbi_mem->start, resource_size(rfbi_mem));
+
+ rfbi.base = devm_ioremap(&pdev->dev, rfbi_mem->start,
+ resource_size(rfbi_mem));
if (!rfbi.base) {
DSSERR("can't ioremap RFBI\n");
- r = -ENOMEM;
- goto err_ioremap;
+ return -ENOMEM;
}
- pm_runtime_enable(&pdev->dev);
-
- r = rfbi_runtime_get();
- if (r)
- goto err_get_rfbi;
-
- msleep(10);
-
clk = clk_get(&pdev->dev, "ick");
if (IS_ERR(clk)) {
DSSERR("can't get ick\n");
- r = PTR_ERR(clk);
- goto err_get_ick;
+ return PTR_ERR(clk);
}
rfbi.l4_khz = clk_get_rate(clk) / 1000;
clk_put(clk);
+ pm_runtime_enable(&pdev->dev);
+
+ r = rfbi_runtime_get();
+ if (r)
+ goto err_runtime_get;
+
+ msleep(10);
+
rev = rfbi_read_reg(RFBI_REVISION);
dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
@@ -959,19 +958,14 @@ static int omap_rfbihw_probe(struct platform_device *pdev)
return 0;
-err_get_ick:
- rfbi_runtime_put();
-err_get_rfbi:
+err_runtime_get:
pm_runtime_disable(&pdev->dev);
- iounmap(rfbi.base);
-err_ioremap:
return r;
}
static int omap_rfbihw_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
- iounmap(rfbi.base);
return 0;
}
diff --git a/drivers/video/omap2/dss/ti_hdmi.h b/drivers/video/omap2/dss/ti_hdmi.h
index 50dadba5070a..1f58b84d6901 100644
--- a/drivers/video/omap2/dss/ti_hdmi.h
+++ b/drivers/video/omap2/dss/ti_hdmi.h
@@ -42,6 +42,7 @@ enum hdmi_clk_refsel {
HDMI_REFSEL_SYSCLK = 3
};
+/* HDMI timing structure */
struct hdmi_video_timings {
u16 x_res;
u16 y_res;
@@ -53,13 +54,9 @@ struct hdmi_video_timings {
u16 vsw;
u16 vfp;
u16 vbp;
-};
-
-/* HDMI timing structure */
-struct hdmi_timings {
- struct hdmi_video_timings timings;
- int vsync_pol;
- int hsync_pol;
+ bool vsync_pol;
+ bool hsync_pol;
+ bool interlace;
};
struct hdmi_cm {
@@ -68,8 +65,7 @@ struct hdmi_cm {
};
struct hdmi_config {
- struct hdmi_timings timings;
- u16 interlace;
+ struct hdmi_video_timings timings;
struct hdmi_cm cm;
};
@@ -117,6 +113,47 @@ struct ti_hdmi_ip_ops {
};
+/*
+ * Refer to section 8.2 in HDMI 1.3 specification for
+ * details about infoframe databytes
+ */
+struct hdmi_core_infoframe_avi {
+ /* Y0, Y1 rgb,yCbCr */
+ u8 db1_format;
+ /* A0 Active information Present */
+ u8 db1_active_info;
+ /* B0, B1 Bar info data valid */
+ u8 db1_bar_info_dv;
+ /* S0, S1 scan information */
+ u8 db1_scan_info;
+ /* C0, C1 colorimetry */
+ u8 db2_colorimetry;
+ /* M0, M1 Aspect ratio (4:3, 16:9) */
+ u8 db2_aspect_ratio;
+ /* R0...R3 Active format aspect ratio */
+ u8 db2_active_fmt_ar;
+ /* ITC IT content. */
+ u8 db3_itc;
+ /* EC0, EC1, EC2 Extended colorimetry */
+ u8 db3_ec;
+ /* Q1, Q0 Quantization range */
+ u8 db3_q_range;
+ /* SC1, SC0 Non-uniform picture scaling */
+ u8 db3_nup_scaling;
+ /* VIC0..6 Video format identification */
+ u8 db4_videocode;
+ /* PR0..PR3 Pixel repetition factor */
+ u8 db5_pixel_repeat;
+ /* Line number end of top bar */
+ u16 db6_7_line_eoftop;
+ /* Line number start of bottom bar */
+ u16 db8_9_line_sofbottom;
+ /* Pixel number end of left bar */
+ u16 db10_11_pixel_eofleft;
+ /* Pixel number start of right bar */
+ u16 db12_13_pixel_sofright;
+};
+
struct hdmi_ip_data {
void __iomem *base_wp; /* HDMI wrapper */
unsigned long core_sys_offset;
@@ -126,6 +163,7 @@ struct hdmi_ip_data {
const struct ti_hdmi_ip_ops *ops;
struct hdmi_config cfg;
struct hdmi_pll_info pll_data;
+ struct hdmi_core_infoframe_avi avi_cfg;
/* ti_hdmi_4xxx_ip private data. These should be in a separate struct */
int hpd_gpio;
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
index 6847a478b459..bfe6fe65c8be 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.c
@@ -587,12 +587,12 @@ static void hdmi_core_video_config(struct hdmi_ip_data *ip_data,
HDMI_CORE_SYS_TMDS_CTRL, cfg->tclk_sel_clkmult, 6, 5);
}
-static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data,
- struct hdmi_core_infoframe_avi info_avi)
+static void hdmi_core_aux_infoframe_avi_config(struct hdmi_ip_data *ip_data)
{
u32 val;
char sum = 0, checksum = 0;
void __iomem *av_base = hdmi_av_base(ip_data);
+ struct hdmi_core_infoframe_avi info_avi = ip_data->avi_cfg;
sum += 0x82 + 0x002 + 0x00D;
hdmi_write_reg(av_base, HDMI_CORE_AV_AVI_TYPE, 0x082);
@@ -682,8 +682,7 @@ static void hdmi_core_av_packet_config(struct hdmi_ip_data *ip_data,
}
static void hdmi_wp_init(struct omap_video_timings *timings,
- struct hdmi_video_format *video_fmt,
- struct hdmi_video_interface *video_int)
+ struct hdmi_video_format *video_fmt)
{
pr_debug("Enter hdmi_wp_init\n");
@@ -698,12 +697,6 @@ static void hdmi_wp_init(struct omap_video_timings *timings,
video_fmt->y_res = 0;
video_fmt->x_res = 0;
- video_int->vsp = 0;
- video_int->hsp = 0;
-
- video_int->interlacing = 0;
- video_int->tm = 0; /* HDMI_TIMING_SLAVE */
-
}
void ti_hdmi_4xxx_wp_video_start(struct hdmi_ip_data *ip_data, bool start)
@@ -716,15 +709,15 @@ static void hdmi_wp_video_init_format(struct hdmi_video_format *video_fmt,
{
pr_debug("Enter hdmi_wp_video_init_format\n");
- video_fmt->y_res = param->timings.timings.y_res;
- video_fmt->x_res = param->timings.timings.x_res;
+ video_fmt->y_res = param->timings.y_res;
+ video_fmt->x_res = param->timings.x_res;
- timings->hbp = param->timings.timings.hbp;
- timings->hfp = param->timings.timings.hfp;
- timings->hsw = param->timings.timings.hsw;
- timings->vbp = param->timings.timings.vbp;
- timings->vfp = param->timings.timings.vfp;
- timings->vsw = param->timings.timings.vsw;
+ timings->hbp = param->timings.hbp;
+ timings->hfp = param->timings.hfp;
+ timings->hsw = param->timings.hsw;
+ timings->vbp = param->timings.vbp;
+ timings->vfp = param->timings.vfp;
+ timings->vsw = param->timings.vsw;
}
static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
@@ -740,17 +733,16 @@ static void hdmi_wp_video_config_format(struct hdmi_ip_data *ip_data,
hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_SIZE, l);
}
-static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data,
- struct hdmi_video_interface *video_int)
+static void hdmi_wp_video_config_interface(struct hdmi_ip_data *ip_data)
{
u32 r;
pr_debug("Enter hdmi_wp_video_config_interface\n");
r = hdmi_read_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG);
- r = FLD_MOD(r, video_int->vsp, 7, 7);
- r = FLD_MOD(r, video_int->hsp, 6, 6);
- r = FLD_MOD(r, video_int->interlacing, 3, 3);
- r = FLD_MOD(r, video_int->tm, 1, 0);
+ r = FLD_MOD(r, ip_data->cfg.timings.vsync_pol, 7, 7);
+ r = FLD_MOD(r, ip_data->cfg.timings.hsync_pol, 6, 6);
+ r = FLD_MOD(r, ip_data->cfg.timings.interlace, 3, 3);
+ r = FLD_MOD(r, 1, 1, 0); /* HDMI_TIMING_MASTER_24BIT */
hdmi_write_reg(hdmi_wp_base(ip_data), HDMI_WP_VIDEO_CFG, r);
}
@@ -778,15 +770,13 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
/* HDMI */
struct omap_video_timings video_timing;
struct hdmi_video_format video_format;
- struct hdmi_video_interface video_interface;
/* HDMI core */
- struct hdmi_core_infoframe_avi avi_cfg;
+ struct hdmi_core_infoframe_avi avi_cfg = ip_data->avi_cfg;
struct hdmi_core_video_config v_core_cfg;
struct hdmi_core_packet_enable_repeat repeat_cfg;
struct hdmi_config *cfg = &ip_data->cfg;
- hdmi_wp_init(&video_timing, &video_format,
- &video_interface);
+ hdmi_wp_init(&video_timing, &video_format);
hdmi_core_init(&v_core_cfg,
&avi_cfg,
@@ -801,12 +791,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
hdmi_wp_video_config_format(ip_data, &video_format);
- video_interface.vsp = cfg->timings.vsync_pol;
- video_interface.hsp = cfg->timings.hsync_pol;
- video_interface.interlacing = cfg->interlace;
- video_interface.tm = 1 ; /* HDMI_TIMING_MASTER_24BIT */
-
- hdmi_wp_video_config_interface(ip_data, &video_interface);
+ hdmi_wp_video_config_interface(ip_data);
/*
* configure core video part
@@ -848,7 +833,7 @@ void ti_hdmi_4xxx_basic_configure(struct hdmi_ip_data *ip_data)
avi_cfg.db10_11_pixel_eofleft = 0;
avi_cfg.db12_13_pixel_sofright = 0;
- hdmi_core_aux_infoframe_avi_config(ip_data, avi_cfg);
+ hdmi_core_aux_infoframe_avi_config(ip_data);
/* enable/repeat the infoframe */
repeat_cfg.avi_infoframe = HDMI_PACKETENABLE;
@@ -1076,13 +1061,9 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data,
u32 r;
void __iomem *av_base = hdmi_av_base(ip_data);
- /* audio clock recovery parameters */
- r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL);
- r = FLD_MOD(r, cfg->use_mclk, 2, 2);
- r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1);
- r = FLD_MOD(r, cfg->cts_mode, 0, 0);
- hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r);
-
+ /*
+ * Parameters for generation of Audio Clock Recovery packets
+ */
REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL1, cfg->n, 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL2, cfg->n >> 8, 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_N_SVAL3, cfg->n >> 16, 7, 0);
@@ -1094,14 +1075,6 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data,
REG_FLD_MOD(av_base,
HDMI_CORE_AV_CTS_SVAL3, cfg->cts >> 16, 7, 0);
} else {
- /*
- * HDMI IP uses this configuration to divide the MCLK to
- * update CTS value.
- */
- REG_FLD_MOD(av_base,
- HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0);
-
- /* Configure clock for audio packets */
REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_1,
cfg->aud_par_busclk, 7, 0);
REG_FLD_MOD(av_base, HDMI_CORE_AV_AUD_PAR_BUSCLK_2,
@@ -1110,6 +1083,25 @@ void hdmi_core_audio_config(struct hdmi_ip_data *ip_data,
(cfg->aud_par_busclk >> 16), 7, 0);
}
+ /* Set ACR clock divisor */
+ REG_FLD_MOD(av_base,
+ HDMI_CORE_AV_FREQ_SVAL, cfg->mclk_mode, 2, 0);
+
+ r = hdmi_read_reg(av_base, HDMI_CORE_AV_ACR_CTRL);
+ /*
+ * Use TMDS clock for ACR packets. For devices that use
+ * the MCLK, this is the first part of the MCLK initialization.
+ */
+ r = FLD_MOD(r, 0, 2, 2);
+
+ r = FLD_MOD(r, cfg->en_acr_pkt, 1, 1);
+ r = FLD_MOD(r, cfg->cts_mode, 0, 0);
+ hdmi_write_reg(av_base, HDMI_CORE_AV_ACR_CTRL, r);
+
+ /* For devices using MCLK, this completes its initialization. */
+ if (cfg->use_mclk)
+ REG_FLD_MOD(av_base, HDMI_CORE_AV_ACR_CTRL, 1, 2, 2);
+
/* Override of SPDIF sample frequency with value in I2S_CHST4 */
REG_FLD_MOD(av_base, HDMI_CORE_AV_SPDIF_CTRL,
cfg->fs_override, 1, 1);
@@ -1205,7 +1197,7 @@ int hdmi_config_audio_acr(struct hdmi_ip_data *ip_data,
{
u32 r;
u32 deep_color = 0;
- u32 pclk = ip_data->cfg.timings.timings.pixel_clock;
+ u32 pclk = ip_data->cfg.timings.pixel_clock;
if (n == NULL || cts == NULL)
return -EINVAL;
diff --git a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
index a442998980f1..a14d1a0e6e41 100644
--- a/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
+++ b/drivers/video/omap2/dss/ti_hdmi_4xxx_ip.h
@@ -450,46 +450,6 @@ struct hdmi_core_video_config {
* Refer to section 8.2 in HDMI 1.3 specification for
* details about infoframe databytes
*/
-struct hdmi_core_infoframe_avi {
- /* Y0, Y1 rgb,yCbCr */
- u8 db1_format;
- /* A0 Active information Present */
- u8 db1_active_info;
- /* B0, B1 Bar info data valid */
- u8 db1_bar_info_dv;
- /* S0, S1 scan information */
- u8 db1_scan_info;
- /* C0, C1 colorimetry */
- u8 db2_colorimetry;
- /* M0, M1 Aspect ratio (4:3, 16:9) */
- u8 db2_aspect_ratio;
- /* R0...R3 Active format aspect ratio */
- u8 db2_active_fmt_ar;
- /* ITC IT content. */
- u8 db3_itc;
- /* EC0, EC1, EC2 Extended colorimetry */
- u8 db3_ec;
- /* Q1, Q0 Quantization range */
- u8 db3_q_range;
- /* SC1, SC0 Non-uniform picture scaling */
- u8 db3_nup_scaling;
- /* VIC0..6 Video format identification */
- u8 db4_videocode;
- /* PR0..PR3 Pixel repetition factor */
- u8 db5_pixel_repeat;
- /* Line number end of top bar */
- u16 db6_7_line_eoftop;
- /* Line number start of bottom bar */
- u16 db8_9_line_sofbottom;
- /* Pixel number end of left bar */
- u16 db10_11_pixel_eofleft;
- /* Pixel number start of right bar */
- u16 db12_13_pixel_sofright;
-};
-/*
- * Refer to section 8.2 in HDMI 1.3 specification for
- * details about infoframe databytes
- */
struct hdmi_core_infoframe_audio {
u8 db1_coding_type;
u8 db1_channel_count;
@@ -517,13 +477,6 @@ struct hdmi_video_format {
u32 x_res; /* pixel per line */
};
-struct hdmi_video_interface {
- int vsp; /* Vsync polarity */
- int hsp; /* Hsync polarity */
- int interlacing;
- int tm; /* Timing mode */
-};
-
struct hdmi_audio_format {
enum hdmi_stereo_channels stereo_channels;
u8 active_chnnls_msk;
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c
index 5c3d0f901510..9c3daf71750c 100644
--- a/drivers/video/omap2/dss/venc.c
+++ b/drivers/video/omap2/dss/venc.c
@@ -699,6 +699,11 @@ void venc_dump_regs(struct seq_file *s)
{
#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r))
+ if (cpu_is_omap44xx()) {
+ seq_printf(s, "VENC currently disabled on OMAP44xx\n");
+ return;
+ }
+
if (venc_runtime_get())
return;
@@ -790,39 +795,41 @@ static int omap_venchw_probe(struct platform_device *pdev)
venc_mem = platform_get_resource(venc.pdev, IORESOURCE_MEM, 0);
if (!venc_mem) {
DSSERR("can't get IORESOURCE_MEM VENC\n");
- r = -EINVAL;
- goto err_ioremap;
+ return -EINVAL;
}
- venc.base = ioremap(venc_mem->start, resource_size(venc_mem));
+
+ venc.base = devm_ioremap(&pdev->dev, venc_mem->start,
+ resource_size(venc_mem));
if (!venc.base) {
DSSERR("can't ioremap VENC\n");
- r = -ENOMEM;
- goto err_ioremap;
+ return -ENOMEM;
}
r = venc_get_clocks(pdev);
if (r)
- goto err_get_clk;
+ return r;
pm_runtime_enable(&pdev->dev);
r = venc_runtime_get();
if (r)
- goto err_get_venc;
+ goto err_runtime_get;
rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff);
dev_dbg(&pdev->dev, "OMAP VENC rev %d\n", rev_id);
venc_runtime_put();
- return omap_dss_register_driver(&venc_driver);
+ r = omap_dss_register_driver(&venc_driver);
+ if (r)
+ goto err_reg_panel_driver;
+
+ return 0;
-err_get_venc:
+err_reg_panel_driver:
+err_runtime_get:
pm_runtime_disable(&pdev->dev);
venc_put_clocks();
-err_get_clk:
- iounmap(venc.base);
-err_ioremap:
return r;
}
@@ -837,7 +844,6 @@ static int omap_venchw_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
venc_put_clocks();
- iounmap(venc.base);
return 0;
}
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
index 16ba6196f330..6a09ef87e14f 100644
--- a/drivers/video/omap2/omapfb/omapfb-ioctl.c
+++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
@@ -215,7 +215,7 @@ static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi)
int r = 0, i;
size_t size;
- if (mi->type > OMAPFB_MEMTYPE_MAX)
+ if (mi->type != OMAPFB_MEMTYPE_SDRAM)
return -EINVAL;
size = PAGE_ALIGN(mi->size);
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index ce158311ff59..b00db4068d21 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -1399,7 +1399,7 @@ static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size,
if (!paddr) {
DBG("allocating %lu bytes for fb %d\n", size, ofbi->id);
- r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr);
+ r = omap_vram_alloc(size, &paddr);
} else {
DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr,
ofbi->id);
@@ -1487,60 +1487,6 @@ static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size,
return omapfb_alloc_fbmem(fbi, size, paddr);
}
-static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt)
-{
- enum omap_color_mode mode;
-
- switch (fmt) {
- case OMAPFB_COLOR_RGB565:
- mode = OMAP_DSS_COLOR_RGB16;
- break;
- case OMAPFB_COLOR_YUV422:
- mode = OMAP_DSS_COLOR_YUV2;
- break;
- case OMAPFB_COLOR_CLUT_8BPP:
- mode = OMAP_DSS_COLOR_CLUT8;
- break;
- case OMAPFB_COLOR_CLUT_4BPP:
- mode = OMAP_DSS_COLOR_CLUT4;
- break;
- case OMAPFB_COLOR_CLUT_2BPP:
- mode = OMAP_DSS_COLOR_CLUT2;
- break;
- case OMAPFB_COLOR_CLUT_1BPP:
- mode = OMAP_DSS_COLOR_CLUT1;
- break;
- case OMAPFB_COLOR_RGB444:
- mode = OMAP_DSS_COLOR_RGB12U;
- break;
- case OMAPFB_COLOR_YUY422:
- mode = OMAP_DSS_COLOR_UYVY;
- break;
- case OMAPFB_COLOR_ARGB16:
- mode = OMAP_DSS_COLOR_ARGB16;
- break;
- case OMAPFB_COLOR_RGB24U:
- mode = OMAP_DSS_COLOR_RGB24U;
- break;
- case OMAPFB_COLOR_RGB24P:
- mode = OMAP_DSS_COLOR_RGB24P;
- break;
- case OMAPFB_COLOR_ARGB32:
- mode = OMAP_DSS_COLOR_ARGB32;
- break;
- case OMAPFB_COLOR_RGBA32:
- mode = OMAP_DSS_COLOR_RGBA32;
- break;
- case OMAPFB_COLOR_RGBX32:
- mode = OMAP_DSS_COLOR_RGBX32;
- break;
- default:
- mode = -EINVAL;
- }
-
- return mode;
-}
-
static int omapfb_parse_vram_param(const char *param, int max_entries,
unsigned long *sizes, unsigned long *paddrs)
{
@@ -1614,23 +1560,6 @@ static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev)
memset(&vram_paddrs, 0, sizeof(vram_paddrs));
}
- if (fbdev->dev->platform_data) {
- struct omapfb_platform_data *opd;
- opd = fbdev->dev->platform_data;
- for (i = 0; i < opd->mem_desc.region_cnt; ++i) {
- if (!vram_sizes[i]) {
- unsigned long size;
- unsigned long paddr;
-
- size = opd->mem_desc.region[i].size;
- paddr = opd->mem_desc.region[i].paddr;
-
- vram_sizes[i] = size;
- vram_paddrs[i] = paddr;
- }
- }
- }
-
for (i = 0; i < fbdev->num_fbs; i++) {
/* allocate memory automatically only for fb0, or if
* excplicitly defined with vram or plat data option */
@@ -1669,7 +1598,7 @@ int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type)
int old_type = rg->type;
int r;
- if (type > OMAPFB_MEMTYPE_MAX)
+ if (type != OMAPFB_MEMTYPE_SDRAM)
return -EINVAL;
size = PAGE_ALIGN(size);
@@ -1828,32 +1757,6 @@ static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi)
var->rotate = def_rotate;
- /*
- * Check if there is a default color format set in the board file,
- * and use this format instead the default deducted from the
- * display bpp.
- */
- if (fbdev->dev->platform_data) {
- struct omapfb_platform_data *opd;
- int id = ofbi->id;
-
- opd = fbdev->dev->platform_data;
- if (opd->mem_desc.region[id].format_used) {
- enum omap_color_mode mode;
- enum omapfb_color_format format;
-
- format = opd->mem_desc.region[id].format;
- mode = fb_format_to_dss_mode(format);
- if (mode < 0) {
- r = mode;
- goto err;
- }
- r = dss_mode_to_fb_mode(mode, var);
- if (r < 0)
- goto err;
- }
- }
-
if (display) {
u16 w, h;
int rotation = (var->rotate + ofbi->rotation[0]) % 4;
diff --git a/drivers/video/omap2/vram.c b/drivers/video/omap2/vram.c
index 9441e2eb3dee..87e421e25afe 100644
--- a/drivers/video/omap2/vram.c
+++ b/drivers/video/omap2/vram.c
@@ -33,7 +33,6 @@
#include <asm/setup.h>
-#include <plat/sram.h>
#include <plat/vram.h>
#include <plat/dma.h>
@@ -43,10 +42,6 @@
#define DBG(format, ...)
#endif
-#define OMAP2_SRAM_START 0x40200000
-/* Maximum size, in reality this is smaller if SRAM is partially locked. */
-#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */
-
/* postponed regions are used to temporarily store region information at boot
* time when we cannot yet allocate the region list */
#define MAX_POSTPONED_REGIONS 10
@@ -74,15 +69,6 @@ struct vram_region {
static DEFINE_MUTEX(region_mutex);
static LIST_HEAD(region_list);
-static inline int region_mem_type(unsigned long paddr)
-{
- if (paddr >= OMAP2_SRAM_START &&
- paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE)
- return OMAP_VRAM_MEMTYPE_SRAM;
- else
- return OMAP_VRAM_MEMTYPE_SDRAM;
-}
-
static struct vram_region *omap_vram_create_region(unsigned long paddr,
unsigned pages)
{
@@ -212,9 +198,6 @@ static int _omap_vram_reserve(unsigned long paddr, unsigned pages)
DBG("checking region %lx %d\n", rm->paddr, rm->pages);
- if (region_mem_type(rm->paddr) != region_mem_type(paddr))
- continue;
-
start = rm->paddr;
end = start + (rm->pages << PAGE_SHIFT) - 1;
if (start > paddr || end < paddr + size - 1)
@@ -320,7 +303,7 @@ err:
return r;
}
-static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr)
+static int _omap_vram_alloc(unsigned pages, unsigned long *paddr)
{
struct vram_region *rm;
struct vram_alloc *alloc;
@@ -330,9 +313,6 @@ static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr)
DBG("checking region %lx %d\n", rm->paddr, rm->pages);
- if (region_mem_type(rm->paddr) != mtype)
- continue;
-
start = rm->paddr;
list_for_each_entry(alloc, &rm->alloc_list, list) {
@@ -365,21 +345,21 @@ found:
return -ENOMEM;
}
-int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr)
+int omap_vram_alloc(size_t size, unsigned long *paddr)
{
unsigned pages;
int r;
- BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size);
+ BUG_ON(!size);
- DBG("alloc mem type %d size %d\n", mtype, size);
+ DBG("alloc mem size %d\n", size);
size = PAGE_ALIGN(size);
pages = size >> PAGE_SHIFT;
mutex_lock(&region_mutex);
- r = _omap_vram_alloc(mtype, pages, paddr);
+ r = _omap_vram_alloc(pages, paddr);
mutex_unlock(&region_mutex);
@@ -501,10 +481,6 @@ arch_initcall(omap_vram_init);
/* boottime vram alloc stuff */
/* set from board file */
-static u32 omap_vram_sram_start __initdata;
-static u32 omap_vram_sram_size __initdata;
-
-/* set from board file */
static u32 omap_vram_sdram_start __initdata;
static u32 omap_vram_sdram_size __initdata;
@@ -587,73 +563,8 @@ void __init omap_vram_reserve_sdram_memblock(void)
pr_info("Reserving %u bytes SDRAM for VRAM\n", size);
}
-/*
- * Called at sram init time, before anything is pushed to the SRAM stack.
- * Because of the stack scheme, we will allocate everything from the
- * start of the lowest address region to the end of SRAM. This will also
- * include padding for page alignment and possible holes between regions.
- *
- * As opposed to the SDRAM case, we'll also do any dynamic allocations at
- * this point, since the driver built as a module would have problem with
- * freeing / reallocating the regions.
- */
-unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart,
- unsigned long sram_vstart,
- unsigned long sram_size,
- unsigned long pstart_avail,
- unsigned long size_avail)
-{
- unsigned long pend_avail;
- unsigned long reserved;
- u32 paddr;
- u32 size;
-
- paddr = omap_vram_sram_start;
- size = omap_vram_sram_size;
-
- if (!size)
- return 0;
-
- reserved = 0;
- pend_avail = pstart_avail + size_avail;
-
- if (!paddr) {
- /* Dynamic allocation */
- if ((size_avail & PAGE_MASK) < size) {
- pr_err("Not enough SRAM for VRAM\n");
- return 0;
- }
- size_avail = (size_avail - size) & PAGE_MASK;
- paddr = pstart_avail + size_avail;
- }
-
- if (paddr < sram_pstart ||
- paddr + size > sram_pstart + sram_size) {
- pr_err("Illegal SRAM region for VRAM\n");
- return 0;
- }
-
- /* Reserve everything above the start of the region. */
- if (pend_avail - paddr > reserved)
- reserved = pend_avail - paddr;
- size_avail = pend_avail - reserved - pstart_avail;
-
- omap_vram_add_region(paddr, size);
-
- if (reserved)
- pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved);
-
- return reserved;
-}
-
void __init omap_vram_set_sdram_vram(u32 size, u32 start)
{
omap_vram_sdram_start = start;
omap_vram_sdram_size = size;
}
-
-void __init omap_vram_set_sram_vram(u32 size, u32 start)
-{
- omap_vram_sram_start = start;
- omap_vram_sram_size = size;
-}
diff --git a/drivers/video/pmag-ba-fb.c b/drivers/video/pmag-ba-fb.c
index 0c69fa20251b..9b4a60b52a4c 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 22fcb9a3d5c0..4e7a9c46e112 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/pvr2fb.c b/drivers/video/pvr2fb.c
index 3a3fdc62c75b..bcd44c32a2ed 100644
--- a/drivers/video/pvr2fb.c
+++ b/drivers/video/pvr2fb.c
@@ -895,7 +895,7 @@ static int __init pvr2fb_dc_init(void)
#ifdef CONFIG_PVR2_DMA
if (request_dma(pvr2dma, "pvr2") != 0) {
- free_irq(HW_EVENT_VSYNC, 0);
+ free_irq(HW_EVENT_VSYNC, fb_info);
return -EBUSY;
}
#endif
@@ -914,7 +914,7 @@ static void __exit pvr2fb_dc_exit(void)
currentpar->mmio_base = 0;
}
- free_irq(HW_EVENT_VSYNC, 0);
+ free_irq(HW_EVENT_VSYNC, fb_info);
#ifdef CONFIG_PVR2_DMA
free_dma(pvr2dma);
#endif
diff --git a/drivers/video/pxa168fb.c b/drivers/video/pxa168fb.c
index 8384b941f6ba..f146089261f4 100644
--- a/drivers/video/pxa168fb.c
+++ b/drivers/video/pxa168fb.c
@@ -21,6 +21,7 @@
#include <linux/fb.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
@@ -670,7 +671,8 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)
/*
* Map LCD controller registers.
*/
- fbi->reg_base = ioremap_nocache(res->start, resource_size(res));
+ fbi->reg_base = devm_ioremap_nocache(&pdev->dev, res->start,
+ resource_size(res));
if (fbi->reg_base == NULL) {
ret = -ENOMEM;
goto failed_free_info;
@@ -739,8 +741,8 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)
/*
* Register irq handler.
*/
- ret = request_irq(irq, pxa168fb_handle_irq, IRQF_SHARED,
- info->fix.id, fbi);
+ ret = devm_request_irq(&pdev->dev, irq, pxa168fb_handle_irq,
+ IRQF_SHARED, info->fix.id, fbi);
if (ret < 0) {
dev_err(&pdev->dev, "unable to request IRQ\n");
ret = -ENXIO;
@@ -759,14 +761,12 @@ static int __devinit pxa168fb_probe(struct platform_device *pdev)
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register pxa168-fb: %d\n", ret);
ret = -ENXIO;
- goto failed_free_irq;
+ goto failed_free_cmap;
}
platform_set_drvdata(pdev, fbi);
return 0;
-failed_free_irq:
- free_irq(irq, fbi);
failed_free_cmap:
fb_dealloc_cmap(&info->cmap);
failed_free_clk:
@@ -808,13 +808,10 @@ static int __devexit pxa168fb_remove(struct platform_device *pdev)
fb_dealloc_cmap(&info->cmap);
irq = platform_get_irq(pdev, 0);
- free_irq(irq, fbi);
dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
info->screen_base, info->fix.smem_start);
- iounmap(fbi->reg_base);
-
clk_disable(fbi->clk);
clk_put(fbi->clk);
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 1d1e4f175e78..3f902557690e 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -54,6 +54,7 @@
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/console.h>
#include <mach/hardware.h>
#include <asm/io.h>
@@ -730,9 +731,12 @@ static int overlayfb_open(struct fb_info *info, int user)
if (user == 0)
return -ENODEV;
- if (ofb->usage++ == 0)
+ if (ofb->usage++ == 0) {
/* unblank the base framebuffer */
+ console_lock();
fb_blank(&ofb->fbi->fb, FB_BLANK_UNBLANK);
+ console_unlock();
+ }
return 0;
}
@@ -1431,7 +1435,7 @@ static void pxafb_enable_controller(struct pxafb_info *fbi)
pr_debug("reg_lccr3 0x%08x\n", (unsigned int) fbi->reg_lccr3);
/* enable LCD controller clock */
- clk_enable(fbi->clk);
+ clk_prepare_enable(fbi->clk);
if (fbi->lccr0 & LCCR0_LCDT)
return;
@@ -1471,7 +1475,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi)
wait_for_completion_timeout(&fbi->disable_done, 200 * HZ / 1000);
/* disable LCD controller clock */
- clk_disable(fbi->clk);
+ clk_disable_unprepare(fbi->clk);
}
/*
diff --git a/drivers/video/q40fb.c b/drivers/video/q40fb.c
index f5a39f5aa900..a104e8cd2f54 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/riva/fbdev.c b/drivers/video/riva/fbdev.c
index 2f58cf9c813b..90df1a60bd16 100644
--- a/drivers/video/riva/fbdev.c
+++ b/drivers/video/riva/fbdev.c
@@ -1816,6 +1816,8 @@ static void __devinit riva_update_default_var(struct fb_var_screeninfo *var,
specs->modedb, specs->modedb_len,
NULL, 8);
} else if (specs->modedb != NULL) {
+ /* get first mode in database as fallback */
+ modedb = specs->modedb[0];
/* get preferred timing */
if (info->monspecs.misc & FB_MISC_1ST_DETAIL) {
int i;
@@ -1826,9 +1828,6 @@ static void __devinit riva_update_default_var(struct fb_var_screeninfo *var,
break;
}
}
- } else {
- /* otherwise, get first mode in database */
- modedb = specs->modedb[0];
}
var->bits_per_pixel = 8;
riva_update_var(var, &modedb);
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
index 0c63b69b6340..f3105160bf98 100644
--- a/drivers/video/s3c-fb.c
+++ b/drivers/video/s3c-fb.c
@@ -48,7 +48,8 @@
#undef writel
#define writel(v, r) do { \
printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \
- __raw_writel(v, r); } while (0)
+ __raw_writel(v, r); \
+} while (0)
#endif /* FB_S3C_DEBUG_REGWRITE */
/* irq_flags bits */
@@ -81,12 +82,14 @@ struct s3c_fb;
* @palette: Address of palette memory, or 0 if none.
* @has_prtcon: Set if has PRTCON register.
* @has_shadowcon: Set if has SHADOWCON register.
+ * @has_blendcon: Set if has BLENDCON register.
* @has_clksel: Set if VIDCON0 register has CLKSEL bit.
+ * @has_fixvclk: Set if VIDCON1 register has FIXVCLK bits.
*/
struct s3c_fb_variant {
unsigned int is_2443:1;
unsigned short nr_windows;
- unsigned short vidtcon;
+ unsigned int vidtcon;
unsigned short wincon;
unsigned short winmap;
unsigned short keycon;
@@ -99,7 +102,9 @@ struct s3c_fb_variant {
unsigned int has_prtcon:1;
unsigned int has_shadowcon:1;
+ unsigned int has_blendcon:1;
unsigned int has_clksel:1;
+ unsigned int has_fixvclk:1;
};
/**
@@ -186,7 +191,6 @@ struct s3c_fb_vsync {
* struct s3c_fb - overall hardware state of the hardware
* @slock: The spinlock protection for this data sturcture.
* @dev: The device that we bound to, for printing, etc.
- * @regs_res: The resource we claimed for the IO registers.
* @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
* @lcd_clk: The clk (sclk) feeding pixclk.
* @regs: The mapped hardware registers.
@@ -202,7 +206,6 @@ struct s3c_fb_vsync {
struct s3c_fb {
spinlock_t slock;
struct device *dev;
- struct resource *regs_res;
struct clk *bus_clk;
struct clk *lcd_clk;
void __iomem *regs;
@@ -565,7 +568,9 @@ static int s3c_fb_set_par(struct fb_info *info)
writel(data, regs + sfb->variant.vidtcon + 4);
data = VIDTCON2_LINEVAL(var->yres - 1) |
- VIDTCON2_HOZVAL(var->xres - 1);
+ VIDTCON2_HOZVAL(var->xres - 1) |
+ VIDTCON2_LINEVAL_E(var->yres - 1) |
+ VIDTCON2_HOZVAL_E(var->xres - 1);
writel(data, regs + sfb->variant.vidtcon + 8);
}
@@ -581,17 +586,23 @@ static int s3c_fb_set_par(struct fb_info *info)
pagewidth = (var->xres * var->bits_per_pixel) >> 3;
data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
- VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
+ VIDW_BUF_SIZE_PAGEWIDTH(pagewidth) |
+ VIDW_BUF_SIZE_OFFSET_E(info->fix.line_length - pagewidth) |
+ VIDW_BUF_SIZE_PAGEWIDTH_E(pagewidth);
writel(data, regs + sfb->variant.buf_size + (win_no * 4));
/* write 'OSD' registers to control position of framebuffer */
- data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
+ data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0) |
+ VIDOSDxA_TOPLEFT_X_E(0) | VIDOSDxA_TOPLEFT_Y_E(0);
writel(data, regs + VIDOSD_A(win_no, sfb->variant));
data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
var->xres - 1)) |
- VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
+ VIDOSDxB_BOTRIGHT_Y(var->yres - 1) |
+ VIDOSDxB_BOTRIGHT_X_E(s3c_fb_align_word(var->bits_per_pixel,
+ var->xres - 1)) |
+ VIDOSDxB_BOTRIGHT_Y_E(var->yres - 1);
writel(data, regs + VIDOSD_B(win_no, sfb->variant));
@@ -692,6 +703,17 @@ static int s3c_fb_set_par(struct fb_info *info)
writel(data, regs + sfb->variant.wincon + (win_no * 4));
writel(0x0, regs + sfb->variant.winmap + (win_no * 4));
+ /* Set alpha value width */
+ if (sfb->variant.has_blendcon) {
+ data = readl(sfb->regs + BLENDCON);
+ data &= ~BLENDCON_NEW_MASK;
+ if (var->transp.length > 4)
+ data |= BLENDCON_NEW_8BIT_ALPHA_VALUE;
+ else
+ data |= BLENDCON_NEW_4BIT_ALPHA_VALUE;
+ writel(data, sfb->regs + BLENDCON);
+ }
+
shadow_protect_win(win, 0);
pm_runtime_put_sync(sfb->dev);
@@ -1346,6 +1368,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
struct resource *res;
int win;
int ret = 0;
+ u32 reg;
platid = platform_get_device_id(pdev);
fbdrv = (struct s3c_fb_driverdata *)platid->driver_data;
@@ -1361,7 +1384,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
return -EINVAL;
}
- sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
+ sfb = devm_kzalloc(dev, sizeof(struct s3c_fb), GFP_KERNEL);
if (!sfb) {
dev_err(dev, "no memory for framebuffers\n");
return -ENOMEM;
@@ -1404,33 +1427,25 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
goto err_lcd_clk;
}
- sfb->regs_res = request_mem_region(res->start, resource_size(res),
- dev_name(dev));
- if (!sfb->regs_res) {
- dev_err(dev, "failed to claim register region\n");
- ret = -ENOENT;
- goto err_lcd_clk;
- }
-
- sfb->regs = ioremap(res->start, resource_size(res));
+ sfb->regs = devm_request_and_ioremap(dev, res);
if (!sfb->regs) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
- goto err_req_region;
+ goto err_lcd_clk;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "failed to acquire irq resource\n");
ret = -ENOENT;
- goto err_ioremap;
+ goto err_lcd_clk;
}
sfb->irq_no = res->start;
- ret = request_irq(sfb->irq_no, s3c_fb_irq,
+ ret = devm_request_irq(dev, sfb->irq_no, s3c_fb_irq,
0, "s3c_fb", sfb);
if (ret) {
dev_err(dev, "irq request failed\n");
- goto err_ioremap;
+ goto err_lcd_clk;
}
dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
@@ -1444,6 +1459,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
writel(pd->vidcon1, sfb->regs + VIDCON1);
+ /* set video clock running at under-run */
+ if (sfb->variant.has_fixvclk) {
+ reg = readl(sfb->regs + VIDCON1);
+ reg &= ~VIDCON1_VCLK_MASK;
+ reg |= VIDCON1_VCLK_RUN;
+ writel(reg, sfb->regs + VIDCON1);
+ }
+
/* zero all windows before we do anything */
for (win = 0; win < fbdrv->variant.nr_windows; win++)
@@ -1484,13 +1507,6 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev)
err_pm_runtime:
pm_runtime_put_sync(sfb->dev);
- free_irq(sfb->irq_no, sfb);
-
-err_ioremap:
- iounmap(sfb->regs);
-
-err_req_region:
- release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
err_lcd_clk:
pm_runtime_disable(sfb->dev);
@@ -1505,7 +1521,6 @@ err_bus_clk:
clk_put(sfb->bus_clk);
err_sfb:
- kfree(sfb);
return ret;
}
@@ -1527,10 +1542,6 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
if (sfb->windows[win])
s3c_fb_release_win(sfb, sfb->windows[win]);
- free_irq(sfb->irq_no, sfb);
-
- iounmap(sfb->regs);
-
if (!sfb->variant.has_clksel) {
clk_disable(sfb->lcd_clk);
clk_put(sfb->lcd_clk);
@@ -1539,12 +1550,9 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev)
clk_disable(sfb->bus_clk);
clk_put(sfb->bus_clk);
- release_mem_region(sfb->regs_res->start, resource_size(sfb->regs_res));
-
pm_runtime_put_sync(sfb->dev);
pm_runtime_disable(sfb->dev);
- kfree(sfb);
return 0;
}
@@ -1579,6 +1587,7 @@ static int s3c_fb_resume(struct device *dev)
struct s3c_fb_platdata *pd = sfb->pdata;
struct s3c_fb_win *win;
int win_no;
+ u32 reg;
clk_enable(sfb->bus_clk);
@@ -1589,6 +1598,14 @@ static int s3c_fb_resume(struct device *dev)
pd->setup_gpio();
writel(pd->vidcon1, sfb->regs + VIDCON1);
+ /* set video clock running at under-run */
+ if (sfb->variant.has_fixvclk) {
+ reg = readl(sfb->regs + VIDCON1);
+ reg &= ~VIDCON1_VCLK_MASK;
+ reg |= VIDCON1_VCLK_RUN;
+ writel(reg, sfb->regs + VIDCON1);
+ }
+
/* zero all windows before we do anything */
for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++)
s3c_fb_clear_win(sfb, win_no);
@@ -1819,6 +1836,7 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pc100 = {
},
.has_prtcon = 1,
+ .has_blendcon = 1,
.has_clksel = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0],
@@ -1850,7 +1868,9 @@ static struct s3c_fb_driverdata s3c_fb_data_s5pv210 = {
},
.has_shadowcon = 1,
+ .has_blendcon = 1,
.has_clksel = 1,
+ .has_fixvclk = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0],
.win[1] = &s3c_fb_data_s5p_wins[1],
@@ -1881,6 +1901,39 @@ static struct s3c_fb_driverdata s3c_fb_data_exynos4 = {
},
.has_shadowcon = 1,
+ .has_blendcon = 1,
+ .has_fixvclk = 1,
+ },
+ .win[0] = &s3c_fb_data_s5p_wins[0],
+ .win[1] = &s3c_fb_data_s5p_wins[1],
+ .win[2] = &s3c_fb_data_s5p_wins[2],
+ .win[3] = &s3c_fb_data_s5p_wins[3],
+ .win[4] = &s3c_fb_data_s5p_wins[4],
+};
+
+static struct s3c_fb_driverdata s3c_fb_data_exynos5 = {
+ .variant = {
+ .nr_windows = 5,
+ .vidtcon = VIDTCON0,
+ .wincon = WINCON(0),
+ .winmap = WINxMAP(0),
+ .keycon = WKEYCON,
+ .osd = VIDOSD_BASE,
+ .osd_stride = 16,
+ .buf_start = VIDW_BUF_START(0),
+ .buf_size = VIDW_BUF_SIZE(0),
+ .buf_end = VIDW_BUF_END(0),
+
+ .palette = {
+ [0] = 0x2400,
+ [1] = 0x2800,
+ [2] = 0x2c00,
+ [3] = 0x3000,
+ [4] = 0x3400,
+ },
+ .has_shadowcon = 1,
+ .has_blendcon = 1,
+ .has_fixvclk = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0],
.win[1] = &s3c_fb_data_s5p_wins[1],
@@ -1944,6 +1997,9 @@ static struct s3c_fb_driverdata s3c_fb_data_s5p64x0 = {
[1] = 0x2800,
[2] = 0x2c00,
},
+
+ .has_blendcon = 1,
+ .has_fixvclk = 1,
},
.win[0] = &s3c_fb_data_s5p_wins[0],
.win[1] = &s3c_fb_data_s5p_wins[1],
@@ -1964,6 +2020,9 @@ static struct platform_device_id s3c_fb_driver_ids[] = {
.name = "exynos4-fb",
.driver_data = (unsigned long)&s3c_fb_data_exynos4,
}, {
+ .name = "exynos5-fb",
+ .driver_data = (unsigned long)&s3c_fb_data_exynos5,
+ }, {
.name = "s3c2443-fb",
.driver_data = (unsigned long)&s3c_fb_data_s3c2443,
}, {
diff --git a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
index 98d55d0e2da5..b6325848ad61 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 1c3b459865d8..fc5d4292fad6 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 beb495044b24..cee7803a0a1c 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/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c
index 05151b82f40f..4c6b84488561 100644
--- a/drivers/video/sh_mipi_dsi.c
+++ b/drivers/video/sh_mipi_dsi.c
@@ -24,6 +24,8 @@
#include <video/sh_mipi_dsi.h>
#include <video/sh_mobile_lcdc.h>
+#include "sh_mobile_lcdcfb.h"
+
#define SYSCTRL 0x0000
#define SYSCONF 0x0004
#define TIMSET 0x0008
@@ -50,16 +52,16 @@
#define MAX_SH_MIPI_DSI 2
struct sh_mipi {
+ struct sh_mobile_lcdc_entity entity;
+
void __iomem *base;
void __iomem *linkbase;
struct clk *dsit_clk;
struct platform_device *pdev;
-
- void *next_board_data;
- void (*next_display_on)(void *board_data, struct fb_info *info);
- void (*next_display_off)(void *board_data);
};
+#define to_sh_mipi(e) container_of(e, struct sh_mipi, entity)
+
static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI];
/* Protect the above array */
@@ -120,7 +122,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable)
static void sh_mipi_shutdown(struct platform_device *pdev)
{
- struct sh_mipi *mipi = platform_get_drvdata(pdev);
+ struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev));
sh_mipi_dsi_enable(mipi, false);
}
@@ -145,77 +147,77 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
pctype = 0;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_cfg[0].xres * 3;
+ linelength = ch->lcd_modes[0].xres * 3;
yuv = false;
break;
case MIPI_RGB565:
pctype = 1;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_cfg[0].xres * 2;
+ linelength = ch->lcd_modes[0].xres * 2;
yuv = false;
break;
case MIPI_RGB666_LP:
pctype = 2;
datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_cfg[0].xres * 3;
+ linelength = ch->lcd_modes[0].xres * 3;
yuv = false;
break;
case MIPI_RGB666:
pctype = 3;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
- linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8;
+ linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8;
yuv = false;
break;
case MIPI_BGR888:
pctype = 8;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_24;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_cfg[0].xres * 3;
+ linelength = ch->lcd_modes[0].xres * 3;
yuv = false;
break;
case MIPI_BGR565:
pctype = 9;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_cfg[0].xres * 2;
+ linelength = ch->lcd_modes[0].xres * 2;
yuv = false;
break;
case MIPI_BGR666_LP:
pctype = 0xa;
datatype = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
pixfmt = MIPI_DCS_PIXEL_FMT_24BIT;
- linelength = ch->lcd_cfg[0].xres * 3;
+ linelength = ch->lcd_modes[0].xres * 3;
yuv = false;
break;
case MIPI_BGR666:
pctype = 0xb;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_18;
pixfmt = MIPI_DCS_PIXEL_FMT_18BIT;
- linelength = (ch->lcd_cfg[0].xres * 18 + 7) / 8;
+ linelength = (ch->lcd_modes[0].xres * 18 + 7) / 8;
yuv = false;
break;
case MIPI_YUYV:
pctype = 4;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_cfg[0].xres * 2;
+ linelength = ch->lcd_modes[0].xres * 2;
yuv = true;
break;
case MIPI_UYVY:
pctype = 5;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR16;
pixfmt = MIPI_DCS_PIXEL_FMT_16BIT;
- linelength = ch->lcd_cfg[0].xres * 2;
+ linelength = ch->lcd_modes[0].xres * 2;
yuv = true;
break;
case MIPI_YUV420_L:
pctype = 6;
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
- linelength = (ch->lcd_cfg[0].xres * 12 + 7) / 8;
+ linelength = (ch->lcd_modes[0].xres * 12 + 7) / 8;
yuv = true;
break;
case MIPI_YUV420:
@@ -223,7 +225,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
datatype = MIPI_DSI_PACKED_PIXEL_STREAM_YCBCR12;
pixfmt = MIPI_DCS_PIXEL_FMT_12BIT;
/* Length of U/V line */
- linelength = (ch->lcd_cfg[0].xres + 1) / 2;
+ linelength = (ch->lcd_modes[0].xres + 1) / 2;
yuv = true;
break;
default:
@@ -271,7 +273,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
iowrite32(0x00000001, base + PHYCTRL);
udelay(200);
/* Deassert resets, power on */
- iowrite32(0x03070001, base + PHYCTRL);
+ iowrite32(0x03070001 | pdata->phyctrl, base + PHYCTRL);
/*
* Default = ULPS enable |
@@ -292,7 +294,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
*/
iowrite32(0x00000006, mipi->linkbase + DTCTR);
/* VSYNC width = 2 (<< 17) */
- iowrite32((ch->lcd_cfg[0].vsync_len << pdata->vsynw_offset) |
+ iowrite32((ch->lcd_modes[0].vsync_len << pdata->vsynw_offset) |
(pdata->clksrc << 16) | (pctype << 12) | datatype,
mipi->linkbase + VMCTR1);
@@ -326,7 +328,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
top = linelength << 16; /* RGBLEN */
bottom = 0x00000001;
if (pdata->flags & SH_MIPI_DSI_HSABM) /* HSALEN */
- bottom = (pdata->lane * ch->lcd_cfg[0].hsync_len) - 10;
+ bottom = (pdata->lane * ch->lcd_modes[0].hsync_len) - 10;
iowrite32(top | bottom , mipi->linkbase + VMLEN1);
/*
@@ -346,18 +348,18 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
div = 2;
if (pdata->flags & SH_MIPI_DSI_HFPBM) { /* HBPLEN */
- top = ch->lcd_cfg[0].hsync_len + ch->lcd_cfg[0].left_margin;
+ top = ch->lcd_modes[0].hsync_len + ch->lcd_modes[0].left_margin;
top = ((pdata->lane * top / div) - 10) << 16;
}
if (pdata->flags & SH_MIPI_DSI_HBPBM) { /* HFPLEN */
- bottom = ch->lcd_cfg[0].right_margin;
+ bottom = ch->lcd_modes[0].right_margin;
bottom = (pdata->lane * bottom / div) - 12;
}
- bpp = linelength / ch->lcd_cfg[0].xres; /* byte / pixel */
+ bpp = linelength / ch->lcd_modes[0].xres; /* byte / pixel */
if ((pdata->lane / div) > bpp) {
- tmp = ch->lcd_cfg[0].xres / bpp; /* output cycle */
- tmp = ch->lcd_cfg[0].xres - tmp; /* (input - output) cycle */
+ tmp = ch->lcd_modes[0].xres / bpp; /* output cycle */
+ tmp = ch->lcd_modes[0].xres - tmp; /* (input - output) cycle */
delay = (pdata->lane * tmp);
}
@@ -392,9 +394,9 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi,
return 0;
}
-static void mipi_display_on(void *arg, struct fb_info *info)
+static int mipi_display_on(struct sh_mobile_lcdc_entity *entity)
{
- struct sh_mipi *mipi = arg;
+ struct sh_mipi *mipi = to_sh_mipi(entity);
struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
int ret;
@@ -410,25 +412,21 @@ static void mipi_display_on(void *arg, struct fb_info *info)
sh_mipi_dsi_enable(mipi, true);
- if (mipi->next_display_on)
- mipi->next_display_on(mipi->next_board_data, info);
-
- return;
+ return SH_MOBILE_LCDC_DISPLAY_CONNECTED;
mipi_display_on_fail1:
pm_runtime_put_sync(&mipi->pdev->dev);
mipi_display_on_fail2:
pdata->set_dot_clock(mipi->pdev, mipi->base, 0);
+
+ return ret;
}
-static void mipi_display_off(void *arg)
+static void mipi_display_off(struct sh_mobile_lcdc_entity *entity)
{
- struct sh_mipi *mipi = arg;
+ struct sh_mipi *mipi = to_sh_mipi(entity);
struct sh_mipi_dsi_info *pdata = mipi->pdev->dev.platform_data;
- if (mipi->next_display_off)
- mipi->next_display_off(mipi->next_board_data);
-
sh_mipi_dsi_enable(mipi, false);
pdata->set_dot_clock(mipi->pdev, mipi->base, 0);
@@ -436,6 +434,11 @@ static void mipi_display_off(void *arg)
pm_runtime_put_sync(&mipi->pdev->dev);
}
+static const struct sh_mobile_lcdc_entity_ops mipi_ops = {
+ .display_on = mipi_display_on,
+ .display_off = mipi_display_off,
+};
+
static int __init sh_mipi_probe(struct platform_device *pdev)
{
struct sh_mipi *mipi;
@@ -467,6 +470,9 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
goto ealloc;
}
+ mipi->entity.owner = THIS_MODULE;
+ mipi->entity.ops = &mipi_ops;
+
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
dev_err(&pdev->dev, "MIPI register region already claimed\n");
ret = -EBUSY;
@@ -521,18 +527,7 @@ static int __init sh_mipi_probe(struct platform_device *pdev)
pm_runtime_resume(&pdev->dev);
mutex_unlock(&array_lock);
- platform_set_drvdata(pdev, mipi);
-
- /* Save original LCDC callbacks */
- mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data;
- mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on;
- mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off;
-
- /* Set up LCDC callbacks */
- pdata->lcd_chan->board_cfg.board_data = mipi;
- pdata->lcd_chan->board_cfg.display_on = mipi_display_on;
- pdata->lcd_chan->board_cfg.display_off = mipi_display_off;
- pdata->lcd_chan->board_cfg.owner = THIS_MODULE;
+ platform_set_drvdata(pdev, &mipi->entity);
return 0;
@@ -558,10 +553,9 @@ efindslot:
static int __exit sh_mipi_remove(struct platform_device *pdev)
{
- struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- struct sh_mipi *mipi = platform_get_drvdata(pdev);
+ struct sh_mipi *mipi = to_sh_mipi(platform_get_drvdata(pdev));
int i, ret;
mutex_lock(&array_lock);
@@ -581,11 +575,6 @@ static int __exit sh_mipi_remove(struct platform_device *pdev)
if (ret < 0)
return ret;
- pdata->lcd_chan->board_cfg.owner = NULL;
- pdata->lcd_chan->board_cfg.display_on = NULL;
- pdata->lcd_chan->board_cfg.display_off = NULL;
- pdata->lcd_chan->board_cfg.board_data = NULL;
-
pm_runtime_disable(&pdev->dev);
clk_disable(mipi->dsit_clk);
clk_put(mipi->dsit_clk);
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
index 647ba984f00f..eafb19da2c07 100644
--- a/drivers/video/sh_mobile_hdmi.c
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -208,6 +208,8 @@ enum hotplug_state {
};
struct sh_hdmi {
+ struct sh_mobile_lcdc_entity entity;
+
void __iomem *base;
enum hotplug_state hp_state; /* hot-plug status */
u8 preprogrammed_vic; /* use a pre-programmed VIC or
@@ -217,14 +219,13 @@ struct sh_hdmi {
u8 edid_blocks;
struct clk *hdmi_clk;
struct device *dev;
- struct fb_info *info;
- struct mutex mutex; /* Protect the info pointer */
struct delayed_work edid_work;
- struct fb_var_screeninfo var;
+ struct fb_videomode mode;
struct fb_monspecs monspec;
- struct notifier_block notifier;
};
+#define entity_to_sh_hdmi(e) container_of(e, struct sh_hdmi, entity)
+
static void hdmi_write(struct sh_hdmi *hdmi, u8 data, u8 reg)
{
iowrite8(data, hdmi->base + reg);
@@ -290,24 +291,24 @@ static struct snd_soc_codec_driver soc_codec_dev_sh_hdmi = {
/* External video parameter settings */
static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
{
- struct fb_var_screeninfo *var = &hdmi->var;
+ struct fb_videomode *mode = &hdmi->mode;
u16 htotal, hblank, hdelay, vtotal, vblank, vdelay, voffset;
u8 sync = 0;
- htotal = var->xres + var->right_margin + var->left_margin + var->hsync_len;
-
- hdelay = var->hsync_len + var->left_margin;
- hblank = var->right_margin + hdelay;
+ htotal = mode->xres + mode->right_margin + mode->left_margin
+ + mode->hsync_len;
+ hdelay = mode->hsync_len + mode->left_margin;
+ hblank = mode->right_margin + hdelay;
/*
* Vertical timing looks a bit different in Figure 18,
* but let's try the same first by setting offset = 0
*/
- vtotal = var->yres + var->upper_margin + var->lower_margin + var->vsync_len;
-
- vdelay = var->vsync_len + var->upper_margin;
- vblank = var->lower_margin + vdelay;
- voffset = min(var->upper_margin / 2, 6U);
+ vtotal = mode->yres + mode->upper_margin + mode->lower_margin
+ + mode->vsync_len;
+ vdelay = mode->vsync_len + mode->upper_margin;
+ vblank = mode->lower_margin + vdelay;
+ voffset = min(mode->upper_margin / 2, 6U);
/*
* [3]: VSYNC polarity: Positive
@@ -315,14 +316,14 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
* [1]: Interlace/Progressive: Progressive
* [0]: External video settings enable: used.
*/
- if (var->sync & FB_SYNC_HOR_HIGH_ACT)
+ if (mode->sync & FB_SYNC_HOR_HIGH_ACT)
sync |= 4;
- if (var->sync & FB_SYNC_VERT_HIGH_ACT)
+ if (mode->sync & FB_SYNC_VERT_HIGH_ACT)
sync |= 8;
dev_dbg(hdmi->dev, "H: %u, %u, %u, %u; V: %u, %u, %u, %u; sync 0x%x\n",
- htotal, hblank, hdelay, var->hsync_len,
- vtotal, vblank, vdelay, var->vsync_len, sync);
+ htotal, hblank, hdelay, mode->hsync_len,
+ vtotal, vblank, vdelay, mode->vsync_len, sync);
hdmi_write(hdmi, sync | (voffset << 4), HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS);
@@ -335,8 +336,8 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
hdmi_write(hdmi, hdelay, HDMI_EXTERNAL_H_DELAY_7_0);
hdmi_write(hdmi, hdelay >> 8, HDMI_EXTERNAL_H_DELAY_9_8);
- hdmi_write(hdmi, var->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0);
- hdmi_write(hdmi, var->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8);
+ hdmi_write(hdmi, mode->hsync_len, HDMI_EXTERNAL_H_DURATION_7_0);
+ hdmi_write(hdmi, mode->hsync_len >> 8, HDMI_EXTERNAL_H_DURATION_9_8);
hdmi_write(hdmi, vtotal, HDMI_EXTERNAL_V_TOTAL_7_0);
hdmi_write(hdmi, vtotal >> 8, HDMI_EXTERNAL_V_TOTAL_9_8);
@@ -345,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi)
hdmi_write(hdmi, vdelay, HDMI_EXTERNAL_V_DELAY);
- hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION);
+ hdmi_write(hdmi, mode->vsync_len, HDMI_EXTERNAL_V_DURATION);
/* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */
if (!hdmi->preprogrammed_vic)
@@ -472,7 +473,7 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi)
*/
static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
{
- if (hdmi->var.pixclock < 10000) {
+ if (hdmi->mode.pixclock < 10000) {
/* for 1080p8bit 148MHz */
hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1);
hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2);
@@ -483,7 +484,7 @@ static void sh_hdmi_phy_config(struct sh_hdmi *hdmi)
hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8);
hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9);
hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10);
- } else if (hdmi->var.pixclock < 30000) {
+ } else if (hdmi->mode.pixclock < 30000) {
/* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */
/*
* [1:0] Speed_A
@@ -732,14 +733,12 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
unsigned long *parent_rate)
{
- struct fb_var_screeninfo tmpvar;
- struct fb_var_screeninfo *var = &tmpvar;
+ struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc;
const struct fb_videomode *mode, *found = NULL;
- struct fb_info *info = hdmi->info;
- struct fb_modelist *modelist = NULL;
unsigned int f_width = 0, f_height = 0, f_refresh = 0;
unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */
bool scanning = false, preferred_bad = false;
+ bool use_edid_mode = false;
u8 edid[128];
char *forced;
int i;
@@ -854,12 +853,9 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
}
/* Check if supported: sufficient fb memory, supported clock-rate */
- fb_videomode_to_var(var, mode);
-
- var->bits_per_pixel = info->var.bits_per_pixel;
-
- if (info && info->fbops->fb_check_var &&
- info->fbops->fb_check_var(var, info)) {
+ if (ch && ch->notify &&
+ ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_MODE, mode,
+ NULL)) {
scanning = true;
preferred_bad = true;
continue;
@@ -867,28 +863,19 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
found = mode;
found_rate_error = rate_error;
+ use_edid_mode = true;
}
- hdmi->var.width = hdmi->monspec.max_x * 10;
- hdmi->var.height = hdmi->monspec.max_y * 10;
-
/*
- * TODO 1: if no ->info is present, postpone running the config until
- * after ->info first gets registered.
+ * TODO 1: if no default mode is present, postpone running the config
+ * until after the LCDC channel is initialized.
* TODO 2: consider registering the HDMI platform device from the LCDC
- * driver, and passing ->info with HDMI platform data.
+ * driver.
*/
- if (info && !found) {
- modelist = info->modelist.next &&
- !list_empty(&info->modelist) ?
- list_entry(info->modelist.next,
- struct fb_modelist, list) :
- NULL;
-
- if (modelist) {
- found = &modelist->mode;
- found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate);
- }
+ if (!found && hdmi->entity.def_mode.xres != 0) {
+ found = &hdmi->entity.def_mode;
+ found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate,
+ parent_rate);
}
/* No cookie today */
@@ -912,12 +899,13 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate,
else
hdmi->preprogrammed_vic = 0;
- dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
- modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external",
- found->xres, found->yres, found->refresh,
- PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
+ dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), "
+ "clock error %luHz\n", use_edid_mode ? "EDID" : "default",
+ hdmi->preprogrammed_vic ? "VIC" : "external", found->xres,
+ found->yres, found->refresh, PICOS2KHZ(found->pixclock) * 1000,
+ found_rate_error);
- fb_videomode_to_var(&hdmi->var, found);
+ hdmi->mode = *found;
sh_hdmi_external_video_param(hdmi);
return 0;
@@ -998,22 +986,12 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id)
return IRQ_HANDLED;
}
-/* locking: called with info->lock held, or before register_framebuffer() */
-static void sh_hdmi_display_on(void *arg, struct fb_info *info)
+static int sh_hdmi_display_on(struct sh_mobile_lcdc_entity *entity)
{
- /*
- * info is guaranteed to be valid, when we are called, because our
- * FB_EVENT_FB_UNBIND notify is also called with info->lock held
- */
- struct sh_hdmi *hdmi = arg;
- struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
- struct sh_mobile_lcdc_chan *ch = info->par;
+ struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity);
- dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__,
- pdata->lcd_dev, info->state);
-
- /* No need to lock */
- hdmi->info = info;
+ dev_dbg(hdmi->dev, "%s(%p): state %x\n", __func__, hdmi,
+ hdmi->hp_state);
/*
* hp_state can be set to
@@ -1021,56 +999,30 @@ static void sh_hdmi_display_on(void *arg, struct fb_info *info)
* HDMI_HOTPLUG_CONNECTED: on monitor plug-in
* HDMI_HOTPLUG_EDID_DONE: on EDID read completion
*/
- switch (hdmi->hp_state) {
- case HDMI_HOTPLUG_EDID_DONE:
+ if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) {
/* PS mode d->e. All functions are active */
hdmi_write(hdmi, 0x80, HDMI_SYSTEM_CTRL);
dev_dbg(hdmi->dev, "HDMI running\n");
- break;
- case HDMI_HOTPLUG_DISCONNECTED:
- info->state = FBINFO_STATE_SUSPENDED;
- default:
- hdmi->var = ch->display_var;
}
+
+ return hdmi->hp_state == HDMI_HOTPLUG_DISCONNECTED
+ ? SH_MOBILE_LCDC_DISPLAY_DISCONNECTED
+ : SH_MOBILE_LCDC_DISPLAY_CONNECTED;
}
-/* locking: called with info->lock held */
-static void sh_hdmi_display_off(void *arg)
+static void sh_hdmi_display_off(struct sh_mobile_lcdc_entity *entity)
{
- struct sh_hdmi *hdmi = arg;
- struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
+ struct sh_hdmi *hdmi = entity_to_sh_hdmi(entity);
- dev_dbg(hdmi->dev, "%s(%p)\n", __func__, pdata->lcd_dev);
+ dev_dbg(hdmi->dev, "%s(%p)\n", __func__, hdmi);
/* PS mode e->a */
hdmi_write(hdmi, 0x10, HDMI_SYSTEM_CTRL);
}
-static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi)
-{
- struct fb_info *info = hdmi->info;
- struct sh_mobile_lcdc_chan *ch = info->par;
- struct fb_var_screeninfo *new_var = &hdmi->var, *old_var = &ch->display_var;
- struct fb_videomode mode1, mode2;
-
- fb_var_to_videomode(&mode1, old_var);
- fb_var_to_videomode(&mode2, new_var);
-
- dev_dbg(info->dev, "Old %ux%u, new %ux%u\n",
- mode1.xres, mode1.yres, mode2.xres, mode2.yres);
-
- if (fb_mode_is_equal(&mode1, &mode2)) {
- /* It can be a different monitor with an equal video-mode */
- old_var->width = new_var->width;
- old_var->height = new_var->height;
- return false;
- }
-
- dev_dbg(info->dev, "Switching %u -> %u lines\n",
- mode1.yres, mode2.yres);
- *old_var = *new_var;
-
- return true;
-}
+static const struct sh_mobile_lcdc_entity_ops sh_hdmi_ops = {
+ .display_on = sh_hdmi_display_on,
+ .display_off = sh_hdmi_display_off,
+};
/**
* sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock
@@ -1111,20 +1063,11 @@ static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate,
static void sh_hdmi_edid_work_fn(struct work_struct *work)
{
struct sh_hdmi *hdmi = container_of(work, struct sh_hdmi, edid_work.work);
- struct fb_info *info;
- struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data;
- struct sh_mobile_lcdc_chan *ch;
+ struct sh_mobile_lcdc_chan *ch = hdmi->entity.lcdc;
int ret;
- dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__,
- pdata->lcd_dev, hdmi->hp_state);
-
- if (!pdata->lcd_dev)
- return;
-
- mutex_lock(&hdmi->mutex);
-
- info = hdmi->info;
+ dev_dbg(hdmi->dev, "%s(%p): begin, hotplug status %d\n", __func__, hdmi,
+ hdmi->hp_state);
if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) {
unsigned long parent_rate = 0, hdmi_rate;
@@ -1145,103 +1088,32 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work)
/* Switched to another (d) power-save mode */
msleep(10);
- if (!info)
- goto out;
-
- ch = info->par;
-
- if (lock_fb_info(info)) {
- console_lock();
-
- /* HDMI plug in */
- if (!sh_hdmi_must_reconfigure(hdmi) &&
- info->state == FBINFO_STATE_RUNNING) {
- /*
- * First activation with the default monitor - just turn
- * on, if we run a resume here, the logo disappears
- */
- info->var.width = hdmi->var.width;
- info->var.height = hdmi->var.height;
- sh_hdmi_display_on(hdmi, info);
- } else {
- /* New monitor or have to wake up */
- fb_set_suspend(info, 0);
- }
-
- console_unlock();
- unlock_fb_info(info);
- }
+ if (ch && ch->notify)
+ ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT,
+ &hdmi->mode, &hdmi->monspec);
} else {
- ret = 0;
- if (!info)
- goto out;
-
hdmi->monspec.modedb_len = 0;
fb_destroy_modedb(hdmi->monspec.modedb);
hdmi->monspec.modedb = NULL;
- if (lock_fb_info(info)) {
- console_lock();
+ if (ch && ch->notify)
+ ch->notify(ch, SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT,
+ NULL, NULL);
- /* HDMI disconnect */
- fb_set_suspend(info, 1);
-
- console_unlock();
- unlock_fb_info(info);
- }
+ ret = 0;
}
out:
if (ret < 0 && ret != -EAGAIN)
hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED;
- mutex_unlock(&hdmi->mutex);
- dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, pdata->lcd_dev);
-}
-
-static int sh_hdmi_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct fb_event *event = data;
- struct fb_info *info = event->info;
- struct sh_mobile_lcdc_chan *ch = info->par;
- struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
- struct sh_hdmi *hdmi = board_cfg->board_data;
-
- if (!hdmi || nb != &hdmi->notifier || hdmi->info != info)
- return NOTIFY_DONE;
-
- switch(action) {
- case FB_EVENT_FB_REGISTERED:
- /* Unneeded, activation taken care by sh_hdmi_display_on() */
- break;
- case FB_EVENT_FB_UNREGISTERED:
- /*
- * We are called from unregister_framebuffer() with the
- * info->lock held. This is bad for us, because we can race with
- * the scheduled work, which has to call fb_set_suspend(), which
- * takes info->lock internally, so, sh_hdmi_edid_work_fn()
- * cannot take and hold info->lock for the whole function
- * duration. Using an additional lock creates a classical AB-BA
- * lock up. Therefore, we have to release the info->lock
- * temporarily, synchronise with the work queue and re-acquire
- * the info->lock.
- */
- unlock_fb_info(info);
- mutex_lock(&hdmi->mutex);
- hdmi->info = NULL;
- mutex_unlock(&hdmi->mutex);
- lock_fb_info(info);
- return NOTIFY_OK;
- }
- return NOTIFY_DONE;
+ dev_dbg(hdmi->dev, "%s(%p): end\n", __func__, hdmi);
}
static int __init sh_hdmi_probe(struct platform_device *pdev)
{
struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct sh_mobile_lcdc_board_cfg *board_cfg;
int irq = platform_get_irq(pdev, 0), ret;
struct sh_hdmi *hdmi;
long rate;
@@ -1255,9 +1127,9 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
return -ENOMEM;
}
- mutex_init(&hdmi->mutex);
-
hdmi->dev = &pdev->dev;
+ hdmi->entity.owner = THIS_MODULE;
+ hdmi->entity.ops = &sh_hdmi_ops;
hdmi->hdmi_clk = clk_get(&pdev->dev, "ick");
if (IS_ERR(hdmi->hdmi_clk)) {
@@ -1297,14 +1169,7 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
goto emap;
}
- platform_set_drvdata(pdev, hdmi);
-
- /* Set up LCDC callbacks */
- board_cfg = &pdata->lcd_chan->board_cfg;
- board_cfg->owner = THIS_MODULE;
- board_cfg->board_data = hdmi;
- board_cfg->display_on = sh_hdmi_display_on;
- board_cfg->display_off = sh_hdmi_display_off;
+ platform_set_drvdata(pdev, &hdmi->entity);
INIT_DELAYED_WORK(&hdmi->edid_work, sh_hdmi_edid_work_fn);
@@ -1329,9 +1194,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev)
goto ecodec;
}
- hdmi->notifier.notifier_call = sh_hdmi_notify;
- fb_register_client(&hdmi->notifier);
-
return 0;
ecodec:
@@ -1347,7 +1209,6 @@ ereqreg:
erate:
clk_put(hdmi->hdmi_clk);
egetclk:
- mutex_destroy(&hdmi->mutex);
kfree(hdmi);
return ret;
@@ -1355,21 +1216,12 @@ egetclk:
static int __exit sh_hdmi_remove(struct platform_device *pdev)
{
- struct sh_mobile_hdmi_info *pdata = pdev->dev.platform_data;
- struct sh_hdmi *hdmi = platform_get_drvdata(pdev);
+ struct sh_hdmi *hdmi = entity_to_sh_hdmi(platform_get_drvdata(pdev));
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- struct sh_mobile_lcdc_board_cfg *board_cfg = &pdata->lcd_chan->board_cfg;
int irq = platform_get_irq(pdev, 0);
snd_soc_unregister_codec(&pdev->dev);
- fb_unregister_client(&hdmi->notifier);
-
- board_cfg->display_on = NULL;
- board_cfg->display_off = NULL;
- board_cfg->board_data = NULL;
- board_cfg->owner = NULL;
-
/* No new work will be scheduled, wait for running ISR */
free_irq(irq, hdmi);
/* Wait for already scheduled work */
@@ -1380,7 +1232,6 @@ static int __exit sh_hdmi_remove(struct platform_device *pdev)
clk_put(hdmi->hdmi_clk);
iounmap(hdmi->base);
release_mem_region(res->start, resource_size(res));
- mutex_destroy(&hdmi->mutex);
kfree(hdmi);
return 0;
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c
index aac5b369d73c..7a0b301587f6 100644
--- a/drivers/video/sh_mobile_lcdcfb.c
+++ b/drivers/video/sh_mobile_lcdcfb.c
@@ -8,26 +8,27 @@
* for more details.
*/
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/mm.h>
+#include <linux/atomic.h>
+#include <linux/backlight.h>
#include <linux/clk.h>
-#include <linux/pm_runtime.h>
-#include <linux/platform_device.h>
+#include <linux/console.h>
#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/videodev2.h>
-#include <linux/vmalloc.h>
#include <linux/ioctl.h>
-#include <linux/slab.h>
-#include <linux/console.h>
-#include <linux/backlight.h>
-#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/videodev2.h>
+#include <linux/vmalloc.h>
+
#include <video/sh_mobile_lcdc.h>
#include <video/sh_mobile_meram.h>
-#include <linux/atomic.h>
#include "sh_mobile_lcdcfb.h"
@@ -37,6 +38,24 @@
#define MAX_XRES 1920
#define MAX_YRES 1080
+struct sh_mobile_lcdc_priv {
+ void __iomem *base;
+ int irq;
+ atomic_t hw_usecnt;
+ struct device *dev;
+ struct clk *dot_clk;
+ unsigned long lddckr;
+ struct sh_mobile_lcdc_chan ch[2];
+ struct notifier_block notifier;
+ int started;
+ int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
+ struct sh_mobile_meram_info *meram_dev;
+};
+
+/* -----------------------------------------------------------------------------
+ * Registers access
+ */
+
static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = {
[LDDCKPAT1R] = 0x400,
[LDDCKPAT2R] = 0x404,
@@ -75,38 +94,6 @@ static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = {
[LDPMR] = 0x63c,
};
-static const struct fb_videomode default_720p = {
- .name = "HDMI 720p",
- .xres = 1280,
- .yres = 720,
-
- .left_margin = 220,
- .right_margin = 110,
- .hsync_len = 40,
-
- .upper_margin = 20,
- .lower_margin = 5,
- .vsync_len = 5,
-
- .pixclock = 13468,
- .refresh = 60,
- .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
-};
-
-struct sh_mobile_lcdc_priv {
- void __iomem *base;
- int irq;
- atomic_t hw_usecnt;
- struct device *dev;
- struct clk *dot_clk;
- unsigned long lddckr;
- struct sh_mobile_lcdc_chan ch[2];
- struct notifier_block notifier;
- int started;
- int forced_fourcc; /* 2 channel LCDC must share fourcc setting */
- struct sh_mobile_meram_info *meram_dev;
-};
-
static bool banked(int reg_nr)
{
switch (reg_nr) {
@@ -127,6 +114,11 @@ static bool banked(int reg_nr)
return false;
}
+static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
+{
+ return chan->cfg->chan == LCDC_CHAN_SUBLCD;
+}
+
static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan,
int reg_nr, unsigned long data)
{
@@ -169,11 +161,72 @@ static void lcdc_wait_bit(struct sh_mobile_lcdc_priv *priv,
cpu_relax();
}
-static int lcdc_chan_is_sublcd(struct sh_mobile_lcdc_chan *chan)
+/* -----------------------------------------------------------------------------
+ * Clock management
+ */
+
+static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
+{
+ if (atomic_inc_and_test(&priv->hw_usecnt)) {
+ if (priv->dot_clk)
+ clk_enable(priv->dot_clk);
+ pm_runtime_get_sync(priv->dev);
+ if (priv->meram_dev && priv->meram_dev->pdev)
+ pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
+ }
+}
+
+static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
{
- return chan->cfg.chan == LCDC_CHAN_SUBLCD;
+ if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
+ if (priv->meram_dev && priv->meram_dev->pdev)
+ pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
+ pm_runtime_put(priv->dev);
+ if (priv->dot_clk)
+ clk_disable(priv->dot_clk);
+ }
}
+static int sh_mobile_lcdc_setup_clocks(struct sh_mobile_lcdc_priv *priv,
+ int clock_source)
+{
+ struct clk *clk;
+ char *str;
+
+ switch (clock_source) {
+ case LCDC_CLK_BUS:
+ str = "bus_clk";
+ priv->lddckr = LDDCKR_ICKSEL_BUS;
+ break;
+ case LCDC_CLK_PERIPHERAL:
+ str = "peripheral_clk";
+ priv->lddckr = LDDCKR_ICKSEL_MIPI;
+ break;
+ case LCDC_CLK_EXTERNAL:
+ str = NULL;
+ priv->lddckr = LDDCKR_ICKSEL_HDMI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (str == NULL)
+ return 0;
+
+ clk = clk_get(priv->dev, str);
+ if (IS_ERR(clk)) {
+ dev_err(priv->dev, "cannot get dot clock %s\n", str);
+ return PTR_ERR(clk);
+ }
+
+ priv->dot_clk = clk;
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Display, panel and deferred I/O
+ */
+
static void lcdc_sys_write_index(void *handle, unsigned long data)
{
struct sh_mobile_lcdc_chan *ch = handle;
@@ -216,74 +269,11 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
lcdc_sys_read_data,
};
-static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var)
-{
- if (var->grayscale > 1)
- return var->grayscale;
-
- switch (var->bits_per_pixel) {
- case 16:
- return V4L2_PIX_FMT_RGB565;
- case 24:
- return V4L2_PIX_FMT_BGR24;
- case 32:
- return V4L2_PIX_FMT_BGR32;
- default:
- return 0;
- }
-}
-
-static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var)
-{
- return var->grayscale > 1;
-}
-
-static bool sh_mobile_format_is_yuv(const struct fb_var_screeninfo *var)
-{
- if (var->grayscale <= 1)
- return false;
-
- switch (var->grayscale) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- case V4L2_PIX_FMT_NV24:
- case V4L2_PIX_FMT_NV42:
- return true;
-
- default:
- return false;
- }
-}
-
-static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv)
-{
- if (atomic_inc_and_test(&priv->hw_usecnt)) {
- if (priv->dot_clk)
- clk_enable(priv->dot_clk);
- pm_runtime_get_sync(priv->dev);
- if (priv->meram_dev && priv->meram_dev->pdev)
- pm_runtime_get_sync(&priv->meram_dev->pdev->dev);
- }
-}
-
-static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv)
-{
- if (atomic_sub_return(1, &priv->hw_usecnt) == -1) {
- if (priv->meram_dev && priv->meram_dev->pdev)
- pm_runtime_put_sync(&priv->meram_dev->pdev->dev);
- pm_runtime_put(priv->dev);
- if (priv->dot_clk)
- clk_disable(priv->dot_clk);
- }
-}
-
static int sh_mobile_lcdc_sginit(struct fb_info *info,
struct list_head *pagelist)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- unsigned int nr_pages_max = info->fix.smem_len >> PAGE_SHIFT;
+ unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
struct page *page;
int nr_pages = 0;
@@ -299,7 +289,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
struct list_head *pagelist)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- struct sh_mobile_lcdc_board_cfg *bcfg = &ch->cfg.board_cfg;
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
/* enable clocks before accessing hardware */
sh_mobile_lcdc_clk_on(ch->lcdc);
@@ -323,16 +313,15 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
/* trigger panel update */
- dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
- if (bcfg->start_transfer)
- bcfg->start_transfer(bcfg->board_data, ch,
- &sh_mobile_lcdc_sys_bus_ops);
+ dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ if (panel->start_transfer)
+ panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops);
lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
- dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+ dma_unmap_sg(ch->lcdc->dev, ch->sglist, nr_pages,
+ DMA_TO_DEVICE);
} else {
- if (bcfg->start_transfer)
- bcfg->start_transfer(bcfg->board_data, ch,
- &sh_mobile_lcdc_sys_bus_ops);
+ if (panel->start_transfer)
+ panel->start_transfer(ch, &sh_mobile_lcdc_sys_bus_ops);
lcdc_write_chan(ch, LDSM2R, LDSM2R_OSTRG);
}
}
@@ -345,6 +334,217 @@ static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)
schedule_delayed_work(&info->deferred_work, fbdefio->delay);
}
+static void sh_mobile_lcdc_display_on(struct sh_mobile_lcdc_chan *ch)
+{
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
+
+ if (ch->tx_dev) {
+ int ret;
+
+ ret = ch->tx_dev->ops->display_on(ch->tx_dev);
+ if (ret < 0)
+ return;
+
+ if (ret == SH_MOBILE_LCDC_DISPLAY_DISCONNECTED)
+ ch->info->state = FBINFO_STATE_SUSPENDED;
+ }
+
+ /* HDMI must be enabled before LCDC configuration */
+ if (panel->display_on)
+ panel->display_on();
+}
+
+static void sh_mobile_lcdc_display_off(struct sh_mobile_lcdc_chan *ch)
+{
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
+
+ if (panel->display_off)
+ panel->display_off();
+
+ if (ch->tx_dev)
+ ch->tx_dev->ops->display_off(ch->tx_dev);
+}
+
+static bool
+sh_mobile_lcdc_must_reconfigure(struct sh_mobile_lcdc_chan *ch,
+ const struct fb_videomode *new_mode)
+{
+ dev_dbg(ch->info->dev, "Old %ux%u, new %ux%u\n",
+ ch->display.mode.xres, ch->display.mode.yres,
+ new_mode->xres, new_mode->yres);
+
+ /* It can be a different monitor with an equal video-mode */
+ if (fb_mode_is_equal(&ch->display.mode, new_mode))
+ return false;
+
+ dev_dbg(ch->info->dev, "Switching %u -> %u lines\n",
+ ch->display.mode.yres, new_mode->yres);
+ ch->display.mode = *new_mode;
+
+ return true;
+}
+
+static int sh_mobile_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info);
+
+static int sh_mobile_lcdc_display_notify(struct sh_mobile_lcdc_chan *ch,
+ enum sh_mobile_lcdc_entity_event event,
+ const struct fb_videomode *mode,
+ const struct fb_monspecs *monspec)
+{
+ struct fb_info *info = ch->info;
+ struct fb_var_screeninfo var;
+ int ret = 0;
+
+ switch (event) {
+ case SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT:
+ /* HDMI plug in */
+ if (lock_fb_info(info)) {
+ console_lock();
+
+ ch->display.width = monspec->max_x * 10;
+ ch->display.height = monspec->max_y * 10;
+
+ if (!sh_mobile_lcdc_must_reconfigure(ch, mode) &&
+ info->state == FBINFO_STATE_RUNNING) {
+ /* First activation with the default monitor.
+ * Just turn on, if we run a resume here, the
+ * logo disappears.
+ */
+ info->var.width = monspec->max_x * 10;
+ info->var.height = monspec->max_y * 10;
+ sh_mobile_lcdc_display_on(ch);
+ } else {
+ /* New monitor or have to wake up */
+ fb_set_suspend(info, 0);
+ }
+
+ console_unlock();
+ unlock_fb_info(info);
+ }
+ break;
+
+ case SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT:
+ /* HDMI disconnect */
+ if (lock_fb_info(info)) {
+ console_lock();
+ fb_set_suspend(info, 1);
+ console_unlock();
+ unlock_fb_info(info);
+ }
+ break;
+
+ case SH_MOBILE_LCDC_EVENT_DISPLAY_MODE:
+ /* Validate a proposed new mode */
+ fb_videomode_to_var(&var, mode);
+ var.bits_per_pixel = info->var.bits_per_pixel;
+ var.grayscale = info->var.grayscale;
+ ret = sh_mobile_check_var(&var, info);
+ break;
+ }
+
+ return ret;
+}
+
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+struct sh_mobile_lcdc_format_info {
+ u32 fourcc;
+ unsigned int bpp;
+ bool yuv;
+ u32 lddfr;
+};
+
+static const struct sh_mobile_lcdc_format_info sh_mobile_format_infos[] = {
+ {
+ .fourcc = V4L2_PIX_FMT_RGB565,
+ .bpp = 16,
+ .yuv = false,
+ .lddfr = LDDFR_PKF_RGB16,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR24,
+ .bpp = 24,
+ .yuv = false,
+ .lddfr = LDDFR_PKF_RGB24,
+ }, {
+ .fourcc = V4L2_PIX_FMT_BGR32,
+ .bpp = 32,
+ .yuv = false,
+ .lddfr = LDDFR_PKF_ARGB32,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV12,
+ .bpp = 12,
+ .yuv = true,
+ .lddfr = LDDFR_CC | LDDFR_YF_420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV21,
+ .bpp = 12,
+ .yuv = true,
+ .lddfr = LDDFR_CC | LDDFR_YF_420,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV16,
+ .bpp = 16,
+ .yuv = true,
+ .lddfr = LDDFR_CC | LDDFR_YF_422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV61,
+ .bpp = 16,
+ .yuv = true,
+ .lddfr = LDDFR_CC | LDDFR_YF_422,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV24,
+ .bpp = 24,
+ .yuv = true,
+ .lddfr = LDDFR_CC | LDDFR_YF_444,
+ }, {
+ .fourcc = V4L2_PIX_FMT_NV42,
+ .bpp = 24,
+ .yuv = true,
+ .lddfr = LDDFR_CC | LDDFR_YF_444,
+ },
+};
+
+static const struct sh_mobile_lcdc_format_info *
+sh_mobile_format_info(u32 fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(sh_mobile_format_infos); ++i) {
+ if (sh_mobile_format_infos[i].fourcc == fourcc)
+ return &sh_mobile_format_infos[i];
+ }
+
+ return NULL;
+}
+
+static int sh_mobile_format_fourcc(const struct fb_var_screeninfo *var)
+{
+ if (var->grayscale > 1)
+ return var->grayscale;
+
+ switch (var->bits_per_pixel) {
+ case 16:
+ return V4L2_PIX_FMT_RGB565;
+ case 24:
+ return V4L2_PIX_FMT_BGR24;
+ case 32:
+ return V4L2_PIX_FMT_BGR32;
+ default:
+ return 0;
+ }
+}
+
+static int sh_mobile_format_is_fourcc(const struct fb_var_screeninfo *var)
+{
+ return var->grayscale > 1;
+}
+
+/* -----------------------------------------------------------------------------
+ * Start, stop and IRQ
+ */
+
static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
{
struct sh_mobile_lcdc_priv *priv = data;
@@ -385,6 +585,26 @@ static irqreturn_t sh_mobile_lcdc_irq(int irq, void *data)
return IRQ_HANDLED;
}
+static int sh_mobile_wait_for_vsync(struct sh_mobile_lcdc_chan *ch)
+{
+ unsigned long ldintr;
+ int ret;
+
+ /* Enable VSync End interrupt and be careful not to acknowledge any
+ * pending interrupt.
+ */
+ ldintr = lcdc_read(ch->lcdc, _LDINTR);
+ ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
+ lcdc_write(ch->lcdc, _LDINTR, ldintr);
+
+ ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
+ msecs_to_jiffies(100));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
int start)
{
@@ -416,53 +636,52 @@ static void sh_mobile_lcdc_start_stop(struct sh_mobile_lcdc_priv *priv,
static void sh_mobile_lcdc_geometry(struct sh_mobile_lcdc_chan *ch)
{
- struct fb_var_screeninfo *var = &ch->info->var, *display_var = &ch->display_var;
+ const struct fb_var_screeninfo *var = &ch->info->var;
+ const struct fb_videomode *mode = &ch->display.mode;
unsigned long h_total, hsync_pos, display_h_total;
u32 tmp;
tmp = ch->ldmt1r_value;
tmp |= (var->sync & FB_SYNC_VERT_HIGH_ACT) ? 0 : LDMT1R_VPOL;
tmp |= (var->sync & FB_SYNC_HOR_HIGH_ACT) ? 0 : LDMT1R_HPOL;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
- tmp |= (ch->cfg.flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DWPOL) ? LDMT1R_DWPOL : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DIPOL) ? LDMT1R_DIPOL : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DAPOL) ? LDMT1R_DAPOL : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_HSCNT) ? LDMT1R_HSCNT : 0;
+ tmp |= (ch->cfg->flags & LCDC_FLAGS_DWCNT) ? LDMT1R_DWCNT : 0;
lcdc_write_chan(ch, LDMT1R, tmp);
/* setup SYS bus */
- lcdc_write_chan(ch, LDMT2R, ch->cfg.sys_bus_cfg.ldmt2r);
- lcdc_write_chan(ch, LDMT3R, ch->cfg.sys_bus_cfg.ldmt3r);
+ lcdc_write_chan(ch, LDMT2R, ch->cfg->sys_bus_cfg.ldmt2r);
+ lcdc_write_chan(ch, LDMT3R, ch->cfg->sys_bus_cfg.ldmt3r);
/* horizontal configuration */
- h_total = display_var->xres + display_var->hsync_len +
- display_var->left_margin + display_var->right_margin;
+ h_total = mode->xres + mode->hsync_len + mode->left_margin
+ + mode->right_margin;
tmp = h_total / 8; /* HTCN */
- tmp |= (min(display_var->xres, var->xres) / 8) << 16; /* HDCN */
+ tmp |= (min(mode->xres, ch->xres) / 8) << 16; /* HDCN */
lcdc_write_chan(ch, LDHCNR, tmp);
- hsync_pos = display_var->xres + display_var->right_margin;
+ hsync_pos = mode->xres + mode->right_margin;
tmp = hsync_pos / 8; /* HSYNP */
- tmp |= (display_var->hsync_len / 8) << 16; /* HSYNW */
+ tmp |= (mode->hsync_len / 8) << 16; /* HSYNW */
lcdc_write_chan(ch, LDHSYNR, tmp);
/* vertical configuration */
- tmp = display_var->yres + display_var->vsync_len +
- display_var->upper_margin + display_var->lower_margin; /* VTLN */
- tmp |= min(display_var->yres, var->yres) << 16; /* VDLN */
+ tmp = mode->yres + mode->vsync_len + mode->upper_margin
+ + mode->lower_margin; /* VTLN */
+ tmp |= min(mode->yres, ch->yres) << 16; /* VDLN */
lcdc_write_chan(ch, LDVLNR, tmp);
- tmp = display_var->yres + display_var->lower_margin; /* VSYNP */
- tmp |= display_var->vsync_len << 16; /* VSYNW */
+ tmp = mode->yres + mode->lower_margin; /* VSYNP */
+ tmp |= mode->vsync_len << 16; /* VSYNW */
lcdc_write_chan(ch, LDVSYNR, tmp);
/* Adjust horizontal synchronisation for HDMI */
- display_h_total = display_var->xres + display_var->hsync_len +
- display_var->left_margin + display_var->right_margin;
- tmp = ((display_var->xres & 7) << 24) |
- ((display_h_total & 7) << 16) |
- ((display_var->hsync_len & 7) << 8) |
- (hsync_pos & 7);
+ display_h_total = mode->xres + mode->hsync_len + mode->left_margin
+ + mode->right_margin;
+ tmp = ((mode->xres & 7) << 24) | ((display_h_total & 7) << 16)
+ | ((mode->hsync_len & 7) << 8) | (hsync_pos & 7);
lcdc_write_chan(ch, LDHAJR, tmp);
}
@@ -498,7 +717,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* Power supply */
lcdc_write_chan(ch, LDPMR, 0);
- m = ch->cfg.clock_divider;
+ m = ch->cfg->clock_divider;
if (!m)
continue;
@@ -525,32 +744,10 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_geometry(ch);
- switch (sh_mobile_format_fourcc(&ch->info->var)) {
- case V4L2_PIX_FMT_RGB565:
- tmp = LDDFR_PKF_RGB16;
- break;
- case V4L2_PIX_FMT_BGR24:
- tmp = LDDFR_PKF_RGB24;
- break;
- case V4L2_PIX_FMT_BGR32:
- tmp = LDDFR_PKF_ARGB32;
- break;
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- tmp = LDDFR_CC | LDDFR_YF_420;
- break;
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- tmp = LDDFR_CC | LDDFR_YF_422;
- break;
- case V4L2_PIX_FMT_NV24:
- case V4L2_PIX_FMT_NV42:
- tmp = LDDFR_CC | LDDFR_YF_444;
- break;
- }
+ tmp = ch->format->lddfr;
- if (sh_mobile_format_is_yuv(&ch->info->var)) {
- switch (ch->info->var.colorspace) {
+ if (ch->format->yuv) {
+ switch (ch->colorspace) {
case V4L2_COLORSPACE_REC709:
tmp |= LDDFR_CF1;
break;
@@ -563,7 +760,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_write_chan(ch, LDDFR, tmp);
lcdc_write_chan(ch, LDMLSR, ch->pitch);
lcdc_write_chan(ch, LDSA1R, ch->base_addr_y);
- if (sh_mobile_format_is_yuv(&ch->info->var))
+ if (ch->format->yuv)
lcdc_write_chan(ch, LDSA2R, ch->base_addr_c);
/* When using deferred I/O mode, configure the LCDC for one-shot
@@ -571,7 +768,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
* continuous read mode.
*/
if (ch->ldmt1r_value & LDMT1R_IFM &&
- ch->cfg.sys_bus_cfg.deferred_io_msec) {
+ ch->cfg->sys_bus_cfg.deferred_io_msec) {
lcdc_write_chan(ch, LDSM1R, LDSM1R_OS);
lcdc_write(priv, _LDINTR, LDINTR_FE);
} else {
@@ -580,7 +777,7 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
}
/* Word and long word swap. */
- switch (sh_mobile_format_fourcc(&priv->ch[0].info->var)) {
+ switch (priv->ch[0].format->fourcc) {
case V4L2_PIX_FMT_RGB565:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV61:
@@ -609,7 +806,6 @@ static void __sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_meram_info *mdev = priv->meram_dev;
- struct sh_mobile_lcdc_board_cfg *board_cfg;
struct sh_mobile_lcdc_chan *ch;
unsigned long tmp;
int ret;
@@ -626,15 +822,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
lcdc_wait_bit(priv, _LDCNT2R, LDCNT2R_BR, 0);
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- ch = &priv->ch[k];
+ const struct sh_mobile_lcdc_panel_cfg *panel;
+ ch = &priv->ch[k];
if (!ch->enabled)
continue;
- board_cfg = &ch->cfg.board_cfg;
- if (board_cfg->setup_sys) {
- ret = board_cfg->setup_sys(board_cfg->board_data, ch,
- &sh_mobile_lcdc_sys_bus_ops);
+ panel = &ch->cfg->panel_cfg;
+ if (panel->setup_sys) {
+ ret = panel->setup_sys(ch, &sh_mobile_lcdc_sys_bus_ops);
if (ret)
return ret;
}
@@ -642,33 +838,30 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
/* Compute frame buffer base address and pitch for each channel. */
for (k = 0; k < ARRAY_SIZE(priv->ch); k++) {
- struct sh_mobile_meram_cfg *cfg;
int pixelformat;
+ void *meram;
ch = &priv->ch[k];
if (!ch->enabled)
continue;
- ch->base_addr_y = ch->info->fix.smem_start;
- ch->base_addr_c = ch->base_addr_y
- + ch->info->var.xres
- * ch->info->var.yres_virtual;
- ch->pitch = ch->info->fix.line_length;
+ ch->base_addr_y = ch->dma_handle;
+ ch->base_addr_c = ch->base_addr_y + ch->xres * ch->yres_virtual;
/* Enable MERAM if possible. */
- cfg = ch->cfg.meram_cfg;
- if (mdev == NULL || mdev->ops == NULL || cfg == NULL)
+ if (mdev == NULL || mdev->ops == NULL ||
+ ch->cfg->meram_cfg == NULL)
continue;
/* we need to de-init configured ICBs before we can
* re-initialize them.
*/
- if (ch->meram_enabled) {
- mdev->ops->meram_unregister(mdev, cfg);
- ch->meram_enabled = 0;
+ if (ch->meram) {
+ mdev->ops->meram_unregister(mdev, ch->meram);
+ ch->meram = NULL;
}
- switch (sh_mobile_format_fourcc(&ch->info->var)) {
+ switch (ch->format->fourcc) {
case V4L2_PIX_FMT_NV12:
case V4L2_PIX_FMT_NV21:
case V4L2_PIX_FMT_NV16:
@@ -687,13 +880,15 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
break;
}
- ret = mdev->ops->meram_register(mdev, cfg, ch->pitch,
- ch->info->var.yres, pixelformat,
- ch->base_addr_y, ch->base_addr_c,
- &ch->base_addr_y, &ch->base_addr_c,
+ meram = mdev->ops->meram_register(mdev, ch->cfg->meram_cfg,
+ ch->pitch, ch->yres, pixelformat,
&ch->pitch);
- if (!ret)
- ch->meram_enabled = 1;
+ if (!IS_ERR(meram)) {
+ mdev->ops->meram_update(mdev, meram,
+ ch->base_addr_y, ch->base_addr_c,
+ &ch->base_addr_y, &ch->base_addr_c);
+ ch->meram = meram;
+ }
}
/* Start the LCDC. */
@@ -707,7 +902,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
if (!ch->enabled)
continue;
- tmp = ch->cfg.sys_bus_cfg.deferred_io_msec;
+ tmp = ch->cfg->sys_bus_cfg.deferred_io_msec;
if (ch->ldmt1r_value & LDMT1R_IFM && tmp) {
ch->defio.deferred_io = sh_mobile_lcdc_deferred_io;
ch->defio.delay = msecs_to_jiffies(tmp);
@@ -715,11 +910,7 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
fb_deferred_io_init(ch->info);
}
- board_cfg = &ch->cfg.board_cfg;
- if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
- board_cfg->display_on(board_cfg->board_data, ch->info);
- module_put(board_cfg->owner);
- }
+ sh_mobile_lcdc_display_on(ch);
if (ch->bl) {
ch->bl->props.power = FB_BLANK_UNBLANK;
@@ -733,7 +924,6 @@ static int sh_mobile_lcdc_start(struct sh_mobile_lcdc_priv *priv)
static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
{
struct sh_mobile_lcdc_chan *ch;
- struct sh_mobile_lcdc_board_cfg *board_cfg;
int k;
/* clean up deferred io and ask board code to disable panel */
@@ -760,20 +950,14 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
backlight_update_status(ch->bl);
}
- board_cfg = &ch->cfg.board_cfg;
- if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
- board_cfg->display_off(board_cfg->board_data);
- module_put(board_cfg->owner);
- }
+ sh_mobile_lcdc_display_off(ch);
/* disable the meram */
- if (ch->meram_enabled) {
- struct sh_mobile_meram_cfg *cfg;
+ if (ch->meram) {
struct sh_mobile_meram_info *mdev;
- cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev;
- mdev->ops->meram_unregister(mdev, cfg);
- ch->meram_enabled = 0;
+ mdev->ops->meram_unregister(mdev, ch->meram);
+ ch->meram = 0;
}
}
@@ -790,86 +974,9 @@ static void sh_mobile_lcdc_stop(struct sh_mobile_lcdc_priv *priv)
sh_mobile_lcdc_clk_off(priv);
}
-static int sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
-{
- int interface_type = ch->cfg.interface_type;
-
- switch (interface_type) {
- case RGB8:
- case RGB9:
- case RGB12A:
- case RGB12B:
- case RGB16:
- case RGB18:
- case RGB24:
- case SYS8A:
- case SYS8B:
- case SYS8C:
- case SYS8D:
- case SYS9:
- case SYS12:
- case SYS16A:
- case SYS16B:
- case SYS16C:
- case SYS18:
- case SYS24:
- break;
- default:
- return -EINVAL;
- }
-
- /* SUBLCD only supports SYS interface */
- if (lcdc_chan_is_sublcd(ch)) {
- if (!(interface_type & LDMT1R_IFM))
- return -EINVAL;
-
- interface_type &= ~LDMT1R_IFM;
- }
-
- ch->ldmt1r_value = interface_type;
- return 0;
-}
-
-static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev,
- int clock_source,
- struct sh_mobile_lcdc_priv *priv)
-{
- char *str;
-
- switch (clock_source) {
- case LCDC_CLK_BUS:
- str = "bus_clk";
- priv->lddckr = LDDCKR_ICKSEL_BUS;
- break;
- case LCDC_CLK_PERIPHERAL:
- str = "peripheral_clk";
- priv->lddckr = LDDCKR_ICKSEL_MIPI;
- break;
- case LCDC_CLK_EXTERNAL:
- str = NULL;
- priv->lddckr = LDDCKR_ICKSEL_HDMI;
- break;
- default:
- return -EINVAL;
- }
-
- if (str) {
- priv->dot_clk = clk_get(&pdev->dev, str);
- if (IS_ERR(priv->dot_clk)) {
- dev_err(&pdev->dev, "cannot get dot clock %s\n", str);
- return PTR_ERR(priv->dot_clk);
- }
- }
-
- /* Runtime PM support involves two step for this driver:
- * 1) Enable Runtime PM
- * 2) Force Runtime PM Resume since hardware is accessed from probe()
- */
- priv->dev = &pdev->dev;
- pm_runtime_enable(priv->dev);
- pm_runtime_resume(priv->dev);
- return 0;
-}
+/* -----------------------------------------------------------------------------
+ * Frame buffer operations
+ */
static int sh_mobile_lcdc_setcolreg(u_int regno,
u_int red, u_int green, u_int blue,
@@ -936,14 +1043,12 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
unsigned long new_pan_offset;
unsigned long base_addr_y, base_addr_c;
unsigned long c_offset;
- bool yuv = sh_mobile_format_is_yuv(&info->var);
- if (!yuv)
- new_pan_offset = var->yoffset * info->fix.line_length
- + var->xoffset * (info->var.bits_per_pixel / 8);
+ if (!ch->format->yuv)
+ new_pan_offset = var->yoffset * ch->pitch
+ + var->xoffset * (ch->format->bpp / 8);
else
- new_pan_offset = var->yoffset * info->fix.line_length
- + var->xoffset;
+ new_pan_offset = var->yoffset * ch->pitch + var->xoffset;
if (new_pan_offset == ch->pan_offset)
return 0; /* No change, do nothing */
@@ -952,39 +1057,33 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
/* Set the source address for the next refresh */
base_addr_y = ch->dma_handle + new_pan_offset;
- if (yuv) {
+ if (ch->format->yuv) {
/* Set y offset */
- c_offset = var->yoffset * info->fix.line_length
- * (info->var.bits_per_pixel - 8) / 8;
- base_addr_c = ch->dma_handle
- + info->var.xres * info->var.yres_virtual
+ c_offset = var->yoffset * ch->pitch
+ * (ch->format->bpp - 8) / 8;
+ base_addr_c = ch->dma_handle + ch->xres * ch->yres_virtual
+ c_offset;
/* Set x offset */
- if (sh_mobile_format_fourcc(&info->var) == V4L2_PIX_FMT_NV24)
+ if (ch->format->fourcc == V4L2_PIX_FMT_NV24)
base_addr_c += 2 * var->xoffset;
else
base_addr_c += var->xoffset;
}
- if (ch->meram_enabled) {
- struct sh_mobile_meram_cfg *cfg;
+ if (ch->meram) {
struct sh_mobile_meram_info *mdev;
- int ret;
- cfg = ch->cfg.meram_cfg;
mdev = priv->meram_dev;
- ret = mdev->ops->meram_update(mdev, cfg,
+ mdev->ops->meram_update(mdev, ch->meram,
base_addr_y, base_addr_c,
&base_addr_y, &base_addr_c);
- if (ret)
- return ret;
}
ch->base_addr_y = base_addr_y;
ch->base_addr_c = base_addr_c;
lcdc_write_chan_mirror(ch, LDSA1R, base_addr_y);
- if (yuv)
+ if (ch->format->yuv)
lcdc_write_chan_mirror(ch, LDSA2R, base_addr_c);
if (lcdc_chan_is_sublcd(ch))
@@ -999,27 +1098,6 @@ static int sh_mobile_fb_pan_display(struct fb_var_screeninfo *var,
return 0;
}
-static int sh_mobile_wait_for_vsync(struct fb_info *info)
-{
- struct sh_mobile_lcdc_chan *ch = info->par;
- unsigned long ldintr;
- int ret;
-
- /* Enable VSync End interrupt and be careful not to acknowledge any
- * pending interrupt.
- */
- ldintr = lcdc_read(ch->lcdc, _LDINTR);
- ldintr |= LDINTR_VEE | LDINTR_STATUS_MASK;
- lcdc_write(ch->lcdc, _LDINTR, ldintr);
-
- ret = wait_for_completion_interruptible_timeout(&ch->vsync_completion,
- msecs_to_jiffies(100));
- if (!ret)
- return -ETIMEDOUT;
-
- return 0;
-}
-
static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
@@ -1027,7 +1105,7 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
switch (cmd) {
case FBIO_WAITFORVSYNC:
- retval = sh_mobile_wait_for_vsync(info);
+ retval = sh_mobile_wait_for_vsync(info->par);
break;
default:
@@ -1040,7 +1118,8 @@ static int sh_mobile_ioctl(struct fb_info *info, unsigned int cmd,
static void sh_mobile_fb_reconfig(struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- struct fb_videomode mode1, mode2;
+ struct fb_var_screeninfo var;
+ struct fb_videomode mode;
struct fb_event event;
int evnt = FB_EVENT_MODE_CHANGE_ALL;
@@ -1048,14 +1127,19 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
/* More framebuffer users are active */
return;
- fb_var_to_videomode(&mode1, &ch->display_var);
- fb_var_to_videomode(&mode2, &info->var);
+ fb_var_to_videomode(&mode, &info->var);
- if (fb_mode_is_equal(&mode1, &mode2))
+ if (fb_mode_is_equal(&ch->display.mode, &mode))
return;
/* Display has been re-plugged, framebuffer is free now, reconfigure */
- if (fb_set_var(info, &ch->display_var) < 0)
+ var = info->var;
+ fb_videomode_to_var(&var, &ch->display.mode);
+ var.width = ch->display.width;
+ var.height = ch->display.height;
+ var.activate = FB_ACTIVATE_NOW;
+
+ if (fb_set_var(info, &var) < 0)
/* Couldn't reconfigure, hopefully, can continue as before */
return;
@@ -1065,7 +1149,7 @@ static void sh_mobile_fb_reconfig(struct fb_info *info)
* user event, we have to call the chain ourselves.
*/
event.info = info;
- event.data = &mode1;
+ event.data = &ch->display.mode;
fb_notifier_call_chain(evnt, &event);
}
@@ -1124,8 +1208,8 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
* distance between two modes is defined as the size of the
* non-overlapping parts of the two rectangles.
*/
- for (i = 0; i < ch->cfg.num_cfg; ++i) {
- const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i];
+ for (i = 0; i < ch->cfg->num_modes; ++i) {
+ const struct fb_videomode *mode = &ch->cfg->lcd_modes[i];
unsigned int dist;
/* We can only round up. */
@@ -1144,7 +1228,7 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
}
/* If no available mode can be used, return an error. */
- if (ch->cfg.num_cfg != 0) {
+ if (ch->cfg->num_modes != 0) {
if (best_dist == (unsigned int)-1)
return -EINVAL;
@@ -1161,32 +1245,17 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
var->yres_virtual = var->yres;
if (sh_mobile_format_is_fourcc(var)) {
- switch (var->grayscale) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- var->bits_per_pixel = 12;
- break;
- case V4L2_PIX_FMT_RGB565:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- var->bits_per_pixel = 16;
- break;
- case V4L2_PIX_FMT_BGR24:
- case V4L2_PIX_FMT_NV24:
- case V4L2_PIX_FMT_NV42:
- var->bits_per_pixel = 24;
- break;
- case V4L2_PIX_FMT_BGR32:
- var->bits_per_pixel = 32;
- break;
- default:
+ const struct sh_mobile_lcdc_format_info *format;
+
+ format = sh_mobile_format_info(var->grayscale);
+ if (format == NULL)
return -EINVAL;
- }
+ var->bits_per_pixel = format->bpp;
/* Default to RGB and JPEG color-spaces for RGB and YUV formats
* respectively.
*/
- if (!sh_mobile_format_is_yuv(var))
+ if (!format->yuv)
var->colorspace = V4L2_COLORSPACE_SRGB;
else if (var->colorspace != V4L2_COLORSPACE_REC709)
var->colorspace = V4L2_COLORSPACE_JPEG;
@@ -1246,22 +1315,28 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in
static int sh_mobile_set_par(struct fb_info *info)
{
struct sh_mobile_lcdc_chan *ch = info->par;
- u32 line_length = info->fix.line_length;
int ret;
sh_mobile_lcdc_stop(ch->lcdc);
- if (sh_mobile_format_is_yuv(&info->var))
- info->fix.line_length = info->var.xres;
+ ch->format = sh_mobile_format_info(sh_mobile_format_fourcc(&info->var));
+ ch->colorspace = info->var.colorspace;
+
+ ch->xres = info->var.xres;
+ ch->xres_virtual = info->var.xres_virtual;
+ ch->yres = info->var.yres;
+ ch->yres_virtual = info->var.yres_virtual;
+
+ if (ch->format->yuv)
+ ch->pitch = info->var.xres;
else
- info->fix.line_length = info->var.xres
- * info->var.bits_per_pixel / 8;
+ ch->pitch = info->var.xres * ch->format->bpp / 8;
ret = sh_mobile_lcdc_start(ch->lcdc);
- if (ret < 0) {
+ if (ret < 0)
dev_err(info->dev, "%s: unable to restart LCDC\n", __func__);
- info->fix.line_length = line_length;
- }
+
+ info->fix.line_length = ch->pitch;
if (sh_mobile_format_is_fourcc(&info->var)) {
info->fix.type = FB_TYPE_FOURCC;
@@ -1290,8 +1365,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
/* blank the screen? */
if (blank > FB_BLANK_UNBLANK && ch->blank_status == FB_BLANK_UNBLANK) {
struct fb_fillrect rect = {
- .width = info->var.xres,
- .height = info->var.yres,
+ .width = ch->xres,
+ .height = ch->yres,
};
sh_mobile_lcdc_fillrect(info, &rect);
}
@@ -1307,8 +1382,8 @@ static int sh_mobile_lcdc_blank(int blank, struct fb_info *info)
* mode will reenable the clocks and update the screen in time,
* so it does not need this. */
if (!info->fbdefio) {
- sh_mobile_wait_for_vsync(info);
- sh_mobile_wait_for_vsync(info);
+ sh_mobile_wait_for_vsync(ch);
+ sh_mobile_wait_for_vsync(ch);
}
sh_mobile_lcdc_clk_off(p);
}
@@ -1334,25 +1409,161 @@ static struct fb_ops sh_mobile_lcdc_ops = {
.fb_set_par = sh_mobile_set_par,
};
+static void
+sh_mobile_lcdc_channel_fb_unregister(struct sh_mobile_lcdc_chan *ch)
+{
+ if (ch->info && ch->info->dev)
+ unregister_framebuffer(ch->info);
+}
+
+static int __devinit
+sh_mobile_lcdc_channel_fb_register(struct sh_mobile_lcdc_chan *ch)
+{
+ struct fb_info *info = ch->info;
+ int ret;
+
+ if (info->fbdefio) {
+ ch->sglist = vmalloc(sizeof(struct scatterlist) *
+ ch->fb_size >> PAGE_SHIFT);
+ if (!ch->sglist) {
+ dev_err(ch->lcdc->dev, "cannot allocate sglist\n");
+ return -ENOMEM;
+ }
+ }
+
+ info->bl_dev = ch->bl;
+
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ return ret;
+
+ dev_info(ch->lcdc->dev, "registered %s/%s as %dx%d %dbpp.\n",
+ dev_name(ch->lcdc->dev), (ch->cfg->chan == LCDC_CHAN_MAINLCD) ?
+ "mainlcd" : "sublcd", info->var.xres, info->var.yres,
+ info->var.bits_per_pixel);
+
+ /* deferred io mode: disable clock to save power */
+ if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
+ sh_mobile_lcdc_clk_off(ch->lcdc);
+
+ return ret;
+}
+
+static void
+sh_mobile_lcdc_channel_fb_cleanup(struct sh_mobile_lcdc_chan *ch)
+{
+ struct fb_info *info = ch->info;
+
+ if (!info || !info->device)
+ return;
+
+ if (ch->sglist)
+ vfree(ch->sglist);
+
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+}
+
+static int __devinit
+sh_mobile_lcdc_channel_fb_init(struct sh_mobile_lcdc_chan *ch,
+ const struct fb_videomode *mode,
+ unsigned int num_modes)
+{
+ struct sh_mobile_lcdc_priv *priv = ch->lcdc;
+ struct fb_var_screeninfo *var;
+ struct fb_info *info;
+ int ret;
+
+ /* Allocate and initialize the frame buffer device. Create the modes
+ * list and allocate the color map.
+ */
+ info = framebuffer_alloc(0, priv->dev);
+ if (info == NULL) {
+ dev_err(priv->dev, "unable to allocate fb_info\n");
+ return -ENOMEM;
+ }
+
+ ch->info = info;
+
+ info->flags = FBINFO_FLAG_DEFAULT;
+ info->fbops = &sh_mobile_lcdc_ops;
+ info->device = priv->dev;
+ info->screen_base = ch->fb_mem;
+ info->pseudo_palette = &ch->pseudo_palette;
+ info->par = ch;
+
+ fb_videomode_to_modelist(mode, num_modes, &info->modelist);
+
+ ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
+ if (ret < 0) {
+ dev_err(priv->dev, "unable to allocate cmap\n");
+ return ret;
+ }
+
+ /* Initialize fixed screen information. Restrict pan to 2 lines steps
+ * for NV12 and NV21.
+ */
+ info->fix = sh_mobile_lcdc_fix;
+ info->fix.smem_start = ch->dma_handle;
+ info->fix.smem_len = ch->fb_size;
+ info->fix.line_length = ch->pitch;
+
+ if (ch->format->yuv)
+ info->fix.visual = FB_VISUAL_FOURCC;
+ else
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ if (ch->format->fourcc == V4L2_PIX_FMT_NV12 ||
+ ch->format->fourcc == V4L2_PIX_FMT_NV21)
+ info->fix.ypanstep = 2;
+
+ /* Initialize variable screen information using the first mode as
+ * default. The default Y virtual resolution is twice the panel size to
+ * allow for double-buffering.
+ */
+ var = &info->var;
+ fb_videomode_to_var(var, mode);
+ var->width = ch->cfg->panel_cfg.width;
+ var->height = ch->cfg->panel_cfg.height;
+ var->yres_virtual = var->yres * 2;
+ var->activate = FB_ACTIVATE_NOW;
+
+ /* Use the legacy API by default for RGB formats, and the FOURCC API
+ * for YUV formats.
+ */
+ if (!ch->format->yuv)
+ var->bits_per_pixel = ch->format->bpp;
+ else
+ var->grayscale = ch->format->fourcc;
+
+ ret = sh_mobile_check_var(var, info);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Backlight
+ */
+
static int sh_mobile_lcdc_update_bl(struct backlight_device *bdev)
{
struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
- struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
int brightness = bdev->props.brightness;
if (bdev->props.power != FB_BLANK_UNBLANK ||
bdev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK))
brightness = 0;
- return cfg->set_brightness(cfg->board_data, brightness);
+ return ch->cfg->bl_info.set_brightness(brightness);
}
static int sh_mobile_lcdc_get_brightness(struct backlight_device *bdev)
{
struct sh_mobile_lcdc_chan *ch = bl_get_data(bdev);
- struct sh_mobile_lcdc_board_cfg *cfg = &ch->cfg.board_cfg;
- return cfg->get_brightness(cfg->board_data);
+ return ch->cfg->bl_info.get_brightness();
}
static int sh_mobile_lcdc_check_fb(struct backlight_device *bdev,
@@ -1373,7 +1584,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
{
struct backlight_device *bl;
- bl = backlight_device_register(ch->cfg.bl_info.name, parent, ch,
+ bl = backlight_device_register(ch->cfg->bl_info.name, parent, ch,
&sh_mobile_lcdc_bl_ops, NULL);
if (IS_ERR(bl)) {
dev_err(parent, "unable to register backlight device: %ld\n",
@@ -1381,7 +1592,7 @@ static struct backlight_device *sh_mobile_lcdc_bl_probe(struct device *parent,
return NULL;
}
- bl->props.max_brightness = ch->cfg.bl_info.max_brightness;
+ bl->props.max_brightness = ch->cfg->bl_info.max_brightness;
bl->props.brightness = bl->props.max_brightness;
backlight_update_status(bl);
@@ -1393,6 +1604,10 @@ static void sh_mobile_lcdc_bl_remove(struct backlight_device *bdev)
backlight_device_unregister(bdev);
}
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
static int sh_mobile_lcdc_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -1436,6 +1651,10 @@ static const struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = {
.runtime_resume = sh_mobile_lcdc_runtime_resume,
};
+/* -----------------------------------------------------------------------------
+ * Framebuffer notifier
+ */
+
/* locking: called with info->lock held */
static int sh_mobile_lcdc_notify(struct notifier_block *nb,
unsigned long action, void *data)
@@ -1443,7 +1662,6 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
struct fb_event *event = data;
struct fb_info *info = event->info;
struct sh_mobile_lcdc_chan *ch = info->par;
- struct sh_mobile_lcdc_board_cfg *board_cfg = &ch->cfg.board_cfg;
if (&ch->lcdc->notifier != nb)
return NOTIFY_DONE;
@@ -1453,10 +1671,7 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
switch(action) {
case FB_EVENT_SUSPEND:
- if (board_cfg->display_off && try_module_get(board_cfg->owner)) {
- board_cfg->display_off(board_cfg->board_data);
- module_put(board_cfg->owner);
- }
+ sh_mobile_lcdc_display_off(ch);
sh_mobile_lcdc_stop(ch->lcdc);
break;
case FB_EVENT_RESUME:
@@ -1464,47 +1679,60 @@ static int sh_mobile_lcdc_notify(struct notifier_block *nb,
sh_mobile_fb_reconfig(info);
mutex_unlock(&ch->open_lock);
- /* HDMI must be enabled before LCDC configuration */
- if (board_cfg->display_on && try_module_get(board_cfg->owner)) {
- board_cfg->display_on(board_cfg->board_data, info);
- module_put(board_cfg->owner);
- }
-
+ sh_mobile_lcdc_display_on(ch);
sh_mobile_lcdc_start(ch->lcdc);
}
return NOTIFY_OK;
}
+/* -----------------------------------------------------------------------------
+ * Probe/remove and driver init/exit
+ */
+
+static const struct fb_videomode default_720p __devinitconst = {
+ .name = "HDMI 720p",
+ .xres = 1280,
+ .yres = 720,
+
+ .left_margin = 220,
+ .right_margin = 110,
+ .hsync_len = 40,
+
+ .upper_margin = 20,
+ .lower_margin = 5,
+ .vsync_len = 5,
+
+ .pixclock = 13468,
+ .refresh = 60,
+ .sync = FB_SYNC_VERT_HIGH_ACT | FB_SYNC_HOR_HIGH_ACT,
+};
+
static int sh_mobile_lcdc_remove(struct platform_device *pdev)
{
struct sh_mobile_lcdc_priv *priv = platform_get_drvdata(pdev);
- struct fb_info *info;
int i;
fb_unregister_client(&priv->notifier);
for (i = 0; i < ARRAY_SIZE(priv->ch); i++)
- if (priv->ch[i].info && priv->ch[i].info->dev)
- unregister_framebuffer(priv->ch[i].info);
+ sh_mobile_lcdc_channel_fb_unregister(&priv->ch[i]);
sh_mobile_lcdc_stop(priv);
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
- info = priv->ch[i].info;
+ struct sh_mobile_lcdc_chan *ch = &priv->ch[i];
- if (!info || !info->device)
- continue;
+ if (ch->tx_dev) {
+ ch->tx_dev->lcdc = NULL;
+ module_put(ch->cfg->tx_dev->dev.driver->owner);
+ }
- if (priv->ch[i].sglist)
- vfree(priv->ch[i].sglist);
+ sh_mobile_lcdc_channel_fb_cleanup(ch);
- if (info->screen_base)
- dma_free_coherent(&pdev->dev, info->fix.smem_len,
- info->screen_base,
- priv->ch[i].dma_handle);
- fb_dealloc_cmap(&info->cmap);
- framebuffer_release(info);
+ if (ch->fb_mem)
+ dma_free_coherent(&pdev->dev, ch->fb_size,
+ ch->fb_mem, ch->dma_handle);
}
for (i = 0; i < ARRAY_SIZE(priv->ch); i++) {
@@ -1512,11 +1740,10 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
sh_mobile_lcdc_bl_remove(priv->ch[i].bl);
}
- if (priv->dot_clk)
+ if (priv->dot_clk) {
+ pm_runtime_disable(&pdev->dev);
clk_put(priv->dot_clk);
-
- if (priv->dev)
- pm_runtime_disable(priv->dev);
+ }
if (priv->base)
iounmap(priv->base);
@@ -1527,49 +1754,82 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev)
return 0;
}
-static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
- struct device *dev)
+static int __devinit sh_mobile_lcdc_check_interface(struct sh_mobile_lcdc_chan *ch)
{
- struct sh_mobile_lcdc_chan_cfg *cfg = &ch->cfg;
+ int interface_type = ch->cfg->interface_type;
+
+ switch (interface_type) {
+ case RGB8:
+ case RGB9:
+ case RGB12A:
+ case RGB12B:
+ case RGB16:
+ case RGB18:
+ case RGB24:
+ case SYS8A:
+ case SYS8B:
+ case SYS8C:
+ case SYS8D:
+ case SYS9:
+ case SYS12:
+ case SYS16A:
+ case SYS16B:
+ case SYS16C:
+ case SYS18:
+ case SYS24:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* SUBLCD only supports SYS interface */
+ if (lcdc_chan_is_sublcd(ch)) {
+ if (!(interface_type & LDMT1R_IFM))
+ return -EINVAL;
+
+ interface_type &= ~LDMT1R_IFM;
+ }
+
+ ch->ldmt1r_value = interface_type;
+ return 0;
+}
+
+static int __devinit
+sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_priv *priv,
+ struct sh_mobile_lcdc_chan *ch)
+{
+ const struct sh_mobile_lcdc_format_info *format;
+ const struct sh_mobile_lcdc_chan_cfg *cfg = ch->cfg;
const struct fb_videomode *max_mode;
const struct fb_videomode *mode;
- struct fb_var_screeninfo *var;
- struct fb_info *info;
+ unsigned int num_modes;
unsigned int max_size;
- int num_cfg;
- void *buf;
- int ret;
- int i;
+ unsigned int i;
mutex_init(&ch->open_lock);
+ ch->notify = sh_mobile_lcdc_display_notify;
- /* Allocate the frame buffer device. */
- ch->info = framebuffer_alloc(0, dev);
- if (!ch->info) {
- dev_err(dev, "unable to allocate fb_info\n");
- return -ENOMEM;
+ /* Validate the format. */
+ format = sh_mobile_format_info(cfg->fourcc);
+ if (format == NULL) {
+ dev_err(priv->dev, "Invalid FOURCC %08x.\n", cfg->fourcc);
+ return -EINVAL;
}
- info = ch->info;
- info->fbops = &sh_mobile_lcdc_ops;
- info->par = ch;
- info->pseudo_palette = &ch->pseudo_palette;
- info->flags = FBINFO_FLAG_DEFAULT;
-
/* Iterate through the modes to validate them and find the highest
* resolution.
*/
max_mode = NULL;
max_size = 0;
- for (i = 0, mode = cfg->lcd_cfg; i < cfg->num_cfg; i++, mode++) {
+ for (i = 0, mode = cfg->lcd_modes; i < cfg->num_modes; i++, mode++) {
unsigned int size = mode->yres * mode->xres;
/* NV12/NV21 buffers must have even number of lines */
if ((cfg->fourcc == V4L2_PIX_FMT_NV12 ||
cfg->fourcc == V4L2_PIX_FMT_NV21) && (mode->yres & 0x1)) {
- dev_err(dev, "yres must be multiple of 2 for YCbCr420 "
- "mode.\n");
+ dev_err(priv->dev, "yres must be multiple of 2 for "
+ "YCbCr420 mode.\n");
return -EINVAL;
}
@@ -1582,93 +1842,59 @@ static int __devinit sh_mobile_lcdc_channel_init(struct sh_mobile_lcdc_chan *ch,
if (!max_size)
max_size = MAX_XRES * MAX_YRES;
else
- dev_dbg(dev, "Found largest videomode %ux%u\n",
+ dev_dbg(priv->dev, "Found largest videomode %ux%u\n",
max_mode->xres, max_mode->yres);
- /* Create the mode list. */
- if (cfg->lcd_cfg == NULL) {
+ if (cfg->lcd_modes == NULL) {
mode = &default_720p;
- num_cfg = 1;
+ num_modes = 1;
} else {
- mode = cfg->lcd_cfg;
- num_cfg = cfg->num_cfg;
+ mode = cfg->lcd_modes;
+ num_modes = cfg->num_modes;
}
- fb_videomode_to_modelist(mode, num_cfg, &info->modelist);
+ /* Use the first mode as default. */
+ ch->format = format;
+ ch->xres = mode->xres;
+ ch->xres_virtual = mode->xres;
+ ch->yres = mode->yres;
+ ch->yres_virtual = mode->yres * 2;
- /* Initialize variable screen information using the first mode as
- * default. The default Y virtual resolution is twice the panel size to
- * allow for double-buffering.
- */
- var = &info->var;
- fb_videomode_to_var(var, mode);
- var->width = cfg->lcd_size_cfg.width;
- var->height = cfg->lcd_size_cfg.height;
- var->yres_virtual = var->yres * 2;
- var->activate = FB_ACTIVATE_NOW;
-
- switch (cfg->fourcc) {
- case V4L2_PIX_FMT_RGB565:
- var->bits_per_pixel = 16;
- break;
- case V4L2_PIX_FMT_BGR24:
- var->bits_per_pixel = 24;
- break;
- case V4L2_PIX_FMT_BGR32:
- var->bits_per_pixel = 32;
- break;
- default:
- var->grayscale = cfg->fourcc;
- break;
+ if (!format->yuv) {
+ ch->colorspace = V4L2_COLORSPACE_SRGB;
+ ch->pitch = ch->xres * format->bpp / 8;
+ } else {
+ ch->colorspace = V4L2_COLORSPACE_REC709;
+ ch->pitch = ch->xres;
}
- /* Make sure the memory size check won't fail. smem_len is initialized
- * later based on var.
- */
- info->fix.smem_len = UINT_MAX;
- ret = sh_mobile_check_var(var, info);
- if (ret)
- return ret;
-
- max_size = max_size * var->bits_per_pixel / 8 * 2;
+ ch->display.width = cfg->panel_cfg.width;
+ ch->display.height = cfg->panel_cfg.height;
+ ch->display.mode = *mode;
- /* Allocate frame buffer memory and color map. */
- buf = dma_alloc_coherent(dev, max_size, &ch->dma_handle, GFP_KERNEL);
- if (!buf) {
- dev_err(dev, "unable to allocate buffer\n");
+ /* Allocate frame buffer memory. */
+ ch->fb_size = max_size * format->bpp / 8 * 2;
+ ch->fb_mem = dma_alloc_coherent(priv->dev, ch->fb_size, &ch->dma_handle,
+ GFP_KERNEL);
+ if (ch->fb_mem == NULL) {
+ dev_err(priv->dev, "unable to allocate buffer\n");
return -ENOMEM;
}
- ret = fb_alloc_cmap(&info->cmap, PALETTE_NR, 0);
- if (ret < 0) {
- dev_err(dev, "unable to allocate cmap\n");
- dma_free_coherent(dev, max_size, buf, ch->dma_handle);
- return ret;
- }
-
- /* Initialize fixed screen information. Restrict pan to 2 lines steps
- * for NV12 and NV21.
- */
- info->fix = sh_mobile_lcdc_fix;
- info->fix.smem_start = ch->dma_handle;
- info->fix.smem_len = max_size;
- if (cfg->fourcc == V4L2_PIX_FMT_NV12 ||
- cfg->fourcc == V4L2_PIX_FMT_NV21)
- info->fix.ypanstep = 2;
-
- if (sh_mobile_format_is_yuv(var)) {
- info->fix.line_length = var->xres;
- info->fix.visual = FB_VISUAL_FOURCC;
- } else {
- info->fix.line_length = var->xres * var->bits_per_pixel / 8;
- info->fix.visual = FB_VISUAL_TRUECOLOR;
+ /* Initialize the transmitter device if present. */
+ if (cfg->tx_dev) {
+ if (!cfg->tx_dev->dev.driver ||
+ !try_module_get(cfg->tx_dev->dev.driver->owner)) {
+ dev_warn(priv->dev,
+ "unable to get transmitter device\n");
+ return -EINVAL;
+ }
+ ch->tx_dev = platform_get_drvdata(cfg->tx_dev);
+ ch->tx_dev->lcdc = ch;
+ ch->tx_dev->def_mode = *mode;
}
- info->screen_base = buf;
- info->device = dev;
- ch->display_var = *var;
-
- return 0;
+ return sh_mobile_lcdc_channel_fb_init(ch, mode, num_modes);
}
static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
@@ -1698,6 +1924,8 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ priv->dev = &pdev->dev;
+ priv->meram_dev = pdata->meram_dev;
platform_set_drvdata(pdev, priv);
error = request_irq(i, sh_mobile_lcdc_irq, 0,
@@ -1714,7 +1942,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
struct sh_mobile_lcdc_chan *ch = priv->ch + num_channels;
ch->lcdc = priv;
- memcpy(&ch->cfg, &pdata->ch[i], sizeof(pdata->ch[i]));
+ ch->cfg = &pdata->ch[i];
error = sh_mobile_lcdc_check_interface(ch);
if (error) {
@@ -1726,7 +1954,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
ch->pan_offset = 0;
/* probe the backlight is there is one defined */
- if (ch->cfg.bl_info.max_brightness)
+ if (ch->cfg->bl_info.max_brightness)
ch->bl = sh_mobile_lcdc_bl_probe(&pdev->dev, ch);
switch (pdata->ch[i].chan) {
@@ -1757,18 +1985,19 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
if (!priv->base)
goto err1;
- error = sh_mobile_lcdc_setup_clocks(pdev, pdata->clock_source, priv);
+ error = sh_mobile_lcdc_setup_clocks(priv, pdata->clock_source);
if (error) {
dev_err(&pdev->dev, "unable to setup clocks\n");
goto err1;
}
- priv->meram_dev = pdata->meram_dev;
+ /* Enable runtime PM. */
+ pm_runtime_enable(&pdev->dev);
for (i = 0; i < num_channels; i++) {
struct sh_mobile_lcdc_chan *ch = priv->ch + i;
- error = sh_mobile_lcdc_channel_init(ch, &pdev->dev);
+ error = sh_mobile_lcdc_channel_init(priv, ch);
if (error)
goto err1;
}
@@ -1781,31 +2010,10 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev)
for (i = 0; i < num_channels; i++) {
struct sh_mobile_lcdc_chan *ch = priv->ch + i;
- struct fb_info *info = ch->info;
-
- if (info->fbdefio) {
- ch->sglist = vmalloc(sizeof(struct scatterlist) *
- info->fix.smem_len >> PAGE_SHIFT);
- if (!ch->sglist) {
- dev_err(&pdev->dev, "cannot allocate sglist\n");
- goto err1;
- }
- }
-
- info->bl_dev = ch->bl;
- error = register_framebuffer(info);
- if (error < 0)
+ error = sh_mobile_lcdc_channel_fb_register(ch);
+ if (error)
goto err1;
-
- dev_info(info->dev, "registered %s/%s as %dx%d %dbpp.\n",
- pdev->name, (ch->cfg.chan == LCDC_CHAN_MAINLCD) ?
- "mainlcd" : "sublcd", info->var.xres, info->var.yres,
- info->var.bits_per_pixel);
-
- /* deferred io mode: disable clock to save power */
- if (info->fbdefio || info->state == FBINFO_STATE_SUSPENDED)
- sh_mobile_lcdc_clk_off(priv);
}
/* Failure ignored */
diff --git a/drivers/video/sh_mobile_lcdcfb.h b/drivers/video/sh_mobile_lcdcfb.h
index a58a0f38848b..da1c26e78a57 100644
--- a/drivers/video/sh_mobile_lcdcfb.h
+++ b/drivers/video/sh_mobile_lcdcfb.h
@@ -14,9 +14,35 @@ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R,
#define PALETTE_NR 16
-struct sh_mobile_lcdc_priv;
-struct fb_info;
struct backlight_device;
+struct fb_info;
+struct module;
+struct sh_mobile_lcdc_chan;
+struct sh_mobile_lcdc_entity;
+struct sh_mobile_lcdc_format_info;
+struct sh_mobile_lcdc_priv;
+
+#define SH_MOBILE_LCDC_DISPLAY_DISCONNECTED 0
+#define SH_MOBILE_LCDC_DISPLAY_CONNECTED 1
+
+struct sh_mobile_lcdc_entity_ops {
+ /* Display */
+ int (*display_on)(struct sh_mobile_lcdc_entity *entity);
+ void (*display_off)(struct sh_mobile_lcdc_entity *entity);
+};
+
+enum sh_mobile_lcdc_entity_event {
+ SH_MOBILE_LCDC_EVENT_DISPLAY_CONNECT,
+ SH_MOBILE_LCDC_EVENT_DISPLAY_DISCONNECT,
+ SH_MOBILE_LCDC_EVENT_DISPLAY_MODE,
+};
+
+struct sh_mobile_lcdc_entity {
+ struct module *owner;
+ const struct sh_mobile_lcdc_entity_ops *ops;
+ struct sh_mobile_lcdc_chan *lcdc;
+ struct fb_videomode def_mode;
+};
/*
* struct sh_mobile_lcdc_chan - LCDC display channel
@@ -27,29 +53,57 @@ struct backlight_device;
*/
struct sh_mobile_lcdc_chan {
struct sh_mobile_lcdc_priv *lcdc;
+ struct sh_mobile_lcdc_entity *tx_dev;
+ const struct sh_mobile_lcdc_chan_cfg *cfg;
+
unsigned long *reg_offs;
unsigned long ldmt1r_value;
unsigned long enabled; /* ME and SE in LDCNT2R */
- struct sh_mobile_lcdc_chan_cfg cfg;
- u32 pseudo_palette[PALETTE_NR];
- struct fb_info *info;
- struct backlight_device *bl;
+ void *meram;
+
+ struct mutex open_lock; /* protects the use counter */
+ int use_count;
+
+ void *fb_mem;
+ unsigned long fb_size;
+
dma_addr_t dma_handle;
- struct fb_deferred_io defio;
- struct scatterlist *sglist;
- unsigned long frame_end;
unsigned long pan_offset;
+
+ unsigned long frame_end;
wait_queue_head_t frame_end_wait;
struct completion vsync_completion;
- struct fb_var_screeninfo display_var;
- int use_count;
- int blank_status;
- struct mutex open_lock; /* protects the use counter */
- int meram_enabled;
+
+ const struct sh_mobile_lcdc_format_info *format;
+ u32 colorspace;
+ unsigned int xres;
+ unsigned int xres_virtual;
+ unsigned int yres;
+ unsigned int yres_virtual;
+ unsigned int pitch;
unsigned long base_addr_y;
unsigned long base_addr_c;
- unsigned int pitch;
+
+ int (*notify)(struct sh_mobile_lcdc_chan *ch,
+ enum sh_mobile_lcdc_entity_event event,
+ const struct fb_videomode *mode,
+ const struct fb_monspecs *monspec);
+
+ /* Backlight */
+ struct backlight_device *bl;
+
+ /* FB */
+ struct fb_info *info;
+ u32 pseudo_palette[PALETTE_NR];
+ struct {
+ unsigned int width;
+ unsigned int height;
+ struct fb_videomode mode;
+ } display;
+ struct fb_deferred_io defio;
+ struct scatterlist *sglist;
+ int blank_status;
};
#endif
diff --git a/drivers/video/sh_mobile_meram.c b/drivers/video/sh_mobile_meram.c
index f45d83ecfd21..82ba830bf95d 100644
--- a/drivers/video/sh_mobile_meram.c
+++ b/drivers/video/sh_mobile_meram.c
@@ -9,16 +9,22 @@
* for more details.
*/
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/genalloc.h>
+#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/io.h>
#include <linux/slab.h>
-#include <linux/platform_device.h>
+
#include <video/sh_mobile_meram.h>
-/* meram registers */
+/* -----------------------------------------------------------------------------
+ * MERAM registers
+ */
+
#define MEVCR1 0x4
#define MEVCR1_RST (1 << 31)
#define MEVCR1_WD (1 << 30)
@@ -81,16 +87,14 @@
((yszm1) << MExxBSIZE_YSZM1_SHIFT) | \
((xszm1) << MExxBSIZE_XSZM1_SHIFT))
-#define SH_MOBILE_MERAM_ICB_NUM 32
-
-static unsigned long common_regs[] = {
+static const unsigned long common_regs[] = {
MEVCR1,
MEQSEL1,
MEQSEL2,
};
-#define CMN_REGS_SIZE ARRAY_SIZE(common_regs)
+#define MERAM_REGS_SIZE ARRAY_SIZE(common_regs)
-static unsigned long icb_regs[] = {
+static const unsigned long icb_regs[] = {
MExxCTL,
MExxBSIZE,
MExxMNCF,
@@ -100,216 +104,269 @@ static unsigned long icb_regs[] = {
};
#define ICB_REGS_SIZE ARRAY_SIZE(icb_regs)
+/*
+ * sh_mobile_meram_icb - MERAM ICB information
+ * @regs: Registers cache
+ * @index: ICB index
+ * @offset: MERAM block offset
+ * @size: MERAM block size in KiB
+ * @cache_unit: Bytes to cache per ICB
+ * @pixelformat: Video pixel format of the data stored in the ICB
+ * @current_reg: Which of Start Address Register A (0) or B (1) is in use
+ */
+struct sh_mobile_meram_icb {
+ unsigned long regs[ICB_REGS_SIZE];
+ unsigned int index;
+ unsigned long offset;
+ unsigned int size;
+
+ unsigned int cache_unit;
+ unsigned int pixelformat;
+ unsigned int current_reg;
+};
+
+#define MERAM_ICB_NUM 32
+
+struct sh_mobile_meram_fb_plane {
+ struct sh_mobile_meram_icb *marker;
+ struct sh_mobile_meram_icb *cache;
+};
+
+struct sh_mobile_meram_fb_cache {
+ unsigned int nplanes;
+ struct sh_mobile_meram_fb_plane planes[2];
+};
+
+/*
+ * sh_mobile_meram_priv - MERAM device
+ * @base: Registers base address
+ * @meram: MERAM physical address
+ * @regs: Registers cache
+ * @lock: Protects used_icb and icbs
+ * @used_icb: Bitmask of used ICBs
+ * @icbs: ICBs
+ * @pool: Allocation pool to manage the MERAM
+ */
struct sh_mobile_meram_priv {
- void __iomem *base;
- struct mutex lock;
- unsigned long used_icb;
- int used_meram_cache_regions;
- unsigned long used_meram_cache[SH_MOBILE_MERAM_ICB_NUM];
- unsigned long cmn_saved_regs[CMN_REGS_SIZE];
- unsigned long icb_saved_regs[ICB_REGS_SIZE * SH_MOBILE_MERAM_ICB_NUM];
+ void __iomem *base;
+ unsigned long meram;
+ unsigned long regs[MERAM_REGS_SIZE];
+
+ struct mutex lock;
+ unsigned long used_icb;
+ struct sh_mobile_meram_icb icbs[MERAM_ICB_NUM];
+
+ struct gen_pool *pool;
};
/* settings */
-#define MERAM_SEC_LINE 15
-#define MERAM_LINE_WIDTH 2048
+#define MERAM_GRANULARITY 1024
+#define MERAM_SEC_LINE 15
+#define MERAM_LINE_WIDTH 2048
-/*
- * MERAM/ICB access functions
+/* -----------------------------------------------------------------------------
+ * Registers access
*/
#define MERAM_ICB_OFFSET(base, idx, off) ((base) + (off) + (idx) * 0x20)
-static inline void meram_write_icb(void __iomem *base, int idx, int off,
- unsigned long val)
+static inline void meram_write_icb(void __iomem *base, unsigned int idx,
+ unsigned int off, unsigned long val)
{
iowrite32(val, MERAM_ICB_OFFSET(base, idx, off));
}
-static inline unsigned long meram_read_icb(void __iomem *base, int idx, int off)
+static inline unsigned long meram_read_icb(void __iomem *base, unsigned int idx,
+ unsigned int off)
{
return ioread32(MERAM_ICB_OFFSET(base, idx, off));
}
-static inline void meram_write_reg(void __iomem *base, int off,
- unsigned long val)
+static inline void meram_write_reg(void __iomem *base, unsigned int off,
+ unsigned long val)
{
iowrite32(val, base + off);
}
-static inline unsigned long meram_read_reg(void __iomem *base, int off)
+static inline unsigned long meram_read_reg(void __iomem *base, unsigned int off)
{
return ioread32(base + off);
}
-/*
- * register ICB
- */
-
-#define MERAM_CACHE_START(p) ((p) >> 16)
-#define MERAM_CACHE_END(p) ((p) & 0xffff)
-#define MERAM_CACHE_SET(o, s) ((((o) & 0xffff) << 16) | \
- (((o) + (s) - 1) & 0xffff))
-
-/*
- * check if there's no overlaps in MERAM allocation.
+/* -----------------------------------------------------------------------------
+ * Allocation
*/
-static inline int meram_check_overlap(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb *new)
+/* Allocate ICBs and MERAM for a plane. */
+static int __meram_alloc(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane,
+ size_t size)
{
- int i;
- int used_start, used_end, meram_start, meram_end;
+ unsigned long mem;
+ unsigned long idx;
- /* valid ICB? */
- if (new->marker_icb & ~0x1f || new->cache_icb & ~0x1f)
- return 1;
+ idx = find_first_zero_bit(&priv->used_icb, 28);
+ if (idx == 28)
+ return -ENOMEM;
+ plane->cache = &priv->icbs[idx];
- if (test_bit(new->marker_icb, &priv->used_icb) ||
- test_bit(new->cache_icb, &priv->used_icb))
- return 1;
+ idx = find_next_zero_bit(&priv->used_icb, 32, 28);
+ if (idx == 32)
+ return -ENOMEM;
+ plane->marker = &priv->icbs[idx];
- for (i = 0; i < priv->used_meram_cache_regions; i++) {
- used_start = MERAM_CACHE_START(priv->used_meram_cache[i]);
- used_end = MERAM_CACHE_END(priv->used_meram_cache[i]);
- meram_start = new->meram_offset;
- meram_end = new->meram_offset + new->meram_size;
+ mem = gen_pool_alloc(priv->pool, size * 1024);
+ if (mem == 0)
+ return -ENOMEM;
- if ((meram_start >= used_start && meram_start < used_end) ||
- (meram_end > used_start && meram_end < used_end))
- return 1;
- }
+ __set_bit(plane->marker->index, &priv->used_icb);
+ __set_bit(plane->cache->index, &priv->used_icb);
+
+ plane->marker->offset = mem - priv->meram;
+ plane->marker->size = size;
return 0;
}
-/*
- * mark the specified ICB as used
- */
+/* Free ICBs and MERAM for a plane. */
+static void __meram_free(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_plane *plane)
+{
+ gen_pool_free(priv->pool, priv->meram + plane->marker->offset,
+ plane->marker->size * 1024);
-static inline void meram_mark(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb *new)
+ __clear_bit(plane->marker->index, &priv->used_icb);
+ __clear_bit(plane->cache->index, &priv->used_icb);
+}
+
+/* Is this a YCbCr(NV12, NV16 or NV24) colorspace? */
+static int is_nvcolor(int cspace)
{
- int n;
+ if (cspace == SH_MOBILE_MERAM_PF_NV ||
+ cspace == SH_MOBILE_MERAM_PF_NV24)
+ return 1;
+ return 0;
+}
- if (new->marker_icb < 0 || new->cache_icb < 0)
- return;
+/* Allocate memory for the ICBs and mark them as used. */
+static struct sh_mobile_meram_fb_cache *
+meram_alloc(struct sh_mobile_meram_priv *priv,
+ const struct sh_mobile_meram_cfg *cfg,
+ int pixelformat)
+{
+ struct sh_mobile_meram_fb_cache *cache;
+ unsigned int nplanes = is_nvcolor(pixelformat) ? 2 : 1;
+ int ret;
- __set_bit(new->marker_icb, &priv->used_icb);
- __set_bit(new->cache_icb, &priv->used_icb);
+ if (cfg->icb[0].meram_size == 0)
+ return ERR_PTR(-EINVAL);
- n = priv->used_meram_cache_regions;
+ if (nplanes == 2 && cfg->icb[1].meram_size == 0)
+ return ERR_PTR(-EINVAL);
- priv->used_meram_cache[n] = MERAM_CACHE_SET(new->meram_offset,
- new->meram_size);
+ cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+ if (cache == NULL)
+ return ERR_PTR(-ENOMEM);
- priv->used_meram_cache_regions++;
-}
+ cache->nplanes = nplanes;
-/*
- * unmark the specified ICB as used
- */
+ ret = __meram_alloc(priv, &cache->planes[0], cfg->icb[0].meram_size);
+ if (ret < 0)
+ goto error;
-static inline void meram_unmark(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb *icb)
-{
- int i;
- unsigned long pattern;
-
- if (icb->marker_icb < 0 || icb->cache_icb < 0)
- return;
-
- __clear_bit(icb->marker_icb, &priv->used_icb);
- __clear_bit(icb->cache_icb, &priv->used_icb);
-
- pattern = MERAM_CACHE_SET(icb->meram_offset, icb->meram_size);
- for (i = 0; i < priv->used_meram_cache_regions; i++) {
- if (priv->used_meram_cache[i] == pattern) {
- while (i < priv->used_meram_cache_regions - 1) {
- priv->used_meram_cache[i] =
- priv->used_meram_cache[i + 1] ;
- i++;
- }
- priv->used_meram_cache[i] = 0;
- priv->used_meram_cache_regions--;
- break;
- }
+ cache->planes[0].marker->current_reg = 1;
+ cache->planes[0].marker->pixelformat = pixelformat;
+
+ if (cache->nplanes == 1)
+ return cache;
+
+ ret = __meram_alloc(priv, &cache->planes[1], cfg->icb[1].meram_size);
+ if (ret < 0) {
+ __meram_free(priv, &cache->planes[0]);
+ goto error;
}
+
+ return cache;
+
+error:
+ kfree(cache);
+ return ERR_PTR(-ENOMEM);
}
-/*
- * is this a YCbCr(NV12, NV16 or NV24) colorspace
- */
-static inline int is_nvcolor(int cspace)
+/* Unmark the specified ICB as used. */
+static void meram_free(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_cache *cache)
{
- if (cspace == SH_MOBILE_MERAM_PF_NV ||
- cspace == SH_MOBILE_MERAM_PF_NV24)
- return 1;
- return 0;
+ __meram_free(priv, &cache->planes[0]);
+ if (cache->nplanes == 2)
+ __meram_free(priv, &cache->planes[1]);
+
+ kfree(cache);
}
-/*
- * set the next address to fetch
- */
-static inline void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_cfg *cfg,
- unsigned long base_addr_y,
- unsigned long base_addr_c)
+/* Set the next address to fetch. */
+static void meram_set_next_addr(struct sh_mobile_meram_priv *priv,
+ struct sh_mobile_meram_fb_cache *cache,
+ unsigned long base_addr_y,
+ unsigned long base_addr_c)
{
+ struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
unsigned long target;
- target = (cfg->current_reg) ? MExxSARA : MExxSARB;
- cfg->current_reg ^= 1;
+ icb->current_reg ^= 1;
+ target = icb->current_reg ? MExxSARB : MExxSARA;
/* set the next address to fetch */
- meram_write_icb(priv->base, cfg->icb[0].cache_icb, target,
+ meram_write_icb(priv->base, cache->planes[0].cache->index, target,
base_addr_y);
- meram_write_icb(priv->base, cfg->icb[0].marker_icb, target,
- base_addr_y + cfg->icb[0].cache_unit);
-
- if (is_nvcolor(cfg->pixelformat)) {
- meram_write_icb(priv->base, cfg->icb[1].cache_icb, target,
- base_addr_c);
- meram_write_icb(priv->base, cfg->icb[1].marker_icb, target,
- base_addr_c + cfg->icb[1].cache_unit);
+ meram_write_icb(priv->base, cache->planes[0].marker->index, target,
+ base_addr_y + cache->planes[0].marker->cache_unit);
+
+ if (cache->nplanes == 2) {
+ meram_write_icb(priv->base, cache->planes[1].cache->index,
+ target, base_addr_c);
+ meram_write_icb(priv->base, cache->planes[1].marker->index,
+ target, base_addr_c +
+ cache->planes[1].marker->cache_unit);
}
}
-/*
- * get the next ICB address
- */
-static inline void meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c)
+/* Get the next ICB address. */
+static void
+meram_get_next_icb_addr(struct sh_mobile_meram_info *pdata,
+ struct sh_mobile_meram_fb_cache *cache,
+ unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{
+ struct sh_mobile_meram_icb *icb = cache->planes[0].marker;
unsigned long icb_offset;
if (pdata->addr_mode == SH_MOBILE_MERAM_MODE0)
- icb_offset = 0x80000000 | (cfg->current_reg << 29);
+ icb_offset = 0x80000000 | (icb->current_reg << 29);
else
- icb_offset = 0xc0000000 | (cfg->current_reg << 23);
+ icb_offset = 0xc0000000 | (icb->current_reg << 23);
- *icb_addr_y = icb_offset | (cfg->icb[0].marker_icb << 24);
- if (is_nvcolor(cfg->pixelformat))
- *icb_addr_c = icb_offset | (cfg->icb[1].marker_icb << 24);
+ *icb_addr_y = icb_offset | (cache->planes[0].marker->index << 24);
+ if (cache->nplanes == 2)
+ *icb_addr_c = icb_offset
+ | (cache->planes[1].marker->index << 24);
}
#define MERAM_CALC_BYTECOUNT(x, y) \
(((x) * (y) + (MERAM_LINE_WIDTH - 1)) & ~(MERAM_LINE_WIDTH - 1))
-/*
- * initialize MERAM
- */
-
+/* Initialize MERAM. */
static int meram_init(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb *icb,
- int xres, int yres, int *out_pitch)
+ struct sh_mobile_meram_fb_plane *plane,
+ unsigned int xres, unsigned int yres,
+ unsigned int *out_pitch)
{
+ struct sh_mobile_meram_icb *marker = plane->marker;
unsigned long total_byte_count = MERAM_CALC_BYTECOUNT(xres, yres);
unsigned long bnm;
- int lcdc_pitch, xpitch, line_cnt;
- int save_lines;
+ unsigned int lcdc_pitch;
+ unsigned int xpitch;
+ unsigned int line_cnt;
+ unsigned int save_lines;
/* adjust pitch to 1024, 2048, 4096 or 8192 */
lcdc_pitch = (xres - 1) | 1023;
@@ -322,13 +379,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
lcdc_pitch = xpitch = MERAM_LINE_WIDTH;
line_cnt = total_byte_count >> 11;
*out_pitch = xres;
- save_lines = (icb->meram_size / 16 / MERAM_SEC_LINE);
+ save_lines = plane->marker->size / 16 / MERAM_SEC_LINE;
save_lines *= MERAM_SEC_LINE;
} else {
xpitch = xres;
line_cnt = yres;
*out_pitch = lcdc_pitch;
- save_lines = icb->meram_size / (lcdc_pitch >> 10) / 2;
+ save_lines = plane->marker->size / (lcdc_pitch >> 10) / 2;
save_lines &= 0xff;
}
bnm = (save_lines - 1) << 16;
@@ -336,19 +393,20 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
/* TODO: we better to check if we have enough MERAM buffer size */
/* set up ICB */
- meram_write_icb(priv->base, icb->cache_icb, MExxBSIZE,
+ meram_write_icb(priv->base, plane->cache->index, MExxBSIZE,
MERAM_MExxBSIZE_VAL(0x0, line_cnt - 1, xpitch - 1));
- meram_write_icb(priv->base, icb->marker_icb, MExxBSIZE,
+ meram_write_icb(priv->base, plane->marker->index, MExxBSIZE,
MERAM_MExxBSIZE_VAL(0xf, line_cnt - 1, xpitch - 1));
- meram_write_icb(priv->base, icb->cache_icb, MExxMNCF, bnm);
- meram_write_icb(priv->base, icb->marker_icb, MExxMNCF, bnm);
+ meram_write_icb(priv->base, plane->cache->index, MExxMNCF, bnm);
+ meram_write_icb(priv->base, plane->marker->index, MExxMNCF, bnm);
- meram_write_icb(priv->base, icb->cache_icb, MExxSBSIZE, xpitch);
- meram_write_icb(priv->base, icb->marker_icb, MExxSBSIZE, xpitch);
+ meram_write_icb(priv->base, plane->cache->index, MExxSBSIZE, xpitch);
+ meram_write_icb(priv->base, plane->marker->index, MExxSBSIZE, xpitch);
/* save a cache unit size */
- icb->cache_unit = xres * save_lines;
+ plane->cache->cache_unit = xres * save_lines;
+ plane->marker->cache_unit = xres * save_lines;
/*
* Set MERAM for framebuffer
@@ -356,13 +414,13 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
* we also chain the cache_icb and the marker_icb.
* we also split the allocated MERAM buffer between two ICBs.
*/
- meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
- MERAM_MExxCTL_VAL(icb->marker_icb, icb->meram_offset) |
- MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
+ meram_write_icb(priv->base, plane->cache->index, MExxCTL,
+ MERAM_MExxCTL_VAL(plane->marker->index, marker->offset)
+ | MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
- meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
- MERAM_MExxCTL_VAL(icb->cache_icb, icb->meram_offset +
- icb->meram_size / 2) |
+ meram_write_icb(priv->base, plane->marker->index, MExxCTL,
+ MERAM_MExxCTL_VAL(plane->cache->index, marker->offset +
+ plane->marker->size / 2) |
MExxCTL_WD1 | MExxCTL_WD0 | MExxCTL_WS | MExxCTL_CM |
MExxCTL_MD_FB);
@@ -370,239 +428,175 @@ static int meram_init(struct sh_mobile_meram_priv *priv,
}
static void meram_deinit(struct sh_mobile_meram_priv *priv,
- struct sh_mobile_meram_icb *icb)
+ struct sh_mobile_meram_fb_plane *plane)
{
/* disable ICB */
- meram_write_icb(priv->base, icb->cache_icb, MExxCTL,
+ meram_write_icb(priv->base, plane->cache->index, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
- meram_write_icb(priv->base, icb->marker_icb, MExxCTL,
+ meram_write_icb(priv->base, plane->marker->index, MExxCTL,
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF);
- icb->cache_unit = 0;
+
+ plane->cache->cache_unit = 0;
+ plane->marker->cache_unit = 0;
}
-/*
- * register the ICB
+/* -----------------------------------------------------------------------------
+ * Registration/unregistration
*/
-static int sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg,
- int xres, int yres, int pixelformat,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c,
- int *pitch)
+static void *sh_mobile_meram_register(struct sh_mobile_meram_info *pdata,
+ const struct sh_mobile_meram_cfg *cfg,
+ unsigned int xres, unsigned int yres,
+ unsigned int pixelformat,
+ unsigned int *pitch)
{
- struct platform_device *pdev;
- struct sh_mobile_meram_priv *priv;
- int n, out_pitch;
- int error = 0;
-
- if (!pdata || !pdata->priv || !pdata->pdev || !cfg)
- return -EINVAL;
+ struct sh_mobile_meram_fb_cache *cache;
+ struct sh_mobile_meram_priv *priv = pdata->priv;
+ struct platform_device *pdev = pdata->pdev;
+ unsigned int out_pitch;
if (pixelformat != SH_MOBILE_MERAM_PF_NV &&
pixelformat != SH_MOBILE_MERAM_PF_NV24 &&
pixelformat != SH_MOBILE_MERAM_PF_RGB)
- return -EINVAL;
-
- priv = pdata->priv;
- pdev = pdata->pdev;
+ return ERR_PTR(-EINVAL);
- dev_dbg(&pdev->dev, "registering %dx%d (%s) (y=%08lx, c=%08lx)",
- xres, yres, (!pixelformat) ? "yuv" : "rgb",
- base_addr_y, base_addr_c);
+ dev_dbg(&pdev->dev, "registering %dx%d (%s)", xres, yres,
+ !pixelformat ? "yuv" : "rgb");
/* we can't handle wider than 8192px */
if (xres > 8192) {
dev_err(&pdev->dev, "width exceeding the limit (> 8192).");
- return -EINVAL;
- }
-
- /* do we have at least one ICB config? */
- if (cfg->icb[0].marker_icb < 0 || cfg->icb[0].cache_icb < 0) {
- dev_err(&pdev->dev, "at least one ICB is required.");
- return -EINVAL;
+ return ERR_PTR(-EINVAL);
}
mutex_lock(&priv->lock);
- if (priv->used_meram_cache_regions + 2 > SH_MOBILE_MERAM_ICB_NUM) {
- dev_err(&pdev->dev, "no more ICB available.");
- error = -EINVAL;
- goto err;
- }
-
- /* make sure that there's no overlaps */
- if (meram_check_overlap(priv, &cfg->icb[0])) {
- dev_err(&pdev->dev, "conflicting config detected.");
- error = -EINVAL;
+ /* We now register the ICBs and allocate the MERAM regions. */
+ cache = meram_alloc(priv, cfg, pixelformat);
+ if (IS_ERR(cache)) {
+ dev_err(&pdev->dev, "MERAM allocation failed (%ld).",
+ PTR_ERR(cache));
goto err;
}
- n = 1;
-
- /* do the same if we have the second ICB set */
- if (cfg->icb[1].marker_icb >= 0 && cfg->icb[1].cache_icb >= 0) {
- if (meram_check_overlap(priv, &cfg->icb[1])) {
- dev_err(&pdev->dev, "conflicting config detected.");
- error = -EINVAL;
- goto err;
- }
- n = 2;
- }
-
- if (is_nvcolor(pixelformat) && n != 2) {
- dev_err(&pdev->dev, "requires two ICB sets for planar Y/C.");
- error = -EINVAL;
- goto err;
- }
-
- /* we now register the ICB */
- cfg->pixelformat = pixelformat;
- meram_mark(priv, &cfg->icb[0]);
- if (is_nvcolor(pixelformat))
- meram_mark(priv, &cfg->icb[1]);
/* initialize MERAM */
- meram_init(priv, &cfg->icb[0], xres, yres, &out_pitch);
+ meram_init(priv, &cache->planes[0], xres, yres, &out_pitch);
*pitch = out_pitch;
if (pixelformat == SH_MOBILE_MERAM_PF_NV)
- meram_init(priv, &cfg->icb[1], xres, (yres + 1) / 2,
+ meram_init(priv, &cache->planes[1], xres, (yres + 1) / 2,
&out_pitch);
else if (pixelformat == SH_MOBILE_MERAM_PF_NV24)
- meram_init(priv, &cfg->icb[1], 2 * xres, (yres + 1) / 2,
+ meram_init(priv, &cache->planes[1], 2 * xres, (yres + 1) / 2,
&out_pitch);
- cfg->current_reg = 1;
- meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
- meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
-
- dev_dbg(&pdev->dev, "registered - can access via y=%08lx, c=%08lx",
- *icb_addr_y, *icb_addr_c);
-
err:
mutex_unlock(&priv->lock);
- return error;
+ return cache;
}
-static int sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg)
+static void
+sh_mobile_meram_unregister(struct sh_mobile_meram_info *pdata, void *data)
{
- struct sh_mobile_meram_priv *priv;
-
- if (!pdata || !pdata->priv || !cfg)
- return -EINVAL;
-
- priv = pdata->priv;
+ struct sh_mobile_meram_fb_cache *cache = data;
+ struct sh_mobile_meram_priv *priv = pdata->priv;
mutex_lock(&priv->lock);
- /* deinit & unmark */
- if (is_nvcolor(cfg->pixelformat)) {
- meram_deinit(priv, &cfg->icb[1]);
- meram_unmark(priv, &cfg->icb[1]);
- }
- meram_deinit(priv, &cfg->icb[0]);
- meram_unmark(priv, &cfg->icb[0]);
+ /* deinit & free */
+ meram_deinit(priv, &cache->planes[0]);
+ if (cache->nplanes == 2)
+ meram_deinit(priv, &cache->planes[1]);
- mutex_unlock(&priv->lock);
+ meram_free(priv, cache);
- return 0;
+ mutex_unlock(&priv->lock);
}
-static int sh_mobile_meram_update(struct sh_mobile_meram_info *pdata,
- struct sh_mobile_meram_cfg *cfg,
- unsigned long base_addr_y,
- unsigned long base_addr_c,
- unsigned long *icb_addr_y,
- unsigned long *icb_addr_c)
+static void
+sh_mobile_meram_update(struct sh_mobile_meram_info *pdata, void *data,
+ unsigned long base_addr_y, unsigned long base_addr_c,
+ unsigned long *icb_addr_y, unsigned long *icb_addr_c)
{
- struct sh_mobile_meram_priv *priv;
-
- if (!pdata || !pdata->priv || !cfg)
- return -EINVAL;
-
- priv = pdata->priv;
+ struct sh_mobile_meram_fb_cache *cache = data;
+ struct sh_mobile_meram_priv *priv = pdata->priv;
mutex_lock(&priv->lock);
- meram_set_next_addr(priv, cfg, base_addr_y, base_addr_c);
- meram_get_next_icb_addr(pdata, cfg, icb_addr_y, icb_addr_c);
+ meram_set_next_addr(priv, cache, base_addr_y, base_addr_c);
+ meram_get_next_icb_addr(pdata, cache, icb_addr_y, icb_addr_c);
mutex_unlock(&priv->lock);
-
- return 0;
}
-static int sh_mobile_meram_runtime_suspend(struct device *dev)
+static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
+ .module = THIS_MODULE,
+ .meram_register = sh_mobile_meram_register,
+ .meram_unregister = sh_mobile_meram_unregister,
+ .meram_update = sh_mobile_meram_update,
+};
+
+/* -----------------------------------------------------------------------------
+ * Power management
+ */
+
+static int sh_mobile_meram_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
- int k, j;
+ unsigned int i, j;
- for (k = 0; k < CMN_REGS_SIZE; k++)
- priv->cmn_saved_regs[k] = meram_read_reg(priv->base,
- common_regs[k]);
+ for (i = 0; i < MERAM_REGS_SIZE; i++)
+ priv->regs[i] = meram_read_reg(priv->base, common_regs[i]);
- for (j = 0; j < 32; j++) {
- if (!test_bit(j, &priv->used_icb))
+ for (i = 0; i < 32; i++) {
+ if (!test_bit(i, &priv->used_icb))
continue;
- for (k = 0; k < ICB_REGS_SIZE; k++) {
- priv->icb_saved_regs[j * ICB_REGS_SIZE + k] =
- meram_read_icb(priv->base, j, icb_regs[k]);
+ for (j = 0; j < ICB_REGS_SIZE; j++) {
+ priv->icbs[i].regs[j] =
+ meram_read_icb(priv->base, i, icb_regs[j]);
/* Reset ICB on resume */
- if (icb_regs[k] == MExxCTL)
- priv->icb_saved_regs[j * ICB_REGS_SIZE + k] |=
+ if (icb_regs[j] == MExxCTL)
+ priv->icbs[i].regs[j] |=
MExxCTL_WBF | MExxCTL_WF | MExxCTL_RF;
}
}
return 0;
}
-static int sh_mobile_meram_runtime_resume(struct device *dev)
+static int sh_mobile_meram_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
- int k, j;
+ unsigned int i, j;
- for (j = 0; j < 32; j++) {
- if (!test_bit(j, &priv->used_icb))
+ for (i = 0; i < 32; i++) {
+ if (!test_bit(i, &priv->used_icb))
continue;
- for (k = 0; k < ICB_REGS_SIZE; k++) {
- meram_write_icb(priv->base, j, icb_regs[k],
- priv->icb_saved_regs[j * ICB_REGS_SIZE + k]);
- }
+ for (j = 0; j < ICB_REGS_SIZE; j++)
+ meram_write_icb(priv->base, i, icb_regs[j],
+ priv->icbs[i].regs[j]);
}
- for (k = 0; k < CMN_REGS_SIZE; k++)
- meram_write_reg(priv->base, common_regs[k],
- priv->cmn_saved_regs[k]);
+ for (i = 0; i < MERAM_REGS_SIZE; i++)
+ meram_write_reg(priv->base, common_regs[i], priv->regs[i]);
return 0;
}
-static const struct dev_pm_ops sh_mobile_meram_dev_pm_ops = {
- .runtime_suspend = sh_mobile_meram_runtime_suspend,
- .runtime_resume = sh_mobile_meram_runtime_resume,
-};
-
-static struct sh_mobile_meram_ops sh_mobile_meram_ops = {
- .module = THIS_MODULE,
- .meram_register = sh_mobile_meram_register,
- .meram_unregister = sh_mobile_meram_unregister,
- .meram_update = sh_mobile_meram_update,
-};
+static UNIVERSAL_DEV_PM_OPS(sh_mobile_meram_dev_pm_ops,
+ sh_mobile_meram_suspend,
+ sh_mobile_meram_resume, NULL);
-/*
- * initialize MERAM
+/* -----------------------------------------------------------------------------
+ * Probe/remove and driver init/exit
*/
-static int sh_mobile_meram_remove(struct platform_device *pdev);
-
static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
{
struct sh_mobile_meram_priv *priv;
struct sh_mobile_meram_info *pdata = pdev->dev.platform_data;
- struct resource *res;
+ struct resource *regs;
+ struct resource *meram;
+ unsigned int i;
int error;
if (!pdata) {
@@ -610,8 +604,9 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (regs == NULL || meram == NULL) {
dev_err(&pdev->dev, "cannot get platform resources\n");
return -ENOENT;
}
@@ -622,32 +617,74 @@ static int __devinit sh_mobile_meram_probe(struct platform_device *pdev)
return -ENOMEM;
}
- platform_set_drvdata(pdev, priv);
-
- /* initialize private data */
+ /* Initialize private data. */
mutex_init(&priv->lock);
- priv->base = ioremap_nocache(res->start, resource_size(res));
+ priv->used_icb = pdata->reserved_icbs;
+
+ for (i = 0; i < MERAM_ICB_NUM; ++i)
+ priv->icbs[i].index = i;
+
+ pdata->ops = &sh_mobile_meram_ops;
+ pdata->priv = priv;
+ pdata->pdev = pdev;
+
+ /* Request memory regions and remap the registers. */
+ if (!request_mem_region(regs->start, resource_size(regs), pdev->name)) {
+ dev_err(&pdev->dev, "MERAM registers region already claimed\n");
+ error = -EBUSY;
+ goto err_req_regs;
+ }
+
+ if (!request_mem_region(meram->start, resource_size(meram),
+ pdev->name)) {
+ dev_err(&pdev->dev, "MERAM memory region already claimed\n");
+ error = -EBUSY;
+ goto err_req_meram;
+ }
+
+ priv->base = ioremap_nocache(regs->start, resource_size(regs));
if (!priv->base) {
dev_err(&pdev->dev, "ioremap failed\n");
error = -EFAULT;
- goto err;
+ goto err_ioremap;
}
- pdata->ops = &sh_mobile_meram_ops;
- pdata->priv = priv;
- pdata->pdev = pdev;
+
+ priv->meram = meram->start;
+
+ /* Create and initialize the MERAM memory pool. */
+ priv->pool = gen_pool_create(ilog2(MERAM_GRANULARITY), -1);
+ if (priv->pool == NULL) {
+ error = -ENOMEM;
+ goto err_genpool;
+ }
+
+ error = gen_pool_add(priv->pool, meram->start, resource_size(meram),
+ -1);
+ if (error < 0)
+ goto err_genpool;
/* initialize ICB addressing mode */
if (pdata->addr_mode == SH_MOBILE_MERAM_MODE1)
meram_write_reg(priv->base, MEVCR1, MEVCR1_AMD1);
+ platform_set_drvdata(pdev, priv);
pm_runtime_enable(&pdev->dev);
dev_info(&pdev->dev, "sh_mobile_meram initialized.");
return 0;
-err:
- sh_mobile_meram_remove(pdev);
+err_genpool:
+ if (priv->pool)
+ gen_pool_destroy(priv->pool);
+ iounmap(priv->base);
+err_ioremap:
+ release_mem_region(meram->start, resource_size(meram));
+err_req_meram:
+ release_mem_region(regs->start, resource_size(regs));
+err_req_regs:
+ mutex_destroy(&priv->lock);
+ kfree(priv);
return error;
}
@@ -656,11 +693,16 @@ err:
static int sh_mobile_meram_remove(struct platform_device *pdev)
{
struct sh_mobile_meram_priv *priv = platform_get_drvdata(pdev);
+ struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ struct resource *meram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
pm_runtime_disable(&pdev->dev);
- if (priv->base)
- iounmap(priv->base);
+ gen_pool_destroy(priv->pool);
+
+ iounmap(priv->base);
+ release_mem_region(meram->start, resource_size(meram));
+ release_mem_region(regs->start, resource_size(regs));
mutex_destroy(&priv->lock);
diff --git a/drivers/video/udlfb.c b/drivers/video/udlfb.c
index a19773149bd7..a159b63e18b9 100644
--- a/drivers/video/udlfb.c
+++ b/drivers/video/udlfb.c
@@ -72,6 +72,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
static bool console = 1; /* Allow fbcon to open framebuffer */
static bool fb_defio = 1; /* Detect mmap writes using page faults */
static bool shadow = 1; /* Optionally disable shadow framebuffer */
+static int pixel_limit; /* Optionally force a pixel resolution limit */
/* dlfb keeps a list of urbs for efficient bulk transfers */
static void dlfb_urb_completion(struct urb *urb);
@@ -918,10 +919,6 @@ static void dlfb_free(struct kref *kref)
{
struct dlfb_data *dev = container_of(kref, struct dlfb_data, kref);
- /* this function will wait for all in-flight urbs to complete */
- if (dev->urbs.count > 0)
- dlfb_free_urb_list(dev);
-
if (dev->backing_buffer)
vfree(dev->backing_buffer);
@@ -940,35 +937,42 @@ static void dlfb_release_urb_work(struct work_struct *work)
up(&unode->dev->urbs.limit_sem);
}
-static void dlfb_free_framebuffer_work(struct work_struct *work)
+static void dlfb_free_framebuffer(struct dlfb_data *dev)
{
- struct dlfb_data *dev = container_of(work, struct dlfb_data,
- free_framebuffer_work.work);
struct fb_info *info = dev->info;
- int node = info->node;
- unregister_framebuffer(info);
+ if (info) {
+ int node = info->node;
- if (info->cmap.len != 0)
- fb_dealloc_cmap(&info->cmap);
- if (info->monspecs.modedb)
- fb_destroy_modedb(info->monspecs.modedb);
- if (info->screen_base)
- vfree(info->screen_base);
+ unregister_framebuffer(info);
- fb_destroy_modelist(&info->modelist);
+ if (info->cmap.len != 0)
+ fb_dealloc_cmap(&info->cmap);
+ if (info->monspecs.modedb)
+ fb_destroy_modedb(info->monspecs.modedb);
+ if (info->screen_base)
+ vfree(info->screen_base);
- dev->info = 0;
+ fb_destroy_modelist(&info->modelist);
- /* Assume info structure is freed after this point */
- framebuffer_release(info);
+ dev->info = NULL;
- pr_warn("fb_info for /dev/fb%d has been freed\n", node);
+ /* Assume info structure is freed after this point */
+ framebuffer_release(info);
+
+ pr_warn("fb_info for /dev/fb%d has been freed\n", node);
+ }
/* ref taken in probe() as part of registering framebfufer */
kref_put(&dev->kref, dlfb_free);
}
+static void dlfb_free_framebuffer_work(struct work_struct *work)
+{
+ struct dlfb_data *dev = container_of(work, struct dlfb_data,
+ free_framebuffer_work.work);
+ dlfb_free_framebuffer(dev);
+}
/*
* Assumes caller is holding info->lock mutex (for open and release at least)
*/
@@ -1012,7 +1016,8 @@ static int dlfb_is_valid_mode(struct fb_videomode *mode,
return 0;
}
- pr_info("%dx%d valid mode\n", mode->xres, mode->yres);
+ pr_info("%dx%d @ %d Hz valid mode\n", mode->xres, mode->yres,
+ mode->refresh);
return 1;
}
@@ -1427,19 +1432,22 @@ static ssize_t edid_store(
struct device *fbdev = container_of(kobj, struct device, kobj);
struct fb_info *fb_info = dev_get_drvdata(fbdev);
struct dlfb_data *dev = fb_info->par;
+ int ret;
/* We only support write of entire EDID at once, no offset*/
if ((src_size != EDID_LENGTH) || (src_off != 0))
- return 0;
+ return -EINVAL;
- dlfb_setup_modes(dev, fb_info, src, src_size);
+ ret = dlfb_setup_modes(dev, fb_info, src, src_size);
+ if (ret)
+ return ret;
- if (dev->edid && (memcmp(src, dev->edid, src_size) == 0)) {
- pr_info("sysfs written EDID is new default\n");
- dlfb_ops_set_par(fb_info);
- return src_size;
- } else
- return 0;
+ if (!dev->edid || memcmp(src, dev->edid, src_size))
+ return -EINVAL;
+
+ pr_info("sysfs written EDID is new default\n");
+ dlfb_ops_set_par(fb_info);
+ return src_size;
}
static ssize_t metrics_reset_store(struct device *fbdev,
@@ -1537,7 +1545,7 @@ static int dlfb_parse_vendor_descriptor(struct dlfb_data *dev,
u8 length;
u16 key;
- key = *((u16 *) desc);
+ key = le16_to_cpu(*((u16 *) desc));
desc += sizeof(u16);
length = *desc;
desc++;
@@ -1570,14 +1578,15 @@ success:
kfree(buf);
return true;
}
+
+static void dlfb_init_framebuffer_work(struct work_struct *work);
+
static int dlfb_usb_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
struct usb_device *usbdev;
struct dlfb_data *dev = 0;
- struct fb_info *info = 0;
int retval = -ENOMEM;
- int i;
/* usb initialization */
@@ -1589,9 +1598,7 @@ static int dlfb_usb_probe(struct usb_interface *interface,
goto error;
}
- /* we need to wait for both usb and fbdev to spin down on disconnect */
kref_init(&dev->kref); /* matching kref_put in usb .disconnect fn */
- kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
dev->udev = usbdev;
dev->gdev = &usbdev->dev; /* our generic struct device * */
@@ -1613,16 +1620,53 @@ static int dlfb_usb_probe(struct usb_interface *interface,
goto error;
}
+ if (pixel_limit) {
+ pr_warn("DL chip limit of %d overriden"
+ " by module param to %d\n",
+ dev->sku_pixel_limit, pixel_limit);
+ dev->sku_pixel_limit = pixel_limit;
+ }
+
+
if (!dlfb_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
retval = -ENOMEM;
pr_err("dlfb_alloc_urb_list failed\n");
goto error;
}
+ kref_get(&dev->kref); /* matching kref_put in free_framebuffer_work */
+
/* We don't register a new USB class. Our client interface is fbdev */
+ /* Workitem keep things fast & simple during USB enumeration */
+ INIT_DELAYED_WORK(&dev->init_framebuffer_work,
+ dlfb_init_framebuffer_work);
+ schedule_delayed_work(&dev->init_framebuffer_work, 0);
+
+ return 0;
+
+error:
+ if (dev) {
+
+ kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */
+ kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */
+
+ /* dev has been deallocated. Do not dereference */
+ }
+
+ return retval;
+}
+
+static void dlfb_init_framebuffer_work(struct work_struct *work)
+{
+ struct dlfb_data *dev = container_of(work, struct dlfb_data,
+ init_framebuffer_work.work);
+ struct fb_info *info;
+ int retval;
+ int i;
+
/* allocates framebuffer driver structure, not framebuffer memory */
- info = framebuffer_alloc(0, &interface->dev);
+ info = framebuffer_alloc(0, dev->gdev);
if (!info) {
retval = -ENOMEM;
pr_err("framebuffer_alloc failed\n");
@@ -1668,15 +1712,13 @@ static int dlfb_usb_probe(struct usb_interface *interface,
for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++) {
retval = device_create_file(info->dev, &fb_device_attrs[i]);
if (retval) {
- pr_err("device_create_file failed %d\n", retval);
- goto err_del_attrs;
+ pr_warn("device_create_file failed %d\n", retval);
}
}
retval = device_create_bin_file(info->dev, &edid_attr);
if (retval) {
- pr_err("device_create_bin_file failed %d\n", retval);
- goto err_del_attrs;
+ pr_warn("device_create_bin_file failed %d\n", retval);
}
pr_info("DisplayLink USB device /dev/fb%d attached. %dx%d resolution."
@@ -1684,38 +1726,10 @@ static int dlfb_usb_probe(struct usb_interface *interface,
info->var.xres, info->var.yres,
((dev->backing_buffer) ?
info->fix.smem_len * 2 : info->fix.smem_len) >> 10);
- return 0;
-
-err_del_attrs:
- for (i -= 1; i >= 0; i--)
- device_remove_file(info->dev, &fb_device_attrs[i]);
+ return;
error:
- if (dev) {
-
- if (info) {
- if (info->cmap.len != 0)
- fb_dealloc_cmap(&info->cmap);
- if (info->monspecs.modedb)
- fb_destroy_modedb(info->monspecs.modedb);
- if (info->screen_base)
- vfree(info->screen_base);
-
- fb_destroy_modelist(&info->modelist);
-
- framebuffer_release(info);
- }
-
- if (dev->backing_buffer)
- vfree(dev->backing_buffer);
-
- kref_put(&dev->kref, dlfb_free); /* ref for framebuffer */
- kref_put(&dev->kref, dlfb_free); /* last ref from kref_init */
-
- /* dev has been deallocated. Do not dereference */
- }
-
- return retval;
+ dlfb_free_framebuffer(dev);
}
static void dlfb_usb_disconnect(struct usb_interface *interface)
@@ -1735,12 +1749,20 @@ static void dlfb_usb_disconnect(struct usb_interface *interface)
/* When non-active we'll update virtual framebuffer, but no new urbs */
atomic_set(&dev->usb_active, 0);
- /* remove udlfb's sysfs interfaces */
- for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
- device_remove_file(info->dev, &fb_device_attrs[i]);
- device_remove_bin_file(info->dev, &edid_attr);
+ /* this function will wait for all in-flight urbs to complete */
+ dlfb_free_urb_list(dev);
+
+ if (info) {
+ /* remove udlfb's sysfs interfaces */
+ for (i = 0; i < ARRAY_SIZE(fb_device_attrs); i++)
+ device_remove_file(info->dev, &fb_device_attrs[i]);
+ device_remove_bin_file(info->dev, &edid_attr);
+ unlink_framebuffer(info);
+ }
usb_set_intfdata(interface, NULL);
+ dev->udev = NULL;
+ dev->gdev = NULL;
/* if clients still have us open, will be freed on last close */
if (dev->fb_count == 0)
@@ -1806,12 +1828,12 @@ static void dlfb_free_urb_list(struct dlfb_data *dev)
int ret;
unsigned long flags;
- pr_notice("Waiting for completes and freeing all render urbs\n");
+ pr_notice("Freeing all render urbs\n");
/* keep waiting and freeing, until we've got 'em all */
while (count--) {
- /* Getting interrupted means a leak, but ok at shutdown*/
+ /* Getting interrupted means a leak, but ok at disconnect */
ret = down_interruptible(&dev->urbs.limit_sem);
if (ret)
break;
@@ -1833,6 +1855,7 @@ static void dlfb_free_urb_list(struct dlfb_data *dev)
kfree(node);
}
+ dev->urbs.count = 0;
}
static int dlfb_alloc_urb_list(struct dlfb_data *dev, int count, size_t size)
@@ -1948,6 +1971,9 @@ MODULE_PARM_DESC(fb_defio, "Page fault detection of mmap writes");
module_param(shadow, bool, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
MODULE_PARM_DESC(shadow, "Shadow vid mem. Disable to save mem but lose perf");
+module_param(pixel_limit, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+MODULE_PARM_DESC(pixel_limit, "Force limit on max mode (in x*y pixels)");
+
MODULE_AUTHOR("Roberto De Ioris <roberto@unbit.it>, "
"Jaya Kumar <jayakumar.lkml@gmail.com>, "
"Bernie Thompson <bernie@plugable.com>");
diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c
index e7f69ef572dc..260cca7ddb41 100644
--- a/drivers/video/uvesafb.c
+++ b/drivers/video/uvesafb.c
@@ -121,7 +121,7 @@ static int uvesafb_helper_start(void)
NULL,
};
- return call_usermodehelper(v86d_path, argv, envp, 1);
+ return call_usermodehelper(v86d_path, argv, envp, UMH_WAIT_PROC);
}
/*
@@ -362,7 +362,7 @@ static u8 *uvesafb_vbe_state_save(struct uvesafb_par *par)
state = kmalloc(par->vbe_state_size, GFP_KERNEL);
if (!state)
- return NULL;
+ return ERR_PTR(-ENOMEM);
task = uvesafb_prep();
if (!task) {
@@ -1172,9 +1172,17 @@ static int uvesafb_open(struct fb_info *info, int user)
{
struct uvesafb_par *par = info->par;
int cnt = atomic_read(&par->ref_count);
+ u8 *buf = NULL;
- if (!cnt && par->vbe_state_size)
- par->vbe_state_orig = uvesafb_vbe_state_save(par);
+ if (!cnt && par->vbe_state_size) {
+ buf = uvesafb_vbe_state_save(par);
+ if (IS_ERR(buf)) {
+ printk(KERN_WARNING "uvesafb: save hardware state"
+ "failed, error code is %ld!\n", PTR_ERR(buf));
+ } else {
+ par->vbe_state_orig = buf;
+ }
+ }
atomic_inc(&par->ref_count);
return 0;
diff --git a/drivers/video/via/Makefile b/drivers/video/via/Makefile
index 5108136e8776..159f26e6adb5 100644
--- a/drivers/video/via/Makefile
+++ b/drivers/video/via/Makefile
@@ -6,4 +6,7 @@ obj-$(CONFIG_FB_VIA) += viafb.o
viafb-y :=viafbdev.o hw.o via_i2c.o dvi.o lcd.o ioctl.o accel.o \
via_utility.o vt1636.o global.o tblDPASetting.o viamode.o \
- via-core.o via-gpio.o via_modesetting.o via_clock.o
+ via-core.o via-gpio.o via_modesetting.o via_clock.o \
+ via_aux.o via_aux_edid.o via_aux_vt1636.o via_aux_vt1632.o \
+ via_aux_vt1631.o via_aux_vt1625.o via_aux_vt1622.o via_aux_vt1621.o \
+ via_aux_sii164.o via_aux_ch7301.o
diff --git a/drivers/video/via/chip.h b/drivers/video/via/chip.h
index 3ebf20c06eef..d32a5076c20f 100644
--- a/drivers/video/via/chip.h
+++ b/drivers/video/via/chip.h
@@ -146,9 +146,6 @@ struct tmds_setting_information {
struct lvds_setting_information {
int iga_path;
- int h_active;
- int v_active;
- int bpp;
int lcd_panel_hres;
int lcd_panel_vres;
int display_method;
diff --git a/drivers/video/via/dvi.c b/drivers/video/via/dvi.c
index 9138e517267c..6be72f0ba21d 100644
--- a/drivers/video/via/dvi.c
+++ b/drivers/video/via/dvi.c
@@ -172,10 +172,11 @@ static int tmds_register_read_bytes(int index, u8 *buff, int buff_len)
}
/* DVI Set Mode */
-void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga)
+void viafb_dvi_set_mode(const struct fb_var_screeninfo *var,
+ u16 cxres, u16 cyres, int iga)
{
struct fb_var_screeninfo dvi_var = *var;
- struct crt_mode_table *rb_mode;
+ const struct fb_videomode *rb_mode;
int maxPixelClock;
maxPixelClock = viaparinfo->shared->tmds_setting_info.max_pixel_clock;
@@ -185,7 +186,7 @@ void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga)
viafb_fill_var_timing_info(&dvi_var, rb_mode);
}
- viafb_fill_crtc_timing(&dvi_var, iga);
+ viafb_fill_crtc_timing(&dvi_var, cxres, cyres, iga);
}
/* Sense DVI Connector */
diff --git a/drivers/video/via/dvi.h b/drivers/video/via/dvi.h
index e2116aaf797a..db757850c216 100644
--- a/drivers/video/via/dvi.h
+++ b/drivers/video/via/dvi.h
@@ -59,6 +59,7 @@ void viafb_dvi_enable(void);
bool __devinit viafb_tmds_trasmitter_identify(void);
void __devinit viafb_init_dvi_size(struct tmds_chip_information *tmds_chip,
struct tmds_setting_information *tmds_setting);
-void viafb_dvi_set_mode(const struct fb_var_screeninfo *var, int iga);
+void viafb_dvi_set_mode(const struct fb_var_screeninfo *var,
+ u16 cxres, u16 cyres, int iga);
#endif /* __DVI_H__ */
diff --git a/drivers/video/via/hw.c b/drivers/video/via/hw.c
index 8497727d66de..898590db5e14 100644
--- a/drivers/video/via/hw.c
+++ b/drivers/video/via/hw.c
@@ -1467,28 +1467,32 @@ void viafb_set_vclock(u32 clk, int set_iga)
via_write_misc_reg_mask(0x0C, 0x0C); /* select external clock */
}
-static struct display_timing var_to_timing(const struct fb_var_screeninfo *var)
+struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
+ u16 cxres, u16 cyres)
{
struct display_timing timing;
+ u16 dx = (var->xres - cxres) / 2, dy = (var->yres - cyres) / 2;
- timing.hor_addr = var->xres;
- timing.hor_sync_start = timing.hor_addr + var->right_margin;
+ timing.hor_addr = cxres;
+ timing.hor_sync_start = timing.hor_addr + var->right_margin + dx;
timing.hor_sync_end = timing.hor_sync_start + var->hsync_len;
- timing.hor_total = timing.hor_sync_end + var->left_margin;
- timing.hor_blank_start = timing.hor_addr;
- timing.hor_blank_end = timing.hor_total;
- timing.ver_addr = var->yres;
- timing.ver_sync_start = timing.ver_addr + var->lower_margin;
+ timing.hor_total = timing.hor_sync_end + var->left_margin + dx;
+ timing.hor_blank_start = timing.hor_addr + dx;
+ timing.hor_blank_end = timing.hor_total - dx;
+ timing.ver_addr = cyres;
+ timing.ver_sync_start = timing.ver_addr + var->lower_margin + dy;
timing.ver_sync_end = timing.ver_sync_start + var->vsync_len;
- timing.ver_total = timing.ver_sync_end + var->upper_margin;
- timing.ver_blank_start = timing.ver_addr;
- timing.ver_blank_end = timing.ver_total;
+ timing.ver_total = timing.ver_sync_end + var->upper_margin + dy;
+ timing.ver_blank_start = timing.ver_addr + dy;
+ timing.ver_blank_end = timing.ver_total - dy;
return timing;
}
-void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga)
+void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var,
+ u16 cxres, u16 cyres, int iga)
{
- struct display_timing crt_reg = var_to_timing(var);
+ struct display_timing crt_reg = var_to_timing(var,
+ cxres ? cxres : var->xres, cyres ? cyres : var->yres);
if (iga == IGA1)
via_set_primary_timing(&crt_reg);
@@ -1526,13 +1530,6 @@ void viafb_update_device_setting(int hres, int vres, int bpp, int flag)
if (flag == 0) {
viaparinfo->tmds_setting_info->h_active = hres;
viaparinfo->tmds_setting_info->v_active = vres;
-
- viaparinfo->lvds_setting_info->h_active = hres;
- viaparinfo->lvds_setting_info->v_active = vres;
- viaparinfo->lvds_setting_info->bpp = bpp;
- viaparinfo->lvds_setting_info2->h_active = hres;
- viaparinfo->lvds_setting_info2->v_active = vres;
- viaparinfo->lvds_setting_info2->bpp = bpp;
} else {
if (viaparinfo->tmds_setting_info->iga_path == IGA2) {
@@ -1540,16 +1537,6 @@ void viafb_update_device_setting(int hres, int vres, int bpp, int flag)
viaparinfo->tmds_setting_info->v_active = vres;
}
- if (viaparinfo->lvds_setting_info->iga_path == IGA2) {
- viaparinfo->lvds_setting_info->h_active = hres;
- viaparinfo->lvds_setting_info->v_active = vres;
- viaparinfo->lvds_setting_info->bpp = bpp;
- }
- if (IGA2 == viaparinfo->lvds_setting_info2->iga_path) {
- viaparinfo->lvds_setting_info2->h_active = hres;
- viaparinfo->lvds_setting_info2->v_active = vres;
- viaparinfo->lvds_setting_info2->bpp = bpp;
- }
}
}
@@ -1758,13 +1745,13 @@ static void set_display_channel(void)
}
}
-static u8 get_sync(struct fb_info *info)
+static u8 get_sync(struct fb_var_screeninfo *var)
{
u8 polarity = 0;
- if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT))
+ if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
polarity |= VIA_HSYNC_NEGATIVE;
- if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT))
+ if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
polarity |= VIA_VSYNC_NEGATIVE;
return polarity;
}
@@ -1844,9 +1831,9 @@ static void hw_init(void)
load_fix_bit_crtc_reg();
}
-int viafb_setmode(int video_bpp, int video_bpp1)
+int viafb_setmode(void)
{
- int j;
+ int j, cxres = 0, cyres = 0;
int port;
u32 devices = viaparinfo->shared->iga1_devices
| viaparinfo->shared->iga2_devices;
@@ -1895,6 +1882,8 @@ int viafb_setmode(int video_bpp, int video_bpp1)
} else if (viafb_SAMM_ON) {
viafb_fill_var_timing_info(&var2, viafb_get_best_mode(
viafb_second_xres, viafb_second_yres, viafb_refresh1));
+ cxres = viafbinfo->var.xres;
+ cyres = viafbinfo->var.yres;
var2.bits_per_pixel = viafbinfo->var.bits_per_pixel;
}
@@ -1902,9 +1891,9 @@ int viafb_setmode(int video_bpp, int video_bpp1)
if (viafb_CRT_ON) {
if (viaparinfo->shared->iga2_devices & VIA_CRT
&& viafb_SAMM_ON)
- viafb_fill_crtc_timing(&var2, IGA2);
+ viafb_fill_crtc_timing(&var2, cxres, cyres, IGA2);
else
- viafb_fill_crtc_timing(&viafbinfo->var,
+ viafb_fill_crtc_timing(&viafbinfo->var, 0, 0,
(viaparinfo->shared->iga1_devices & VIA_CRT)
? IGA1 : IGA2);
@@ -1922,17 +1911,17 @@ int viafb_setmode(int video_bpp, int video_bpp1)
if (viafb_DVI_ON) {
if (viaparinfo->shared->tmds_setting_info.iga_path == IGA2
&& viafb_SAMM_ON)
- viafb_dvi_set_mode(&var2, IGA2);
+ viafb_dvi_set_mode(&var2, cxres, cyres, IGA2);
else
- viafb_dvi_set_mode(&viafbinfo->var,
+ viafb_dvi_set_mode(&viafbinfo->var, 0, 0,
viaparinfo->tmds_setting_info->iga_path);
}
if (viafb_LCD_ON) {
if (viafb_SAMM_ON &&
(viaparinfo->lvds_setting_info->iga_path == IGA2)) {
- viaparinfo->lvds_setting_info->bpp = video_bpp1;
- viafb_lcd_set_mode(viaparinfo->lvds_setting_info,
+ viafb_lcd_set_mode(&var2, cxres, cyres,
+ viaparinfo->lvds_setting_info,
&viaparinfo->chip_info->lvds_chip_info);
} else {
/* IGA1 doesn't have LCD scaling, so set it center. */
@@ -1940,16 +1929,16 @@ int viafb_setmode(int video_bpp, int video_bpp1)
viaparinfo->lvds_setting_info->display_method =
LCD_CENTERING;
}
- viaparinfo->lvds_setting_info->bpp = video_bpp;
- viafb_lcd_set_mode(viaparinfo->lvds_setting_info,
+ viafb_lcd_set_mode(&viafbinfo->var, 0, 0,
+ viaparinfo->lvds_setting_info,
&viaparinfo->chip_info->lvds_chip_info);
}
}
if (viafb_LCD2_ON) {
if (viafb_SAMM_ON &&
(viaparinfo->lvds_setting_info2->iga_path == IGA2)) {
- viaparinfo->lvds_setting_info2->bpp = video_bpp1;
- viafb_lcd_set_mode(viaparinfo->lvds_setting_info2,
+ viafb_lcd_set_mode(&var2, cxres, cyres,
+ viaparinfo->lvds_setting_info2,
&viaparinfo->chip_info->lvds_chip_info2);
} else {
/* IGA1 doesn't have LCD scaling, so set it center. */
@@ -1957,8 +1946,8 @@ int viafb_setmode(int video_bpp, int video_bpp1)
viaparinfo->lvds_setting_info2->display_method =
LCD_CENTERING;
}
- viaparinfo->lvds_setting_info2->bpp = video_bpp;
- viafb_lcd_set_mode(viaparinfo->lvds_setting_info2,
+ viafb_lcd_set_mode(&viafbinfo->var, 0, 0,
+ viaparinfo->lvds_setting_info2,
&viaparinfo->chip_info->lvds_chip_info2);
}
}
@@ -1971,7 +1960,7 @@ int viafb_setmode(int video_bpp, int video_bpp1)
if (!viafb_hotplug) {
viafb_hotplug_Xres = viafbinfo->var.xres;
viafb_hotplug_Yres = viafbinfo->var.yres;
- viafb_hotplug_bpp = video_bpp;
+ viafb_hotplug_bpp = viafbinfo->var.bits_per_pixel;
viafb_hotplug_refresh = viafb_refresh;
if (viafb_DVI_ON)
@@ -1980,13 +1969,13 @@ int viafb_setmode(int video_bpp, int video_bpp1)
viafb_DeviceStatus = CRT_Device;
}
device_on();
- if (!viafb_dual_fb)
- via_set_sync_polarity(devices, get_sync(viafbinfo));
+ if (!viafb_SAMM_ON)
+ via_set_sync_polarity(devices, get_sync(&viafbinfo->var));
else {
via_set_sync_polarity(viaparinfo->shared->iga1_devices,
- get_sync(viafbinfo));
+ get_sync(&viafbinfo->var));
via_set_sync_polarity(viaparinfo->shared->iga2_devices,
- get_sync(viafbinfo1));
+ get_sync(&var2));
}
clock.set_engine_pll_state(VIA_STATE_ON);
@@ -2023,20 +2012,20 @@ int viafb_setmode(int video_bpp, int video_bpp1)
int viafb_get_refresh(int hres, int vres, u32 long_refresh)
{
- struct crt_mode_table *best;
+ const struct fb_videomode *best;
best = viafb_get_best_mode(hres, vres, long_refresh);
if (!best)
return 60;
- if (abs(best->refresh_rate - long_refresh) > 3) {
+ if (abs(best->refresh - long_refresh) > 3) {
if (hres == 1200 && vres == 900)
return 49; /* OLPC DCON only supports 50 Hz */
else
return 60;
}
- return best->refresh_rate;
+ return best->refresh;
}
static void device_off(void)
@@ -2129,26 +2118,17 @@ void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\
}
}
-/*According var's xres, yres fill var's other timing information*/
void viafb_fill_var_timing_info(struct fb_var_screeninfo *var,
- struct crt_mode_table *mode)
+ const struct fb_videomode *mode)
{
- struct display_timing crt_reg;
-
- crt_reg = mode->crtc;
- var->pixclock = 1000000000 / (crt_reg.hor_total * crt_reg.ver_total)
- * 1000 / mode->refresh_rate;
- var->left_margin =
- crt_reg.hor_total - (crt_reg.hor_sync_start + crt_reg.hor_sync_end);
- var->right_margin = crt_reg.hor_sync_start - crt_reg.hor_addr;
- var->hsync_len = crt_reg.hor_sync_end;
- var->upper_margin =
- crt_reg.ver_total - (crt_reg.ver_sync_start + crt_reg.ver_sync_end);
- var->lower_margin = crt_reg.ver_sync_start - crt_reg.ver_addr;
- var->vsync_len = crt_reg.ver_sync_end;
- var->sync = 0;
- if (mode->h_sync_polarity == POSITIVE)
- var->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (mode->v_sync_polarity == POSITIVE)
- var->sync |= FB_SYNC_VERT_HIGH_ACT;
+ var->pixclock = mode->pixclock;
+ var->xres = mode->xres;
+ var->yres = mode->yres;
+ var->left_margin = mode->left_margin;
+ var->right_margin = mode->right_margin;
+ var->hsync_len = mode->hsync_len;
+ var->upper_margin = mode->upper_margin;
+ var->lower_margin = mode->lower_margin;
+ var->vsync_len = mode->vsync_len;
+ var->sync = mode->sync;
}
diff --git a/drivers/video/via/hw.h b/drivers/video/via/hw.h
index 4db5b6e8d8d0..6be243cfc823 100644
--- a/drivers/video/via/hw.h
+++ b/drivers/video/via/hw.h
@@ -637,7 +637,10 @@ extern int viafb_LCD_ON;
extern int viafb_DVI_ON;
extern int viafb_hotplug;
-void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var, int iga);
+struct display_timing var_to_timing(const struct fb_var_screeninfo *var,
+ u16 cxres, u16 cyres);
+void viafb_fill_crtc_timing(const struct fb_var_screeninfo *var,
+ u16 cxres, u16 cyres, int iga);
void viafb_set_vclock(u32 CLK, int set_iga);
void viafb_load_reg(int timing_value, int viafb_load_reg_num,
struct io_register *reg,
@@ -657,9 +660,9 @@ void viafb_load_FIFO_reg(int set_iga, int hor_active, int ver_active);
void viafb_set_dpa_gfx(int output_interface, struct GFX_DPA_SETTING\
*p_gfx_dpa_setting);
-int viafb_setmode(int video_bpp, int video_bpp1);
+int viafb_setmode(void);
void viafb_fill_var_timing_info(struct fb_var_screeninfo *var,
- struct crt_mode_table *mode);
+ const struct fb_videomode *mode);
void __devinit viafb_init_chip_info(int chip_type);
void __devinit viafb_init_dac(int set_iga);
int viafb_get_refresh(int hres, int vres, u32 float_refresh);
diff --git a/drivers/video/via/lcd.c b/drivers/video/via/lcd.c
index 5f3b4e394e82..165037910536 100644
--- a/drivers/video/via/lcd.c
+++ b/drivers/video/via/lcd.c
@@ -53,10 +53,6 @@ static void __devinit fp_id_to_vindex(int panel_id);
static int lvds_register_read(int index);
static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres,
int panel_vres);
-static void via_pitch_alignment_patch_lcd(
- struct lvds_setting_information *plvds_setting_info,
- struct lvds_chip_information
- *plvds_chip_info);
static void lcd_patch_skew_dvp0(struct lvds_setting_information
*plvds_setting_info,
struct lvds_chip_information *plvds_chip_info);
@@ -79,9 +75,6 @@ static void check_diport_of_integrated_lvds(
struct lvds_chip_information *plvds_chip_info,
struct lvds_setting_information
*plvds_setting_info);
-static struct display_timing lcd_centering_timging(struct display_timing
- mode_crt_reg,
- struct display_timing panel_crt_reg);
static inline bool check_lvds_chip(int device_id_subaddr, int device_id)
{
@@ -454,20 +447,17 @@ static void load_lcd_scaling(int set_hres, int set_vres, int panel_hres,
}
}
-static void via_pitch_alignment_patch_lcd(
- struct lvds_setting_information *plvds_setting_info,
- struct lvds_chip_information
- *plvds_chip_info)
+static void via_pitch_alignment_patch_lcd(int iga_path, int hres, int bpp)
{
unsigned char cr13, cr35, cr65, cr66, cr67;
unsigned long dwScreenPitch = 0;
unsigned long dwPitch;
- dwPitch = plvds_setting_info->h_active * (plvds_setting_info->bpp >> 3);
+ dwPitch = hres * (bpp >> 3);
if (dwPitch & 0x1F) {
dwScreenPitch = ((dwPitch + 31) & ~31) >> 3;
- if (plvds_setting_info->iga_path == IGA2) {
- if (plvds_setting_info->bpp > 8) {
+ if (iga_path == IGA2) {
+ if (bpp > 8) {
cr66 = (unsigned char)(dwScreenPitch & 0xFF);
viafb_write_reg(CR66, VIACR, cr66);
cr67 = viafb_read_reg(VIACR, CR67) & 0xFC;
@@ -485,7 +475,7 @@ static void via_pitch_alignment_patch_lcd(
cr65 += 2;
viafb_write_reg(CR65, VIACR, cr65);
} else {
- if (plvds_setting_info->bpp > 8) {
+ if (bpp > 8) {
cr13 = (unsigned char)(dwScreenPitch & 0xFF);
viafb_write_reg(CR13, VIACR, cr13);
cr35 = viafb_read_reg(VIACR, CR35) & 0x1F;
@@ -548,49 +538,45 @@ static void lcd_patch_skew(struct lvds_setting_information
}
/* LCD Set Mode */
-void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info,
+void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres,
+ u16 cyres, struct lvds_setting_information *plvds_setting_info,
struct lvds_chip_information *plvds_chip_info)
{
int set_iga = plvds_setting_info->iga_path;
- int mode_bpp = plvds_setting_info->bpp;
- int set_hres = plvds_setting_info->h_active;
- int set_vres = plvds_setting_info->v_active;
+ int mode_bpp = var->bits_per_pixel;
+ int set_hres = cxres ? cxres : var->xres;
+ int set_vres = cyres ? cyres : var->yres;
int panel_hres = plvds_setting_info->lcd_panel_hres;
int panel_vres = plvds_setting_info->lcd_panel_vres;
u32 clock;
- struct display_timing mode_crt_reg, panel_crt_reg, timing;
- struct crt_mode_table *mode_crt_table, *panel_crt_table;
+ struct display_timing timing;
+ struct fb_var_screeninfo panel_var;
+ const struct fb_videomode *mode_crt_table, *panel_crt_table;
DEBUG_MSG(KERN_INFO "viafb_lcd_set_mode!!\n");
/* Get mode table */
mode_crt_table = viafb_get_best_mode(set_hres, set_vres, 60);
- mode_crt_reg = mode_crt_table->crtc;
/* Get panel table Pointer */
panel_crt_table = viafb_get_best_mode(panel_hres, panel_vres, 60);
- panel_crt_reg = panel_crt_table->crtc;
+ viafb_fill_var_timing_info(&panel_var, panel_crt_table);
DEBUG_MSG(KERN_INFO "bellow viafb_lcd_set_mode!!\n");
if (VT1636_LVDS == plvds_chip_info->lvds_chip_name)
viafb_init_lvds_vt1636(plvds_setting_info, plvds_chip_info);
- clock = panel_crt_reg.hor_total * panel_crt_reg.ver_total
- * panel_crt_table->refresh_rate;
+ clock = PICOS2KHZ(panel_crt_table->pixclock) * 1000;
plvds_setting_info->vclk = clock;
if (set_iga == IGA2 && (set_hres < panel_hres || set_vres < panel_vres)
&& plvds_setting_info->display_method == LCD_EXPANDSION) {
- timing = panel_crt_reg;
+ timing = var_to_timing(&panel_var, panel_hres, panel_vres);
load_lcd_scaling(set_hres, set_vres, panel_hres, panel_vres);
} else {
- timing = lcd_centering_timging(mode_crt_reg, panel_crt_reg);
+ timing = var_to_timing(&panel_var, set_hres, set_vres);
if (set_iga == IGA2)
/* disable scaling */
via_write_reg_mask(VIACR, 0x79, 0x00,
BIT0 + BIT1 + BIT2);
}
- timing.hor_blank_end += timing.hor_blank_start;
- timing.hor_sync_end += timing.hor_sync_start;
- timing.ver_blank_end += timing.ver_blank_start;
- timing.ver_sync_end += timing.ver_sync_start;
if (set_iga == IGA1)
via_set_primary_timing(&timing);
else if (set_iga == IGA2)
@@ -613,7 +599,8 @@ void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info,
viafb_write_reg_mask(CR6A, VIACR, 0x01, BIT0);
/* Patch for non 32bit alignment mode */
- via_pitch_alignment_patch_lcd(plvds_setting_info, plvds_chip_info);
+ via_pitch_alignment_patch_lcd(plvds_setting_info->iga_path, set_hres,
+ var->bits_per_pixel);
}
static void integrated_lvds_disable(struct lvds_setting_information
@@ -973,37 +960,6 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information
}
}
-static struct display_timing lcd_centering_timging(struct display_timing
- mode_crt_reg,
- struct display_timing panel_crt_reg)
-{
- struct display_timing crt_reg;
-
- crt_reg.hor_total = panel_crt_reg.hor_total;
- crt_reg.hor_addr = mode_crt_reg.hor_addr;
- crt_reg.hor_blank_start =
- (panel_crt_reg.hor_addr - mode_crt_reg.hor_addr) / 2 +
- crt_reg.hor_addr;
- crt_reg.hor_blank_end = panel_crt_reg.hor_blank_end;
- crt_reg.hor_sync_start =
- (panel_crt_reg.hor_sync_start -
- panel_crt_reg.hor_blank_start) + crt_reg.hor_blank_start;
- crt_reg.hor_sync_end = panel_crt_reg.hor_sync_end;
-
- crt_reg.ver_total = panel_crt_reg.ver_total;
- crt_reg.ver_addr = mode_crt_reg.ver_addr;
- crt_reg.ver_blank_start =
- (panel_crt_reg.ver_addr - mode_crt_reg.ver_addr) / 2 +
- crt_reg.ver_addr;
- crt_reg.ver_blank_end = panel_crt_reg.ver_blank_end;
- crt_reg.ver_sync_start =
- (panel_crt_reg.ver_sync_start -
- panel_crt_reg.ver_blank_start) + crt_reg.ver_blank_start;
- crt_reg.ver_sync_end = panel_crt_reg.ver_sync_end;
-
- return crt_reg;
-}
-
bool viafb_lcd_get_mobile_state(bool *mobile)
{
unsigned char __iomem *romptr, *tableptr, *biosptr;
diff --git a/drivers/video/via/lcd.h b/drivers/video/via/lcd.h
index 77ca7b862e68..8f3e4e06156c 100644
--- a/drivers/video/via/lcd.h
+++ b/drivers/video/via/lcd.h
@@ -76,7 +76,8 @@ void __devinit viafb_init_lvds_output_interface(struct lvds_chip_information
*plvds_chip_info,
struct lvds_setting_information
*plvds_setting_info);
-void viafb_lcd_set_mode(struct lvds_setting_information *plvds_setting_info,
+void viafb_lcd_set_mode(const struct fb_var_screeninfo *var, u16 cxres,
+ u16 cyres, struct lvds_setting_information *plvds_setting_info,
struct lvds_chip_information *plvds_chip_info);
bool __devinit viafb_lvds_trasmitter_identify(void);
void viafb_init_lvds_output_interface(struct lvds_chip_information
diff --git a/drivers/video/via/share.h b/drivers/video/via/share.h
index c01c1c162726..3158dfc90bed 100644
--- a/drivers/video/via/share.h
+++ b/drivers/video/via/share.h
@@ -283,337 +283,6 @@
#define HW_LAYOUT_LCD1_LCD2 0x04
#define HW_LAYOUT_LCD_EXTERNAL_LCD2 0x10
-/* Definition Refresh Rate */
-#define REFRESH_49 49
-#define REFRESH_50 50
-#define REFRESH_60 60
-#define REFRESH_75 75
-#define REFRESH_85 85
-#define REFRESH_100 100
-#define REFRESH_120 120
-
-/* Definition Sync Polarity*/
-#define NEGATIVE 1
-#define POSITIVE 0
-
-/*480x640@60 Sync Polarity (GTF)
-*/
-#define M480X640_R60_HSP NEGATIVE
-#define M480X640_R60_VSP POSITIVE
-
-/*640x480@60 Sync Polarity (VESA Mode)
-*/
-#define M640X480_R60_HSP NEGATIVE
-#define M640X480_R60_VSP NEGATIVE
-
-/*640x480@75 Sync Polarity (VESA Mode)
-*/
-#define M640X480_R75_HSP NEGATIVE
-#define M640X480_R75_VSP NEGATIVE
-
-/*640x480@85 Sync Polarity (VESA Mode)
-*/
-#define M640X480_R85_HSP NEGATIVE
-#define M640X480_R85_VSP NEGATIVE
-
-/*640x480@100 Sync Polarity (GTF Mode)
-*/
-#define M640X480_R100_HSP NEGATIVE
-#define M640X480_R100_VSP POSITIVE
-
-/*640x480@120 Sync Polarity (GTF Mode)
-*/
-#define M640X480_R120_HSP NEGATIVE
-#define M640X480_R120_VSP POSITIVE
-
-/*720x480@60 Sync Polarity (GTF Mode)
-*/
-#define M720X480_R60_HSP NEGATIVE
-#define M720X480_R60_VSP POSITIVE
-
-/*720x576@60 Sync Polarity (GTF Mode)
-*/
-#define M720X576_R60_HSP NEGATIVE
-#define M720X576_R60_VSP POSITIVE
-
-/*800x600@60 Sync Polarity (VESA Mode)
-*/
-#define M800X600_R60_HSP POSITIVE
-#define M800X600_R60_VSP POSITIVE
-
-/*800x600@75 Sync Polarity (VESA Mode)
-*/
-#define M800X600_R75_HSP POSITIVE
-#define M800X600_R75_VSP POSITIVE
-
-/*800x600@85 Sync Polarity (VESA Mode)
-*/
-#define M800X600_R85_HSP POSITIVE
-#define M800X600_R85_VSP POSITIVE
-
-/*800x600@100 Sync Polarity (GTF Mode)
-*/
-#define M800X600_R100_HSP NEGATIVE
-#define M800X600_R100_VSP POSITIVE
-
-/*800x600@120 Sync Polarity (GTF Mode)
-*/
-#define M800X600_R120_HSP NEGATIVE
-#define M800X600_R120_VSP POSITIVE
-
-/*800x480@60 Sync Polarity (CVT Mode)
-*/
-#define M800X480_R60_HSP NEGATIVE
-#define M800X480_R60_VSP POSITIVE
-
-/*848x480@60 Sync Polarity (CVT Mode)
-*/
-#define M848X480_R60_HSP NEGATIVE
-#define M848X480_R60_VSP POSITIVE
-
-/*852x480@60 Sync Polarity (GTF Mode)
-*/
-#define M852X480_R60_HSP NEGATIVE
-#define M852X480_R60_VSP POSITIVE
-
-/*1024x512@60 Sync Polarity (GTF Mode)
-*/
-#define M1024X512_R60_HSP NEGATIVE
-#define M1024X512_R60_VSP POSITIVE
-
-/*1024x600@60 Sync Polarity (GTF Mode)
-*/
-#define M1024X600_R60_HSP NEGATIVE
-#define M1024X600_R60_VSP POSITIVE
-
-/*1024x768@60 Sync Polarity (VESA Mode)
-*/
-#define M1024X768_R60_HSP NEGATIVE
-#define M1024X768_R60_VSP NEGATIVE
-
-/*1024x768@75 Sync Polarity (VESA Mode)
-*/
-#define M1024X768_R75_HSP POSITIVE
-#define M1024X768_R75_VSP POSITIVE
-
-/*1024x768@85 Sync Polarity (VESA Mode)
-*/
-#define M1024X768_R85_HSP POSITIVE
-#define M1024X768_R85_VSP POSITIVE
-
-/*1024x768@100 Sync Polarity (GTF Mode)
-*/
-#define M1024X768_R100_HSP NEGATIVE
-#define M1024X768_R100_VSP POSITIVE
-
-/*1152x864@75 Sync Polarity (VESA Mode)
-*/
-#define M1152X864_R75_HSP POSITIVE
-#define M1152X864_R75_VSP POSITIVE
-
-/*1280x720@60 Sync Polarity (GTF Mode)
-*/
-#define M1280X720_R60_HSP NEGATIVE
-#define M1280X720_R60_VSP POSITIVE
-
-/* 1280x768@50 Sync Polarity (GTF Mode) */
-#define M1280X768_R50_HSP NEGATIVE
-#define M1280X768_R50_VSP POSITIVE
-
-/*1280x768@60 Sync Polarity (GTF Mode)
-*/
-#define M1280X768_R60_HSP NEGATIVE
-#define M1280X768_R60_VSP POSITIVE
-
-/*1280x800@60 Sync Polarity (CVT Mode)
-*/
-#define M1280X800_R60_HSP NEGATIVE
-#define M1280X800_R60_VSP POSITIVE
-
-/*1280x960@60 Sync Polarity (VESA Mode)
-*/
-#define M1280X960_R60_HSP POSITIVE
-#define M1280X960_R60_VSP POSITIVE
-
-/*1280x1024@60 Sync Polarity (VESA Mode)
-*/
-#define M1280X1024_R60_HSP POSITIVE
-#define M1280X1024_R60_VSP POSITIVE
-
-/* 1360x768@60 Sync Polarity (CVT Mode) */
-#define M1360X768_R60_HSP POSITIVE
-#define M1360X768_R60_VSP POSITIVE
-
-/* 1360x768@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1360X768_RB_R60_HSP POSITIVE
-#define M1360X768_RB_R60_VSP NEGATIVE
-
-/* 1368x768@50 Sync Polarity (GTF Mode) */
-#define M1368X768_R50_HSP NEGATIVE
-#define M1368X768_R50_VSP POSITIVE
-
-/* 1368x768@60 Sync Polarity (VESA Mode) */
-#define M1368X768_R60_HSP NEGATIVE
-#define M1368X768_R60_VSP POSITIVE
-
-/*1280x1024@75 Sync Polarity (VESA Mode)
-*/
-#define M1280X1024_R75_HSP POSITIVE
-#define M1280X1024_R75_VSP POSITIVE
-
-/*1280x1024@85 Sync Polarity (VESA Mode)
-*/
-#define M1280X1024_R85_HSP POSITIVE
-#define M1280X1024_R85_VSP POSITIVE
-
-/*1440x1050@60 Sync Polarity (GTF Mode)
-*/
-#define M1440X1050_R60_HSP NEGATIVE
-#define M1440X1050_R60_VSP POSITIVE
-
-/*1600x1200@60 Sync Polarity (VESA Mode)
-*/
-#define M1600X1200_R60_HSP POSITIVE
-#define M1600X1200_R60_VSP POSITIVE
-
-/*1600x1200@75 Sync Polarity (VESA Mode)
-*/
-#define M1600X1200_R75_HSP POSITIVE
-#define M1600X1200_R75_VSP POSITIVE
-
-/* 1680x1050@60 Sync Polarity (CVT Mode) */
-#define M1680x1050_R60_HSP NEGATIVE
-#define M1680x1050_R60_VSP NEGATIVE
-
-/* 1680x1050@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1680x1050_RB_R60_HSP POSITIVE
-#define M1680x1050_RB_R60_VSP NEGATIVE
-
-/* 1680x1050@75 Sync Polarity (CVT Mode) */
-#define M1680x1050_R75_HSP NEGATIVE
-#define M1680x1050_R75_VSP POSITIVE
-
-/*1920x1080@60 Sync Polarity (CVT Mode)
-*/
-#define M1920X1080_R60_HSP NEGATIVE
-#define M1920X1080_R60_VSP POSITIVE
-
-/* 1920x1080@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1920X1080_RB_R60_HSP POSITIVE
-#define M1920X1080_RB_R60_VSP NEGATIVE
-
-/*1920x1440@60 Sync Polarity (VESA Mode)
-*/
-#define M1920X1440_R60_HSP NEGATIVE
-#define M1920X1440_R60_VSP POSITIVE
-
-/*1920x1440@75 Sync Polarity (VESA Mode)
-*/
-#define M1920X1440_R75_HSP NEGATIVE
-#define M1920X1440_R75_VSP POSITIVE
-
-#if 0
-/* 1400x1050@60 Sync Polarity (VESA Mode) */
-#define M1400X1050_R60_HSP NEGATIVE
-#define M1400X1050_R60_VSP NEGATIVE
-#endif
-
-/* 1400x1050@60 Sync Polarity (CVT Mode) */
-#define M1400X1050_R60_HSP NEGATIVE
-#define M1400X1050_R60_VSP POSITIVE
-
-/* 1400x1050@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1400X1050_RB_R60_HSP POSITIVE
-#define M1400X1050_RB_R60_VSP NEGATIVE
-
-/* 1400x1050@75 Sync Polarity (CVT Mode) */
-#define M1400X1050_R75_HSP NEGATIVE
-#define M1400X1050_R75_VSP POSITIVE
-
-/* 960x600@60 Sync Polarity (CVT Mode) */
-#define M960X600_R60_HSP NEGATIVE
-#define M960X600_R60_VSP POSITIVE
-
-/* 1000x600@60 Sync Polarity (GTF Mode) */
-#define M1000X600_R60_HSP NEGATIVE
-#define M1000X600_R60_VSP POSITIVE
-
-/* 1024x576@60 Sync Polarity (GTF Mode) */
-#define M1024X576_R60_HSP NEGATIVE
-#define M1024X576_R60_VSP POSITIVE
-
-/*1024x600@60 Sync Polarity (GTF Mode)*/
-#define M1024X600_R60_HSP NEGATIVE
-#define M1024X600_R60_VSP POSITIVE
-
-/* 1088x612@60 Sync Polarity (CVT Mode) */
-#define M1088X612_R60_HSP NEGATIVE
-#define M1088X612_R60_VSP POSITIVE
-
-/* 1152x720@60 Sync Polarity (CVT Mode) */
-#define M1152X720_R60_HSP NEGATIVE
-#define M1152X720_R60_VSP POSITIVE
-
-/* 1200x720@60 Sync Polarity (GTF Mode) */
-#define M1200X720_R60_HSP NEGATIVE
-#define M1200X720_R60_VSP POSITIVE
-
-/* 1200x900@60 Sync Polarity (DCON) */
-#define M1200X900_R60_HSP POSITIVE
-#define M1200X900_R60_VSP POSITIVE
-
-/* 1280x600@60 Sync Polarity (GTF Mode) */
-#define M1280x600_R60_HSP NEGATIVE
-#define M1280x600_R60_VSP POSITIVE
-
-/* 1280x720@50 Sync Polarity (GTF Mode) */
-#define M1280X720_R50_HSP NEGATIVE
-#define M1280X720_R50_VSP POSITIVE
-
-/* 1440x900@60 Sync Polarity (CVT Mode) */
-#define M1440X900_R60_HSP NEGATIVE
-#define M1440X900_R60_VSP POSITIVE
-
-/* 1440x900@75 Sync Polarity (CVT Mode) */
-#define M1440X900_R75_HSP NEGATIVE
-#define M1440X900_R75_VSP POSITIVE
-
-/* 1440x900@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1440X900_RB_R60_HSP POSITIVE
-#define M1440X900_RB_R60_VSP NEGATIVE
-
-/* 1600x900@60 Sync Polarity (CVT Mode) */
-#define M1600X900_R60_HSP NEGATIVE
-#define M1600X900_R60_VSP POSITIVE
-
-/* 1600x900@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1600X900_RB_R60_HSP POSITIVE
-#define M1600X900_RB_R60_VSP NEGATIVE
-
-/* 1600x1024@60 Sync Polarity (GTF Mode) */
-#define M1600X1024_R60_HSP NEGATIVE
-#define M1600X1024_R60_VSP POSITIVE
-
-/* 1792x1344@60 Sync Polarity (DMT Mode) */
-#define M1792x1344_R60_HSP NEGATIVE
-#define M1792x1344_R60_VSP POSITIVE
-
-/* 1856x1392@60 Sync Polarity (DMT Mode) */
-#define M1856x1392_R60_HSP NEGATIVE
-#define M1856x1392_R60_VSP POSITIVE
-
-/* 1920x1200@60 Sync Polarity (CVT Mode) */
-#define M1920X1200_R60_HSP NEGATIVE
-#define M1920X1200_R60_VSP POSITIVE
-
-/* 1920x1200@60 Sync Polarity (CVT Reduce Blanking Mode) */
-#define M1920X1200_RB_R60_HSP POSITIVE
-#define M1920X1200_RB_R60_VSP NEGATIVE
-
-/* 2048x1536@60 Sync Polarity (CVT Mode) */
-#define M2048x1536_R60_HSP NEGATIVE
-#define M2048x1536_R60_VSP POSITIVE
-
/* Definition CRTC Timing Index */
#define H_TOTAL_INDEX 0
#define H_ADDR_INDEX 1
diff --git a/drivers/video/via/via_aux.c b/drivers/video/via/via_aux.c
new file mode 100644
index 000000000000..4a0a55cdac3d
--- /dev/null
+++ b/drivers/video/via/via_aux.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * infrastructure for devices connected via I2C
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap)
+{
+ struct via_aux_bus *bus;
+
+ if (!adap)
+ return NULL;
+
+ bus = kmalloc(sizeof(*bus), GFP_KERNEL);
+ if (!bus)
+ return NULL;
+
+ bus->adap = adap;
+ INIT_LIST_HEAD(&bus->drivers);
+
+ via_aux_edid_probe(bus);
+ via_aux_vt1636_probe(bus);
+ via_aux_vt1632_probe(bus);
+ via_aux_vt1631_probe(bus);
+ via_aux_vt1625_probe(bus);
+ via_aux_vt1622_probe(bus);
+ via_aux_vt1621_probe(bus);
+ via_aux_sii164_probe(bus);
+ via_aux_ch7301_probe(bus);
+
+ return bus;
+}
+
+void via_aux_free(struct via_aux_bus *bus)
+{
+ struct via_aux_drv *pos, *n;
+
+ if (!bus)
+ return;
+
+ list_for_each_entry_safe(pos, n, &bus->drivers, chain) {
+ if (pos->cleanup)
+ pos->cleanup(pos);
+
+ list_del(&pos->chain);
+ kfree(pos->data);
+ kfree(pos);
+ }
+
+ kfree(bus);
+}
+
+const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus)
+{
+ struct via_aux_drv *pos;
+ const struct fb_videomode *mode = NULL;
+
+ if (!bus)
+ return NULL;
+
+ list_for_each_entry(pos, &bus->drivers, chain) {
+ if (pos->get_preferred_mode)
+ mode = pos->get_preferred_mode(pos);
+ }
+
+ return mode;
+}
diff --git a/drivers/video/via/via_aux.h b/drivers/video/via/via_aux.h
new file mode 100644
index 000000000000..a8de3f038cea
--- /dev/null
+++ b/drivers/video/via/via_aux.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * infrastructure for devices connected via I2C
+ */
+
+#ifndef __VIA_AUX_H__
+#define __VIA_AUX_H__
+
+
+#include <linux/list.h>
+#include <linux/i2c.h>
+#include <linux/fb.h>
+
+
+struct via_aux_bus {
+ struct i2c_adapter *adap; /* the I2C device to access the bus */
+ struct list_head drivers; /* drivers for devices on this bus */
+};
+
+struct via_aux_drv {
+ struct list_head chain; /* chain to support multiple drivers */
+
+ struct via_aux_bus *bus; /* the I2C bus used */
+ u8 addr; /* the I2C slave address */
+
+ const char *name; /* human readable name of the driver */
+ void *data; /* private data of this driver */
+
+ void (*cleanup)(struct via_aux_drv *drv);
+ const struct fb_videomode* (*get_preferred_mode)
+ (struct via_aux_drv *drv);
+};
+
+
+struct via_aux_bus *via_aux_probe(struct i2c_adapter *adap);
+void via_aux_free(struct via_aux_bus *bus);
+const struct fb_videomode *via_aux_get_preferred_mode(struct via_aux_bus *bus);
+
+
+static inline bool via_aux_add(struct via_aux_drv *drv)
+{
+ struct via_aux_drv *data = kmalloc(sizeof(*data), GFP_KERNEL);
+
+ if (!data)
+ return false;
+
+ *data = *drv;
+ list_add_tail(&data->chain, &data->bus->drivers);
+ return true;
+}
+
+static inline bool via_aux_read(struct via_aux_drv *drv, u8 start, u8 *buf,
+ u8 len)
+{
+ struct i2c_msg msg[2] = {
+ {.addr = drv->addr, .flags = 0, .len = 1, .buf = &start},
+ {.addr = drv->addr, .flags = I2C_M_RD, .len = len, .buf = buf} };
+
+ return i2c_transfer(drv->bus->adap, msg, 2) == 2;
+}
+
+
+/* probe functions of existing drivers - should only be called in via_aux.c */
+void via_aux_ch7301_probe(struct via_aux_bus *bus);
+void via_aux_edid_probe(struct via_aux_bus *bus);
+void via_aux_sii164_probe(struct via_aux_bus *bus);
+void via_aux_vt1636_probe(struct via_aux_bus *bus);
+void via_aux_vt1632_probe(struct via_aux_bus *bus);
+void via_aux_vt1631_probe(struct via_aux_bus *bus);
+void via_aux_vt1625_probe(struct via_aux_bus *bus);
+void via_aux_vt1622_probe(struct via_aux_bus *bus);
+void via_aux_vt1621_probe(struct via_aux_bus *bus);
+
+
+#endif /* __VIA_AUX_H__ */
diff --git a/drivers/video/via/via_aux_ch7301.c b/drivers/video/via/via_aux_ch7301.c
new file mode 100644
index 000000000000..1cbe5037a6b0
--- /dev/null
+++ b/drivers/video/via/via_aux_ch7301.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for Chrontel CH7301 DVI Transmitter
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "CH7301 DVI Transmitter";
+
+
+static void probe(struct via_aux_bus *bus, u8 addr)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = addr,
+ .name = name};
+ u8 tmp;
+
+ if (!via_aux_read(&drv, 0x4B, &tmp, 1) || tmp != 0x17)
+ return;
+
+ printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr);
+ via_aux_add(&drv);
+}
+
+void via_aux_ch7301_probe(struct via_aux_bus *bus)
+{
+ probe(bus, 0x75);
+ probe(bus, 0x76);
+}
diff --git a/drivers/video/via/via_aux_edid.c b/drivers/video/via/via_aux_edid.c
new file mode 100644
index 000000000000..754d4509033f
--- /dev/null
+++ b/drivers/video/via/via_aux_edid.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * generic EDID driver
+ */
+
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include "via_aux.h"
+#include "../edid.h"
+
+
+static const char *name = "EDID";
+
+
+static void query_edid(struct via_aux_drv *drv)
+{
+ struct fb_monspecs *spec = drv->data;
+ unsigned char edid[EDID_LENGTH];
+ bool valid = false;
+
+ if (spec) {
+ fb_destroy_modedb(spec->modedb);
+ } else {
+ spec = kmalloc(sizeof(*spec), GFP_KERNEL);
+ if (!spec)
+ return;
+ }
+
+ spec->version = spec->revision = 0;
+ if (via_aux_read(drv, 0x00, edid, EDID_LENGTH)) {
+ fb_edid_to_monspecs(edid, spec);
+ valid = spec->version || spec->revision;
+ }
+
+ if (!valid) {
+ kfree(spec);
+ spec = NULL;
+ } else
+ printk(KERN_DEBUG "EDID: %s %s\n", spec->manufacturer, spec->monitor);
+
+ drv->data = spec;
+}
+
+static const struct fb_videomode *get_preferred_mode(struct via_aux_drv *drv)
+{
+ struct fb_monspecs *spec = drv->data;
+ int i;
+
+ if (!spec || !spec->modedb || !(spec->misc & FB_MISC_1ST_DETAIL))
+ return NULL;
+
+ for (i = 0; i < spec->modedb_len; i++) {
+ if (spec->modedb[i].flag & FB_MODE_IS_FIRST &&
+ spec->modedb[i].flag & FB_MODE_IS_DETAILED)
+ return &spec->modedb[i];
+ }
+
+ return NULL;
+}
+
+static void cleanup(struct via_aux_drv *drv)
+{
+ struct fb_monspecs *spec = drv->data;
+
+ if (spec)
+ fb_destroy_modedb(spec->modedb);
+}
+
+void via_aux_edid_probe(struct via_aux_bus *bus)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = 0x50,
+ .name = name,
+ .cleanup = cleanup,
+ .get_preferred_mode = get_preferred_mode};
+
+ query_edid(&drv);
+
+ /* as EDID devices can be connected/disconnected just add the driver */
+ via_aux_add(&drv);
+}
diff --git a/drivers/video/via/via_aux_sii164.c b/drivers/video/via/via_aux_sii164.c
new file mode 100644
index 000000000000..ca1b35f033b1
--- /dev/null
+++ b/drivers/video/via/via_aux_sii164.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for Silicon Image SiI 164 PanelLink Transmitter
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "SiI 164 PanelLink Transmitter";
+
+
+static void probe(struct via_aux_bus *bus, u8 addr)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = addr,
+ .name = name};
+ /* check vendor id and device id */
+ const u8 id[] = {0x01, 0x00, 0x06, 0x00}, len = ARRAY_SIZE(id);
+ u8 tmp[len];
+
+ if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
+ return;
+
+ printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr);
+ via_aux_add(&drv);
+}
+
+void via_aux_sii164_probe(struct via_aux_bus *bus)
+{
+ u8 i;
+
+ for (i = 0x38; i <= 0x3F; i++)
+ probe(bus, i);
+}
diff --git a/drivers/video/via/via_aux_vt1621.c b/drivers/video/via/via_aux_vt1621.c
new file mode 100644
index 000000000000..38eca8479898
--- /dev/null
+++ b/drivers/video/via/via_aux_vt1621.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for VIA VT1621(M) TV Encoder
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "VT1621(M) TV Encoder";
+
+
+void via_aux_vt1621_probe(struct via_aux_bus *bus)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = 0x20,
+ .name = name};
+ u8 tmp;
+
+ if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x02)
+ return;
+
+ printk(KERN_INFO "viafb: Found %s\n", name);
+ via_aux_add(&drv);
+}
diff --git a/drivers/video/via/via_aux_vt1622.c b/drivers/video/via/via_aux_vt1622.c
new file mode 100644
index 000000000000..8c79c68ba683
--- /dev/null
+++ b/drivers/video/via/via_aux_vt1622.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for VIA VT1622(M) Digital TV Encoder
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "VT1622(M) Digital TV Encoder";
+
+
+static void probe(struct via_aux_bus *bus, u8 addr)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = addr,
+ .name = name};
+ u8 tmp;
+
+ if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x03)
+ return;
+
+ printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr);
+ via_aux_add(&drv);
+}
+
+void via_aux_vt1622_probe(struct via_aux_bus *bus)
+{
+ probe(bus, 0x20);
+ probe(bus, 0x21);
+}
diff --git a/drivers/video/via/via_aux_vt1625.c b/drivers/video/via/via_aux_vt1625.c
new file mode 100644
index 000000000000..03eb30165d36
--- /dev/null
+++ b/drivers/video/via/via_aux_vt1625.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for VIA VT1625(M) HDTV Encoder
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "VT1625(M) HDTV Encoder";
+
+
+static void probe(struct via_aux_bus *bus, u8 addr)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = addr,
+ .name = name};
+ u8 tmp;
+
+ if (!via_aux_read(&drv, 0x1B, &tmp, 1) || tmp != 0x50)
+ return;
+
+ printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr);
+ via_aux_add(&drv);
+}
+
+void via_aux_vt1625_probe(struct via_aux_bus *bus)
+{
+ probe(bus, 0x20);
+ probe(bus, 0x21);
+}
diff --git a/drivers/video/via/via_aux_vt1631.c b/drivers/video/via/via_aux_vt1631.c
new file mode 100644
index 000000000000..06e742f1f723
--- /dev/null
+++ b/drivers/video/via/via_aux_vt1631.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for VIA VT1631 LVDS Transmitter
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "VT1631 LVDS Transmitter";
+
+
+void via_aux_vt1631_probe(struct via_aux_bus *bus)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = 0x38,
+ .name = name};
+ /* check vendor id and device id */
+ const u8 id[] = {0x06, 0x11, 0x91, 0x31}, len = ARRAY_SIZE(id);
+ u8 tmp[len];
+
+ if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
+ return;
+
+ printk(KERN_INFO "viafb: Found %s\n", name);
+ via_aux_add(&drv);
+}
diff --git a/drivers/video/via/via_aux_vt1632.c b/drivers/video/via/via_aux_vt1632.c
new file mode 100644
index 000000000000..d24f4cd97401
--- /dev/null
+++ b/drivers/video/via/via_aux_vt1632.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for VIA VT1632 DVI Transmitter
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "VT1632 DVI Transmitter";
+
+
+static void probe(struct via_aux_bus *bus, u8 addr)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = addr,
+ .name = name};
+ /* check vendor id and device id */
+ const u8 id[] = {0x06, 0x11, 0x92, 0x31}, len = ARRAY_SIZE(id);
+ u8 tmp[len];
+
+ if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
+ return;
+
+ printk(KERN_INFO "viafb: Found %s at address 0x%x\n", name, addr);
+ via_aux_add(&drv);
+}
+
+void via_aux_vt1632_probe(struct via_aux_bus *bus)
+{
+ u8 i;
+
+ for (i = 0x08; i <= 0x0F; i++)
+ probe(bus, i);
+}
diff --git a/drivers/video/via/via_aux_vt1636.c b/drivers/video/via/via_aux_vt1636.c
new file mode 100644
index 000000000000..9e015c101d4d
--- /dev/null
+++ b/drivers/video/via/via_aux_vt1636.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2011 Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation;
+ * either version 2, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTIES OR REPRESENTATIONS; 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.
+ */
+/*
+ * driver for VIA VT1636 LVDS Transmitter
+ */
+
+#include <linux/slab.h>
+#include "via_aux.h"
+
+
+static const char *name = "VT1636 LVDS Transmitter";
+
+
+void via_aux_vt1636_probe(struct via_aux_bus *bus)
+{
+ struct via_aux_drv drv = {
+ .bus = bus,
+ .addr = 0x40,
+ .name = name};
+ /* check vendor id and device id */
+ const u8 id[] = {0x06, 0x11, 0x45, 0x33}, len = ARRAY_SIZE(id);
+ u8 tmp[len];
+
+ if (!via_aux_read(&drv, 0x00, tmp, len) || memcmp(id, tmp, len))
+ return;
+
+ printk(KERN_INFO "viafb: Found %s\n", name);
+ via_aux_add(&drv);
+}
diff --git a/drivers/video/via/via_i2c.c b/drivers/video/via/via_i2c.c
index 78f1405dbab7..dd53058bbbb7 100644
--- a/drivers/video/via/via_i2c.c
+++ b/drivers/video/via/via_i2c.c
@@ -51,7 +51,7 @@ static void via_i2c_setscl(void *data, int state)
val |= 0x01;
break;
case VIA_PORT_GPIO:
- val |= 0x80;
+ val |= 0x82;
break;
default:
printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n");
@@ -67,6 +67,9 @@ static int via_i2c_getscl(void *data)
int ret = 0;
spin_lock_irqsave(&i2c_vdev->reg_lock, flags);
+ if (adap_data->type == VIA_PORT_GPIO)
+ via_write_reg_mask(adap_data->io_port, adap_data->ioport_index,
+ 0, 0x80);
if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x08)
ret = 1;
spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags);
@@ -80,6 +83,9 @@ static int via_i2c_getsda(void *data)
int ret = 0;
spin_lock_irqsave(&i2c_vdev->reg_lock, flags);
+ if (adap_data->type == VIA_PORT_GPIO)
+ via_write_reg_mask(adap_data->io_port, adap_data->ioport_index,
+ 0, 0x40);
if (via_read_reg(adap_data->io_port, adap_data->ioport_index) & 0x04)
ret = 1;
spin_unlock_irqrestore(&i2c_vdev->reg_lock, flags);
@@ -103,7 +109,7 @@ static void via_i2c_setsda(void *data, int state)
val |= 0x01;
break;
case VIA_PORT_GPIO:
- val |= 0x40;
+ val |= 0x42;
break;
default:
printk(KERN_ERR "viafb_i2c: specify wrong i2c type.\n");
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c
index a13c258bd32f..0c8837565bc7 100644
--- a/drivers/video/via/viafbdev.c
+++ b/drivers/video/via/viafbdev.c
@@ -24,6 +24,7 @@
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/via-core.h>
+#include <linux/via_i2c.h>
#include <asm/olpc.h>
#define _MASTER_FILE
@@ -286,26 +287,22 @@ static int viafb_set_par(struct fb_info *info)
viafb_second_yres, viafb_bpp1, 1);
}
- refresh = viafb_get_refresh(info->var.xres, info->var.yres,
- get_var_refresh(&info->var));
- if (viafb_get_best_mode(viafbinfo->var.xres, viafbinfo->var.yres,
- refresh)) {
- if (viafb_dual_fb && viapar->iga_path == IGA2) {
- viafb_bpp1 = info->var.bits_per_pixel;
- viafb_refresh1 = refresh;
- } else {
- viafb_bpp = info->var.bits_per_pixel;
- viafb_refresh = refresh;
- }
-
- if (info->var.accel_flags & FB_ACCELF_TEXT)
- info->flags &= ~FBINFO_HWACCEL_DISABLED;
- else
- info->flags |= FBINFO_HWACCEL_DISABLED;
- viafb_setmode(info->var.bits_per_pixel, viafb_bpp1);
- viafb_pan_display(&info->var, info);
+ refresh = get_var_refresh(&info->var);
+ if (viafb_dual_fb && viapar->iga_path == IGA2) {
+ viafb_bpp1 = info->var.bits_per_pixel;
+ viafb_refresh1 = refresh;
+ } else {
+ viafb_bpp = info->var.bits_per_pixel;
+ viafb_refresh = refresh;
}
+ if (info->var.accel_flags & FB_ACCELF_TEXT)
+ info->flags &= ~FBINFO_HWACCEL_DISABLED;
+ else
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+ viafb_setmode();
+ viafb_pan_display(&info->var, info);
+
return 0;
}
@@ -1670,12 +1667,23 @@ static void viafb_remove_proc(struct viafb_shared *shared)
}
#undef IS_VT1636
-static int parse_mode(const char *str, u32 *xres, u32 *yres)
+static int parse_mode(const char *str, u32 devices, u32 *xres, u32 *yres)
{
+ const struct fb_videomode *mode = NULL;
char *ptr;
if (!str) {
- if (machine_is_olpc()) {
+ if (devices == VIA_CRT)
+ mode = via_aux_get_preferred_mode(
+ viaparinfo->shared->i2c_26);
+ else if (devices == VIA_DVP1)
+ mode = via_aux_get_preferred_mode(
+ viaparinfo->shared->i2c_31);
+
+ if (mode) {
+ *xres = mode->xres;
+ *yres = mode->yres;
+ } else if (machine_is_olpc()) {
*xres = 1200;
*yres = 900;
} else {
@@ -1729,6 +1737,31 @@ static struct viafb_pm_hooks viafb_fb_pm_hooks = {
#endif
+static void __devinit i2c_bus_probe(struct viafb_shared *shared)
+{
+ /* should be always CRT */
+ printk(KERN_INFO "viafb: Probing I2C bus 0x26\n");
+ shared->i2c_26 = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_26));
+
+ /* seems to be usually DVP1 */
+ printk(KERN_INFO "viafb: Probing I2C bus 0x31\n");
+ shared->i2c_31 = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_31));
+
+ /* FIXME: what is this? */
+ if (!machine_is_olpc()) {
+ printk(KERN_INFO "viafb: Probing I2C bus 0x2C\n");
+ shared->i2c_2C = via_aux_probe(viafb_find_i2c_adapter(VIA_PORT_2C));
+ }
+
+ printk(KERN_INFO "viafb: Finished I2C bus probing");
+}
+
+static void i2c_bus_free(struct viafb_shared *shared)
+{
+ via_aux_free(shared->i2c_26);
+ via_aux_free(shared->i2c_31);
+ via_aux_free(shared->i2c_2C);
+}
int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
{
@@ -1762,6 +1795,7 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
&viaparinfo->shared->lvds_setting_info2;
viaparinfo->chip_info = &viaparinfo->shared->chip_info;
+ i2c_bus_probe(viaparinfo->shared);
if (viafb_dual_fb)
viafb_SAMM_ON = 1;
parse_lcd_port();
@@ -1804,10 +1838,11 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
viafb_second_size * 1024 * 1024;
}
- parse_mode(viafb_mode, &default_xres, &default_yres);
+ parse_mode(viafb_mode, viaparinfo->shared->iga1_devices,
+ &default_xres, &default_yres);
if (viafb_SAMM_ON == 1)
- parse_mode(viafb_mode1, &viafb_second_xres,
- &viafb_second_yres);
+ parse_mode(viafb_mode1, viaparinfo->shared->iga2_devices,
+ &viafb_second_xres, &viafb_second_yres);
default_var.xres = default_xres;
default_var.yres = default_yres;
@@ -1915,6 +1950,7 @@ out_fb1_release:
if (viafbinfo1)
framebuffer_release(viafbinfo1);
out_fb_release:
+ i2c_bus_free(viaparinfo->shared);
framebuffer_release(viafbinfo);
return rc;
}
@@ -1927,6 +1963,7 @@ void __devexit via_fb_pci_remove(struct pci_dev *pdev)
if (viafb_dual_fb)
unregister_framebuffer(viafbinfo1);
viafb_remove_proc(viaparinfo->shared);
+ i2c_bus_free(viaparinfo->shared);
framebuffer_release(viafbinfo);
if (viafb_dual_fb)
framebuffer_release(viafbinfo1);
@@ -2033,9 +2070,9 @@ int __init viafb_init(void)
if (r < 0)
return r;
#endif
- if (parse_mode(viafb_mode, &dummy_x, &dummy_y)
+ if (parse_mode(viafb_mode, 0, &dummy_x, &dummy_y)
|| !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh)
- || parse_mode(viafb_mode1, &dummy_x, &dummy_y)
+ || parse_mode(viafb_mode1, 0, &dummy_x, &dummy_y)
|| !viafb_get_best_mode(dummy_x, dummy_y, viafb_refresh1)
|| viafb_bpp < 0 || viafb_bpp > 32
|| viafb_bpp1 < 0 || viafb_bpp1 > 32
diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h
index d9440635d1d4..f6b2ddf56e94 100644
--- a/drivers/video/via/viafbdev.h
+++ b/drivers/video/via/viafbdev.h
@@ -26,6 +26,7 @@
#include <linux/fb.h>
#include <linux/spinlock.h>
+#include "via_aux.h"
#include "ioctl.h"
#include "share.h"
#include "chip.h"
@@ -48,6 +49,11 @@ struct viafb_shared {
struct proc_dir_entry *iga2_proc_entry;
struct viafb_dev *vdev; /* Global dev info */
+ /* I2C busses that may have auxiliary devices */
+ struct via_aux_bus *i2c_26;
+ struct via_aux_bus *i2c_31;
+ struct via_aux_bus *i2c_2C;
+
/* All the information will be needed to set engine */
struct tmds_setting_information tmds_setting_info;
struct lvds_setting_information lvds_setting_info;
diff --git a/drivers/video/via/viamode.c b/drivers/video/via/viamode.c
index 0911cac1b2ff..0666ab01cf4a 100644
--- a/drivers/video/via/viamode.c
+++ b/drivers/video/via/viamode.c
@@ -268,591 +268,78 @@ struct VPITTable VPIT = {
/* Mode Table */
/********************/
-/* 480x640 */
-static struct crt_mode_table CRTM480x640[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M480X640_R60_HSP, M480X640_R60_VSP,
- {624, 480, 480, 144, 504, 48, 663, 640, 640, 23, 641, 3} } /* GTF*/
-};
-
-/* 640x480*/
-static struct crt_mode_table CRTM640x480[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M640X480_R60_HSP, M640X480_R60_VSP,
- {800, 640, 640, 160, 656, 96, 525, 480, 480, 45, 490, 2} },
- {REFRESH_75, M640X480_R75_HSP, M640X480_R75_VSP,
- {840, 640, 640, 200, 656, 64, 500, 480, 480, 20, 481, 3} },
- {REFRESH_85, M640X480_R85_HSP, M640X480_R85_VSP,
- {832, 640, 640, 192, 696, 56, 509, 480, 480, 29, 481, 3} },
- {REFRESH_100, M640X480_R100_HSP, M640X480_R100_VSP,
- {848, 640, 640, 208, 680, 64, 509, 480, 480, 29, 481, 3} }, /*GTF*/
- {REFRESH_120, M640X480_R120_HSP, M640X480_R120_VSP,
- {848, 640, 640, 208, 680, 64, 515, 480, 480, 35, 481, 3} } /*GTF*/
-};
-
-/*720x480 (GTF)*/
-static struct crt_mode_table CRTM720x480[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M720X480_R60_HSP, M720X480_R60_VSP,
- {896, 720, 720, 176, 736, 72, 497, 480, 480, 17, 481, 3} }
-
-};
-
-/*720x576 (GTF)*/
-static struct crt_mode_table CRTM720x576[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M720X576_R60_HSP, M720X576_R60_VSP,
- {912, 720, 720, 192, 744, 72, 597, 576, 576, 21, 577, 3} }
-};
-
-/* 800x480 (CVT) */
-static struct crt_mode_table CRTM800x480[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M800X480_R60_HSP, M800X480_R60_VSP,
- {992, 800, 800, 192, 824, 72, 500, 480, 480, 20, 483, 7} }
-};
-
-/* 800x600*/
-static struct crt_mode_table CRTM800x600[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M800X600_R60_HSP, M800X600_R60_VSP,
- {1056, 800, 800, 256, 840, 128, 628, 600, 600, 28, 601, 4} },
- {REFRESH_75, M800X600_R75_HSP, M800X600_R75_VSP,
- {1056, 800, 800, 256, 816, 80, 625, 600, 600, 25, 601, 3} },
- {REFRESH_85, M800X600_R85_HSP, M800X600_R85_VSP,
- {1048, 800, 800, 248, 832, 64, 631, 600, 600, 31, 601, 3} },
- {REFRESH_100, M800X600_R100_HSP, M800X600_R100_VSP,
- {1072, 800, 800, 272, 848, 88, 636, 600, 600, 36, 601, 3} },
- {REFRESH_120, M800X600_R120_HSP, M800X600_R120_VSP,
- {1088, 800, 800, 288, 856, 88, 643, 600, 600, 43, 601, 3} }
-};
-
-/* 848x480 (CVT) */
-static struct crt_mode_table CRTM848x480[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M848X480_R60_HSP, M848X480_R60_VSP,
- {1056, 848, 848, 208, 872, 80, 500, 480, 480, 20, 483, 5} }
-};
-
-/*856x480 (GTF) convert to 852x480*/
-static struct crt_mode_table CRTM852x480[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M852X480_R60_HSP, M852X480_R60_VSP,
- {1064, 856, 856, 208, 872, 88, 497, 480, 480, 17, 481, 3} }
-};
-
-/*1024x512 (GTF)*/
-static struct crt_mode_table CRTM1024x512[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1024X512_R60_HSP, M1024X512_R60_VSP,
- {1296, 1024, 1024, 272, 1056, 104, 531, 512, 512, 19, 513, 3} }
-
-};
-
-/* 1024x600*/
-static struct crt_mode_table CRTM1024x600[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1024X600_R60_HSP, M1024X600_R60_VSP,
- {1312, 1024, 1024, 288, 1064, 104, 622, 600, 600, 22, 601, 3} },
-};
-
-/* 1024x768*/
-static struct crt_mode_table CRTM1024x768[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1024X768_R60_HSP, M1024X768_R60_VSP,
- {1344, 1024, 1024, 320, 1048, 136, 806, 768, 768, 38, 771, 6} },
- {REFRESH_75, M1024X768_R75_HSP, M1024X768_R75_VSP,
- {1312, 1024, 1024, 288, 1040, 96, 800, 768, 768, 32, 769, 3} },
- {REFRESH_85, M1024X768_R85_HSP, M1024X768_R85_VSP,
- {1376, 1024, 1024, 352, 1072, 96, 808, 768, 768, 40, 769, 3} },
- {REFRESH_100, M1024X768_R100_HSP, M1024X768_R100_VSP,
- {1392, 1024, 1024, 368, 1096, 112, 814, 768, 768, 46, 769, 3} }
-};
-
-/* 1152x864*/
-static struct crt_mode_table CRTM1152x864[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_75, M1152X864_R75_HSP, M1152X864_R75_VSP,
- {1600, 1152, 1152, 448, 1216, 128, 900, 864, 864, 36, 865, 3} }
-
-};
-
-/* 1280x720 (HDMI 720P)*/
-static struct crt_mode_table CRTM1280x720[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1280X720_R60_HSP, M1280X720_R60_VSP,
- {1648, 1280, 1280, 368, 1392, 40, 750, 720, 720, 30, 725, 5} },
- {REFRESH_50, M1280X720_R50_HSP, M1280X720_R50_VSP,
- {1632, 1280, 1280, 352, 1328, 128, 741, 720, 720, 21, 721, 3} }
-};
-
-/*1280x768 (GTF)*/
-static struct crt_mode_table CRTM1280x768[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1280X768_R60_HSP, M1280X768_R60_VSP,
- {1680, 1280, 1280, 400, 1344, 136, 795, 768, 768, 27, 769, 3} },
- {REFRESH_50, M1280X768_R50_HSP, M1280X768_R50_VSP,
- {1648, 1280, 1280, 368, 1336, 128, 791, 768, 768, 23, 769, 3} }
-};
-
-/* 1280x800 (CVT) */
-static struct crt_mode_table CRTM1280x800[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1280X800_R60_HSP, M1280X800_R60_VSP,
- {1680, 1280, 1280, 400, 1352, 128, 831, 800, 800, 31, 803, 6} }
-};
-
-/*1280x960*/
-static struct crt_mode_table CRTM1280x960[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1280X960_R60_HSP, M1280X960_R60_VSP,
- {1800, 1280, 1280, 520, 1376, 112, 1000, 960, 960, 40, 961, 3} }
-};
-
-/* 1280x1024*/
-static struct crt_mode_table CRTM1280x1024[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1280X1024_R60_HSP, M1280X1024_R60_VSP,
- {1688, 1280, 1280, 408, 1328, 112, 1066, 1024, 1024, 42, 1025,
- 3} },
- {REFRESH_75, M1280X1024_R75_HSP, M1280X1024_R75_VSP,
- {1688, 1280, 1280, 408, 1296, 144, 1066, 1024, 1024, 42, 1025,
- 3} },
- {REFRESH_85, M1280X1024_R85_HSP, M1280X1024_R85_VSP,
- {1728, 1280, 1280, 448, 1344, 160, 1072, 1024, 1024, 48, 1025, 3} }
-};
-
-/* 1368x768 (GTF) */
-static struct crt_mode_table CRTM1368x768[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1368X768_R60_HSP, M1368X768_R60_VSP,
- {1800, 1368, 1368, 432, 1440, 144, 795, 768, 768, 27, 769, 3} }
-};
-
-/*1440x1050 (GTF)*/
-static struct crt_mode_table CRTM1440x1050[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1440X1050_R60_HSP, M1440X1050_R60_VSP,
- {1936, 1440, 1440, 496, 1536, 152, 1077, 1040, 1040, 37, 1041, 3} }
-};
-
-/* 1600x1200*/
-static struct crt_mode_table CRTM1600x1200[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1600X1200_R60_HSP, M1600X1200_R60_VSP,
- {2160, 1600, 1600, 560, 1664, 192, 1250, 1200, 1200, 50, 1201,
- 3} },
- {REFRESH_75, M1600X1200_R75_HSP, M1600X1200_R75_VSP,
- {2160, 1600, 1600, 560, 1664, 192, 1250, 1200, 1200, 50, 1201, 3} }
-
-};
-
-/* 1680x1050 (CVT) */
-static struct crt_mode_table CRTM1680x1050[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1680x1050_R60_HSP, M1680x1050_R60_VSP,
- {2240, 1680, 1680, 560, 1784, 176, 1089, 1050, 1050, 39, 1053,
- 6} },
- {REFRESH_75, M1680x1050_R75_HSP, M1680x1050_R75_VSP,
- {2272, 1680, 1680, 592, 1800, 176, 1099, 1050, 1050, 49, 1053, 6} }
-};
-
-/* 1680x1050 (CVT Reduce Blanking) */
-static struct crt_mode_table CRTM1680x1050_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1680x1050_RB_R60_HSP, M1680x1050_RB_R60_VSP,
- {1840, 1680, 1680, 160, 1728, 32, 1080, 1050, 1050, 30, 1053, 6} }
-};
-
-/* 1920x1080 (CVT)*/
-static struct crt_mode_table CRTM1920x1080[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1920X1080_R60_HSP, M1920X1080_R60_VSP,
- {2576, 1920, 1920, 656, 2048, 200, 1120, 1080, 1080, 40, 1083, 5} }
-};
-
-/* 1920x1080 (CVT with Reduce Blanking) */
-static struct crt_mode_table CRTM1920x1080_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1920X1080_RB_R60_HSP, M1920X1080_RB_R60_VSP,
- {2080, 1920, 1920, 160, 1968, 32, 1111, 1080, 1080, 31, 1083, 5} }
-};
-
-/* 1920x1440*/
-static struct crt_mode_table CRTM1920x1440[] = {
- /*r_rate,hsp,vsp */
- /*HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1920X1440_R60_HSP, M1920X1440_R60_VSP,
- {2600, 1920, 1920, 680, 2048, 208, 1500, 1440, 1440, 60, 1441,
- 3} },
- {REFRESH_75, M1920X1440_R75_HSP, M1920X1440_R75_VSP,
- {2640, 1920, 1920, 720, 2064, 224, 1500, 1440, 1440, 60, 1441, 3} }
-};
-
-/* 1400x1050 (CVT) */
-static struct crt_mode_table CRTM1400x1050[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1400X1050_R60_HSP, M1400X1050_R60_VSP,
- {1864, 1400, 1400, 464, 1488, 144, 1089, 1050, 1050, 39, 1053,
- 4} },
- {REFRESH_75, M1400X1050_R75_HSP, M1400X1050_R75_VSP,
- {1896, 1400, 1400, 496, 1504, 144, 1099, 1050, 1050, 49, 1053, 4} }
-};
-
-/* 1400x1050 (CVT Reduce Blanking) */
-static struct crt_mode_table CRTM1400x1050_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1400X1050_RB_R60_HSP, M1400X1050_RB_R60_VSP,
- {1560, 1400, 1400, 160, 1448, 32, 1080, 1050, 1050, 30, 1053, 4} }
-};
-
-/* 960x600 (CVT) */
-static struct crt_mode_table CRTM960x600[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M960X600_R60_HSP, M960X600_R60_VSP,
- {1216, 960, 960, 256, 992, 96, 624, 600, 600, 24, 603, 6} }
-};
-
-/* 1000x600 (GTF) */
-static struct crt_mode_table CRTM1000x600[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1000X600_R60_HSP, M1000X600_R60_VSP,
- {1288, 1000, 1000, 288, 1040, 104, 622, 600, 600, 22, 601, 3} }
-};
-
-/* 1024x576 (GTF) */
-static struct crt_mode_table CRTM1024x576[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1024X576_R60_HSP, M1024X576_R60_VSP,
- {1312, 1024, 1024, 288, 1064, 104, 597, 576, 576, 21, 577, 3} }
-};
-
-/* 1088x612 (CVT) */
-static struct crt_mode_table CRTM1088x612[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1088X612_R60_HSP, M1088X612_R60_VSP,
- {1392, 1088, 1088, 304, 1136, 104, 636, 612, 612, 24, 615, 5} }
-};
-
-/* 1152x720 (CVT) */
-static struct crt_mode_table CRTM1152x720[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1152X720_R60_HSP, M1152X720_R60_VSP,
- {1488, 1152, 1152, 336, 1208, 112, 748, 720, 720, 28, 723, 6} }
-};
-
-/* 1200x720 (GTF) */
-static struct crt_mode_table CRTM1200x720[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1200X720_R60_HSP, M1200X720_R60_VSP,
- {1568, 1200, 1200, 368, 1256, 128, 746, 720, 720, 26, 721, 3} }
-};
-
-/* 1200x900 (DCON) */
-static struct crt_mode_table DCON1200x900[] = {
- /* r_rate, hsp, vsp */
- {REFRESH_49, M1200X900_R60_HSP, M1200X900_R60_VSP,
- /* The correct htotal is 1240, but this doesn't raster on VX855. */
- /* Via suggested changing to a multiple of 16, hence 1264. */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {1264, 1200, 1200, 64, 1211, 32, 912, 900, 900, 12, 901, 10} }
-};
-
-/* 1280x600 (GTF) */
-static struct crt_mode_table CRTM1280x600[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1280x600_R60_HSP, M1280x600_R60_VSP,
- {1648, 1280, 1280, 368, 1336, 128, 622, 600, 600, 22, 601, 3} }
-};
-
-/* 1360x768 (CVT) */
-static struct crt_mode_table CRTM1360x768[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1360X768_R60_HSP, M1360X768_R60_VSP,
- {1776, 1360, 1360, 416, 1432, 136, 798, 768, 768, 30, 771, 5} }
-};
-
-/* 1360x768 (CVT Reduce Blanking) */
-static struct crt_mode_table CRTM1360x768_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1360X768_RB_R60_HSP, M1360X768_RB_R60_VSP,
- {1520, 1360, 1360, 160, 1408, 32, 790, 768, 768, 22, 771, 5} }
-};
-
-/* 1366x768 (GTF) */
-static struct crt_mode_table CRTM1366x768[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1368X768_R60_HSP, M1368X768_R60_VSP,
- {1800, 1368, 1368, 432, 1440, 144, 795, 768, 768, 27, 769, 3} },
- {REFRESH_50, M1368X768_R50_HSP, M1368X768_R50_VSP,
- {1768, 1368, 1368, 400, 1424, 144, 791, 768, 768, 23, 769, 3} }
-};
-
-/* 1440x900 (CVT) */
-static struct crt_mode_table CRTM1440x900[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1440X900_R60_HSP, M1440X900_R60_VSP,
- {1904, 1440, 1440, 464, 1520, 152, 934, 900, 900, 34, 903, 6} },
- {REFRESH_75, M1440X900_R75_HSP, M1440X900_R75_VSP,
- {1936, 1440, 1440, 496, 1536, 152, 942, 900, 900, 42, 903, 6} }
-};
-
-/* 1440x900 (CVT Reduce Blanking) */
-static struct crt_mode_table CRTM1440x900_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1440X900_RB_R60_HSP, M1440X900_RB_R60_VSP,
- {1600, 1440, 1440, 160, 1488, 32, 926, 900, 900, 26, 903, 6} }
-};
-
-/* 1600x900 (CVT) */
-static struct crt_mode_table CRTM1600x900[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1600X900_R60_HSP, M1600X900_R60_VSP,
- {2112, 1600, 1600, 512, 1688, 168, 934, 900, 900, 34, 903, 5} }
-};
-
-/* 1600x900 (CVT Reduce Blanking) */
-static struct crt_mode_table CRTM1600x900_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1600X900_RB_R60_HSP, M1600X900_RB_R60_VSP,
- {1760, 1600, 1600, 160, 1648, 32, 926, 900, 900, 26, 903, 5} }
-};
-
-/* 1600x1024 (GTF) */
-static struct crt_mode_table CRTM1600x1024[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1600X1024_R60_HSP, M1600X1024_R60_VSP,
- {2144, 1600, 1600, 544, 1704, 168, 1060, 1024, 1024, 36, 1025, 3} }
-};
-
-/* 1792x1344 (DMT) */
-static struct crt_mode_table CRTM1792x1344[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1792x1344_R60_HSP, M1792x1344_R60_VSP,
- {2448, 1792, 1792, 656, 1920, 200, 1394, 1344, 1344, 50, 1345, 3} }
-};
-
-/* 1856x1392 (DMT) */
-static struct crt_mode_table CRTM1856x1392[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1856x1392_R60_HSP, M1856x1392_R60_VSP,
- {2528, 1856, 1856, 672, 1952, 224, 1439, 1392, 1392, 47, 1393, 3} }
-};
-
-/* 1920x1200 (CVT) */
-static struct crt_mode_table CRTM1920x1200[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1920X1200_R60_HSP, M1920X1200_R60_VSP,
- {2592, 1920, 1920, 672, 2056, 200, 1245, 1200, 1200, 45, 1203, 6} }
-};
-
-/* 1920x1200 (CVT with Reduce Blanking) */
-static struct crt_mode_table CRTM1920x1200_RB[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M1920X1200_RB_R60_HSP, M1920X1200_RB_R60_VSP,
- {2080, 1920, 1920, 160, 1968, 32, 1235, 1200, 1200, 35, 1203, 6} }
-};
-
-/* 2048x1536 (CVT) */
-static struct crt_mode_table CRTM2048x1536[] = {
- /* r_rate, hsp, vsp */
- /* HT, HA, HBS, HBE, HSS, HSE, VT, VA, VBS, VBE, VSS, VSE */
- {REFRESH_60, M2048x1536_R60_HSP, M2048x1536_R60_VSP,
- {2800, 2048, 2048, 752, 2200, 224, 1592, 1536, 1536, 56, 1539, 4} }
-};
-
-static struct VideoModeTable viafb_modes[] = {
- /* Display : 480x640 (GTF) */
- {CRTM480x640, ARRAY_SIZE(CRTM480x640)},
-
- /* Display : 640x480 */
- {CRTM640x480, ARRAY_SIZE(CRTM640x480)},
-
- /* Display : 720x480 (GTF) */
- {CRTM720x480, ARRAY_SIZE(CRTM720x480)},
-
- /* Display : 720x576 (GTF) */
- {CRTM720x576, ARRAY_SIZE(CRTM720x576)},
-
- /* Display : 800x600 */
- {CRTM800x600, ARRAY_SIZE(CRTM800x600)},
-
- /* Display : 800x480 (CVT) */
- {CRTM800x480, ARRAY_SIZE(CRTM800x480)},
-
- /* Display : 848x480 (CVT) */
- {CRTM848x480, ARRAY_SIZE(CRTM848x480)},
-
- /* Display : 852x480 (GTF) */
- {CRTM852x480, ARRAY_SIZE(CRTM852x480)},
-
- /* Display : 1024x512 (GTF) */
- {CRTM1024x512, ARRAY_SIZE(CRTM1024x512)},
-
- /* Display : 1024x600 */
- {CRTM1024x600, ARRAY_SIZE(CRTM1024x600)},
-
- /* Display : 1024x768 */
- {CRTM1024x768, ARRAY_SIZE(CRTM1024x768)},
-
- /* Display : 1152x864 */
- {CRTM1152x864, ARRAY_SIZE(CRTM1152x864)},
-
- /* Display : 1280x768 (GTF) */
- {CRTM1280x768, ARRAY_SIZE(CRTM1280x768)},
-
- /* Display : 960x600 (CVT) */
- {CRTM960x600, ARRAY_SIZE(CRTM960x600)},
-
- /* Display : 1000x600 (GTF) */
- {CRTM1000x600, ARRAY_SIZE(CRTM1000x600)},
-
- /* Display : 1024x576 (GTF) */
- {CRTM1024x576, ARRAY_SIZE(CRTM1024x576)},
-
- /* Display : 1088x612 (GTF) */
- {CRTM1088x612, ARRAY_SIZE(CRTM1088x612)},
-
- /* Display : 1152x720 (CVT) */
- {CRTM1152x720, ARRAY_SIZE(CRTM1152x720)},
-
- /* Display : 1200x720 (GTF) */
- {CRTM1200x720, ARRAY_SIZE(CRTM1200x720)},
-
- /* Display : 1200x900 (DCON) */
- {DCON1200x900, ARRAY_SIZE(DCON1200x900)},
-
- /* Display : 1280x600 (GTF) */
- {CRTM1280x600, ARRAY_SIZE(CRTM1280x600)},
-
- /* Display : 1280x800 (CVT) */
- {CRTM1280x800, ARRAY_SIZE(CRTM1280x800)},
-
- /* Display : 1280x960 */
- {CRTM1280x960, ARRAY_SIZE(CRTM1280x960)},
-
- /* Display : 1280x1024 */
- {CRTM1280x1024, ARRAY_SIZE(CRTM1280x1024)},
-
- /* Display : 1360x768 (CVT) */
- {CRTM1360x768, ARRAY_SIZE(CRTM1360x768)},
-
- /* Display : 1366x768 */
- {CRTM1366x768, ARRAY_SIZE(CRTM1366x768)},
-
- /* Display : 1368x768 (GTF) */
- {CRTM1368x768, ARRAY_SIZE(CRTM1368x768)},
-
- /* Display : 1440x900 (CVT) */
- {CRTM1440x900, ARRAY_SIZE(CRTM1440x900)},
-
- /* Display : 1440x1050 (GTF) */
- {CRTM1440x1050, ARRAY_SIZE(CRTM1440x1050)},
-
- /* Display : 1600x900 (CVT) */
- {CRTM1600x900, ARRAY_SIZE(CRTM1600x900)},
-
- /* Display : 1600x1024 (GTF) */
- {CRTM1600x1024, ARRAY_SIZE(CRTM1600x1024)},
-
- /* Display : 1600x1200 */
- {CRTM1600x1200, ARRAY_SIZE(CRTM1600x1200)},
-
- /* Display : 1680x1050 (CVT) */
- {CRTM1680x1050, ARRAY_SIZE(CRTM1680x1050)},
-
- /* Display : 1792x1344 (DMT) */
- {CRTM1792x1344, ARRAY_SIZE(CRTM1792x1344)},
-
- /* Display : 1856x1392 (DMT) */
- {CRTM1856x1392, ARRAY_SIZE(CRTM1856x1392)},
-
- /* Display : 1920x1440 */
- {CRTM1920x1440, ARRAY_SIZE(CRTM1920x1440)},
-
- /* Display : 2048x1536 */
- {CRTM2048x1536, ARRAY_SIZE(CRTM2048x1536)},
-
- /* Display : 1280x720 */
- {CRTM1280x720, ARRAY_SIZE(CRTM1280x720)},
-
- /* Display : 1920x1080 (CVT) */
- {CRTM1920x1080, ARRAY_SIZE(CRTM1920x1080)},
-
- /* Display : 1920x1200 (CVT) */
- {CRTM1920x1200, ARRAY_SIZE(CRTM1920x1200)},
-
- /* Display : 1400x1050 (CVT) */
- {CRTM1400x1050, ARRAY_SIZE(CRTM1400x1050)}
-};
-
-static struct VideoModeTable viafb_rb_modes[] = {
- /* Display : 1360x768 (CVT Reduce Blanking) */
- {CRTM1360x768_RB, ARRAY_SIZE(CRTM1360x768_RB)},
-
- /* Display : 1440x900 (CVT Reduce Blanking) */
- {CRTM1440x900_RB, ARRAY_SIZE(CRTM1440x900_RB)},
-
- /* Display : 1400x1050 (CVT Reduce Blanking) */
- {CRTM1400x1050_RB, ARRAY_SIZE(CRTM1400x1050_RB)},
-
- /* Display : 1600x900 (CVT Reduce Blanking) */
- {CRTM1600x900_RB, ARRAY_SIZE(CRTM1600x900_RB)},
-
- /* Display : 1680x1050 (CVT Reduce Blanking) */
- {CRTM1680x1050_RB, ARRAY_SIZE(CRTM1680x1050_RB)},
-
- /* Display : 1920x1080 (CVT Reduce Blanking) */
- {CRTM1920x1080_RB, ARRAY_SIZE(CRTM1920x1080_RB)},
-
- /* Display : 1920x1200 (CVT Reduce Blanking) */
- {CRTM1920x1200_RB, ARRAY_SIZE(CRTM1920x1200_RB)}
-};
+static const struct fb_videomode viafb_modes[] = {
+ {NULL, 60, 480, 640, 40285, 72, 24, 19, 1, 48, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 640, 480, 39682, 48, 16, 33, 10, 96, 2, 0, 0, 0},
+ {NULL, 75, 640, 480, 31746, 120, 16, 16, 1, 64, 3, 0, 0, 0},
+ {NULL, 85, 640, 480, 27780, 80, 56, 25, 1, 56, 3, 0, 0, 0},
+ {NULL, 100, 640, 480, 23167, 104, 40, 25, 1, 64, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 120, 640, 480, 19081, 104, 40, 31, 1, 64, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 720, 480, 37426, 88, 16, 13, 1, 72, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 720, 576, 30611, 96, 24, 17, 1, 72, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 800, 600, 25131, 88, 40, 23, 1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 800, 600, 20202, 160, 16, 21, 1, 80, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 85, 800, 600, 17790, 152, 32, 27, 1, 64, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 100, 800, 600, 14667, 136, 48, 32, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 120, 800, 600, 11911, 144, 56, 39, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 800, 480, 33602, 96, 24, 10, 3, 72, 7, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 848, 480, 31565, 104, 24, 12, 3, 80, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 856, 480, 31517, 104, 16, 13, 1, 88, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1024, 512, 24218, 136, 32, 15, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1024, 600, 20423, 144, 40, 18, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1024, 768, 15385, 160, 24, 29, 3, 136, 6, 0, 0, 0},
+ {NULL, 75, 1024, 768, 12703, 176, 16, 28, 1, 96, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 85, 1024, 768, 10581, 208, 48, 36, 1, 96, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 100, 1024, 768, 8825, 184, 72, 42, 1, 112, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 1152, 864, 9259, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1280, 768, 12478, 200, 64, 23, 1, 136, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 50, 1280, 768, 15342, 184, 56, 19, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 960, 600, 21964, 128, 32, 15, 3, 96, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1000, 600, 20803, 144, 40, 18, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1024, 576, 21278, 144, 40, 17, 1, 104, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1088, 612, 18825, 152, 48, 16, 3, 104, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1152, 720, 14974, 168, 56, 19, 3, 112, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1200, 720, 14248, 184, 56, 22, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 49, 1200, 900, 17703, 21, 11, 1, 1, 32, 10, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1280, 600, 16259, 184, 56, 18, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1280, 800, 11938, 200, 72, 22, 3, 128, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1280, 960, 9259, 312, 96, 36, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1280, 1024, 9262, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 1280, 1024, 7409, 248, 16, 38, 1, 144, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 85, 1280, 1024, 6351, 224, 64, 44, 1, 160, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1360, 768, 11759, 208, 72, 22, 3, 136, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1368, 768, 11646, 216, 72, 23, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 50, 1368, 768, 14301, 200, 56, 19, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1368, 768, 11646, 216, 72, 23, 1, 144, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1440, 900, 9372, 232, 80, 25, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 1440, 900, 7311, 248, 96, 33, 3, 152, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1440, 1040, 7993, 248, 96, 33, 1, 152, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1600, 900, 8449, 256, 88, 26, 3, 168, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1600, 1024, 7333, 272, 104, 32, 1, 168, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1600, 1200, 6172, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 1600, 1200, 4938, 304, 64, 46, 1, 192, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1680, 1050, 6832, 280, 104, 30, 3, 176, 6, 0, 0, 0},
+ {NULL, 75, 1680, 1050, 5339, 296, 120, 40, 3, 176, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1792, 1344, 4883, 328, 128, 46, 1, 200, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1856, 1392, 4581, 352, 96, 43, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1920, 1440, 4273, 344, 128, 56, 1, 208, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 1920, 1440, 3367, 352, 144, 56, 1, 224, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 2048, 1536, 3738, 376, 152, 49, 3, 224, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1280, 720, 13484, 216, 112, 20, 5, 40, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 50, 1280, 720, 16538, 176, 48, 17, 1, 128, 3, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1920, 1080, 5776, 328, 128, 32, 3, 200, 5, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1920, 1200, 5164, 336, 136, 36, 3, 200, 6, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 60, 1400, 1050, 8210, 232, 88, 32, 3, 144, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0},
+ {NULL, 75, 1400, 1050, 6398, 248, 104, 42, 3, 144, 4, FB_SYNC_VERT_HIGH_ACT, 0, 0} };
+
+static const struct fb_videomode viafb_rb_modes[] = {
+ {NULL, 60, 1360, 768, 13879, 80, 48, 14, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0},
+ {NULL, 60, 1440, 900, 11249, 80, 48, 17, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0},
+ {NULL, 60, 1400, 1050, 9892, 80, 48, 23, 3, 32, 4, FB_SYNC_HOR_HIGH_ACT, 0, 0},
+ {NULL, 60, 1600, 900, 10226, 80, 48, 18, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0},
+ {NULL, 60, 1680, 1050, 8387, 80, 48, 21, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0},
+ {NULL, 60, 1920, 1080, 7212, 80, 48, 23, 3, 32, 5, FB_SYNC_HOR_HIGH_ACT, 0, 0},
+ {NULL, 60, 1920, 1200, 6488, 80, 48, 26, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, 0, 0} };
int NUM_TOTAL_CN400_ModeXregs = ARRAY_SIZE(CN400_ModeXregs);
int NUM_TOTAL_CN700_ModeXregs = ARRAY_SIZE(CN700_ModeXregs);
@@ -863,56 +350,34 @@ int NUM_TOTAL_CLE266_ModeXregs = ARRAY_SIZE(CLE266_ModeXregs);
int NUM_TOTAL_PATCH_MODE = ARRAY_SIZE(res_patch_table);
-static struct VideoModeTable *get_modes(struct VideoModeTable *vmt, int n,
- int hres, int vres)
-{
- int i;
-
- for (i = 0; i < n; i++)
- if (vmt[i].mode_array &&
- vmt[i].crtc[0].crtc.hor_addr == hres &&
- vmt[i].crtc[0].crtc.ver_addr == vres)
- return &viafb_modes[i];
-
- return NULL;
-}
-
-static struct crt_mode_table *get_best_mode(struct VideoModeTable *vmt,
- int refresh)
+static const struct fb_videomode *get_best_mode(
+ const struct fb_videomode *modes, int n,
+ int hres, int vres, int refresh)
{
- struct crt_mode_table *best;
+ const struct fb_videomode *best = NULL;
int i;
- if (!vmt)
- return NULL;
+ for (i = 0; i < n; i++) {
+ if (modes[i].xres != hres || modes[i].yres != vres)
+ continue;
- best = &vmt->crtc[0];
- for (i = 1; i < vmt->mode_array; i++) {
- if (abs(vmt->crtc[i].refresh_rate - refresh)
- < abs(best->refresh_rate - refresh))
- best = &vmt->crtc[i];
+ if (!best || abs(modes[i].refresh - refresh) <
+ abs(best->refresh - refresh))
+ best = &modes[i];
}
return best;
}
-static struct VideoModeTable *viafb_get_mode(int hres, int vres)
-{
- return get_modes(viafb_modes, ARRAY_SIZE(viafb_modes), hres, vres);
-}
-
-struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh)
+const struct fb_videomode *viafb_get_best_mode(int hres, int vres, int refresh)
{
- return get_best_mode(viafb_get_mode(hres, vres), refresh);
+ return get_best_mode(viafb_modes, ARRAY_SIZE(viafb_modes),
+ hres, vres, refresh);
}
-static struct VideoModeTable *viafb_get_rb_mode(int hres, int vres)
-{
- return get_modes(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes), hres,
- vres);
-}
-
-struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh)
+const struct fb_videomode *viafb_get_best_rb_mode(int hres, int vres,
+ int refresh)
{
- return get_best_mode(viafb_get_rb_mode(hres, vres), refresh);
+ return get_best_mode(viafb_rb_modes, ARRAY_SIZE(viafb_rb_modes),
+ hres, vres, refresh);
}
diff --git a/drivers/video/via/viamode.h b/drivers/video/via/viamode.h
index 5917a2b00e1b..dd19106698e7 100644
--- a/drivers/video/via/viamode.h
+++ b/drivers/video/via/viamode.h
@@ -31,11 +31,6 @@ struct VPITTable {
unsigned char AR[StdAR];
};
-struct VideoModeTable {
- struct crt_mode_table *crtc;
- int mode_array;
-};
-
struct patch_table {
int table_length;
struct io_reg *io_reg_table;
@@ -60,7 +55,9 @@ extern struct io_reg PM1024x768[];
extern struct patch_table res_patch_table[];
extern struct VPITTable VPIT;
-struct crt_mode_table *viafb_get_best_mode(int hres, int vres, int refresh);
-struct crt_mode_table *viafb_get_best_rb_mode(int hres, int vres, int refresh);
+const struct fb_videomode *viafb_get_best_mode(int hres, int vres,
+ int refresh);
+const struct fb_videomode *viafb_get_best_rb_mode(int hres, int vres,
+ int refresh);
#endif /* __VIAMODE_H__ */
diff --git a/drivers/virtio/config.c b/drivers/virtio/config.c
index 983d482fba40..f70bcd2ff98f 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/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 7e9e8f4d8f0c..37096246c937 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.
@@ -170,7 +172,7 @@ config HAVE_S3C2410_WATCHDOG
config S3C2410_WATCHDOG
tristate "S3C2410 Watchdog"
- depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG
+ depends on HAVE_S3C2410_WATCHDOG
select WATCHDOG_CORE
help
Watchdog timer block in the Samsung SoCs. This will reboot
@@ -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 b6a2b58cbe64..4397881c83f4 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 4d40965d2c9f..64ae9e9fed94 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 f16dcbd475fb..41b84936a521 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 46f4b85b46de..5eee55012e33 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 502773ad5acd..639ae9a23fbc 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 4ca5d40304b2..2896430ce42c 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 b3046dc4b56c..7ef99a169e3b 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) ")");
@@ -51,7 +53,7 @@ static unsigned long at91wdt_busy;
*/
static inline void at91_wdt_stop(void)
{
- at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN);
+ at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN);
}
/*
@@ -59,9 +61,9 @@ static inline void at91_wdt_stop(void)
*/
static inline void at91_wdt_start(void)
{
- at91_sys_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
+ at91_st_write(AT91_ST_WDMR, AT91_ST_EXTEN | AT91_ST_RSTEN |
(((65536 * wdt_time) >> 8) & AT91_ST_WDV));
- at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
+ at91_st_write(AT91_ST_CR, AT91_ST_WDRST);
}
/*
@@ -69,7 +71,7 @@ static inline void at91_wdt_start(void)
*/
static inline void at91_wdt_reload(void)
{
- at91_sys_write(AT91_ST_CR, AT91_ST_WDRST);
+ at91_st_write(AT91_ST_CR, AT91_ST_WDRST);
}
/* ......................................................................... */
@@ -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 00562566ef5f..05e1be85fdee 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 9db808349f8b..1f9371f49c40 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 5c5f4b14fd05..bc0e91e78e86 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 8dc7de641e26..8379dc32fd90 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 b9fa9b71583a..38bc383e0677 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 7c0fdfca2646..ce0ab4415eff 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 5b89f7d6cd0f..6876430a9f5e 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 251c863d71dd..7e888393de1f 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 1b793dfd868f..95b1b954de1b 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 63d7b58f1c7d..06de1211a444 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 726b7df61fd0..77050037597a 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.
@@ -23,232 +26,156 @@
* - Add a few missing ioctls
*/
+#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>
-#include <mach/hardware.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_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x))
-#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00)
-#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04)
+#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)
{
- __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG);
-}
-
-static void wdt_disable(void)
-{
- __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG);
-}
+ if (time_before(jiffies, next_heartbeat))
+ writel(0x5555, mmio_base + EP93XX_WATCHDOG);
-static inline void wdt_ping(void)
-{
- __raw_writew(0x5555, EP93XX_WDT_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 __init ep93xx_wdt_init(void)
+static int __devinit ep93xx_wdt_probe(struct platform_device *pdev)
{
+ struct resource *res;
+ unsigned long val;
int err;
- err = misc_register(&ep93xx_wdt_miscdev);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENXIO;
- boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0;
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
- printk(KERN_INFO PFX "EP93XX watchdog, driver version "
- WDT_VERSION "%s\n",
- (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08)
- ? " (nCS1 disable detected)" : "");
+ mmio_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!mmio_base)
+ return -ENXIO;
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 void __exit ep93xx_wdt_exit(void)
+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;
}
-module_init(ep93xx_wdt_init);
-module_exit(ep93xx_wdt_exit);
-
-module_param(nowayout, int, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
+static struct platform_driver ep93xx_wdt_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ep93xx-wdt",
+ },
+ .probe = ep93xx_wdt_probe,
+ .remove = __devexit_p(ep93xx_wdt_remove),
+};
-module_param(timeout, int, 0);
-MODULE_PARM_DESC(timeout,
- "Watchdog timeout in seconds. (1<=timeout<=3600, default="
- __MODULE_STRING(WDT_TIMEOUT) ")");
+module_platform_driver(ep93xx_wdt_driver);
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 3946c51099c0..cd31b8a2a729 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 e45ca2b4bfbe..c65b0a5a020c 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 b146082bd85a..17f4cae770c6 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 9b49b125ad5a..dc563b680abd 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 3c166d3f4e55..cbc7ceef2786 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 db45091ef434..738032a36bcf 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 481d1ad43464..2721d29ce243 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 bdf401b240b5..9fecb95645a3 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 0149d8dfc81d..184c0bfc87a4 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 c7481ad51629..bc3fb8fe89ab 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 c44c3334003a..7a2b734fcdc7 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 1475e09f9af2..6d90f7a2ce22 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 1abdc0454c54..9dda2d08af91 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 d2b074a82db6..f3ac608deb6a 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 82fa7a92a8d2..d964faf1a250 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 8d2d8502d3e8..f4cce6d66a55 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 a2d9a1266a23..8a741bcb5124 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 084f71aa855a..3f047a58d3ae 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 4fc2e9ac26f7..5580b4fff7fe 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 17ef300bccc5..978615ef899d 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 51757a520e8f..59e75d9a6b7f 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 d3a63be2e28d..a9593a3a32a0 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 4d43286074aa..663cad86c633 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 1332b838cc58..bf84f788e592 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 af63ecfbfa6f..8f4a74e91619 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 bc820d16699a..37e4b52dbce9 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 20feb4d3d791..40f7bf1f8654 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 82ccd36e2c90..7c741dc987bd 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 97f8a48d8b78..c53d025e70df 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 529085b8b8fb..ea4c7448b754 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 809f41c30c44..6bbb9efc6125 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 7c0d8630e641..461208831428 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 f359ab85c3bc..55d2f66dbeae 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 d19ff5145e82..8285d65cd207 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 4ad78f868515..788aa158e78c 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 e78d89986768..5afb89b48650 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 06f7922606c0..75694cf24f86 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 b8d14f88f0b5..c891399bed6a 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 d8de1ddd176a..7b14d1847927 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 2d22e996e996..7d3d471f810c 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 dfae030a7ef2..6b8432f61d05 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 a7b5ad2a98bd..1b62a7dfcc95 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 bf7bc8aa1c01..547353a50ebb 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 c7e17ceafa6a..49e1b1c2135c 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 404172f02c9b..04e5a6de47d7 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 016245419fad..d54e04df45e4 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>
@@ -66,7 +69,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 +172,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 b01a30e5a663..25c7a3f9652d 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 626d0e8e56c3..63632ec87c7e 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 93ac58953122..719edc8fdeb3 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 514ec23050f7..d4781e05f017 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 eaca366b7234..0c3e9f66ef77 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 d5d399464599..90d5527ca886 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 c01daca8405a..3fb83b0c28c2 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 b2840409ebc7..707e027e5002 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 029467e34636..bd86f32d63ab 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 e67b76c0526c..8ae7c282d465 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 a267dc078daf..93958a7763e6 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 97b8184614ae..6d665f9c1d58 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 bf16ffb4d21e..fe83beb8f1b7 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 87e0527669d8..59108e48ada3 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 eef1524ae52e..bbb170e50055 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,25 +386,16 @@ 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,
.remove = __devexit_p(sp805_wdt_remove),
};
-static int __init sp805_wdt_init(void)
-{
- return amba_driver_register(&sp805_wdt_driver);
-}
-module_init(sp805_wdt_init);
-
-static void __exit sp805_wdt_exit(void)
-{
- amba_driver_unregister(&sp805_wdt_driver);
-}
-module_exit(sp805_wdt_exit);
+module_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 e37d81178b9e..21d96b92bfd7 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 1490293dc7da..8df050d800e6 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 0764c6239b98..249f11305d26 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 9e9ed7bfabcb..98e16373e640 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 8f07dd4bd94a..465e08273c97 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 576a388a1164..92f1326f0cfc 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 af08972de506..cd9f3c1e1af4 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 be9c4d839e15..274be0bfaf24 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 24587d2060c4..7874ae06232b 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 6e6743d1066f..5d2c902825c2 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 c3c3188c34d7..25aba6e00a23 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 cfa1a1518aad..14d768bfa267 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 1199da0f98cf..8558da912c42 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 94ec22b9e66b..0a77655cda60 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 d2ef002be96b..ee4333c01109 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 f55135662d78..5eec74053882 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 a2f01c9f5c34..65a402344933 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 e0fc3baa9197..1c888c7d4cce 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 263c883f0806..b1815c5ed7a7 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 5d7113c7e501..3c76693447fd 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 49bd9d395562..e4a25b51165c 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);
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index a1ced521cf74..94243136f6bf 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -178,4 +178,20 @@ config XEN_PRIVCMD
depends on XEN
default m
+config XEN_ACPI_PROCESSOR
+ tristate "Xen ACPI processor"
+ depends on XEN && X86 && ACPI_PROCESSOR && CPU_FREQ
+ default m
+ help
+ This ACPI processor uploads Power Management information to the Xen hypervisor.
+
+ To do that the driver parses the Power Management data and uploads said
+ information to the Xen hypervisor. Then the Xen hypervisor can select the
+ proper Cx and Pxx states. It also registers itslef as the SMM so that
+ other drivers (such as ACPI cpufreq scaling driver) will not load.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xen_acpi_processor If you do not know what to choose,
+ select M here. If the CPUFREQ drivers are built in, select Y here.
+
endmenu
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index aa31337192cc..9adc5be57b13 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -20,7 +20,7 @@ obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
obj-$(CONFIG_XEN_DOM0) += pci.o
obj-$(CONFIG_XEN_PCIDEV_BACKEND) += xen-pciback/
obj-$(CONFIG_XEN_PRIVCMD) += xen-privcmd.o
-
+obj-$(CONFIG_XEN_ACPI_PROCESSOR) += xen-acpi-processor.o
xen-evtchn-y := evtchn.o
xen-gntdev-y := gntdev.o
xen-gntalloc-y := gntalloc.o
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index e5e5812a1014..4b33acd8ed4e 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -37,6 +37,7 @@
#include <asm/idle.h>
#include <asm/io_apic.h>
#include <asm/sync_bitops.h>
+#include <asm/xen/page.h>
#include <asm/xen/pci.h>
#include <asm/xen/hypercall.h>
#include <asm/xen/hypervisor.h>
@@ -109,6 +110,8 @@ struct irq_info {
#define PIRQ_SHAREABLE (1 << 1)
static int *evtchn_to_irq;
+static unsigned long *pirq_eoi_map;
+static bool (*pirq_needs_eoi)(unsigned irq);
static DEFINE_PER_CPU(unsigned long [NR_EVENT_CHANNELS/BITS_PER_LONG],
cpu_evtchn_mask);
@@ -269,10 +272,14 @@ static unsigned int cpu_from_evtchn(unsigned int evtchn)
return ret;
}
-static bool pirq_needs_eoi(unsigned irq)
+static bool pirq_check_eoi_map(unsigned irq)
{
- struct irq_info *info = info_for_irq(irq);
+ return test_bit(irq, pirq_eoi_map);
+}
+static bool pirq_needs_eoi_flag(unsigned irq)
+{
+ struct irq_info *info = info_for_irq(irq);
BUG_ON(info->type != IRQT_PIRQ);
return info->u.pirq.flags & PIRQ_NEEDS_EOI;
@@ -1768,7 +1775,7 @@ void xen_callback_vector(void) {}
void __init xen_init_IRQ(void)
{
- int i;
+ int i, rc;
evtchn_to_irq = kcalloc(NR_EVENT_CHANNELS, sizeof(*evtchn_to_irq),
GFP_KERNEL);
@@ -1782,6 +1789,8 @@ void __init xen_init_IRQ(void)
for (i = 0; i < NR_EVENT_CHANNELS; i++)
mask_evtchn(i);
+ pirq_needs_eoi = pirq_needs_eoi_flag;
+
if (xen_hvm_domain()) {
xen_callback_vector();
native_init_IRQ();
@@ -1789,8 +1798,19 @@ void __init xen_init_IRQ(void)
* __acpi_register_gsi can point at the right function */
pci_xen_hvm_init();
} else {
+ struct physdev_pirq_eoi_gmfn eoi_gmfn;
+
irq_ctx_init(smp_processor_id());
if (xen_initial_domain())
pci_xen_initial_domain();
+
+ pirq_eoi_map = (void *)__get_free_page(GFP_KERNEL|__GFP_ZERO);
+ eoi_gmfn.gmfn = virt_to_mfn(pirq_eoi_map);
+ rc = HYPERVISOR_physdev_op(PHYSDEVOP_pirq_eoi_gmfn_v2, &eoi_gmfn);
+ if (rc != 0) {
+ free_page((unsigned long) pirq_eoi_map);
+ pirq_eoi_map = NULL;
+ } else
+ pirq_needs_eoi = pirq_check_eoi_map;
}
}
diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c
index 319dd0a94d51..2389e581e23c 100644
--- a/drivers/xen/platform-pci.c
+++ b/drivers/xen/platform-pci.c
@@ -186,11 +186,6 @@ static struct pci_driver platform_driver = {
static int __init platform_pci_module_init(void)
{
- /* no unplug has been done, IGNORE hasn't been specified: just
- * return now */
- if (!xen_platform_pci_unplug)
- return -ENODEV;
-
return pci_register_driver(&platform_driver);
}
diff --git a/drivers/xen/sys-hypervisor.c b/drivers/xen/sys-hypervisor.c
index 1e0fe01eb670..fdb6d229c9bb 100644
--- a/drivers/xen/sys-hypervisor.c
+++ b/drivers/xen/sys-hypervisor.c
@@ -97,7 +97,7 @@ static struct attribute *version_attrs[] = {
NULL
};
-static struct attribute_group version_group = {
+static const struct attribute_group version_group = {
.name = "version",
.attrs = version_attrs,
};
@@ -210,7 +210,7 @@ static struct attribute *xen_compile_attrs[] = {
NULL
};
-static struct attribute_group xen_compilation_group = {
+static const struct attribute_group xen_compilation_group = {
.name = "compilation",
.attrs = xen_compile_attrs,
};
@@ -340,7 +340,7 @@ static struct attribute *xen_properties_attrs[] = {
NULL
};
-static struct attribute_group xen_properties_group = {
+static const struct attribute_group xen_properties_group = {
.name = "properties",
.attrs = xen_properties_attrs,
};
diff --git a/drivers/xen/tmem.c b/drivers/xen/tmem.c
index d369965e8f8a..dcb79521e6c8 100644
--- a/drivers/xen/tmem.c
+++ b/drivers/xen/tmem.c
@@ -9,7 +9,6 @@
#include <linux/types.h>
#include <linux/init.h>
#include <linux/pagemap.h>
-#include <linux/module.h>
#include <linux/cleancache.h>
/* temporary ifdef until include/linux/frontswap.h is upstream */
@@ -128,15 +127,13 @@ static int xen_tmem_flush_object(u32 pool_id, struct tmem_oid oid)
return xen_tmem_op(TMEM_FLUSH_OBJECT, pool_id, oid, 0, 0, 0, 0, 0);
}
-int tmem_enabled __read_mostly;
-EXPORT_SYMBOL(tmem_enabled);
+bool __read_mostly tmem_enabled = false;
static int __init enable_tmem(char *s)
{
- tmem_enabled = 1;
+ tmem_enabled = true;
return 1;
}
-
__setup("tmem", enable_tmem);
#ifdef CONFIG_CLEANCACHE
@@ -229,22 +226,21 @@ static int tmem_cleancache_init_shared_fs(char *uuid, size_t pagesize)
return xen_tmem_new_pool(shared_uuid, TMEM_POOL_SHARED, pagesize);
}
-static int use_cleancache = 1;
+static bool __initdata use_cleancache = true;
static int __init no_cleancache(char *s)
{
- use_cleancache = 0;
+ use_cleancache = false;
return 1;
}
-
__setup("nocleancache", no_cleancache);
-static struct cleancache_ops tmem_cleancache_ops = {
+static struct cleancache_ops __initdata tmem_cleancache_ops = {
.put_page = tmem_cleancache_put_page,
.get_page = tmem_cleancache_get_page,
- .flush_page = tmem_cleancache_flush_page,
- .flush_inode = tmem_cleancache_flush_inode,
- .flush_fs = tmem_cleancache_flush_fs,
+ .invalidate_page = tmem_cleancache_flush_page,
+ .invalidate_inode = tmem_cleancache_flush_inode,
+ .invalidate_fs = tmem_cleancache_flush_fs,
.init_shared_fs = tmem_cleancache_init_shared_fs,
.init_fs = tmem_cleancache_init_fs
};
@@ -356,21 +352,20 @@ static void tmem_frontswap_init(unsigned ignored)
xen_tmem_new_pool(private, TMEM_POOL_PERSIST, PAGE_SIZE);
}
-static int __initdata use_frontswap = 1;
+static bool __initdata use_frontswap = true;
static int __init no_frontswap(char *s)
{
- use_frontswap = 0;
+ use_frontswap = false;
return 1;
}
-
__setup("nofrontswap", no_frontswap);
-static struct frontswap_ops tmem_frontswap_ops = {
+static struct frontswap_ops __initdata tmem_frontswap_ops = {
.put_page = tmem_frontswap_put_page,
.get_page = tmem_frontswap_get_page,
- .flush_page = tmem_frontswap_flush_page,
- .flush_area = tmem_frontswap_flush_area,
+ .invalidate_page = tmem_frontswap_flush_page,
+ .invalidate_area = tmem_frontswap_flush_area,
.init = tmem_frontswap_init
};
#endif
diff --git a/drivers/xen/xen-acpi-processor.c b/drivers/xen/xen-acpi-processor.c
new file mode 100644
index 000000000000..174b5653cd8a
--- /dev/null
+++ b/drivers/xen/xen-acpi-processor.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright 2012 by Oracle Inc
+ * Author: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+ *
+ * This code borrows ideas from https://lkml.org/lkml/2011/11/30/249
+ * so many thanks go to Kevin Tian <kevin.tian@intel.com>
+ * and Yu Ke <ke.yu@intel.com>.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#include <linux/cpumask.h>
+#include <linux/cpufreq.h>
+#include <linux/freezer.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/processor.h>
+
+#include <xen/interface/platform.h>
+#include <asm/xen/hypercall.h>
+
+#define DRV_NAME "xen-acpi-processor: "
+
+static int no_hypercall;
+MODULE_PARM_DESC(off, "Inhibit the hypercall.");
+module_param_named(off, no_hypercall, int, 0400);
+
+/*
+ * Note: Do not convert the acpi_id* below to cpumask_var_t or use cpumask_bit
+ * - as those shrink to nr_cpu_bits (which is dependent on possible_cpu), which
+ * can be less than what we want to put in. Instead use the 'nr_acpi_bits'
+ * which is dynamically computed based on the MADT or x2APIC table.
+ */
+static unsigned int nr_acpi_bits;
+/* Mutex to protect the acpi_ids_done - for CPU hotplug use. */
+static DEFINE_MUTEX(acpi_ids_mutex);
+/* Which ACPI ID we have processed from 'struct acpi_processor'. */
+static unsigned long *acpi_ids_done;
+/* Which ACPI ID exist in the SSDT/DSDT processor definitions. */
+static unsigned long __initdata *acpi_id_present;
+/* And if there is an _CST definition (or a PBLK) for the ACPI IDs */
+static unsigned long __initdata *acpi_id_cst_present;
+
+static int push_cxx_to_hypervisor(struct acpi_processor *_pr)
+{
+ struct xen_platform_op op = {
+ .cmd = XENPF_set_processor_pminfo,
+ .interface_version = XENPF_INTERFACE_VERSION,
+ .u.set_pminfo.id = _pr->acpi_id,
+ .u.set_pminfo.type = XEN_PM_CX,
+ };
+ struct xen_processor_cx *dst_cx, *dst_cx_states = NULL;
+ struct acpi_processor_cx *cx;
+ unsigned int i, ok;
+ int ret = 0;
+
+ dst_cx_states = kcalloc(_pr->power.count,
+ sizeof(struct xen_processor_cx), GFP_KERNEL);
+ if (!dst_cx_states)
+ return -ENOMEM;
+
+ for (ok = 0, i = 1; i <= _pr->power.count; i++) {
+ cx = &_pr->power.states[i];
+ if (!cx->valid)
+ continue;
+
+ dst_cx = &(dst_cx_states[ok++]);
+
+ dst_cx->reg.space_id = ACPI_ADR_SPACE_SYSTEM_IO;
+ if (cx->entry_method == ACPI_CSTATE_SYSTEMIO) {
+ dst_cx->reg.bit_width = 8;
+ dst_cx->reg.bit_offset = 0;
+ dst_cx->reg.access_size = 1;
+ } else {
+ dst_cx->reg.space_id = ACPI_ADR_SPACE_FIXED_HARDWARE;
+ if (cx->entry_method == ACPI_CSTATE_FFH) {
+ /* NATIVE_CSTATE_BEYOND_HALT */
+ dst_cx->reg.bit_offset = 2;
+ dst_cx->reg.bit_width = 1; /* VENDOR_INTEL */
+ }
+ dst_cx->reg.access_size = 0;
+ }
+ dst_cx->reg.address = cx->address;
+
+ dst_cx->type = cx->type;
+ dst_cx->latency = cx->latency;
+ dst_cx->power = cx->power;
+
+ dst_cx->dpcnt = 0;
+ set_xen_guest_handle(dst_cx->dp, NULL);
+ }
+ if (!ok) {
+ pr_debug(DRV_NAME "No _Cx for ACPI CPU %u\n", _pr->acpi_id);
+ kfree(dst_cx_states);
+ return -EINVAL;
+ }
+ op.u.set_pminfo.power.count = ok;
+ op.u.set_pminfo.power.flags.bm_control = _pr->flags.bm_control;
+ op.u.set_pminfo.power.flags.bm_check = _pr->flags.bm_check;
+ op.u.set_pminfo.power.flags.has_cst = _pr->flags.has_cst;
+ op.u.set_pminfo.power.flags.power_setup_done =
+ _pr->flags.power_setup_done;
+
+ set_xen_guest_handle(op.u.set_pminfo.power.states, dst_cx_states);
+
+ if (!no_hypercall)
+ ret = HYPERVISOR_dom0_op(&op);
+
+ if (!ret) {
+ pr_debug("ACPI CPU%u - C-states uploaded.\n", _pr->acpi_id);
+ for (i = 1; i <= _pr->power.count; i++) {
+ cx = &_pr->power.states[i];
+ if (!cx->valid)
+ continue;
+ pr_debug(" C%d: %s %d uS\n",
+ cx->type, cx->desc, (u32)cx->latency);
+ }
+ } else
+ pr_err(DRV_NAME "(CX): Hypervisor error (%d) for ACPI CPU%u\n",
+ ret, _pr->acpi_id);
+
+ kfree(dst_cx_states);
+
+ return ret;
+}
+static struct xen_processor_px *
+xen_copy_pss_data(struct acpi_processor *_pr,
+ struct xen_processor_performance *dst_perf)
+{
+ struct xen_processor_px *dst_states = NULL;
+ unsigned int i;
+
+ BUILD_BUG_ON(sizeof(struct xen_processor_px) !=
+ sizeof(struct acpi_processor_px));
+
+ dst_states = kcalloc(_pr->performance->state_count,
+ sizeof(struct xen_processor_px), GFP_KERNEL);
+ if (!dst_states)
+ return ERR_PTR(-ENOMEM);
+
+ dst_perf->state_count = _pr->performance->state_count;
+ for (i = 0; i < _pr->performance->state_count; i++) {
+ /* Fortunatly for us, they are both the same size */
+ memcpy(&(dst_states[i]), &(_pr->performance->states[i]),
+ sizeof(struct acpi_processor_px));
+ }
+ return dst_states;
+}
+static int xen_copy_psd_data(struct acpi_processor *_pr,
+ struct xen_processor_performance *dst)
+{
+ struct acpi_psd_package *pdomain;
+
+ BUILD_BUG_ON(sizeof(struct xen_psd_package) !=
+ sizeof(struct acpi_psd_package));
+
+ /* This information is enumerated only if acpi_processor_preregister_performance
+ * has been called.
+ */
+ dst->shared_type = _pr->performance->shared_type;
+
+ pdomain = &(_pr->performance->domain_info);
+
+ /* 'acpi_processor_preregister_performance' does not parse if the
+ * num_processors <= 1, but Xen still requires it. Do it manually here.
+ */
+ if (pdomain->num_processors <= 1) {
+ if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ALL)
+ dst->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_HW_ALL)
+ dst->shared_type = CPUFREQ_SHARED_TYPE_HW;
+ else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
+ dst->shared_type = CPUFREQ_SHARED_TYPE_ANY;
+
+ }
+ memcpy(&(dst->domain_info), pdomain, sizeof(struct acpi_psd_package));
+ return 0;
+}
+static int xen_copy_pct_data(struct acpi_pct_register *pct,
+ struct xen_pct_register *dst_pct)
+{
+ /* It would be nice if you could just do 'memcpy(pct, dst_pct') but
+ * sadly the Xen structure did not have the proper padding so the
+ * descriptor field takes two (dst_pct) bytes instead of one (pct).
+ */
+ dst_pct->descriptor = pct->descriptor;
+ dst_pct->length = pct->length;
+ dst_pct->space_id = pct->space_id;
+ dst_pct->bit_width = pct->bit_width;
+ dst_pct->bit_offset = pct->bit_offset;
+ dst_pct->reserved = pct->reserved;
+ dst_pct->address = pct->address;
+ return 0;
+}
+static int push_pxx_to_hypervisor(struct acpi_processor *_pr)
+{
+ int ret = 0;
+ struct xen_platform_op op = {
+ .cmd = XENPF_set_processor_pminfo,
+ .interface_version = XENPF_INTERFACE_VERSION,
+ .u.set_pminfo.id = _pr->acpi_id,
+ .u.set_pminfo.type = XEN_PM_PX,
+ };
+ struct xen_processor_performance *dst_perf;
+ struct xen_processor_px *dst_states = NULL;
+
+ dst_perf = &op.u.set_pminfo.perf;
+
+ dst_perf->platform_limit = _pr->performance_platform_limit;
+ dst_perf->flags |= XEN_PX_PPC;
+ xen_copy_pct_data(&(_pr->performance->control_register),
+ &dst_perf->control_register);
+ xen_copy_pct_data(&(_pr->performance->status_register),
+ &dst_perf->status_register);
+ dst_perf->flags |= XEN_PX_PCT;
+ dst_states = xen_copy_pss_data(_pr, dst_perf);
+ if (!IS_ERR_OR_NULL(dst_states)) {
+ set_xen_guest_handle(dst_perf->states, dst_states);
+ dst_perf->flags |= XEN_PX_PSS;
+ }
+ if (!xen_copy_psd_data(_pr, dst_perf))
+ dst_perf->flags |= XEN_PX_PSD;
+
+ if (dst_perf->flags != (XEN_PX_PSD | XEN_PX_PSS | XEN_PX_PCT | XEN_PX_PPC)) {
+ pr_warn(DRV_NAME "ACPI CPU%u missing some P-state data (%x), skipping.\n",
+ _pr->acpi_id, dst_perf->flags);
+ ret = -ENODEV;
+ goto err_free;
+ }
+
+ if (!no_hypercall)
+ ret = HYPERVISOR_dom0_op(&op);
+
+ if (!ret) {
+ struct acpi_processor_performance *perf;
+ unsigned int i;
+
+ perf = _pr->performance;
+ pr_debug("ACPI CPU%u - P-states uploaded.\n", _pr->acpi_id);
+ for (i = 0; i < perf->state_count; i++) {
+ pr_debug(" %cP%d: %d MHz, %d mW, %d uS\n",
+ (i == perf->state ? '*' : ' '), i,
+ (u32) perf->states[i].core_frequency,
+ (u32) perf->states[i].power,
+ (u32) perf->states[i].transition_latency);
+ }
+ } else if (ret != -EINVAL)
+ /* EINVAL means the ACPI ID is incorrect - meaning the ACPI
+ * table is referencing a non-existing CPU - which can happen
+ * with broken ACPI tables. */
+ pr_warn(DRV_NAME "(_PXX): Hypervisor error (%d) for ACPI CPU%u\n",
+ ret, _pr->acpi_id);
+err_free:
+ if (!IS_ERR_OR_NULL(dst_states))
+ kfree(dst_states);
+
+ return ret;
+}
+static int upload_pm_data(struct acpi_processor *_pr)
+{
+ int err = 0;
+
+ mutex_lock(&acpi_ids_mutex);
+ if (__test_and_set_bit(_pr->acpi_id, acpi_ids_done)) {
+ mutex_unlock(&acpi_ids_mutex);
+ return -EBUSY;
+ }
+ if (_pr->flags.power)
+ err = push_cxx_to_hypervisor(_pr);
+
+ if (_pr->performance && _pr->performance->states)
+ err |= push_pxx_to_hypervisor(_pr);
+
+ mutex_unlock(&acpi_ids_mutex);
+ return err;
+}
+static unsigned int __init get_max_acpi_id(void)
+{
+ struct xenpf_pcpuinfo *info;
+ struct xen_platform_op op = {
+ .cmd = XENPF_get_cpuinfo,
+ .interface_version = XENPF_INTERFACE_VERSION,
+ };
+ int ret = 0;
+ unsigned int i, last_cpu, max_acpi_id = 0;
+
+ info = &op.u.pcpu_info;
+ info->xen_cpuid = 0;
+
+ ret = HYPERVISOR_dom0_op(&op);
+ if (ret)
+ return NR_CPUS;
+
+ /* The max_present is the same irregardless of the xen_cpuid */
+ last_cpu = op.u.pcpu_info.max_present;
+ for (i = 0; i <= last_cpu; i++) {
+ info->xen_cpuid = i;
+ ret = HYPERVISOR_dom0_op(&op);
+ if (ret)
+ continue;
+ max_acpi_id = max(info->acpi_id, max_acpi_id);
+ }
+ max_acpi_id *= 2; /* Slack for CPU hotplug support. */
+ pr_debug(DRV_NAME "Max ACPI ID: %u\n", max_acpi_id);
+ return max_acpi_id;
+}
+/*
+ * The read_acpi_id and check_acpi_ids are there to support the Xen
+ * oddity of virtual CPUs != physical CPUs in the initial domain.
+ * The user can supply 'xen_max_vcpus=X' on the Xen hypervisor line
+ * which will band the amount of CPUs the initial domain can see.
+ * In general that is OK, except it plays havoc with any of the
+ * for_each_[present|online]_cpu macros which are banded to the virtual
+ * CPU amount.
+ */
+static acpi_status __init
+read_acpi_id(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ u32 acpi_id;
+ acpi_status status;
+ acpi_object_type acpi_type;
+ unsigned long long tmp;
+ union acpi_object object = { 0 };
+ struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
+ acpi_io_address pblk = 0;
+
+ status = acpi_get_type(handle, &acpi_type);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ switch (acpi_type) {
+ case ACPI_TYPE_PROCESSOR:
+ status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+ acpi_id = object.processor.proc_id;
+ pblk = object.processor.pblk_address;
+ break;
+ case ACPI_TYPE_DEVICE:
+ status = acpi_evaluate_integer(handle, "_UID", NULL, &tmp);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+ acpi_id = tmp;
+ break;
+ default:
+ return AE_OK;
+ }
+ /* There are more ACPI Processor objects than in x2APIC or MADT.
+ * This can happen with incorrect ACPI SSDT declerations. */
+ if (acpi_id > nr_acpi_bits) {
+ pr_debug(DRV_NAME "We only have %u, trying to set %u\n",
+ nr_acpi_bits, acpi_id);
+ return AE_OK;
+ }
+ /* OK, There is a ACPI Processor object */
+ __set_bit(acpi_id, acpi_id_present);
+
+ pr_debug(DRV_NAME "ACPI CPU%u w/ PBLK:0x%lx\n", acpi_id,
+ (unsigned long)pblk);
+
+ status = acpi_evaluate_object(handle, "_CST", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ if (!pblk)
+ return AE_OK;
+ }
+ /* .. and it has a C-state */
+ __set_bit(acpi_id, acpi_id_cst_present);
+
+ return AE_OK;
+}
+static int __init check_acpi_ids(struct acpi_processor *pr_backup)
+{
+
+ if (!pr_backup)
+ return -ENODEV;
+
+ /* All online CPUs have been processed at this stage. Now verify
+ * whether in fact "online CPUs" == physical CPUs.
+ */
+ acpi_id_present = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
+ if (!acpi_id_present)
+ return -ENOMEM;
+
+ acpi_id_cst_present = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
+ if (!acpi_id_cst_present) {
+ kfree(acpi_id_present);
+ return -ENOMEM;
+ }
+
+ acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+ ACPI_UINT32_MAX,
+ read_acpi_id, NULL, NULL, NULL);
+ acpi_get_devices("ACPI0007", read_acpi_id, NULL, NULL);
+
+ if (!bitmap_equal(acpi_id_present, acpi_ids_done, nr_acpi_bits)) {
+ unsigned int i;
+ for_each_set_bit(i, acpi_id_present, nr_acpi_bits) {
+ pr_backup->acpi_id = i;
+ /* Mask out C-states if there are no _CST or PBLK */
+ pr_backup->flags.power = test_bit(i, acpi_id_cst_present);
+ (void)upload_pm_data(pr_backup);
+ }
+ }
+ kfree(acpi_id_present);
+ acpi_id_present = NULL;
+ kfree(acpi_id_cst_present);
+ acpi_id_cst_present = NULL;
+ return 0;
+}
+static int __init check_prereq(void)
+{
+ struct cpuinfo_x86 *c = &cpu_data(0);
+
+ if (!xen_initial_domain())
+ return -ENODEV;
+
+ if (!acpi_gbl_FADT.smi_command)
+ return -ENODEV;
+
+ if (c->x86_vendor == X86_VENDOR_INTEL) {
+ if (!cpu_has(c, X86_FEATURE_EST))
+ return -ENODEV;
+
+ return 0;
+ }
+ if (c->x86_vendor == X86_VENDOR_AMD) {
+ /* Copied from powernow-k8.h, can't include ../cpufreq/powernow
+ * as we get compile warnings for the static functions.
+ */
+#define CPUID_FREQ_VOLT_CAPABILITIES 0x80000007
+#define USE_HW_PSTATE 0x00000080
+ u32 eax, ebx, ecx, edx;
+ cpuid(CPUID_FREQ_VOLT_CAPABILITIES, &eax, &ebx, &ecx, &edx);
+ if ((edx & USE_HW_PSTATE) != USE_HW_PSTATE)
+ return -ENODEV;
+ return 0;
+ }
+ return -ENODEV;
+}
+/* acpi_perf_data is a pointer to percpu data. */
+static struct acpi_processor_performance __percpu *acpi_perf_data;
+
+static void free_acpi_perf_data(void)
+{
+ unsigned int i;
+
+ /* Freeing a NULL pointer is OK, and alloc_percpu zeroes. */
+ for_each_possible_cpu(i)
+ free_cpumask_var(per_cpu_ptr(acpi_perf_data, i)
+ ->shared_cpu_map);
+ free_percpu(acpi_perf_data);
+}
+
+static int __init xen_acpi_processor_init(void)
+{
+ struct acpi_processor *pr_backup = NULL;
+ unsigned int i;
+ int rc = check_prereq();
+
+ if (rc)
+ return rc;
+
+ nr_acpi_bits = get_max_acpi_id() + 1;
+ acpi_ids_done = kcalloc(BITS_TO_LONGS(nr_acpi_bits), sizeof(unsigned long), GFP_KERNEL);
+ if (!acpi_ids_done)
+ return -ENOMEM;
+
+ acpi_perf_data = alloc_percpu(struct acpi_processor_performance);
+ if (!acpi_perf_data) {
+ pr_debug(DRV_NAME "Memory allocation error for acpi_perf_data.\n");
+ kfree(acpi_ids_done);
+ return -ENOMEM;
+ }
+ for_each_possible_cpu(i) {
+ if (!zalloc_cpumask_var_node(
+ &per_cpu_ptr(acpi_perf_data, i)->shared_cpu_map,
+ GFP_KERNEL, cpu_to_node(i))) {
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ }
+
+ /* Do initialization in ACPI core. It is OK to fail here. */
+ (void)acpi_processor_preregister_performance(acpi_perf_data);
+
+ for_each_possible_cpu(i) {
+ struct acpi_processor_performance *perf;
+
+ perf = per_cpu_ptr(acpi_perf_data, i);
+ rc = acpi_processor_register_performance(perf, i);
+ if (rc)
+ goto err_out;
+ }
+ rc = acpi_processor_notify_smm(THIS_MODULE);
+ if (rc)
+ goto err_unregister;
+
+ for_each_possible_cpu(i) {
+ struct acpi_processor *_pr;
+ _pr = per_cpu(processors, i /* APIC ID */);
+ if (!_pr)
+ continue;
+
+ if (!pr_backup) {
+ pr_backup = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
+ memcpy(pr_backup, _pr, sizeof(struct acpi_processor));
+ }
+ (void)upload_pm_data(_pr);
+ }
+ rc = check_acpi_ids(pr_backup);
+ if (rc)
+ goto err_unregister;
+
+ kfree(pr_backup);
+
+ return 0;
+err_unregister:
+ for_each_possible_cpu(i) {
+ struct acpi_processor_performance *perf;
+ perf = per_cpu_ptr(acpi_perf_data, i);
+ acpi_processor_unregister_performance(perf, i);
+ }
+err_out:
+ /* Freeing a NULL pointer is OK: alloc_percpu zeroes. */
+ free_acpi_perf_data();
+ kfree(acpi_ids_done);
+ return rc;
+}
+static void __exit xen_acpi_processor_exit(void)
+{
+ int i;
+
+ kfree(acpi_ids_done);
+ for_each_possible_cpu(i) {
+ struct acpi_processor_performance *perf;
+ perf = per_cpu_ptr(acpi_perf_data, i);
+ acpi_processor_unregister_performance(perf, i);
+ }
+ free_acpi_perf_data();
+}
+
+MODULE_AUTHOR("Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>");
+MODULE_DESCRIPTION("Xen ACPI Processor P-states (and Cx) driver which uploads PM data to Xen hypervisor");
+MODULE_LICENSE("GPL");
+
+/* We want to be loaded before the CPU freq scaling drivers are loaded.
+ * They are loaded in late_initcall. */
+device_initcall(xen_acpi_processor_init);
+module_exit(xen_acpi_processor_exit);
diff --git a/drivers/xen/xen-balloon.c b/drivers/xen/xen-balloon.c
index 596e6a7b17d6..8f37e23f6d13 100644
--- a/drivers/xen/xen-balloon.c
+++ b/drivers/xen/xen-balloon.c
@@ -207,7 +207,7 @@ static struct attribute *balloon_info_attrs[] = {
NULL
};
-static struct attribute_group balloon_info_group = {
+static const struct attribute_group balloon_info_group = {
.name = "info",
.attrs = balloon_info_attrs
};
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 19834d1c7c36..097e536e8672 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -85,19 +85,34 @@ static struct pcistub_device *pcistub_device_alloc(struct pci_dev *dev)
static void pcistub_device_release(struct kref *kref)
{
struct pcistub_device *psdev;
+ struct xen_pcibk_dev_data *dev_data;
psdev = container_of(kref, struct pcistub_device, kref);
+ dev_data = pci_get_drvdata(psdev->dev);
dev_dbg(&psdev->dev->dev, "pcistub_device_release\n");
xen_unregister_device_domain_owner(psdev->dev);
- /* Clean-up the device */
+ /* Call the reset function which does not take lock as this
+ * is called from "unbind" which takes a device_lock mutex.
+ */
+ __pci_reset_function_locked(psdev->dev);
+ if (pci_load_and_free_saved_state(psdev->dev,
+ &dev_data->pci_saved_state)) {
+ dev_dbg(&psdev->dev->dev, "Could not reload PCI state\n");
+ } else
+ pci_restore_state(psdev->dev);
+
+ /* Disable the device */
xen_pcibk_reset_device(psdev->dev);
+
+ kfree(dev_data);
+ pci_set_drvdata(psdev->dev, NULL);
+
+ /* Clean-up the device */
xen_pcibk_config_free_dyn_fields(psdev->dev);
xen_pcibk_config_free_dev(psdev->dev);
- kfree(pci_get_drvdata(psdev->dev));
- pci_set_drvdata(psdev->dev, NULL);
psdev->dev->dev_flags &= ~PCI_DEV_FLAGS_ASSIGNED;
pci_dev_put(psdev->dev);
@@ -231,7 +246,17 @@ void pcistub_put_pci_dev(struct pci_dev *dev)
/* Cleanup our device
* (so it's ready for the next domain)
*/
+
+ /* This is OK - we are running from workqueue context
+ * and want to inhibit the user from fiddling with 'reset'
+ */
+ pci_reset_function(dev);
+ pci_restore_state(psdev->dev);
+
+ /* This disables the device. */
xen_pcibk_reset_device(found_psdev->dev);
+
+ /* And cleanup up our emulated fields. */
xen_pcibk_config_free_dyn_fields(found_psdev->dev);
xen_pcibk_config_reset_dev(found_psdev->dev);
@@ -328,6 +353,16 @@ static int __devinit pcistub_init_device(struct pci_dev *dev)
if (err)
goto config_release;
+ dev_dbg(&dev->dev, "reseting (FLR, D3, etc) the device\n");
+ __pci_reset_function_locked(dev);
+
+ /* We need the device active to save the state. */
+ dev_dbg(&dev->dev, "save state of device\n");
+ pci_save_state(dev);
+ dev_data->pci_saved_state = pci_store_saved_state(dev);
+ if (!dev_data->pci_saved_state)
+ dev_err(&dev->dev, "Could not store PCI conf saved state!\n");
+
/* Now disable the device (this also ensures some private device
* data is setup before we export)
*/
diff --git a/drivers/xen/xen-pciback/pciback.h b/drivers/xen/xen-pciback/pciback.h
index e9b4011c5f9a..a7def010eba3 100644
--- a/drivers/xen/xen-pciback/pciback.h
+++ b/drivers/xen/xen-pciback/pciback.h
@@ -41,6 +41,7 @@ struct xen_pcibk_device {
struct xen_pcibk_dev_data {
struct list_head config_fields;
+ struct pci_saved_state *pci_saved_state;
unsigned int permissive:1;
unsigned int warned_on_write:1;
unsigned int enable_intx:1;
diff --git a/drivers/xen/xen-selfballoon.c b/drivers/xen/xen-selfballoon.c
index 767ff656d5a7..146c94897016 100644
--- a/drivers/xen/xen-selfballoon.c
+++ b/drivers/xen/xen-selfballoon.c
@@ -488,7 +488,7 @@ static struct attribute *selfballoon_attrs[] = {
NULL
};
-static struct attribute_group selfballoon_group = {
+static const struct attribute_group selfballoon_group = {
.name = "selfballoon",
.attrs = selfballoon_attrs
};
diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c
index 566d2adbd6ea..b3e146edb51d 100644
--- a/drivers/xen/xenbus/xenbus_client.c
+++ b/drivers/xen/xenbus/xenbus_client.c
@@ -569,7 +569,7 @@ int xenbus_map_ring(struct xenbus_device *dev, int gnt_ref,
{
struct gnttab_map_grant_ref op;
- gnttab_set_map_op(&op, (phys_addr_t)vaddr, GNTMAP_host_map, gnt_ref,
+ gnttab_set_map_op(&op, (unsigned long)vaddr, GNTMAP_host_map, gnt_ref,
dev->otherend_id);
if (HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1))
@@ -662,7 +662,7 @@ static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr)
goto found;
}
}
- node = NULL;
+ node = addr = NULL;
found:
spin_unlock(&xenbus_valloc_lock);
@@ -698,7 +698,7 @@ int xenbus_unmap_ring(struct xenbus_device *dev,
{
struct gnttab_unmap_grant_ref op;
- gnttab_set_unmap_op(&op, (phys_addr_t)vaddr, GNTMAP_host_map, handle);
+ gnttab_set_unmap_op(&op, (unsigned long)vaddr, GNTMAP_host_map, handle);
if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))
BUG();
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index 3864967202b5..b793723e724d 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -257,11 +257,12 @@ int xenbus_dev_remove(struct device *_dev)
DPRINTK("%s", dev->nodename);
free_otherend_watch(dev);
- free_otherend_details(dev);
if (drv->remove)
drv->remove(dev);
+ free_otherend_details(dev);
+
xenbus_switch_state(dev, XenbusStateClosed);
return 0;
}
diff --git a/drivers/xen/xenbus/xenbus_probe_frontend.c b/drivers/xen/xenbus/xenbus_probe_frontend.c
index 9c57819df51a..f20c5f178b40 100644
--- a/drivers/xen/xenbus/xenbus_probe_frontend.c
+++ b/drivers/xen/xenbus/xenbus_probe_frontend.c
@@ -53,6 +53,12 @@ static int xenbus_probe_frontend(struct xen_bus_type *bus, const char *type,
char *nodename;
int err;
+ /* ignore console/0 */
+ if (!strncmp(type, "console", 7) && !strncmp(name, "0", 1)) {
+ DPRINTK("Ignoring buggy device entry console/0");
+ return 0;
+ }
+
nodename = kasprintf(GFP_KERNEL, "%s/%s/%s", bus->root, type, name);
if (!nodename)
return -ENOMEM;